From 59be3e856f218f55be0f73bcfed6265772437ec7 Mon Sep 17 00:00:00 2001 From: Chase Wilson Date: Fri, 17 Jun 2022 11:39:59 -0500 Subject: [PATCH 001/309] Stabilized Option::unzip() --- library/core/src/option.rs | 11 +++++++---- library/core/tests/lib.rs | 1 - 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 28ea45ed235d..1d6784af2a5c 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -1711,8 +1711,6 @@ impl Option<(T, U)> { /// # Examples /// /// ``` - /// #![feature(unzip_option)] - /// /// let x = Some((1, "hi")); /// let y = None::<(u8, u32)>; /// @@ -1720,8 +1718,13 @@ impl Option<(T, U)> { /// assert_eq!(y.unzip(), (None, None)); /// ``` #[inline] - #[unstable(feature = "unzip_option", issue = "87800", reason = "recently added")] - pub const fn unzip(self) -> (Option, Option) { + #[stable(feature = "unzip_option", since = "1.63.0")] + #[rustc_const_unstable(feature = "const_option", issue = "67441")] + pub const fn unzip(self) -> (Option, Option) + where + T: ~const Destruct, + U: ~const Destruct, + { match self { Some((a, b)) => (Some(a), Some(b)), None => (None, None), diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 63c9602abe75..30b9cb4dadc8 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -91,7 +91,6 @@ #![feature(strict_provenance)] #![feature(trusted_random_access)] #![feature(unsize)] -#![feature(unzip_option)] #![feature(const_array_from_ref)] #![feature(const_slice_from_ref)] #![feature(waker_getters)] From c03d10c3264b5518de8d2296e403aa50447622f0 Mon Sep 17 00:00:00 2001 From: Frank Steffahn Date: Fri, 22 Jul 2022 01:35:39 +0200 Subject: [PATCH 002/309] Remove redundant lifetime bound from `impl Borrow for Cow` The lifetime bound `B::Owned: 'a` is redundant and doesn't make a difference, because `Cow<'a, B>` comes with an implicit `B: 'a`, and associated types will outlive lifetimes outlived by the `Self` type (and all the trait's generic parameters, of which there are none in this case), so the implicit `B: 'a` implies `B::Owned: 'a` anyway. The explicit lifetime bound here does however end up in documentation, and that's confusing in my opinion, so let's remove it ^^ --- library/alloc/src/borrow.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/library/alloc/src/borrow.rs b/library/alloc/src/borrow.rs index 904a53bb4acc..83a1385599be 100644 --- a/library/alloc/src/borrow.rs +++ b/library/alloc/src/borrow.rs @@ -21,7 +21,6 @@ use Cow::*; impl<'a, B: ?Sized> Borrow for Cow<'a, B> where B: ToOwned, - ::Owned: 'a, { fn borrow(&self) -> &B { &**self From 289ad1ac3882135fd3bc1387536978248dead9da Mon Sep 17 00:00:00 2001 From: zohnannor Date: Fri, 12 Aug 2022 22:43:52 +0300 Subject: [PATCH 003/309] Clarify `array:from_fn` documentation --- library/core/src/array/mod.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index c9823a136bc4..4d1ba6b1650c 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -31,6 +31,10 @@ pub use iter::IntoIter; /// # Example /// /// ```rust +/// // type inference is helping us here, the way `from_fn` knows how many +/// // elements to produce is the length of array down there: only arrays of +/// // equal lengths can be compared, so the const generic parameter `N` is +/// // inferred to be 5, thus creating array of 5 elements. /// let array = core::array::from_fn(|i| i); /// assert_eq!(array, [0, 1, 2, 3, 4]); /// ``` From 5b32aa074762faa5d7132aa9d2943cf35a05d833 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Wed, 11 May 2022 17:52:00 +0400 Subject: [PATCH 004/309] Add pointer masking convenience functions This commit adds the following functions all of which have a signature `pointer, usize -> pointer`: - `<*mut T>::mask` - `<*const T>::mask` - `intrinsics::ptr_mask` These functions are equivalent to `.map_addr(|a| a & mask)` but they utilize `llvm.ptrmask` llvm intrinsic. *masks your pointers* --- src/intrinsics/mod.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/intrinsics/mod.rs b/src/intrinsics/mod.rs index b2a83e1d4ebc..65e964c786b5 100644 --- a/src/intrinsics/mod.rs +++ b/src/intrinsics/mod.rs @@ -540,6 +540,13 @@ fn codegen_regular_intrinsic_call<'tcx>( ret.write_cvalue(fx, CValue::by_val(res, base.layout())); } + sym::ptr_mask => { + intrinsic_args!(fx, args => (ptr, mask); intrinsic); + let ptr_val = ptr.load_scalar(fx); + + fx.bcx.ins().band(ptr_val, mask); + } + sym::transmute => { intrinsic_args!(fx, args => (from); intrinsic); From 63a137605f837eebe4af0074fc23c9684b12e97e Mon Sep 17 00:00:00 2001 From: Waffle Maybe Date: Wed, 11 May 2022 21:43:13 +0400 Subject: [PATCH 005/309] use shorter `ptr_mask` impl in cg cranelift Co-authored-by: bjorn3 --- src/intrinsics/mod.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/intrinsics/mod.rs b/src/intrinsics/mod.rs index 65e964c786b5..35880bb3e22b 100644 --- a/src/intrinsics/mod.rs +++ b/src/intrinsics/mod.rs @@ -542,9 +542,7 @@ fn codegen_regular_intrinsic_call<'tcx>( sym::ptr_mask => { intrinsic_args!(fx, args => (ptr, mask); intrinsic); - let ptr_val = ptr.load_scalar(fx); - - fx.bcx.ins().band(ptr_val, mask); + fx.bcx.ins().band(ptr, mask); } sym::transmute => { From 5f357c2c51e5663f5c75b389c3fb3501753ac3be Mon Sep 17 00:00:00 2001 From: Waffle Maybe Date: Sun, 7 Aug 2022 22:32:26 +0400 Subject: [PATCH 006/309] fix cg cranelift Co-authored-by: bjorn3 <17426603+bjorn3@users.noreply.github.com> --- src/intrinsics/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/intrinsics/mod.rs b/src/intrinsics/mod.rs index 35880bb3e22b..9fe43157d508 100644 --- a/src/intrinsics/mod.rs +++ b/src/intrinsics/mod.rs @@ -542,6 +542,8 @@ fn codegen_regular_intrinsic_call<'tcx>( sym::ptr_mask => { intrinsic_args!(fx, args => (ptr, mask); intrinsic); + let ptr = ptr.load_scalar(fx); + let mask = mask.load_scalar(fx); fx.bcx.ins().band(ptr, mask); } From 155b4c28c11b56bfe94acab0006530694acfcee4 Mon Sep 17 00:00:00 2001 From: Konrad Borowski Date: Sun, 21 Aug 2022 15:05:58 +0000 Subject: [PATCH 007/309] Use ptr::metadata in <[T]>::len implementation This avoids duplication of ptr::metadata code. --- library/core/src/ptr/metadata.rs | 14 +++++++------- library/core/src/ptr/mod.rs | 1 - library/core/src/slice/mod.rs | 11 ++--------- 3 files changed, 9 insertions(+), 17 deletions(-) diff --git a/library/core/src/ptr/metadata.rs b/library/core/src/ptr/metadata.rs index 8865c834c88d..caa10f1818b4 100644 --- a/library/core/src/ptr/metadata.rs +++ b/library/core/src/ptr/metadata.rs @@ -135,16 +135,16 @@ pub const fn from_raw_parts_mut( } #[repr(C)] -pub(crate) union PtrRepr { - pub(crate) const_ptr: *const T, - pub(crate) mut_ptr: *mut T, - pub(crate) components: PtrComponents, +union PtrRepr { + const_ptr: *const T, + mut_ptr: *mut T, + components: PtrComponents, } #[repr(C)] -pub(crate) struct PtrComponents { - pub(crate) data_address: *const (), - pub(crate) metadata: ::Metadata, +struct PtrComponents { + data_address: *const (), + metadata: ::Metadata, } // Manual impl needed to avoid `T: Copy` bound. diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 203531f66aa3..4869b6ee23dd 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -390,7 +390,6 @@ pub use crate::intrinsics::copy; pub use crate::intrinsics::write_bytes; mod metadata; -pub(crate) use metadata::PtrRepr; #[unstable(feature = "ptr_metadata", issue = "81513")] pub use metadata::{from_raw_parts, from_raw_parts_mut, metadata, DynMetadata, Pointee, Thin}; diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index e79d47c9f98c..be24f2a1b812 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -123,18 +123,11 @@ impl [T] { #[lang = "slice_len_fn"] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_slice_len", since = "1.39.0")] + #[rustc_allow_const_fn_unstable(ptr_metadata)] #[inline] #[must_use] - // SAFETY: const sound because we transmute out the length field as a usize (which it must be) pub const fn len(&self) -> usize { - // FIXME: Replace with `crate::ptr::metadata(self)` when that is const-stable. - // As of this writing this causes a "Const-stable functions can only call other - // const-stable functions" error. - - // SAFETY: Accessing the value from the `PtrRepr` union is safe since *const T - // and PtrComponents have the same memory layouts. Only std can make this - // guarantee. - unsafe { crate::ptr::PtrRepr { const_ptr: self }.components.metadata } + ptr::metadata(self) } /// Returns `true` if the slice has a length of 0. From cfef0a4f8df6085a81f130674e9c2f67b042fd02 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 24 Aug 2022 18:40:58 +0200 Subject: [PATCH 008/309] Merge commit 'e9d1a0a7b0b28dd422f1a790ccde532acafbf193' into sync_cg_clif-2022-08-24 --- .cirrus.yml | 2 +- .github/workflows/main.yml | 10 +- .gitignore | 3 + Cargo.lock | 149 +++-- Cargo.toml | 14 +- Readme.md | 4 +- build_sysroot/Cargo.lock | 20 +- build_system/abi_checker.rs | 60 ++ build_system/build_backend.rs | 4 +- build_system/build_sysroot.rs | 10 +- build_system/mod.rs | 58 +- build_system/prepare.rs | 11 +- build_system/rustc_info.rs | 9 + build_system/tests.rs | 618 ++++++++++++++++++ build_system/utils.rs | 29 +- clean_all.sh | 2 +- config.txt | 35 + example/mini_core.rs | 12 +- example/mini_core_hello_world.rs | 117 +++- ...01-abi-checker-Disable-failing-tests.patch | 36 + .../0023-sysroot-Ignore-failing-tests.patch | 12 + rust-toolchain | 2 +- scripts/tests.sh | 203 ------ src/abi/pass_mode.rs | 6 +- src/base.rs | 227 +++---- src/common.rs | 46 +- src/concurrency_limiter.rs | 168 +++++ src/debuginfo/emit.rs | 2 +- src/debuginfo/line_info.rs | 211 +++--- src/debuginfo/mod.rs | 284 ++------ src/discriminant.rs | 20 +- src/driver/aot.rs | 558 +++++++++------- src/driver/jit.rs | 49 +- src/global_asm.rs | 114 ++++ src/inline_asm.rs | 176 +++-- src/intrinsics/cpuid.rs | 2 +- src/intrinsics/llvm.rs | 1 + src/intrinsics/mod.rs | 47 +- src/intrinsics/simd.rs | 33 +- src/lib.rs | 55 +- src/optimize/mod.rs | 17 - src/pretty_clif.rs | 61 +- src/toolchain.rs | 6 +- src/trap.rs | 25 +- src/value_and_place.rs | 10 +- test.sh | 13 +- 46 files changed, 2292 insertions(+), 1259 deletions(-) create mode 100644 build_system/abi_checker.rs create mode 100644 build_system/tests.rs create mode 100644 patches/0001-abi-checker-Disable-failing-tests.patch delete mode 100755 scripts/tests.sh create mode 100644 src/concurrency_limiter.rs create mode 100644 src/global_asm.rs diff --git a/.cirrus.yml b/.cirrus.yml index 61da6a2491c5..732edd66196d 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -22,4 +22,4 @@ task: - # Reduce amount of benchmark runs as they are slow - export COMPILE_RUNS=2 - export RUN_RUNS=2 - - ./test.sh + - ./y.rs test diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index aa556a21bf8c..e8897e9ae814 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -103,7 +103,7 @@ jobs: # Enable extra checks export CG_CLIF_ENABLE_VERIFIER=1 - ./test.sh + ./y.rs test - name: Package prebuilt cg_clif run: tar cvfJ cg_clif.tar.xz build @@ -162,14 +162,14 @@ jobs: #name: Test run: | # Enable backtraces for easier debugging - #export RUST_BACKTRACE=1 + #$Env:RUST_BACKTRACE=1 # Reduce amount of benchmark runs as they are slow - #export COMPILE_RUNS=2 - #export RUN_RUNS=2 + #$Env:COMPILE_RUNS=2 + #$Env:RUN_RUNS=2 # Enable extra checks - #export CG_CLIF_ENABLE_VERIFIER=1 + #$Env:CG_CLIF_ENABLE_VERIFIER=1 ./y.exe build diff --git a/.gitignore b/.gitignore index 5aeaf3a17880..6fd3e4443de5 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,8 @@ perf.data.old *.string* /y.bin /y.bin.dSYM +/y.exe +/y.pdb /build /build_sysroot/sysroot_src /build_sysroot/compiler-builtins @@ -17,3 +19,4 @@ perf.data.old /regex /simple-raytracer /portable-simd +/abi-checker diff --git a/Cargo.lock b/Cargo.lock index 402fbb16f97e..edae7e471578 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -50,18 +50,18 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cranelift-bforest" -version = "0.85.3" +version = "0.87.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "749d0d6022c9038dccf480bdde2a38d435937335bf2bb0f14e815d94517cdce8" +checksum = "93945adbccc8d731503d3038814a51e8317497c9e205411820348132fa01a358" dependencies = [ "cranelift-entity", ] [[package]] name = "cranelift-codegen" -version = "0.85.3" +version = "0.87.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94370cc7b37bf652ccd8bb8f09bd900997f7ccf97520edfc75554bb5c4abbea" +checksum = "2b482acc9d0d0d1ad3288a90a8150ee648be3dce8dc8c8669ff026f72debdc31" dependencies = [ "cranelift-bforest", "cranelift-codegen-meta", @@ -77,30 +77,30 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" -version = "0.85.3" +version = "0.87.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a3cea8fdab90e44018c5b9a1dfd460d8ee265ac354337150222a354628bdb6" +checksum = "f9ec188d71e663192ef9048f204e410a7283b609942efc9fcc77da6d496edbb8" dependencies = [ "cranelift-codegen-shared", ] [[package]] name = "cranelift-codegen-shared" -version = "0.85.3" +version = "0.87.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ac72f76f2698598951ab26d8c96eaa854810e693e7dd52523958b5909fde6b2" +checksum = "3ad794b1b1c2c7bd9f7b76cfe0f084eaf7753e55d56191c3f7d89e8fa4978b99" [[package]] name = "cranelift-entity" -version = "0.85.3" +version = "0.87.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09eaeacfcd2356fe0e66b295e8f9d59fdd1ac3ace53ba50de14d628ec902f72d" +checksum = "342da0d5056f4119d3c311c4aab2460ceb6ee6e127bb395b76dd2279a09ea7a5" [[package]] name = "cranelift-frontend" -version = "0.85.3" +version = "0.87.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dba69c9980d5ffd62c18a2bde927855fcd7c8dc92f29feaf8636052662cbd99c" +checksum = "dfff792f775b07d4d9cfe9f1c767ce755c6cbadda1bbd6db18a1c75ff9f7376a" dependencies = [ "cranelift-codegen", "log", @@ -110,15 +110,15 @@ dependencies = [ [[package]] name = "cranelift-isle" -version = "0.85.3" +version = "0.87.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2920dc1e05cac40304456ed3301fde2c09bd6a9b0210bcfa2f101398d628d5b" +checksum = "8d51089478849f2ac8ef60a8a2d5346c8d4abfec0e45ac5b24530ef9f9499e1e" [[package]] name = "cranelift-jit" -version = "0.85.3" +version = "0.87.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c3c5ed067f2c81577e431f3039148a9c187b33cc79e0d1731fede27d801ec56" +checksum = "095936e41720f86004b4c57ce88e6a13af28646bb3a6fb4afbebd5ae90c50029" dependencies = [ "anyhow", "cranelift-codegen", @@ -129,14 +129,14 @@ dependencies = [ "log", "region", "target-lexicon", - "winapi", + "windows-sys", ] [[package]] name = "cranelift-module" -version = "0.85.3" +version = "0.87.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eee6784303bf9af235237a4885f7417e09a35df896d38ea969a0081064b3ede4" +checksum = "704a1aea4723d97eafe0fb7af110f6f6868b1ac95f5380bbc9adb2a3b8cf97e8" dependencies = [ "anyhow", "cranelift-codegen", @@ -144,9 +144,9 @@ dependencies = [ [[package]] name = "cranelift-native" -version = "0.85.3" +version = "0.87.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f04dfa45f9b2a6f587c564d6b63388e00cd6589d2df6ea2758cf79e1a13285e6" +checksum = "885debe62f2078638d6585f54c9f05f5c2008f22ce5a2a9100ada785fc065dbd" dependencies = [ "cranelift-codegen", "libc", @@ -155,9 +155,9 @@ dependencies = [ [[package]] name = "cranelift-object" -version = "0.85.3" +version = "0.87.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf38b2c505db749276793116c0cb30bd096206c7810e471677a453134881881" +checksum = "aac1310cf1081ae8eca916c92cd163b977c77cab6e831fa812273c26ff921816" dependencies = [ "anyhow", "cranelift-codegen", @@ -187,9 +187,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad" +checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" dependencies = [ "cfg-if", "libc", @@ -198,27 +198,21 @@ dependencies = [ [[package]] name = "gimli" -version = "0.26.1" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4" +checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" dependencies = [ "indexmap", ] -[[package]] -name = "hashbrown" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" -dependencies = [ - "ahash", -] - [[package]] name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash", +] [[package]] name = "indexmap" @@ -227,14 +221,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" dependencies = [ "autocfg", - "hashbrown 0.12.3", + "hashbrown", ] [[package]] name = "libc" -version = "0.2.126" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" +checksum = "505e71a4706fa491e9b1b55f51b95d4037d0821ee40131190475f692b35b009b" [[package]] name = "libloading" @@ -248,9 +242,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.14" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ "cfg-if", ] @@ -266,33 +260,33 @@ dependencies = [ [[package]] name = "memchr" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "object" -version = "0.28.4" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e42c982f2d955fac81dd7e1d0e1426a7d702acd9c98d19ab01083a6a0328c424" +checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" dependencies = [ "crc32fast", - "hashbrown 0.11.2", + "hashbrown", "indexmap", "memchr", ] [[package]] name = "once_cell" -version = "1.10.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" +checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" [[package]] name = "regalloc2" -version = "0.2.3" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a8d23b35d7177df3b9d31ed8a9ab4bf625c668be77a319d4f5efd4a5257701c" +checksum = "d43a209257d978ef079f3d446331d0f1794f5e0fc19b306a199983857833a779" dependencies = [ "fxhash", "log", @@ -340,15 +334,15 @@ checksum = "03b634d87b960ab1a38c4fe143b508576f075e7c978bfad18217645ebfdfa2ec" [[package]] name = "smallvec" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc88c725d61fc6c3132893370cac4a0200e3fedf5da8331c570664b1987f5ca2" +checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" [[package]] name = "target-lexicon" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7fa7e55043acb85fca6b3c01485a2eeb6b69c5d21002e273c79e465f43b7ac1" +checksum = "c02424087780c9b71cc96799eaeddff35af2bc513278cda5c99fc1f5d026d3c1" [[package]] name = "version_check" @@ -358,9 +352,9 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "wasi" -version = "0.10.2+wasi-snapshot-preview1" +version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "winapi" @@ -383,3 +377,46 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +dependencies = [ + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" + +[[package]] +name = "windows_i686_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" + +[[package]] +name = "windows_i686_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" diff --git a/Cargo.toml b/Cargo.toml index 61e977e3e69b..e7c342748548 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,15 +8,15 @@ crate-type = ["dylib"] [dependencies] # These have to be in sync with each other -cranelift-codegen = { version = "0.85.3", features = ["unwind", "all-arch"] } -cranelift-frontend = "0.85.3" -cranelift-module = "0.85.3" -cranelift-native = "0.85.3" -cranelift-jit = { version = "0.85.3", optional = true } -cranelift-object = "0.85.3" +cranelift-codegen = { version = "0.87.0", features = ["unwind", "all-arch"] } +cranelift-frontend = "0.87.0" +cranelift-module = "0.87.0" +cranelift-native = "0.87.0" +cranelift-jit = { version = "0.87.0", optional = true } +cranelift-object = "0.87.0" target-lexicon = "0.12.0" gimli = { version = "0.26.0", default-features = false, features = ["write"]} -object = { version = "0.28.0", default-features = false, features = ["std", "read_core", "write", "archive", "coff", "elf", "macho", "pe"] } +object = { version = "0.29.0", default-features = false, features = ["std", "read_core", "write", "archive", "coff", "elf", "macho", "pe"] } ar = { git = "https://github.com/bjorn3/rust-ar.git", branch = "do_not_remove_cg_clif_ranlib" } indexmap = "1.9.1" diff --git a/Readme.md b/Readme.md index 8a2db5a43ecb..1e84c7fa3657 100644 --- a/Readme.md +++ b/Readme.md @@ -52,9 +52,7 @@ configuration options. ## Not yet supported * Inline assembly ([no cranelift support](https://github.com/bytecodealliance/wasmtime/issues/1041)) - * On Linux there is support for invoking an external assembler for `global_asm!` and `asm!`. - `llvm_asm!` will remain unimplemented forever. `asm!` doesn't yet support reg classes. You - have to specify specific registers instead. + * On UNIX there is support for invoking an external assembler for `global_asm!` and `asm!`. * SIMD ([tracked here](https://github.com/bjorn3/rustc_codegen_cranelift/issues/171), some basic things work) ## License diff --git a/build_sysroot/Cargo.lock b/build_sysroot/Cargo.lock index 7b2cdd273366..6c5043bb6f8e 100644 --- a/build_sysroot/Cargo.lock +++ b/build_sysroot/Cargo.lock @@ -56,9 +56,9 @@ dependencies = [ [[package]] name = "compiler_builtins" -version = "0.1.75" +version = "0.1.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6e3183e88f659a862835db8f4b67dbeed3d93e44dd4927eef78edb1c149d784" +checksum = "4f873ce2bd3550b0b565f878b3d04ea8253f4259dc3d20223af2e1ba86f5ecca" dependencies = [ "rustc-std-workspace-core", ] @@ -69,9 +69,9 @@ version = "0.0.0" [[package]] name = "dlmalloc" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6fe28e0bf9357092740362502f5cc7955d8dc125ebda71dec72336c2e15c62e" +checksum = "203540e710bfadb90e5e29930baf5d10270cec1f43ab34f46f78b147b2de715a" dependencies = [ "compiler_builtins", "libc", @@ -80,9 +80,9 @@ dependencies = [ [[package]] name = "fortanix-sgx-abi" -version = "0.3.3" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c56c422ef86062869b2d57ae87270608dc5929969dd130a6e248979cf4fb6ca6" +checksum = "57cafc2274c10fab234f176b25903ce17e690fca7597090d50880e047a0389c5" dependencies = [ "compiler_builtins", "rustc-std-workspace-core", @@ -123,9 +123,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7668753748e445859e4e373c3d41117235d9feed578392f5a3a73efdc751ca4a" +checksum = "897cd85af6387be149f55acf168e41be176a02de7872403aaab184afc2f327e6" dependencies = [ "compiler_builtins", "libc", @@ -135,9 +135,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.126" +version = "0.2.132" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" +checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5" dependencies = [ "rustc-std-workspace-core", ] diff --git a/build_system/abi_checker.rs b/build_system/abi_checker.rs new file mode 100644 index 000000000000..67dbd0a38a4f --- /dev/null +++ b/build_system/abi_checker.rs @@ -0,0 +1,60 @@ +use super::build_sysroot; +use super::config; +use super::utils::spawn_and_wait; +use build_system::SysrootKind; +use std::env; +use std::path::Path; +use std::process::Command; + +pub(crate) fn run( + channel: &str, + sysroot_kind: SysrootKind, + target_dir: &Path, + cg_clif_build_dir: &Path, + host_triple: &str, + target_triple: &str, +) { + if !config::get_bool("testsuite.abi-checker") { + eprintln!("[SKIP] abi-checker"); + return; + } + + if host_triple != target_triple { + eprintln!("[SKIP] abi-checker (cross-compilation not supported)"); + return; + } + + eprintln!("Building sysroot for abi-checker"); + build_sysroot::build_sysroot( + channel, + sysroot_kind, + target_dir, + cg_clif_build_dir, + host_triple, + target_triple, + ); + + eprintln!("Running abi-checker"); + let mut abi_checker_path = env::current_dir().unwrap(); + abi_checker_path.push("abi-checker"); + env::set_current_dir(abi_checker_path.clone()).unwrap(); + + let build_dir = abi_checker_path.parent().unwrap().join("build"); + let cg_clif_dylib_path = build_dir.join(if cfg!(windows) { "bin" } else { "lib" }).join( + env::consts::DLL_PREFIX.to_string() + "rustc_codegen_cranelift" + env::consts::DLL_SUFFIX, + ); + + let pairs = ["rustc_calls_cgclif", "cgclif_calls_rustc", "cgclif_calls_cc", "cc_calls_cgclif"]; + + let mut cmd = Command::new("cargo"); + cmd.arg("run"); + cmd.arg("--target"); + cmd.arg(target_triple); + cmd.arg("--"); + cmd.arg("--pairs"); + cmd.args(pairs); + cmd.arg("--add-rustc-codegen-backend"); + cmd.arg(format!("cgclif:{}", cg_clif_dylib_path.display())); + + spawn_and_wait(cmd); +} diff --git a/build_system/build_backend.rs b/build_system/build_backend.rs index 48faec8bc4b9..9e59b8199b41 100644 --- a/build_system/build_backend.rs +++ b/build_system/build_backend.rs @@ -2,6 +2,8 @@ use std::env; use std::path::{Path, PathBuf}; use std::process::Command; +use super::utils::is_ci; + pub(crate) fn build_backend( channel: &str, host_triple: &str, @@ -14,7 +16,7 @@ pub(crate) fn build_backend( let mut rustflags = env::var("RUSTFLAGS").unwrap_or_default(); - if env::var("CI").as_ref().map(|val| &**val) == Ok("true") { + if is_ci() { // Deny warnings on CI rustflags += " -Dwarnings"; diff --git a/build_system/build_sysroot.rs b/build_system/build_sysroot.rs index 16cce83dd9c8..7e205b0fd0b3 100644 --- a/build_system/build_sysroot.rs +++ b/build_system/build_sysroot.rs @@ -2,7 +2,7 @@ use std::fs; use std::path::{Path, PathBuf}; use std::process::{self, Command}; -use super::rustc_info::{get_file_name, get_rustc_version}; +use super::rustc_info::{get_file_name, get_rustc_version, get_wrapper_file_name}; use super::utils::{spawn_and_wait, try_hard_link}; use super::SysrootKind; @@ -10,10 +10,12 @@ pub(crate) fn build_sysroot( channel: &str, sysroot_kind: SysrootKind, target_dir: &Path, - cg_clif_build_dir: PathBuf, + cg_clif_build_dir: &Path, host_triple: &str, target_triple: &str, ) { + eprintln!("[BUILD] sysroot {:?}", sysroot_kind); + if target_dir.exists() { fs::remove_dir_all(target_dir).unwrap(); } @@ -35,11 +37,13 @@ pub(crate) fn build_sysroot( // Build and copy rustc and cargo wrappers for wrapper in ["rustc-clif", "cargo-clif"] { + let wrapper_name = get_wrapper_file_name(wrapper, "bin"); + let mut build_cargo_wrapper_cmd = Command::new("rustc"); build_cargo_wrapper_cmd .arg(PathBuf::from("scripts").join(format!("{wrapper}.rs"))) .arg("-o") - .arg(target_dir.join(wrapper)) + .arg(target_dir.join(wrapper_name)) .arg("-g"); spawn_and_wait(build_cargo_wrapper_cmd); } diff --git a/build_system/mod.rs b/build_system/mod.rs index b897b7fbacfc..c3706dc6f820 100644 --- a/build_system/mod.rs +++ b/build_system/mod.rs @@ -2,11 +2,15 @@ use std::env; use std::path::PathBuf; use std::process; +use self::utils::is_ci; + +mod abi_checker; mod build_backend; mod build_sysroot; mod config; mod prepare; mod rustc_info; +mod tests; mod utils; fn usage() { @@ -15,6 +19,9 @@ fn usage() { eprintln!( " ./y.rs build [--debug] [--sysroot none|clif|llvm] [--target-dir DIR] [--no-unstable-features]" ); + eprintln!( + " ./y.rs test [--debug] [--sysroot none|clif|llvm] [--target-dir DIR] [--no-unstable-features]" + ); } macro_rules! arg_error { @@ -25,11 +32,13 @@ macro_rules! arg_error { }}; } +#[derive(PartialEq, Debug)] enum Command { Build, + Test, } -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug)] pub(crate) enum SysrootKind { None, Clif, @@ -42,16 +51,22 @@ pub fn main() { // The target dir is expected in the default location. Guard against the user changing it. env::set_var("CARGO_TARGET_DIR", "target"); + if is_ci() { + // Disabling incr comp reduces cache size and incr comp doesn't save as much on CI anyway + env::set_var("CARGO_BUILD_INCREMENTAL", "false"); + } + let mut args = env::args().skip(1); let command = match args.next().as_deref() { Some("prepare") => { if args.next().is_some() { - arg_error!("./x.rs prepare doesn't expect arguments"); + arg_error!("./y.rs prepare doesn't expect arguments"); } prepare::prepare(); process::exit(0); } Some("build") => Command::Build, + Some("test") => Command::Test, Some(flag) if flag.starts_with('-') => arg_error!("Expected command found flag {}", flag), Some(command) => arg_error!("Unknown command {}", command), None => { @@ -117,12 +132,35 @@ pub fn main() { let cg_clif_build_dir = build_backend::build_backend(channel, &host_triple, use_unstable_features); - build_sysroot::build_sysroot( - channel, - sysroot_kind, - &target_dir, - cg_clif_build_dir, - &host_triple, - &target_triple, - ); + match command { + Command::Test => { + tests::run_tests( + channel, + sysroot_kind, + &target_dir, + &cg_clif_build_dir, + &host_triple, + &target_triple, + ); + + abi_checker::run( + channel, + sysroot_kind, + &target_dir, + &cg_clif_build_dir, + &host_triple, + &target_triple, + ); + } + Command::Build => { + build_sysroot::build_sysroot( + channel, + sysroot_kind, + &target_dir, + &cg_clif_build_dir, + &host_triple, + &target_triple, + ); + } + } } diff --git a/build_system/prepare.rs b/build_system/prepare.rs index 8bb00352d3fe..d23b7f00dcf1 100644 --- a/build_system/prepare.rs +++ b/build_system/prepare.rs @@ -14,6 +14,14 @@ pub(crate) fn prepare() { eprintln!("[INSTALL] hyperfine"); Command::new("cargo").arg("install").arg("hyperfine").spawn().unwrap().wait().unwrap(); + clone_repo_shallow_github( + "abi-checker", + "Gankra", + "abi-checker", + "a2232d45f202846f5c02203c9f27355360f9a2ff", + ); + apply_patches("abi-checker", Path::new("abi-checker")); + clone_repo_shallow_github( "rand", "rust-random", @@ -50,8 +58,7 @@ pub(crate) fn prepare() { spawn_and_wait(build_cmd); fs::copy( Path::new("simple-raytracer/target/debug").join(get_file_name("main", "bin")), - // FIXME use get_file_name here too once testing is migrated to rust - "simple-raytracer/raytracer_cg_llvm", + Path::new("simple-raytracer").join(get_file_name("raytracer_cg_llvm", "bin")), ) .unwrap(); } diff --git a/build_system/rustc_info.rs b/build_system/rustc_info.rs index 9206bb02bd3d..913b589afcc8 100644 --- a/build_system/rustc_info.rs +++ b/build_system/rustc_info.rs @@ -63,3 +63,12 @@ pub(crate) fn get_file_name(crate_name: &str, crate_type: &str) -> String { assert!(file_name.contains(crate_name)); file_name } + +/// Similar to `get_file_name`, but converts any dashes (`-`) in the `crate_name` to +/// underscores (`_`). This is specially made for the the rustc and cargo wrappers +/// which have a dash in the name, and that is not allowed in a crate name. +pub(crate) fn get_wrapper_file_name(crate_name: &str, crate_type: &str) -> String { + let crate_name = crate_name.replace('-', "_"); + let wrapper_name = get_file_name(&crate_name, crate_type); + wrapper_name.replace('_', "-") +} diff --git a/build_system/tests.rs b/build_system/tests.rs new file mode 100644 index 000000000000..dc83b10958e0 --- /dev/null +++ b/build_system/tests.rs @@ -0,0 +1,618 @@ +use super::build_sysroot; +use super::config; +use super::rustc_info::get_wrapper_file_name; +use super::utils::{spawn_and_wait, spawn_and_wait_with_input}; +use build_system::SysrootKind; +use std::env; +use std::ffi::OsStr; +use std::fs; +use std::path::{Path, PathBuf}; +use std::process::Command; + +struct TestCase { + config: &'static str, + func: &'static dyn Fn(&TestRunner), +} + +impl TestCase { + const fn new(config: &'static str, func: &'static dyn Fn(&TestRunner)) -> Self { + Self { config, func } + } +} + +const NO_SYSROOT_SUITE: &[TestCase] = &[ + TestCase::new("build.mini_core", &|runner| { + runner.run_rustc([ + "example/mini_core.rs", + "--crate-name", + "mini_core", + "--crate-type", + "lib,dylib", + "--target", + &runner.target_triple, + ]); + }), + TestCase::new("build.example", &|runner| { + runner.run_rustc([ + "example/example.rs", + "--crate-type", + "lib", + "--target", + &runner.target_triple, + ]); + }), + TestCase::new("jit.mini_core_hello_world", &|runner| { + let mut jit_cmd = runner.rustc_command([ + "-Zunstable-options", + "-Cllvm-args=mode=jit", + "-Cprefer-dynamic", + "example/mini_core_hello_world.rs", + "--cfg", + "jit", + "--target", + &runner.host_triple, + ]); + jit_cmd.env("CG_CLIF_JIT_ARGS", "abc bcd"); + spawn_and_wait(jit_cmd); + + eprintln!("[JIT-lazy] mini_core_hello_world"); + let mut jit_cmd = runner.rustc_command([ + "-Zunstable-options", + "-Cllvm-args=mode=jit-lazy", + "-Cprefer-dynamic", + "example/mini_core_hello_world.rs", + "--cfg", + "jit", + "--target", + &runner.host_triple, + ]); + jit_cmd.env("CG_CLIF_JIT_ARGS", "abc bcd"); + spawn_and_wait(jit_cmd); + }), + TestCase::new("aot.mini_core_hello_world", &|runner| { + runner.run_rustc([ + "example/mini_core_hello_world.rs", + "--crate-name", + "mini_core_hello_world", + "--crate-type", + "bin", + "-g", + "--target", + &runner.target_triple, + ]); + runner.run_out_command("mini_core_hello_world", ["abc", "bcd"]); + }), +]; + +const BASE_SYSROOT_SUITE: &[TestCase] = &[ + TestCase::new("aot.arbitrary_self_types_pointers_and_wrappers", &|runner| { + runner.run_rustc([ + "example/arbitrary_self_types_pointers_and_wrappers.rs", + "--crate-name", + "arbitrary_self_types_pointers_and_wrappers", + "--crate-type", + "bin", + "--target", + &runner.target_triple, + ]); + runner.run_out_command("arbitrary_self_types_pointers_and_wrappers", []); + }), + TestCase::new("aot.issue_91827_extern_types", &|runner| { + runner.run_rustc([ + "example/issue-91827-extern-types.rs", + "--crate-name", + "issue_91827_extern_types", + "--crate-type", + "bin", + "--target", + &runner.target_triple, + ]); + runner.run_out_command("issue_91827_extern_types", []); + }), + TestCase::new("build.alloc_system", &|runner| { + runner.run_rustc([ + "example/alloc_system.rs", + "--crate-type", + "lib", + "--target", + &runner.target_triple, + ]); + }), + TestCase::new("aot.alloc_example", &|runner| { + runner.run_rustc([ + "example/alloc_example.rs", + "--crate-type", + "bin", + "--target", + &runner.target_triple, + ]); + runner.run_out_command("alloc_example", []); + }), + TestCase::new("jit.std_example", &|runner| { + runner.run_rustc([ + "-Zunstable-options", + "-Cllvm-args=mode=jit", + "-Cprefer-dynamic", + "example/std_example.rs", + "--target", + &runner.host_triple, + ]); + + eprintln!("[JIT-lazy] std_example"); + runner.run_rustc([ + "-Zunstable-options", + "-Cllvm-args=mode=jit-lazy", + "-Cprefer-dynamic", + "example/std_example.rs", + "--target", + &runner.host_triple, + ]); + }), + TestCase::new("aot.std_example", &|runner| { + runner.run_rustc([ + "example/std_example.rs", + "--crate-type", + "bin", + "--target", + &runner.target_triple, + ]); + runner.run_out_command("std_example", ["arg"]); + }), + TestCase::new("aot.dst_field_align", &|runner| { + runner.run_rustc([ + "example/dst-field-align.rs", + "--crate-name", + "dst_field_align", + "--crate-type", + "bin", + "--target", + &runner.target_triple, + ]); + runner.run_out_command("dst_field_align", []); + }), + TestCase::new("aot.subslice-patterns-const-eval", &|runner| { + runner.run_rustc([ + "example/subslice-patterns-const-eval.rs", + "--crate-type", + "bin", + "-Cpanic=abort", + "--target", + &runner.target_triple, + ]); + runner.run_out_command("subslice-patterns-const-eval", []); + }), + TestCase::new("aot.track-caller-attribute", &|runner| { + runner.run_rustc([ + "example/track-caller-attribute.rs", + "--crate-type", + "bin", + "-Cpanic=abort", + "--target", + &runner.target_triple, + ]); + runner.run_out_command("track-caller-attribute", []); + }), + TestCase::new("aot.float-minmax-pass", &|runner| { + runner.run_rustc([ + "example/float-minmax-pass.rs", + "--crate-type", + "bin", + "-Cpanic=abort", + "--target", + &runner.target_triple, + ]); + runner.run_out_command("float-minmax-pass", []); + }), + TestCase::new("aot.mod_bench", &|runner| { + runner.run_rustc([ + "example/mod_bench.rs", + "--crate-type", + "bin", + "--target", + &runner.target_triple, + ]); + runner.run_out_command("mod_bench", []); + }), +]; + +const EXTENDED_SYSROOT_SUITE: &[TestCase] = &[ + TestCase::new("test.rust-random/rand", &|runner| { + runner.in_dir(["rand"], |runner| { + runner.run_cargo(["clean"]); + + if runner.host_triple == runner.target_triple { + eprintln!("[TEST] rust-random/rand"); + runner.run_cargo(["test", "--workspace"]); + } else { + eprintln!("[AOT] rust-random/rand"); + runner.run_cargo([ + "build", + "--workspace", + "--target", + &runner.target_triple, + "--tests", + ]); + } + }); + }), + TestCase::new("bench.simple-raytracer", &|runner| { + runner.in_dir(["simple-raytracer"], |runner| { + let run_runs = env::var("RUN_RUNS").unwrap_or("10".to_string()); + + if runner.host_triple == runner.target_triple { + eprintln!("[BENCH COMPILE] ebobby/simple-raytracer"); + let mut bench_compile = Command::new("hyperfine"); + bench_compile.arg("--runs"); + bench_compile.arg(&run_runs); + bench_compile.arg("--warmup"); + bench_compile.arg("1"); + bench_compile.arg("--prepare"); + bench_compile.arg(format!("{:?}", runner.cargo_command(["clean"]))); + + if cfg!(windows) { + bench_compile.arg("cmd /C \"set RUSTFLAGS= && cargo build\""); + } else { + bench_compile.arg("RUSTFLAGS='' cargo build"); + } + + bench_compile.arg(format!("{:?}", runner.cargo_command(["build"]))); + spawn_and_wait(bench_compile); + + eprintln!("[BENCH RUN] ebobby/simple-raytracer"); + fs::copy(PathBuf::from("./target/debug/main"), PathBuf::from("raytracer_cg_clif")) + .unwrap(); + + let mut bench_run = Command::new("hyperfine"); + bench_run.arg("--runs"); + bench_run.arg(&run_runs); + bench_run.arg(PathBuf::from("./raytracer_cg_llvm")); + bench_run.arg(PathBuf::from("./raytracer_cg_clif")); + spawn_and_wait(bench_run); + } else { + runner.run_cargo(["clean"]); + eprintln!("[BENCH COMPILE] ebobby/simple-raytracer (skipped)"); + eprintln!("[COMPILE] ebobby/simple-raytracer"); + runner.run_cargo(["build", "--target", &runner.target_triple]); + eprintln!("[BENCH RUN] ebobby/simple-raytracer (skipped)"); + } + }); + }), + TestCase::new("test.libcore", &|runner| { + runner.in_dir(["build_sysroot", "sysroot_src", "library", "core", "tests"], |runner| { + runner.run_cargo(["clean"]); + + if runner.host_triple == runner.target_triple { + runner.run_cargo(["test"]); + } else { + eprintln!("Cross-Compiling: Not running tests"); + runner.run_cargo(["build", "--target", &runner.target_triple, "--tests"]); + } + }); + }), + TestCase::new("test.regex-shootout-regex-dna", &|runner| { + runner.in_dir(["regex"], |runner| { + runner.run_cargo(["clean"]); + + // newer aho_corasick versions throw a deprecation warning + let lint_rust_flags = format!("{} --cap-lints warn", runner.rust_flags); + + let mut build_cmd = runner.cargo_command([ + "build", + "--example", + "shootout-regex-dna", + "--target", + &runner.target_triple, + ]); + build_cmd.env("RUSTFLAGS", lint_rust_flags.clone()); + spawn_and_wait(build_cmd); + + if runner.host_triple == runner.target_triple { + let mut run_cmd = runner.cargo_command([ + "run", + "--example", + "shootout-regex-dna", + "--target", + &runner.target_triple, + ]); + run_cmd.env("RUSTFLAGS", lint_rust_flags); + + let input = + fs::read_to_string(PathBuf::from("examples/regexdna-input.txt")).unwrap(); + let expected_path = PathBuf::from("examples/regexdna-output.txt"); + let expected = fs::read_to_string(&expected_path).unwrap(); + + let output = spawn_and_wait_with_input(run_cmd, input); + // Make sure `[codegen mono items] start` doesn't poison the diff + let output = output + .lines() + .filter(|line| !line.contains("codegen mono items")) + .chain(Some("")) // This just adds the trailing newline + .collect::>() + .join("\r\n"); + + let output_matches = expected.lines().eq(output.lines()); + if !output_matches { + let res_path = PathBuf::from("res.txt"); + fs::write(&res_path, &output).unwrap(); + + if cfg!(windows) { + println!("Output files don't match!"); + println!("Expected Output:\n{}", expected); + println!("Actual Output:\n{}", output); + } else { + let mut diff = Command::new("diff"); + diff.arg("-u"); + diff.arg(res_path); + diff.arg(expected_path); + spawn_and_wait(diff); + } + + std::process::exit(1); + } + } + }); + }), + TestCase::new("test.regex", &|runner| { + runner.in_dir(["regex"], |runner| { + runner.run_cargo(["clean"]); + + // newer aho_corasick versions throw a deprecation warning + let lint_rust_flags = format!("{} --cap-lints warn", runner.rust_flags); + + if runner.host_triple == runner.target_triple { + let mut run_cmd = runner.cargo_command([ + "test", + "--tests", + "--", + "--exclude-should-panic", + "--test-threads", + "1", + "-Zunstable-options", + "-q", + ]); + run_cmd.env("RUSTFLAGS", lint_rust_flags); + spawn_and_wait(run_cmd); + } else { + eprintln!("Cross-Compiling: Not running tests"); + let mut build_cmd = + runner.cargo_command(["build", "--tests", "--target", &runner.target_triple]); + build_cmd.env("RUSTFLAGS", lint_rust_flags.clone()); + spawn_and_wait(build_cmd); + } + }); + }), + TestCase::new("test.portable-simd", &|runner| { + runner.in_dir(["portable-simd"], |runner| { + runner.run_cargo(["clean"]); + runner.run_cargo(["build", "--all-targets", "--target", &runner.target_triple]); + + if runner.host_triple == runner.target_triple { + runner.run_cargo(["test", "-q"]); + } + }); + }), +]; + +pub(crate) fn run_tests( + channel: &str, + sysroot_kind: SysrootKind, + target_dir: &Path, + cg_clif_build_dir: &Path, + host_triple: &str, + target_triple: &str, +) { + let runner = TestRunner::new(host_triple.to_string(), target_triple.to_string()); + + if config::get_bool("testsuite.no_sysroot") { + build_sysroot::build_sysroot( + channel, + SysrootKind::None, + &target_dir, + cg_clif_build_dir, + &host_triple, + &target_triple, + ); + + let _ = fs::remove_dir_all(Path::new("target").join("out")); + runner.run_testsuite(NO_SYSROOT_SUITE); + } else { + eprintln!("[SKIP] no_sysroot tests"); + } + + let run_base_sysroot = config::get_bool("testsuite.base_sysroot"); + let run_extended_sysroot = config::get_bool("testsuite.extended_sysroot"); + + if run_base_sysroot || run_extended_sysroot { + build_sysroot::build_sysroot( + channel, + sysroot_kind, + &target_dir, + cg_clif_build_dir, + &host_triple, + &target_triple, + ); + } + + if run_base_sysroot { + runner.run_testsuite(BASE_SYSROOT_SUITE); + } else { + eprintln!("[SKIP] base_sysroot tests"); + } + + if run_extended_sysroot { + runner.run_testsuite(EXTENDED_SYSROOT_SUITE); + } else { + eprintln!("[SKIP] extended_sysroot tests"); + } +} + +struct TestRunner { + root_dir: PathBuf, + out_dir: PathBuf, + jit_supported: bool, + rust_flags: String, + run_wrapper: Vec, + host_triple: String, + target_triple: String, +} + +impl TestRunner { + pub fn new(host_triple: String, target_triple: String) -> Self { + let root_dir = env::current_dir().unwrap(); + + let mut out_dir = root_dir.clone(); + out_dir.push("target"); + out_dir.push("out"); + + let is_native = host_triple == target_triple; + let jit_supported = target_triple.contains("x86_64") && is_native && !host_triple.contains("windows"); + + let mut rust_flags = env::var("RUSTFLAGS").ok().unwrap_or("".to_string()); + let mut run_wrapper = Vec::new(); + + if !is_native { + match target_triple.as_str() { + "aarch64-unknown-linux-gnu" => { + // We are cross-compiling for aarch64. Use the correct linker and run tests in qemu. + rust_flags = format!("-Clinker=aarch64-linux-gnu-gcc{}", rust_flags); + run_wrapper = vec!["qemu-aarch64", "-L", "/usr/aarch64-linux-gnu"]; + } + "x86_64-pc-windows-gnu" => { + // We are cross-compiling for Windows. Run tests in wine. + run_wrapper = vec!["wine"]; + } + _ => { + println!("Unknown non-native platform"); + } + } + } + + // FIXME fix `#[linkage = "extern_weak"]` without this + if host_triple.contains("darwin") { + rust_flags = format!("{} -Clink-arg=-undefined -Clink-arg=dynamic_lookup", rust_flags); + } + + Self { + root_dir, + out_dir, + jit_supported, + rust_flags, + run_wrapper: run_wrapper.iter().map(|s| s.to_string()).collect(), + host_triple, + target_triple, + } + } + + pub fn run_testsuite(&self, tests: &[TestCase]) { + for &TestCase { config, func } in tests { + let (tag, testname) = config.split_once('.').unwrap(); + let tag = tag.to_uppercase(); + let is_jit_test = tag == "JIT"; + + if !config::get_bool(config) || (is_jit_test && !self.jit_supported) { + eprintln!("[{tag}] {testname} (skipped)"); + continue; + } else { + eprintln!("[{tag}] {testname}"); + } + + func(self); + } + } + + fn in_dir<'a, I, F>(&self, dir: I, callback: F) + where + I: IntoIterator, + F: FnOnce(&TestRunner), + { + let current = env::current_dir().unwrap(); + let mut new = current.clone(); + for d in dir { + new.push(d); + } + + env::set_current_dir(new).unwrap(); + callback(self); + env::set_current_dir(current).unwrap(); + } + + fn rustc_command(&self, args: I) -> Command + where + I: IntoIterator, + S: AsRef, + { + let mut rustc_clif = self.root_dir.clone(); + rustc_clif.push("build"); + rustc_clif.push(get_wrapper_file_name("rustc-clif", "bin")); + + let mut cmd = Command::new(rustc_clif); + cmd.args(self.rust_flags.split_whitespace()); + cmd.arg("-L"); + cmd.arg(format!("crate={}", self.out_dir.display())); + cmd.arg("--out-dir"); + cmd.arg(format!("{}", self.out_dir.display())); + cmd.arg("-Cdebuginfo=2"); + cmd.args(args); + cmd + } + + fn run_rustc(&self, args: I) + where + I: IntoIterator, + S: AsRef, + { + spawn_and_wait(self.rustc_command(args)); + } + + fn run_out_command<'a, I>(&self, name: &str, args: I) + where + I: IntoIterator, + { + let mut full_cmd = vec![]; + + // Prepend the RUN_WRAPPER's + if !self.run_wrapper.is_empty() { + full_cmd.extend(self.run_wrapper.iter().cloned()); + } + + full_cmd.push({ + let mut out_path = self.out_dir.clone(); + out_path.push(name); + out_path.to_str().unwrap().to_string() + }); + + for arg in args.into_iter() { + full_cmd.push(arg.to_string()); + } + + let mut cmd_iter = full_cmd.into_iter(); + let first = cmd_iter.next().unwrap(); + + let mut cmd = Command::new(first); + cmd.args(cmd_iter); + + spawn_and_wait(cmd); + } + + fn cargo_command(&self, args: I) -> Command + where + I: IntoIterator, + S: AsRef, + { + let mut cargo_clif = self.root_dir.clone(); + cargo_clif.push("build"); + cargo_clif.push(get_wrapper_file_name("cargo-clif", "bin")); + + let mut cmd = Command::new(cargo_clif); + cmd.args(args); + cmd.env("RUSTFLAGS", &self.rust_flags); + cmd + } + + fn run_cargo<'a, I>(&self, args: I) + where + I: IntoIterator, + { + spawn_and_wait(self.cargo_command(args)); + } +} diff --git a/build_system/utils.rs b/build_system/utils.rs index 12b5d70fad85..bdf8f8ecd997 100644 --- a/build_system/utils.rs +++ b/build_system/utils.rs @@ -1,6 +1,8 @@ +use std::env; use std::fs; +use std::io::Write; use std::path::Path; -use std::process::{self, Command}; +use std::process::{self, Command, Stdio}; #[track_caller] pub(crate) fn try_hard_link(src: impl AsRef, dst: impl AsRef) { @@ -18,6 +20,27 @@ pub(crate) fn spawn_and_wait(mut cmd: Command) { } } +#[track_caller] +pub(crate) fn spawn_and_wait_with_input(mut cmd: Command, input: String) -> String { + let mut child = cmd + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .spawn() + .expect("Failed to spawn child process"); + + let mut stdin = child.stdin.take().expect("Failed to open stdin"); + std::thread::spawn(move || { + stdin.write_all(input.as_bytes()).expect("Failed to write to stdin"); + }); + + let output = child.wait_with_output().expect("Failed to read stdout"); + if !output.status.success() { + process::exit(1); + } + + String::from_utf8(output.stdout).unwrap() +} + pub(crate) fn copy_dir_recursively(from: &Path, to: &Path) { for entry in fs::read_dir(from).unwrap() { let entry = entry.unwrap(); @@ -33,3 +56,7 @@ pub(crate) fn copy_dir_recursively(from: &Path, to: &Path) { } } } + +pub(crate) fn is_ci() -> bool { + env::var("CI").as_ref().map(|val| &**val) == Ok("true") +} diff --git a/clean_all.sh b/clean_all.sh index ea1f8c1e8920..62e52bd19580 100755 --- a/clean_all.sh +++ b/clean_all.sh @@ -3,4 +3,4 @@ set -e rm -rf build_sysroot/{sysroot_src/,target/,compiler-builtins/,rustc_version} rm -rf target/ build/ perf.data{,.old} y.bin -rm -rf rand/ regex/ simple-raytracer/ portable-simd/ +rm -rf rand/ regex/ simple-raytracer/ portable-simd/ abi-checker/ diff --git a/config.txt b/config.txt index b14db27d6206..2264d301d592 100644 --- a/config.txt +++ b/config.txt @@ -15,3 +15,38 @@ # This option can be changed while the build system is already running for as long as sysroot # building hasn't started yet. #keep_sysroot + + +# Testsuite +# +# Each test suite item has a corresponding key here. The default is to run all tests. +# Comment any of these lines to skip individual tests. + +testsuite.no_sysroot +build.mini_core +build.example +jit.mini_core_hello_world +aot.mini_core_hello_world + +testsuite.base_sysroot +aot.arbitrary_self_types_pointers_and_wrappers +aot.issue_91827_extern_types +build.alloc_system +aot.alloc_example +jit.std_example +aot.std_example +aot.dst_field_align +aot.subslice-patterns-const-eval +aot.track-caller-attribute +aot.float-minmax-pass +aot.mod_bench + +testsuite.extended_sysroot +test.rust-random/rand +bench.simple-raytracer +test.libcore +test.regex-shootout-regex-dna +test.regex +test.portable-simd + +testsuite.abi-checker diff --git a/example/mini_core.rs b/example/mini_core.rs index 8b6042a3d663..42f8aa50ba1a 100644 --- a/example/mini_core.rs +++ b/example/mini_core.rs @@ -535,7 +535,7 @@ unsafe fn allocate(size: usize, _align: usize) -> *mut u8 { } #[lang = "box_free"] -unsafe fn box_free(ptr: Unique, alloc: ()) { +unsafe fn box_free(ptr: Unique, _alloc: ()) { libc::free(ptr.pointer.0 as *mut u8); } @@ -575,11 +575,19 @@ pub mod intrinsics { } pub mod libc { + // With the new Universal CRT, msvc has switched to all the printf functions being inline wrapper + // functions. legacy_stdio_definitions.lib which provides the printf wrapper functions as normal + // symbols to link against. + #[cfg_attr(unix, link(name = "c"))] + #[cfg_attr(target_env="msvc", link(name="legacy_stdio_definitions"))] + extern "C" { + pub fn printf(format: *const i8, ...) -> i32; + } + #[cfg_attr(unix, link(name = "c"))] #[cfg_attr(target_env = "msvc", link(name = "msvcrt"))] extern "C" { pub fn puts(s: *const i8) -> i32; - pub fn printf(format: *const i8, ...) -> i32; pub fn malloc(size: usize) -> *mut u8; pub fn free(ptr: *mut u8); pub fn memcpy(dst: *mut u8, src: *const u8, size: usize); diff --git a/example/mini_core_hello_world.rs b/example/mini_core_hello_world.rs index aa1f239bae23..e83be3a3df5c 100644 --- a/example/mini_core_hello_world.rs +++ b/example/mini_core_hello_world.rs @@ -139,7 +139,7 @@ pub struct bool_11 { field10: bool, } -extern "C" fn bool_struct_in_11(arg0: bool_11) {} +extern "C" fn bool_struct_in_11(_arg0: bool_11) {} #[allow(unreachable_code)] // FIXME false positive fn main() { @@ -321,7 +321,7 @@ fn main() { #[cfg(not(any(jit, windows)))] test_tls(); - #[cfg(all(not(jit), target_arch = "x86_64", target_os = "linux"))] + #[cfg(all(not(jit), target_arch = "x86_64", any(target_os = "linux", target_os = "darwin")))] unsafe { global_asm_test(); } @@ -343,7 +343,7 @@ fn main() { } } -#[cfg(all(not(jit), target_arch = "x86_64", target_os = "linux"))] +#[cfg(all(not(jit), target_arch = "x86_64", any(target_os = "linux", target_os = "darwin")))] extern "C" { fn global_asm_test(); } @@ -358,6 +358,16 @@ global_asm! { " } +#[cfg(all(not(jit), target_arch = "x86_64", target_os = "darwin"))] +global_asm! { + " + .global _global_asm_test + _global_asm_test: + // comment that would normally be removed by LLVM + ret + " +} + #[repr(C)] enum c_void { _1, @@ -375,6 +385,7 @@ struct pthread_attr_t { } #[link(name = "pthread")] +#[cfg(unix)] extern "C" { fn pthread_attr_init(attr: *mut pthread_attr_t) -> c_int; @@ -391,6 +402,91 @@ extern "C" { ) -> c_int; } +type DWORD = u32; +type LPDWORD = *mut u32; + +type LPVOID = *mut c_void; +type HANDLE = *mut c_void; + +#[link(name = "msvcrt")] +#[cfg(windows)] +extern "C" { + fn WaitForSingleObject( + hHandle: LPVOID, + dwMilliseconds: DWORD + ) -> DWORD; + + fn CreateThread( + lpThreadAttributes: LPVOID, // Technically LPSECURITY_ATTRIBUTES, but we don't use it anyway + dwStackSize: usize, + lpStartAddress: extern "C" fn(_: *mut c_void) -> *mut c_void, + lpParameter: LPVOID, + dwCreationFlags: DWORD, + lpThreadId: LPDWORD + ) -> HANDLE; +} + +struct Thread { + #[cfg(windows)] + handle: HANDLE, + #[cfg(unix)] + handle: pthread_t, +} + +impl Thread { + unsafe fn create(f: extern "C" fn(_: *mut c_void) -> *mut c_void) -> Self { + #[cfg(unix)] + { + let mut attr: pthread_attr_t = zeroed(); + let mut thread: pthread_t = 0; + + if pthread_attr_init(&mut attr) != 0 { + assert!(false); + } + + if pthread_create(&mut thread, &attr, f, 0 as *mut c_void) != 0 { + assert!(false); + } + + Thread { + handle: thread, + } + } + + #[cfg(windows)] + { + let handle = CreateThread(0 as *mut c_void, 0, f, 0 as *mut c_void, 0, 0 as *mut u32); + + if (handle as u64) == 0 { + assert!(false); + } + + Thread { + handle, + } + } + } + + + unsafe fn join(self) { + #[cfg(unix)] + { + let mut res = 0 as *mut c_void; + pthread_join(self.handle, &mut res); + } + + #[cfg(windows)] + { + // The INFINITE macro is used to signal operations that do not timeout. + let infinite = 0xffffffff; + assert!(WaitForSingleObject(self.handle, infinite) == 0); + } + } +} + + + + #[thread_local] #[cfg(not(jit))] static mut TLS: u8 = 42; @@ -404,21 +500,10 @@ extern "C" fn mutate_tls(_: *mut c_void) -> *mut c_void { #[cfg(not(jit))] fn test_tls() { unsafe { - let mut attr: pthread_attr_t = zeroed(); - let mut thread: pthread_t = 0; - assert_eq!(TLS, 42); - if pthread_attr_init(&mut attr) != 0 { - assert!(false); - } - - if pthread_create(&mut thread, &attr, mutate_tls, 0 as *mut c_void) != 0 { - assert!(false); - } - - let mut res = 0 as *mut c_void; - pthread_join(thread, &mut res); + let thread = Thread::create(mutate_tls); + thread.join(); // TLS of main thread must not have been changed by the other thread. assert_eq!(TLS, 42); diff --git a/patches/0001-abi-checker-Disable-failing-tests.patch b/patches/0001-abi-checker-Disable-failing-tests.patch new file mode 100644 index 000000000000..526366a75987 --- /dev/null +++ b/patches/0001-abi-checker-Disable-failing-tests.patch @@ -0,0 +1,36 @@ +From 1a315ba225577dbbd1f449d9609f16f984f68708 Mon Sep 17 00:00:00 2001 +From: Afonso Bordado +Date: Fri, 12 Aug 2022 22:51:58 +0000 +Subject: [PATCH] Disable abi-checker tests + +--- + src/report.rs | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/src/report.rs b/src/report.rs +index 7346f5e..8347762 100644 +--- a/src/report.rs ++++ b/src/report.rs +@@ -45,6 +45,20 @@ pub fn get_test_rules(test: &TestKey, caller: &dyn AbiImpl, callee: &dyn AbiImpl + // + // THIS AREA RESERVED FOR VENDORS TO APPLY PATCHES + ++ // Currently MSVC has some broken ABI issues. Furthermore, they cause ++ // a STATUS_ACCESS_VIOLATION, so we can't even run them. Ensure that they compile and link. ++ if cfg!(windows) && (test.test_name == "bool" || test.test_name == "ui128") { ++ result.run = Link; ++ result.check = Pass(Link); ++ } ++ ++ // structs is broken in the current release of cranelift for aarch64. ++ // It has been fixed for cranelift 0.88: https://github.com/bytecodealliance/wasmtime/pull/4634 ++ if cfg!(target_arch = "aarch64") && test.test_name == "structs" { ++ result.run = Link; ++ result.check = Pass(Link); ++ } ++ + // END OF VENDOR RESERVED AREA + // + // +-- +2.34.1 diff --git a/patches/0023-sysroot-Ignore-failing-tests.patch b/patches/0023-sysroot-Ignore-failing-tests.patch index 50ef0bd9418c..f3cd7ee77e26 100644 --- a/patches/0023-sysroot-Ignore-failing-tests.patch +++ b/patches/0023-sysroot-Ignore-failing-tests.patch @@ -46,5 +46,17 @@ index 4bc44e9..8e3c7a4 100644 #[test] fn cell_allows_array_cycle() { +diff --git a/library/core/tests/atomic.rs b/library/core/tests/atomic.rs +index 13b12db..96fe4b9 100644 +--- a/library/core/tests/atomic.rs ++++ b/library/core/tests/atomic.rs +@@ -185,6 +185,7 @@ fn ptr_bitops() { + } + + #[test] ++#[cfg_attr(target_arch = "s390x", ignore)] // s390x backend doesn't support stack alignment >8 bytes + #[cfg(any(not(target_arch = "arm"), target_os = "linux"))] // Missing intrinsic in compiler-builtins + fn ptr_bitops_tagging() { + #[repr(align(16))] -- 2.21.0 (Apple Git-122) diff --git a/rust-toolchain b/rust-toolchain index 3ab395d89d50..14f2746ecb19 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2022-07-25" +channel = "nightly-2022-08-24" components = ["rust-src", "rustc-dev", "llvm-tools-preview"] diff --git a/scripts/tests.sh b/scripts/tests.sh deleted file mode 100755 index 9b5ffa409604..000000000000 --- a/scripts/tests.sh +++ /dev/null @@ -1,203 +0,0 @@ -#!/usr/bin/env bash - -set -e - -export CG_CLIF_DISPLAY_CG_TIME=1 -export CG_CLIF_DISABLE_INCR_CACHE=1 - -export HOST_TRIPLE=$(rustc -vV | grep host | cut -d: -f2 | tr -d " ") -export TARGET_TRIPLE=${TARGET_TRIPLE:-$HOST_TRIPLE} - -export RUN_WRAPPER='' - -case "$TARGET_TRIPLE" in - x86_64*) - export JIT_SUPPORTED=1 - ;; - *) - export JIT_SUPPORTED=0 - ;; -esac - -if [[ "$HOST_TRIPLE" != "$TARGET_TRIPLE" ]]; then - export JIT_SUPPORTED=0 - if [[ "$TARGET_TRIPLE" == "aarch64-unknown-linux-gnu" ]]; then - # We are cross-compiling for aarch64. Use the correct linker and run tests in qemu. - export RUSTFLAGS='-Clinker=aarch64-linux-gnu-gcc '$RUSTFLAGS - export RUN_WRAPPER='qemu-aarch64 -L /usr/aarch64-linux-gnu' - elif [[ "$TARGET_TRIPLE" == "x86_64-pc-windows-gnu" ]]; then - # We are cross-compiling for Windows. Run tests in wine. - export RUN_WRAPPER='wine' - else - echo "Unknown non-native platform" - fi -fi - -# FIXME fix `#[linkage = "extern_weak"]` without this -if [[ "$(uname)" == 'Darwin' ]]; then - export RUSTFLAGS="$RUSTFLAGS -Clink-arg=-undefined -Clink-arg=dynamic_lookup" -fi - -MY_RUSTC="$(pwd)/build/rustc-clif $RUSTFLAGS -L crate=target/out --out-dir target/out -Cdebuginfo=2" - -function no_sysroot_tests() { - echo "[BUILD] mini_core" - $MY_RUSTC example/mini_core.rs --crate-name mini_core --crate-type lib,dylib --target "$TARGET_TRIPLE" - - echo "[BUILD] example" - $MY_RUSTC example/example.rs --crate-type lib --target "$TARGET_TRIPLE" - - if [[ "$JIT_SUPPORTED" = "1" ]]; then - echo "[JIT] mini_core_hello_world" - CG_CLIF_JIT_ARGS="abc bcd" $MY_RUSTC -Zunstable-options -Cllvm-args=mode=jit -Cprefer-dynamic example/mini_core_hello_world.rs --cfg jit --target "$HOST_TRIPLE" - - echo "[JIT-lazy] mini_core_hello_world" - CG_CLIF_JIT_ARGS="abc bcd" $MY_RUSTC -Zunstable-options -Cllvm-args=mode=jit-lazy -Cprefer-dynamic example/mini_core_hello_world.rs --cfg jit --target "$HOST_TRIPLE" - else - echo "[JIT] mini_core_hello_world (skipped)" - fi - - echo "[AOT] mini_core_hello_world" - $MY_RUSTC example/mini_core_hello_world.rs --crate-name mini_core_hello_world --crate-type bin -g --target "$TARGET_TRIPLE" - $RUN_WRAPPER ./target/out/mini_core_hello_world abc bcd - # (echo "break set -n main"; echo "run"; sleep 1; echo "si -c 10"; sleep 1; echo "frame variable") | lldb -- ./target/out/mini_core_hello_world abc bcd -} - -function base_sysroot_tests() { - echo "[AOT] arbitrary_self_types_pointers_and_wrappers" - $MY_RUSTC example/arbitrary_self_types_pointers_and_wrappers.rs --crate-name arbitrary_self_types_pointers_and_wrappers --crate-type bin --target "$TARGET_TRIPLE" - $RUN_WRAPPER ./target/out/arbitrary_self_types_pointers_and_wrappers - - echo "[AOT] issue_91827_extern_types" - $MY_RUSTC example/issue-91827-extern-types.rs --crate-name issue_91827_extern_types --crate-type bin --target "$TARGET_TRIPLE" - $RUN_WRAPPER ./target/out/issue_91827_extern_types - - echo "[BUILD] alloc_system" - $MY_RUSTC example/alloc_system.rs --crate-type lib --target "$TARGET_TRIPLE" - - echo "[AOT] alloc_example" - $MY_RUSTC example/alloc_example.rs --crate-type bin --target "$TARGET_TRIPLE" - $RUN_WRAPPER ./target/out/alloc_example - - if [[ "$JIT_SUPPORTED" = "1" ]]; then - echo "[JIT] std_example" - $MY_RUSTC -Zunstable-options -Cllvm-args=mode=jit -Cprefer-dynamic example/std_example.rs --target "$HOST_TRIPLE" - - echo "[JIT-lazy] std_example" - $MY_RUSTC -Zunstable-options -Cllvm-args=mode=jit-lazy -Cprefer-dynamic example/std_example.rs --target "$HOST_TRIPLE" - else - echo "[JIT] std_example (skipped)" - fi - - echo "[AOT] std_example" - $MY_RUSTC example/std_example.rs --crate-type bin --target "$TARGET_TRIPLE" - $RUN_WRAPPER ./target/out/std_example arg - - echo "[AOT] dst_field_align" - $MY_RUSTC example/dst-field-align.rs --crate-name dst_field_align --crate-type bin --target "$TARGET_TRIPLE" - $RUN_WRAPPER ./target/out/dst_field_align - - echo "[AOT] subslice-patterns-const-eval" - $MY_RUSTC example/subslice-patterns-const-eval.rs --crate-type bin -Cpanic=abort --target "$TARGET_TRIPLE" - $RUN_WRAPPER ./target/out/subslice-patterns-const-eval - - echo "[AOT] track-caller-attribute" - $MY_RUSTC example/track-caller-attribute.rs --crate-type bin -Cpanic=abort --target "$TARGET_TRIPLE" - $RUN_WRAPPER ./target/out/track-caller-attribute - - echo "[AOT] float-minmax-pass" - $MY_RUSTC example/float-minmax-pass.rs --crate-type bin -Cpanic=abort --target "$TARGET_TRIPLE" - $RUN_WRAPPER ./target/out/float-minmax-pass - - echo "[AOT] mod_bench" - $MY_RUSTC example/mod_bench.rs --crate-type bin --target "$TARGET_TRIPLE" - $RUN_WRAPPER ./target/out/mod_bench -} - -function extended_sysroot_tests() { - pushd rand - ../build/cargo-clif clean - if [[ "$HOST_TRIPLE" = "$TARGET_TRIPLE" ]]; then - echo "[TEST] rust-random/rand" - ../build/cargo-clif test --workspace - else - echo "[AOT] rust-random/rand" - ../build/cargo-clif build --workspace --target $TARGET_TRIPLE --tests - fi - popd - - pushd simple-raytracer - if [[ "$HOST_TRIPLE" = "$TARGET_TRIPLE" ]]; then - echo "[BENCH COMPILE] ebobby/simple-raytracer" - hyperfine --runs "${RUN_RUNS:-10}" --warmup 1 --prepare "../build/cargo-clif clean" \ - "RUSTFLAGS='' cargo build" \ - "../build/cargo-clif build" - - echo "[BENCH RUN] ebobby/simple-raytracer" - cp ./target/debug/main ./raytracer_cg_clif - hyperfine --runs "${RUN_RUNS:-10}" ./raytracer_cg_llvm ./raytracer_cg_clif - else - ../build/cargo-clif clean - echo "[BENCH COMPILE] ebobby/simple-raytracer (skipped)" - echo "[COMPILE] ebobby/simple-raytracer" - ../build/cargo-clif build --target $TARGET_TRIPLE - echo "[BENCH RUN] ebobby/simple-raytracer (skipped)" - fi - popd - - pushd build_sysroot/sysroot_src/library/core/tests - echo "[TEST] libcore" - ../../../../../build/cargo-clif clean - if [[ "$HOST_TRIPLE" = "$TARGET_TRIPLE" ]]; then - ../../../../../build/cargo-clif test - else - ../../../../../build/cargo-clif build --target $TARGET_TRIPLE --tests - fi - popd - - pushd regex - echo "[TEST] rust-lang/regex example shootout-regex-dna" - ../build/cargo-clif clean - export RUSTFLAGS="$RUSTFLAGS --cap-lints warn" # newer aho_corasick versions throw a deprecation warning - # Make sure `[codegen mono items] start` doesn't poison the diff - ../build/cargo-clif build --example shootout-regex-dna --target $TARGET_TRIPLE - if [[ "$HOST_TRIPLE" = "$TARGET_TRIPLE" ]]; then - cat examples/regexdna-input.txt \ - | ../build/cargo-clif run --example shootout-regex-dna --target $TARGET_TRIPLE \ - | grep -v "Spawned thread" > res.txt - diff -u res.txt examples/regexdna-output.txt - fi - - if [[ "$HOST_TRIPLE" = "$TARGET_TRIPLE" ]]; then - echo "[TEST] rust-lang/regex tests" - ../build/cargo-clif test --tests -- --exclude-should-panic --test-threads 1 -Zunstable-options -q - else - echo "[AOT] rust-lang/regex tests" - ../build/cargo-clif build --tests --target $TARGET_TRIPLE - fi - popd - - pushd portable-simd - echo "[TEST] rust-lang/portable-simd" - ../build/cargo-clif clean - ../build/cargo-clif build --all-targets --target $TARGET_TRIPLE - if [[ "$HOST_TRIPLE" = "$TARGET_TRIPLE" ]]; then - ../build/cargo-clif test -q - fi - popd -} - -case "$1" in - "no_sysroot") - no_sysroot_tests - ;; - "base_sysroot") - base_sysroot_tests - ;; - "extended_sysroot") - extended_sysroot_tests - ;; - *) - echo "unknown test suite" - ;; -esac diff --git a/src/abi/pass_mode.rs b/src/abi/pass_mode.rs index 6c10baa53d41..f4ad76b3bab3 100644 --- a/src/abi/pass_mode.rs +++ b/src/abi/pass_mode.rs @@ -23,7 +23,7 @@ fn reg_to_abi_param(reg: Reg) -> AbiParam { (RegKind::Integer, 9..=16) => types::I128, (RegKind::Float, 4) => types::F32, (RegKind::Float, 8) => types::F64, - (RegKind::Vector, size) => types::I8.by(u16::try_from(size).unwrap()).unwrap(), + (RegKind::Vector, size) => types::I8.by(u32::try_from(size).unwrap()).unwrap(), _ => unreachable!("{:?}", reg), }; AbiParam::new(clif_ty) @@ -184,7 +184,7 @@ pub(super) fn from_casted_value<'tcx>( let abi_params = cast_target_to_abi_params(cast); let abi_param_size: u32 = abi_params.iter().map(|param| param.value_type.bytes()).sum(); let layout_size = u32::try_from(layout.size.bytes()).unwrap(); - let stack_slot = fx.bcx.create_stack_slot(StackSlotData { + let stack_slot = fx.bcx.create_sized_stack_slot(StackSlotData { kind: StackSlotKind::ExplicitSlot, // FIXME Don't force the size to a multiple of 16 bytes once Cranelift gets a way to // specify stack slot alignment. @@ -193,7 +193,7 @@ pub(super) fn from_casted_value<'tcx>( // larger alignment than the integer. size: (std::cmp::max(abi_param_size, layout_size) + 15) / 16 * 16, }); - let ptr = Pointer::new(fx.bcx.ins().stack_addr(pointer_ty(fx.tcx), stack_slot, 0)); + let ptr = Pointer::stack_slot(stack_slot); let mut offset = 0; let mut block_params_iter = block_params.iter().copied(); for param in abi_params { diff --git a/src/base.rs b/src/base.rs index 122e103ff62b..44c34d6c8cb7 100644 --- a/src/base.rs +++ b/src/base.rs @@ -6,21 +6,43 @@ use rustc_middle::ty::adjustment::PointerCast; use rustc_middle::ty::layout::FnAbiOf; use rustc_middle::ty::print::with_no_trimmed_paths; -use indexmap::IndexSet; - use crate::constant::ConstantCx; +use crate::debuginfo::FunctionDebugContext; use crate::prelude::*; use crate::pretty_clif::CommentWriter; -pub(crate) fn codegen_fn<'tcx>( - cx: &mut crate::CodegenCx<'tcx>, +pub(crate) struct CodegenedFunction { + symbol_name: String, + func_id: FuncId, + func: Function, + clif_comments: CommentWriter, + func_debug_cx: Option, +} + +#[cfg_attr(not(feature = "jit"), allow(dead_code))] +pub(crate) fn codegen_and_compile_fn<'tcx>( + tcx: TyCtxt<'tcx>, + cx: &mut crate::CodegenCx, + cached_context: &mut Context, module: &mut dyn Module, instance: Instance<'tcx>, ) { - let tcx = cx.tcx; - let _inst_guard = crate::PrintOnPanic(|| format!("{:?} {}", instance, tcx.symbol_name(instance).name)); + + let cached_func = std::mem::replace(&mut cached_context.func, Function::new()); + let codegened_func = codegen_fn(tcx, cx, cached_func, module, instance); + + compile_fn(cx, cached_context, module, codegened_func); +} + +pub(crate) fn codegen_fn<'tcx>( + tcx: TyCtxt<'tcx>, + cx: &mut crate::CodegenCx, + cached_func: Function, + module: &mut dyn Module, + instance: Instance<'tcx>, +) -> CodegenedFunction { debug_assert!(!instance.substs.needs_infer()); let mir = tcx.instance_mir(instance.def); @@ -34,15 +56,14 @@ pub(crate) fn codegen_fn<'tcx>( }); // Declare function - let symbol_name = tcx.symbol_name(instance); + let symbol_name = tcx.symbol_name(instance).name.to_string(); let sig = get_function_sig(tcx, module.isa().triple(), instance); - let func_id = module.declare_function(symbol_name.name, Linkage::Local, &sig).unwrap(); - - cx.cached_context.clear(); + let func_id = module.declare_function(&symbol_name, Linkage::Local, &sig).unwrap(); // Make the FunctionBuilder let mut func_ctx = FunctionBuilderContext::new(); - let mut func = std::mem::replace(&mut cx.cached_context.func, Function::new()); + let mut func = cached_func; + func.clear(); func.name = ExternalName::user(0, func_id.as_u32()); func.signature = sig; func.collect_debug_info(); @@ -59,6 +80,12 @@ pub(crate) fn codegen_fn<'tcx>( let pointer_type = target_config.pointer_type(); let clif_comments = crate::pretty_clif::CommentWriter::new(tcx, instance); + let func_debug_cx = if let Some(debug_context) = &mut cx.debug_context { + Some(debug_context.define_function(tcx, &symbol_name, mir.span)) + } else { + None + }; + let mut fx = FunctionCx { cx, module, @@ -66,6 +93,7 @@ pub(crate) fn codegen_fn<'tcx>( target_config, pointer_type, constants_cx: ConstantCx::new(), + func_debug_cx, instance, symbol_name, @@ -78,81 +106,48 @@ pub(crate) fn codegen_fn<'tcx>( caller_location: None, // set by `codegen_fn_prelude` clif_comments, - source_info_set: indexmap::IndexSet::new(), + last_source_file: None, next_ssa_var: 0, }; - let arg_uninhabited = fx - .mir - .args_iter() - .any(|arg| fx.layout_of(fx.monomorphize(fx.mir.local_decls[arg].ty)).abi.is_uninhabited()); - - if !crate::constant::check_constants(&mut fx) { - fx.bcx.append_block_params_for_function_params(fx.block_map[START_BLOCK]); - fx.bcx.switch_to_block(fx.block_map[START_BLOCK]); - crate::trap::trap_unreachable(&mut fx, "compilation should have been aborted"); - } else if arg_uninhabited { - fx.bcx.append_block_params_for_function_params(fx.block_map[START_BLOCK]); - fx.bcx.switch_to_block(fx.block_map[START_BLOCK]); - fx.bcx.ins().trap(TrapCode::UnreachableCodeReached); - } else { - tcx.sess.time("codegen clif ir", || { - tcx.sess - .time("codegen prelude", || crate::abi::codegen_fn_prelude(&mut fx, start_block)); - codegen_fn_content(&mut fx); - }); - } + tcx.sess.time("codegen clif ir", || codegen_fn_body(&mut fx, start_block)); // Recover all necessary data from fx, before accessing func will prevent future access to it. - let instance = fx.instance; + let symbol_name = fx.symbol_name; let clif_comments = fx.clif_comments; - let source_info_set = fx.source_info_set; - let local_map = fx.local_map; + let func_debug_cx = fx.func_debug_cx; fx.constants_cx.finalize(fx.tcx, &mut *fx.module); - crate::pretty_clif::write_clif_file( - tcx, - "unopt", - module.isa(), - instance, - &func, - &clif_comments, - ); + if cx.should_write_ir { + crate::pretty_clif::write_clif_file( + tcx.output_filenames(()), + &symbol_name, + "unopt", + module.isa(), + &func, + &clif_comments, + ); + } // Verify function verify_func(tcx, &clif_comments, &func); - compile_fn( - cx, - module, - instance, - symbol_name.name, - func_id, - func, - clif_comments, - source_info_set, - local_map, - ); + CodegenedFunction { symbol_name, func_id, func, clif_comments, func_debug_cx } } -fn compile_fn<'tcx>( - cx: &mut crate::CodegenCx<'tcx>, +pub(crate) fn compile_fn( + cx: &mut crate::CodegenCx, + cached_context: &mut Context, module: &mut dyn Module, - instance: Instance<'tcx>, - symbol_name: &str, - func_id: FuncId, - func: Function, - mut clif_comments: CommentWriter, - source_info_set: IndexSet, - local_map: IndexVec>, + codegened_func: CodegenedFunction, ) { - let tcx = cx.tcx; + let clif_comments = codegened_func.clif_comments; // Store function in context - let context = &mut cx.cached_context; + let context = cached_context; context.clear(); - context.func = func; + context.func = codegened_func.func; // If the return block is not reachable, then the SSA builder may have inserted an `iconst.i128` // instruction, which doesn't have an encoding. @@ -164,17 +159,6 @@ fn compile_fn<'tcx>( // invalidate it when it would change. context.domtree.clear(); - // Perform rust specific optimizations - tcx.sess.time("optimize clif ir", || { - crate::optimize::optimize_function( - tcx, - module.isa(), - instance, - context, - &mut clif_comments, - ); - }); - #[cfg(any())] // This is never true let _clif_guard = { use std::fmt::Write; @@ -203,46 +187,44 @@ fn compile_fn<'tcx>( }; // Define function - tcx.sess.time("define function", || { - context.want_disasm = crate::pretty_clif::should_write_ir(tcx); - module.define_function(func_id, context).unwrap(); + cx.profiler.verbose_generic_activity("define function").run(|| { + context.want_disasm = cx.should_write_ir; + module.define_function(codegened_func.func_id, context).unwrap(); }); - // Write optimized function to file for debugging - crate::pretty_clif::write_clif_file( - tcx, - "opt", - module.isa(), - instance, - &context.func, - &clif_comments, - ); + if cx.should_write_ir { + // Write optimized function to file for debugging + crate::pretty_clif::write_clif_file( + &cx.output_filenames, + &codegened_func.symbol_name, + "opt", + module.isa(), + &context.func, + &clif_comments, + ); - if let Some(disasm) = &context.mach_compile_result.as_ref().unwrap().disasm { - crate::pretty_clif::write_ir_file( - tcx, - || format!("{}.vcode", tcx.symbol_name(instance).name), - |file| file.write_all(disasm.as_bytes()), - ) + if let Some(disasm) = &context.compiled_code().unwrap().disasm { + crate::pretty_clif::write_ir_file( + &cx.output_filenames, + &format!("{}.vcode", codegened_func.symbol_name), + |file| file.write_all(disasm.as_bytes()), + ) + } } // Define debuginfo for function let isa = module.isa(); let debug_context = &mut cx.debug_context; let unwind_context = &mut cx.unwind_context; - tcx.sess.time("generate debug info", || { + cx.profiler.verbose_generic_activity("generate debug info").run(|| { if let Some(debug_context) = debug_context { - debug_context.define_function( - instance, - func_id, - symbol_name, - isa, + codegened_func.func_debug_cx.unwrap().finalize( + debug_context, + codegened_func.func_id, context, - &source_info_set, - local_map, ); } - unwind_context.add_function(func_id, &context, isa); + unwind_context.add_function(codegened_func.func_id, &context, isa); }); } @@ -268,7 +250,27 @@ pub(crate) fn verify_func( }); } -fn codegen_fn_content(fx: &mut FunctionCx<'_, '_, '_>) { +fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) { + if !crate::constant::check_constants(fx) { + fx.bcx.append_block_params_for_function_params(fx.block_map[START_BLOCK]); + fx.bcx.switch_to_block(fx.block_map[START_BLOCK]); + // compilation should have been aborted + fx.bcx.ins().trap(TrapCode::UnreachableCodeReached); + return; + } + + let arg_uninhabited = fx + .mir + .args_iter() + .any(|arg| fx.layout_of(fx.monomorphize(fx.mir.local_decls[arg].ty)).abi.is_uninhabited()); + if arg_uninhabited { + fx.bcx.append_block_params_for_function_params(fx.block_map[START_BLOCK]); + fx.bcx.switch_to_block(fx.block_map[START_BLOCK]); + fx.bcx.ins().trap(TrapCode::UnreachableCodeReached); + return; + } + fx.tcx.sess.time("codegen prelude", || crate::abi::codegen_fn_prelude(fx, start_block)); + for (bb, bb_data) in fx.mir.basic_blocks().iter_enumerated() { let block = fx.get_block(bb); fx.bcx.switch_to_block(block); @@ -457,17 +459,8 @@ fn codegen_fn_content(fx: &mut FunctionCx<'_, '_, '_>) { template, operands, *options, + *destination, ); - - match *destination { - Some(destination) => { - let destination_block = fx.get_block(destination); - fx.bcx.ins().jump(destination_block, &[]); - } - None => { - fx.bcx.ins().trap(TrapCode::UnreachableCodeReached); - } - } } TerminatorKind::Resume | TerminatorKind::Abort => { // FIXME implement unwinding @@ -711,9 +704,7 @@ fn codegen_stmt<'tcx>( Rvalue::Discriminant(place) => { let place = codegen_place(fx, place); let value = place.to_cvalue(fx); - let discr = - crate::discriminant::codegen_get_discriminant(fx, value, dest_layout); - lval.write_cvalue(fx, discr); + crate::discriminant::codegen_get_discriminant(fx, lval, value, dest_layout); } Rvalue::Repeat(ref operand, times) => { let operand = codegen_operand(fx, operand); diff --git a/src/common.rs b/src/common.rs index f9dc1b5169e1..589594465783 100644 --- a/src/common.rs +++ b/src/common.rs @@ -1,14 +1,18 @@ use cranelift_codegen::isa::TargetFrontendConfig; +use gimli::write::FileId; + +use rustc_data_structures::sync::Lrc; use rustc_index::vec::IndexVec; use rustc_middle::ty::layout::{ FnAbiError, FnAbiOfHelpers, FnAbiRequest, LayoutError, LayoutOfHelpers, }; -use rustc_middle::ty::SymbolName; +use rustc_span::SourceFile; use rustc_target::abi::call::FnAbi; use rustc_target::abi::{Integer, Primitive}; use rustc_target::spec::{HasTargetSpec, Target}; use crate::constant::ConstantCx; +use crate::debuginfo::FunctionDebugContext; use crate::prelude::*; pub(crate) fn pointer_ty(tcx: TyCtxt<'_>) -> types::Type { @@ -74,7 +78,7 @@ fn clif_type_from_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option unreachable!(), }; - match scalar_to_clif_type(tcx, element).by(u16::try_from(count).unwrap()) { + match scalar_to_clif_type(tcx, element).by(u32::try_from(count).unwrap()) { // Cranelift currently only implements icmp for 128bit vectors. Some(vector_ty) if vector_ty.bits() == 128 => vector_ty, _ => return None, @@ -232,15 +236,16 @@ pub(crate) fn type_sign(ty: Ty<'_>) -> bool { } pub(crate) struct FunctionCx<'m, 'clif, 'tcx: 'm> { - pub(crate) cx: &'clif mut crate::CodegenCx<'tcx>, + pub(crate) cx: &'clif mut crate::CodegenCx, pub(crate) module: &'m mut dyn Module, pub(crate) tcx: TyCtxt<'tcx>, pub(crate) target_config: TargetFrontendConfig, // Cached from module pub(crate) pointer_type: Type, // Cached from module pub(crate) constants_cx: ConstantCx, + pub(crate) func_debug_cx: Option, pub(crate) instance: Instance<'tcx>, - pub(crate) symbol_name: SymbolName<'tcx>, + pub(crate) symbol_name: String, pub(crate) mir: &'tcx Body<'tcx>, pub(crate) fn_abi: Option<&'tcx FnAbi<'tcx, Ty<'tcx>>>, @@ -252,7 +257,11 @@ pub(crate) struct FunctionCx<'m, 'clif, 'tcx: 'm> { pub(crate) caller_location: Option>, pub(crate) clif_comments: crate::pretty_clif::CommentWriter, - pub(crate) source_info_set: indexmap::IndexSet, + + /// Last accessed source file and it's debuginfo file id. + /// + /// For optimization purposes only + pub(crate) last_source_file: Option<(Lrc, FileId)>, /// This should only be accessed by `CPlace::new_var`. pub(crate) next_ssa_var: u32, @@ -336,8 +345,31 @@ impl<'tcx> FunctionCx<'_, '_, 'tcx> { } pub(crate) fn set_debug_loc(&mut self, source_info: mir::SourceInfo) { - let (index, _) = self.source_info_set.insert_full(source_info); - self.bcx.set_srcloc(SourceLoc::new(index as u32)); + if let Some(debug_context) = &mut self.cx.debug_context { + let (file, line, column) = + DebugContext::get_span_loc(self.tcx, self.mir.span, source_info.span); + + // add_source_file is very slow. + // Optimize for the common case of the current file not being changed. + let mut cached_file_id = None; + if let Some((ref last_source_file, last_file_id)) = self.last_source_file { + // If the allocations are not equal, the files may still be equal, but that + // doesn't matter, as this is just an optimization. + if rustc_data_structures::sync::Lrc::ptr_eq(last_source_file, &file) { + cached_file_id = Some(last_file_id); + } + } + + let file_id = if let Some(file_id) = cached_file_id { + file_id + } else { + debug_context.add_source_file(&file) + }; + + let source_loc = + self.func_debug_cx.as_mut().unwrap().add_dbg_loc(file_id, line, column); + self.bcx.set_srcloc(source_loc); + } } // Note: must be kept in sync with get_caller_location from cg_ssa diff --git a/src/concurrency_limiter.rs b/src/concurrency_limiter.rs new file mode 100644 index 000000000000..dfde97920461 --- /dev/null +++ b/src/concurrency_limiter.rs @@ -0,0 +1,168 @@ +use std::sync::{Arc, Condvar, Mutex}; + +use rustc_session::Session; + +use jobserver::HelperThread; + +// FIXME don't panic when a worker thread panics + +pub(super) struct ConcurrencyLimiter { + helper_thread: Option, + state: Arc>, + available_token_condvar: Arc, +} + +impl ConcurrencyLimiter { + pub(super) fn new(sess: &Session, pending_jobs: usize) -> Self { + let state = Arc::new(Mutex::new(state::ConcurrencyLimiterState::new(pending_jobs))); + let available_token_condvar = Arc::new(Condvar::new()); + + let state_helper = state.clone(); + let available_token_condvar_helper = available_token_condvar.clone(); + let helper_thread = sess + .jobserver + .clone() + .into_helper_thread(move |token| { + let mut state = state_helper.lock().unwrap(); + state.add_new_token(token.unwrap()); + available_token_condvar_helper.notify_one(); + }) + .unwrap(); + ConcurrencyLimiter { + helper_thread: Some(helper_thread), + state, + available_token_condvar: Arc::new(Condvar::new()), + } + } + + pub(super) fn acquire(&mut self) -> ConcurrencyLimiterToken { + let mut state = self.state.lock().unwrap(); + loop { + state.assert_invariants(); + + if state.try_start_job() { + return ConcurrencyLimiterToken { + state: self.state.clone(), + available_token_condvar: self.available_token_condvar.clone(), + }; + } + + self.helper_thread.as_mut().unwrap().request_token(); + state = self.available_token_condvar.wait(state).unwrap(); + } + } + + pub(super) fn job_already_done(&mut self) { + let mut state = self.state.lock().unwrap(); + state.job_already_done(); + } +} + +impl Drop for ConcurrencyLimiter { + fn drop(&mut self) { + // + self.helper_thread.take(); + + // Assert that all jobs have finished + let state = Mutex::get_mut(Arc::get_mut(&mut self.state).unwrap()).unwrap(); + state.assert_done(); + } +} + +#[derive(Debug)] +pub(super) struct ConcurrencyLimiterToken { + state: Arc>, + available_token_condvar: Arc, +} + +impl Drop for ConcurrencyLimiterToken { + fn drop(&mut self) { + let mut state = self.state.lock().unwrap(); + state.job_finished(); + self.available_token_condvar.notify_one(); + } +} + +mod state { + use jobserver::Acquired; + + #[derive(Debug)] + pub(super) struct ConcurrencyLimiterState { + pending_jobs: usize, + active_jobs: usize, + + // None is used to represent the implicit token, Some to represent explicit tokens + tokens: Vec>, + } + + impl ConcurrencyLimiterState { + pub(super) fn new(pending_jobs: usize) -> Self { + ConcurrencyLimiterState { pending_jobs, active_jobs: 0, tokens: vec![None] } + } + + pub(super) fn assert_invariants(&self) { + // There must be no excess active jobs + assert!(self.active_jobs <= self.pending_jobs); + + // There may not be more active jobs than there are tokens + assert!(self.active_jobs <= self.tokens.len()); + } + + pub(super) fn assert_done(&self) { + assert_eq!(self.pending_jobs, 0); + assert_eq!(self.active_jobs, 0); + } + + pub(super) fn add_new_token(&mut self, token: Acquired) { + self.tokens.push(Some(token)); + self.drop_excess_capacity(); + } + + pub(super) fn try_start_job(&mut self) -> bool { + if self.active_jobs < self.tokens.len() { + // Using existing token + self.job_started(); + return true; + } + + false + } + + pub(super) fn job_started(&mut self) { + self.assert_invariants(); + self.active_jobs += 1; + self.drop_excess_capacity(); + self.assert_invariants(); + } + + pub(super) fn job_finished(&mut self) { + self.assert_invariants(); + self.pending_jobs -= 1; + self.active_jobs -= 1; + self.assert_invariants(); + self.drop_excess_capacity(); + self.assert_invariants(); + } + + pub(super) fn job_already_done(&mut self) { + self.assert_invariants(); + self.pending_jobs -= 1; + self.assert_invariants(); + self.drop_excess_capacity(); + self.assert_invariants(); + } + + fn drop_excess_capacity(&mut self) { + self.assert_invariants(); + + // Drop all tokens that can never be used anymore + self.tokens.truncate(std::cmp::max(self.pending_jobs, 1)); + + // Keep some excess tokens to satisfy requests faster + const MAX_EXTRA_CAPACITY: usize = 2; + self.tokens.truncate(std::cmp::max(self.active_jobs + MAX_EXTRA_CAPACITY, 1)); + + self.assert_invariants(); + } + } +} diff --git a/src/debuginfo/emit.rs b/src/debuginfo/emit.rs index 589910ede968..9583cd2ec60f 100644 --- a/src/debuginfo/emit.rs +++ b/src/debuginfo/emit.rs @@ -9,7 +9,7 @@ use gimli::{RunTimeEndian, SectionId}; use super::object::WriteDebugInfo; use super::DebugContext; -impl DebugContext<'_> { +impl DebugContext { pub(crate) fn emit(&mut self, product: &mut ObjectProduct) { let unit_range_list_id = self.dwarf.unit.ranges.add(self.unit_range_list.clone()); let root = self.dwarf.unit.root(); diff --git a/src/debuginfo/line_info.rs b/src/debuginfo/line_info.rs index bbcb9591373d..3ad0c420eaf0 100644 --- a/src/debuginfo/line_info.rs +++ b/src/debuginfo/line_info.rs @@ -3,8 +3,10 @@ use std::ffi::OsStr; use std::path::{Component, Path}; +use crate::debuginfo::FunctionDebugContext; use crate::prelude::*; +use rustc_data_structures::sync::Lrc; use rustc_span::{ FileName, Pos, SourceFile, SourceFileAndLine, SourceFileHash, SourceFileHashAlgorithm, }; @@ -14,7 +16,6 @@ use cranelift_codegen::MachSrcLoc; use gimli::write::{ Address, AttributeValue, FileId, FileInfo, LineProgram, LineString, LineStringTable, - UnitEntryId, }; // OPTIMIZATION: It is cheaper to do this in one pass than using `.parent()` and `.file_name()`. @@ -47,9 +48,9 @@ fn osstr_as_utf8_bytes(path: &OsStr) -> &[u8] { } } -pub(crate) const MD5_LEN: usize = 16; +const MD5_LEN: usize = 16; -pub(crate) fn make_file_info(hash: SourceFileHash) -> Option { +fn make_file_info(hash: SourceFileHash) -> Option { if hash.kind == SourceFileHashAlgorithm::Md5 { let mut buf = [0u8; MD5_LEN]; buf.copy_from_slice(hash.hash_bytes()); @@ -59,160 +60,132 @@ pub(crate) fn make_file_info(hash: SourceFileHash) -> Option { } } -fn line_program_add_file( - line_program: &mut LineProgram, - line_strings: &mut LineStringTable, - file: &SourceFile, -) -> FileId { - match &file.name { - FileName::Real(path) => { - let (dir_path, file_name) = split_path_dir_and_file(path.remapped_path_if_available()); - let dir_name = osstr_as_utf8_bytes(dir_path.as_os_str()); - let file_name = osstr_as_utf8_bytes(file_name); +impl DebugContext { + pub(crate) fn get_span_loc( + tcx: TyCtxt<'_>, + function_span: Span, + span: Span, + ) -> (Lrc, u64, u64) { + // Based on https://github.com/rust-lang/rust/blob/e369d87b015a84653343032833d65d0545fd3f26/src/librustc_codegen_ssa/mir/mod.rs#L116-L131 + // In order to have a good line stepping behavior in debugger, we overwrite debug + // locations of macro expansions with that of the outermost expansion site + // (unless the crate is being compiled with `-Z debug-macros`). + let span = if !span.from_expansion() || tcx.sess.opts.unstable_opts.debug_macros { + span + } else { + // Walk up the macro expansion chain until we reach a non-expanded span. + // We also stop at the function body level because no line stepping can occur + // at the level above that. + rustc_span::hygiene::walk_chain(span, function_span.ctxt()) + }; - let dir_id = if !dir_name.is_empty() { - let dir_name = LineString::new(dir_name, line_program.encoding(), line_strings); - line_program.add_directory(dir_name) - } else { - line_program.default_directory() - }; - let file_name = LineString::new(file_name, line_program.encoding(), line_strings); + match tcx.sess.source_map().lookup_line(span.lo()) { + Ok(SourceFileAndLine { sf: file, line }) => { + let line_pos = file.line_begin_pos(span.lo()); - let info = make_file_info(file.src_hash); - - line_program.file_has_md5 &= info.is_some(); - line_program.add_file(file_name, dir_id, info) + ( + file, + u64::try_from(line).unwrap() + 1, + u64::from((span.lo() - line_pos).to_u32()) + 1, + ) + } + Err(file) => (file, 0, 0), } - // FIXME give more appropriate file names - filename => { - let dir_id = line_program.default_directory(); - let dummy_file_name = LineString::new( - filename.prefer_remapped().to_string().into_bytes(), - line_program.encoding(), - line_strings, - ); - line_program.add_file(dummy_file_name, dir_id, None) + } + + pub(crate) fn add_source_file(&mut self, source_file: &SourceFile) -> FileId { + let line_program: &mut LineProgram = &mut self.dwarf.unit.line_program; + let line_strings: &mut LineStringTable = &mut self.dwarf.line_strings; + + match &source_file.name { + FileName::Real(path) => { + let (dir_path, file_name) = + split_path_dir_and_file(path.remapped_path_if_available()); + let dir_name = osstr_as_utf8_bytes(dir_path.as_os_str()); + let file_name = osstr_as_utf8_bytes(file_name); + + let dir_id = if !dir_name.is_empty() { + let dir_name = LineString::new(dir_name, line_program.encoding(), line_strings); + line_program.add_directory(dir_name) + } else { + line_program.default_directory() + }; + let file_name = LineString::new(file_name, line_program.encoding(), line_strings); + + let info = make_file_info(source_file.src_hash); + + line_program.file_has_md5 &= info.is_some(); + line_program.add_file(file_name, dir_id, info) + } + // FIXME give more appropriate file names + filename => { + let dir_id = line_program.default_directory(); + let dummy_file_name = LineString::new( + filename.prefer_remapped().to_string().into_bytes(), + line_program.encoding(), + line_strings, + ); + line_program.add_file(dummy_file_name, dir_id, None) + } } } } -impl<'tcx> DebugContext<'tcx> { - pub(super) fn emit_location(&mut self, entry_id: UnitEntryId, span: Span) { - let loc = self.tcx.sess.source_map().lookup_char_pos(span.lo()); - - let file_id = line_program_add_file( - &mut self.dwarf.unit.line_program, - &mut self.dwarf.line_strings, - &loc.file, - ); - - let entry = self.dwarf.unit.get_mut(entry_id); - - entry.set(gimli::DW_AT_decl_file, AttributeValue::FileIndex(Some(file_id))); - entry.set(gimli::DW_AT_decl_line, AttributeValue::Udata(loc.line as u64)); - entry.set(gimli::DW_AT_decl_column, AttributeValue::Udata(loc.col.to_usize() as u64)); +impl FunctionDebugContext { + pub(crate) fn add_dbg_loc(&mut self, file_id: FileId, line: u64, column: u64) -> SourceLoc { + let (index, _) = self.source_loc_set.insert_full((file_id, line, column)); + SourceLoc::new(u32::try_from(index).unwrap()) } pub(super) fn create_debug_lines( &mut self, + debug_context: &mut DebugContext, symbol: usize, - entry_id: UnitEntryId, context: &Context, - function_span: Span, - source_info_set: &indexmap::IndexSet, ) -> CodeOffset { - let tcx = self.tcx; - let line_program = &mut self.dwarf.unit.line_program; + let create_row_for_span = + |debug_context: &mut DebugContext, source_loc: (FileId, u64, u64)| { + let (file_id, line, col) = source_loc; - let line_strings = &mut self.dwarf.line_strings; - let mut last_span = None; - let mut last_file = None; - let mut create_row_for_span = |line_program: &mut LineProgram, span: Span| { - if let Some(last_span) = last_span { - if span == last_span { - line_program.generate_row(); - return; - } - } - last_span = Some(span); - - // Based on https://github.com/rust-lang/rust/blob/e369d87b015a84653343032833d65d0545fd3f26/src/librustc_codegen_ssa/mir/mod.rs#L116-L131 - // In order to have a good line stepping behavior in debugger, we overwrite debug - // locations of macro expansions with that of the outermost expansion site - // (unless the crate is being compiled with `-Z debug-macros`). - let span = if !span.from_expansion() || tcx.sess.opts.unstable_opts.debug_macros { - span - } else { - // Walk up the macro expansion chain until we reach a non-expanded span. - // We also stop at the function body level because no line stepping can occur - // at the level above that. - rustc_span::hygiene::walk_chain(span, function_span.ctxt()) + debug_context.dwarf.unit.line_program.row().file = file_id; + debug_context.dwarf.unit.line_program.row().line = line; + debug_context.dwarf.unit.line_program.row().column = col; + debug_context.dwarf.unit.line_program.generate_row(); }; - let (file, line, col) = match tcx.sess.source_map().lookup_line(span.lo()) { - Ok(SourceFileAndLine { sf: file, line }) => { - let line_pos = file.line_begin_pos(span.lo()); - - ( - file, - u64::try_from(line).unwrap() + 1, - u64::from((span.lo() - line_pos).to_u32()) + 1, - ) - } - Err(file) => (file, 0, 0), - }; - - // line_program_add_file is very slow. - // Optimize for the common case of the current file not being changed. - let current_file_changed = if let Some(last_file) = &last_file { - // If the allocations are not equal, then the files may still be equal, but that - // is not a problem, as this is just an optimization. - !rustc_data_structures::sync::Lrc::ptr_eq(last_file, &file) - } else { - true - }; - if current_file_changed { - let file_id = line_program_add_file(line_program, line_strings, &file); - line_program.row().file = file_id; - last_file = Some(file); - } - - line_program.row().line = line; - line_program.row().column = col; - line_program.generate_row(); - }; - - line_program.begin_sequence(Some(Address::Symbol { symbol, addend: 0 })); + debug_context + .dwarf + .unit + .line_program + .begin_sequence(Some(Address::Symbol { symbol, addend: 0 })); let mut func_end = 0; - let mcr = context.mach_compile_result.as_ref().unwrap(); + let mcr = context.compiled_code().unwrap(); for &MachSrcLoc { start, end, loc } in mcr.buffer.get_srclocs_sorted() { - line_program.row().address_offset = u64::from(start); + debug_context.dwarf.unit.line_program.row().address_offset = u64::from(start); if !loc.is_default() { - let source_info = *source_info_set.get_index(loc.bits() as usize).unwrap(); - create_row_for_span(line_program, source_info.span); + let source_loc = *self.source_loc_set.get_index(loc.bits() as usize).unwrap(); + create_row_for_span(debug_context, source_loc); } else { - create_row_for_span(line_program, function_span); + create_row_for_span(debug_context, self.function_source_loc); } func_end = end; } - line_program.end_sequence(u64::from(func_end)); + debug_context.dwarf.unit.line_program.end_sequence(u64::from(func_end)); let func_end = mcr.buffer.total_size(); assert_ne!(func_end, 0); - let entry = self.dwarf.unit.get_mut(entry_id); + let entry = debug_context.dwarf.unit.get_mut(self.entry_id); entry.set( gimli::DW_AT_low_pc, AttributeValue::Address(Address::Symbol { symbol, addend: 0 }), ); entry.set(gimli::DW_AT_high_pc, AttributeValue::Udata(u64::from(func_end))); - self.emit_location(entry_id, function_span); - func_end } } diff --git a/src/debuginfo/mod.rs b/src/debuginfo/mod.rs index 693092ba543e..c55db2017ee6 100644 --- a/src/debuginfo/mod.rs +++ b/src/debuginfo/mod.rs @@ -7,35 +7,34 @@ mod unwind; use crate::prelude::*; -use rustc_index::vec::IndexVec; - -use cranelift_codegen::entity::EntityRef; -use cranelift_codegen::ir::{Endianness, LabelValueLoc, ValueLabel}; +use cranelift_codegen::ir::Endianness; use cranelift_codegen::isa::TargetIsa; -use cranelift_codegen::ValueLocRange; use gimli::write::{ - Address, AttributeValue, DwarfUnit, Expression, LineProgram, LineString, Location, - LocationList, Range, RangeList, UnitEntryId, + Address, AttributeValue, DwarfUnit, FileId, LineProgram, LineString, Range, RangeList, + UnitEntryId, }; -use gimli::{Encoding, Format, LineEncoding, RunTimeEndian, X86_64}; +use gimli::{Encoding, Format, LineEncoding, RunTimeEndian}; +use indexmap::IndexSet; pub(crate) use emit::{DebugReloc, DebugRelocName}; pub(crate) use unwind::UnwindContext; -pub(crate) struct DebugContext<'tcx> { - tcx: TyCtxt<'tcx>, - +pub(crate) struct DebugContext { endian: RunTimeEndian, dwarf: DwarfUnit, unit_range_list: RangeList, - - types: FxHashMap, UnitEntryId>, } -impl<'tcx> DebugContext<'tcx> { - pub(crate) fn new(tcx: TyCtxt<'tcx>, isa: &dyn TargetIsa) -> Self { +pub(crate) struct FunctionDebugContext { + entry_id: UnitEntryId, + function_source_loc: (FileId, u64, u64), + source_loc_set: indexmap::IndexSet<(FileId, u64, u64)>, +} + +impl DebugContext { + pub(crate) fn new(tcx: TyCtxt<'_>, isa: &dyn TargetIsa) -> Self { let encoding = Encoding { format: Format::Dwarf32, // FIXME this should be configurable @@ -101,127 +100,18 @@ impl<'tcx> DebugContext<'tcx> { root.set(gimli::DW_AT_low_pc, AttributeValue::Address(Address::Constant(0))); } - DebugContext { - tcx, - - endian, - - dwarf, - unit_range_list: RangeList(Vec::new()), - - types: FxHashMap::default(), - } - } - - fn dwarf_ty(&mut self, ty: Ty<'tcx>) -> UnitEntryId { - if let Some(type_id) = self.types.get(&ty) { - return *type_id; - } - - let new_entry = |dwarf: &mut DwarfUnit, tag| dwarf.unit.add(dwarf.unit.root(), tag); - - let primitive = |dwarf: &mut DwarfUnit, ate| { - let type_id = new_entry(dwarf, gimli::DW_TAG_base_type); - let type_entry = dwarf.unit.get_mut(type_id); - type_entry.set(gimli::DW_AT_encoding, AttributeValue::Encoding(ate)); - type_id - }; - - let name = format!("{}", ty); - let layout = self.tcx.layout_of(ParamEnv::reveal_all().and(ty)).unwrap(); - - let type_id = match ty.kind() { - ty::Bool => primitive(&mut self.dwarf, gimli::DW_ATE_boolean), - ty::Char => primitive(&mut self.dwarf, gimli::DW_ATE_UTF), - ty::Uint(_) => primitive(&mut self.dwarf, gimli::DW_ATE_unsigned), - ty::Int(_) => primitive(&mut self.dwarf, gimli::DW_ATE_signed), - ty::Float(_) => primitive(&mut self.dwarf, gimli::DW_ATE_float), - ty::Ref(_, pointee_ty, _mutbl) - | ty::RawPtr(ty::TypeAndMut { ty: pointee_ty, mutbl: _mutbl }) => { - let type_id = new_entry(&mut self.dwarf, gimli::DW_TAG_pointer_type); - - // Ensure that type is inserted before recursing to avoid duplicates - self.types.insert(ty, type_id); - - let pointee = self.dwarf_ty(*pointee_ty); - - let type_entry = self.dwarf.unit.get_mut(type_id); - - //type_entry.set(gimli::DW_AT_mutable, AttributeValue::Flag(mutbl == rustc_hir::Mutability::Mut)); - type_entry.set(gimli::DW_AT_type, AttributeValue::UnitRef(pointee)); - - type_id - } - ty::Adt(adt_def, _substs) if adt_def.is_struct() && !layout.is_unsized() => { - let type_id = new_entry(&mut self.dwarf, gimli::DW_TAG_structure_type); - - // Ensure that type is inserted before recursing to avoid duplicates - self.types.insert(ty, type_id); - - let variant = adt_def.non_enum_variant(); - - for (field_idx, field_def) in variant.fields.iter().enumerate() { - let field_offset = layout.fields.offset(field_idx); - let field_layout = layout.field( - &layout::LayoutCx { tcx: self.tcx, param_env: ParamEnv::reveal_all() }, - field_idx, - ); - - let field_type = self.dwarf_ty(field_layout.ty); - - let field_id = self.dwarf.unit.add(type_id, gimli::DW_TAG_member); - let field_entry = self.dwarf.unit.get_mut(field_id); - - field_entry.set( - gimli::DW_AT_name, - AttributeValue::String(field_def.name.as_str().to_string().into_bytes()), - ); - field_entry.set( - gimli::DW_AT_data_member_location, - AttributeValue::Udata(field_offset.bytes()), - ); - field_entry.set(gimli::DW_AT_type, AttributeValue::UnitRef(field_type)); - } - - type_id - } - _ => new_entry(&mut self.dwarf, gimli::DW_TAG_structure_type), - }; - - let type_entry = self.dwarf.unit.get_mut(type_id); - - type_entry.set(gimli::DW_AT_name, AttributeValue::String(name.into_bytes())); - type_entry.set(gimli::DW_AT_byte_size, AttributeValue::Udata(layout.size.bytes())); - - self.types.insert(ty, type_id); - - type_id - } - - fn define_local(&mut self, scope: UnitEntryId, name: String, ty: Ty<'tcx>) -> UnitEntryId { - let dw_ty = self.dwarf_ty(ty); - - let var_id = self.dwarf.unit.add(scope, gimli::DW_TAG_variable); - let var_entry = self.dwarf.unit.get_mut(var_id); - - var_entry.set(gimli::DW_AT_name, AttributeValue::String(name.into_bytes())); - var_entry.set(gimli::DW_AT_type, AttributeValue::UnitRef(dw_ty)); - - var_id + DebugContext { endian, dwarf, unit_range_list: RangeList(Vec::new()) } } pub(crate) fn define_function( &mut self, - instance: Instance<'tcx>, - func_id: FuncId, + tcx: TyCtxt<'_>, name: &str, - isa: &dyn TargetIsa, - context: &Context, - source_info_set: &indexmap::IndexSet, - local_map: IndexVec>, - ) { - let symbol = func_id.as_u32() as usize; - let mir = self.tcx.instance_mir(instance.def); + function_span: Span, + ) -> FunctionDebugContext { + let (file, line, column) = DebugContext::get_span_loc(tcx, function_span, function_span); + + let file_id = self.add_source_file(&file); // FIXME: add to appropriate scope instead of root let scope = self.dwarf.unit.root(); @@ -233,14 +123,35 @@ impl<'tcx> DebugContext<'tcx> { entry.set(gimli::DW_AT_name, AttributeValue::StringRef(name_id)); entry.set(gimli::DW_AT_linkage_name, AttributeValue::StringRef(name_id)); - let end = self.create_debug_lines(symbol, entry_id, context, mir.span, source_info_set); + entry.set(gimli::DW_AT_decl_file, AttributeValue::FileIndex(Some(file_id))); + entry.set(gimli::DW_AT_decl_line, AttributeValue::Udata(line)); + entry.set(gimli::DW_AT_decl_column, AttributeValue::Udata(column)); - self.unit_range_list.0.push(Range::StartLength { + FunctionDebugContext { + entry_id, + function_source_loc: (file_id, line, column), + source_loc_set: IndexSet::new(), + } + } +} + +impl FunctionDebugContext { + pub(crate) fn finalize( + mut self, + debug_context: &mut DebugContext, + func_id: FuncId, + context: &Context, + ) { + let symbol = func_id.as_u32() as usize; + + let end = self.create_debug_lines(debug_context, symbol, context); + + debug_context.unit_range_list.0.push(Range::StartLength { begin: Address::Symbol { symbol, addend: 0 }, length: u64::from(end), }); - let func_entry = self.dwarf.unit.get_mut(entry_id); + let func_entry = debug_context.dwarf.unit.get_mut(self.entry_id); // Gdb requires both DW_AT_low_pc and DW_AT_high_pc. Otherwise the DW_TAG_subprogram is skipped. func_entry.set( gimli::DW_AT_low_pc, @@ -248,110 +159,5 @@ impl<'tcx> DebugContext<'tcx> { ); // Using Udata for DW_AT_high_pc requires at least DWARF4 func_entry.set(gimli::DW_AT_high_pc, AttributeValue::Udata(u64::from(end))); - - // FIXME make it more reliable and implement scopes before re-enabling this. - if false { - let value_labels_ranges = std::collections::HashMap::new(); // FIXME - - for (local, _local_decl) in mir.local_decls.iter_enumerated() { - let ty = self.tcx.subst_and_normalize_erasing_regions( - instance.substs, - ty::ParamEnv::reveal_all(), - mir.local_decls[local].ty, - ); - let var_id = self.define_local(entry_id, format!("{:?}", local), ty); - - let location = place_location( - self, - isa, - symbol, - &local_map, - &value_labels_ranges, - Place { local, projection: ty::List::empty() }, - ); - - let var_entry = self.dwarf.unit.get_mut(var_id); - var_entry.set(gimli::DW_AT_location, location); - } - } - - // FIXME create locals for all entries in mir.var_debug_info - } -} - -fn place_location<'tcx>( - debug_context: &mut DebugContext<'tcx>, - isa: &dyn TargetIsa, - symbol: usize, - local_map: &IndexVec>, - #[allow(rustc::default_hash_types)] value_labels_ranges: &std::collections::HashMap< - ValueLabel, - Vec, - >, - place: Place<'tcx>, -) -> AttributeValue { - assert!(place.projection.is_empty()); // FIXME implement them - - match local_map[place.local].inner() { - CPlaceInner::Var(_local, var) => { - let value_label = cranelift_codegen::ir::ValueLabel::new(var.index()); - if let Some(value_loc_ranges) = value_labels_ranges.get(&value_label) { - let loc_list = LocationList( - value_loc_ranges - .iter() - .map(|value_loc_range| Location::StartEnd { - begin: Address::Symbol { - symbol, - addend: i64::from(value_loc_range.start), - }, - end: Address::Symbol { symbol, addend: i64::from(value_loc_range.end) }, - data: translate_loc(isa, value_loc_range.loc).unwrap(), - }) - .collect(), - ); - let loc_list_id = debug_context.dwarf.unit.locations.add(loc_list); - - AttributeValue::LocationListRef(loc_list_id) - } else { - // FIXME set value labels for unused locals - - AttributeValue::Exprloc(Expression::new()) - } - } - CPlaceInner::VarPair(_, _, _) => { - // FIXME implement this - - AttributeValue::Exprloc(Expression::new()) - } - CPlaceInner::VarLane(_, _, _) => { - // FIXME implement this - - AttributeValue::Exprloc(Expression::new()) - } - CPlaceInner::Addr(_, _) => { - // FIXME implement this (used by arguments and returns) - - AttributeValue::Exprloc(Expression::new()) - - // For PointerBase::Stack: - //AttributeValue::Exprloc(translate_loc(ValueLoc::Stack(*stack_slot)).unwrap()) - } - } -} - -// Adapted from https://github.com/CraneStation/wasmtime/blob/5a1845b4caf7a5dba8eda1fef05213a532ed4259/crates/debug/src/transform/expression.rs#L59-L137 -fn translate_loc(isa: &dyn TargetIsa, loc: LabelValueLoc) -> Option { - match loc { - LabelValueLoc::Reg(reg) => { - let machine_reg = isa.map_regalloc_reg_to_dwarf(reg).unwrap(); - let mut expr = Expression::new(); - expr.op_reg(gimli::Register(machine_reg)); - Some(expr) - } - LabelValueLoc::SPOffset(offset) => { - let mut expr = Expression::new(); - expr.op_breg(X86_64::RSP, offset); - Some(expr) - } } } diff --git a/src/discriminant.rs b/src/discriminant.rs index f619bb5ed5e5..e41ae1fbdbac 100644 --- a/src/discriminant.rs +++ b/src/discriminant.rs @@ -62,16 +62,14 @@ pub(crate) fn codegen_set_discriminant<'tcx>( pub(crate) fn codegen_get_discriminant<'tcx>( fx: &mut FunctionCx<'_, '_, 'tcx>, + dest: CPlace<'tcx>, value: CValue<'tcx>, dest_layout: TyAndLayout<'tcx>, -) -> CValue<'tcx> { +) { let layout = value.layout(); - if layout.abi == Abi::Uninhabited { - let true_ = fx.bcx.ins().iconst(types::I32, 1); - fx.bcx.ins().trapnz(true_, TrapCode::UnreachableCodeReached); - // Return a dummy value - return CValue::by_ref(Pointer::const_addr(fx, 0), dest_layout); + if layout.abi.is_uninhabited() { + return; } let (tag_scalar, tag_field, tag_encoding) = match &layout.variants { @@ -89,7 +87,9 @@ pub(crate) fn codegen_get_discriminant<'tcx>( } else { ty::ScalarInt::try_from_uint(discr_val, dest_layout.size).unwrap() }; - return CValue::const_val(fx, dest_layout, discr_val); + let res = CValue::const_val(fx, dest_layout, discr_val); + dest.write_cvalue(fx, res); + return; } Variants::Multiple { tag, tag_field, tag_encoding, variants: _ } => { (tag, *tag_field, tag_encoding) @@ -110,7 +110,8 @@ pub(crate) fn codegen_get_discriminant<'tcx>( _ => false, }; let val = clif_intcast(fx, tag, cast_to, signed); - CValue::by_val(val, dest_layout) + let res = CValue::by_val(val, dest_layout); + dest.write_cvalue(fx, res); } TagEncoding::Niche { dataful_variant, ref niche_variants, niche_start } => { // Rebase from niche values to discriminants, and check @@ -170,7 +171,8 @@ pub(crate) fn codegen_get_discriminant<'tcx>( let dataful_variant = fx.bcx.ins().iconst(cast_to, i64::from(dataful_variant.as_u32())); let discr = fx.bcx.ins().select(is_niche, niche_discr, dataful_variant); - CValue::by_val(discr, dest_layout) + let res = CValue::by_val(discr, dest_layout); + dest.write_cvalue(fx, res); } } } diff --git a/src/driver/aot.rs b/src/driver/aot.rs index 3cd1ef5639ef..8eabe1cbcb15 100644 --- a/src/driver/aot.rs +++ b/src/driver/aot.rs @@ -1,33 +1,129 @@ //! The AOT driver uses [`cranelift_object`] to write object files suitable for linking into a //! standalone executable. +use std::fs::File; use std::path::PathBuf; +use std::sync::Arc; +use std::thread::JoinHandle; -use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_codegen_ssa::back::metadata::create_compressed_metadata_file; use rustc_codegen_ssa::{CodegenResults, CompiledModule, CrateInfo, ModuleKind}; +use rustc_data_structures::profiling::SelfProfilerRef; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_metadata::EncodedMetadata; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; use rustc_middle::mir::mono::{CodegenUnit, MonoItem}; use rustc_session::cgu_reuse_tracker::CguReuse; -use rustc_session::config::{DebugInfo, OutputType}; +use rustc_session::config::{DebugInfo, OutputFilenames, OutputType}; use rustc_session::Session; -use cranelift_codegen::isa::TargetIsa; use cranelift_object::{ObjectBuilder, ObjectModule}; +use crate::concurrency_limiter::{ConcurrencyLimiter, ConcurrencyLimiterToken}; +use crate::global_asm::GlobalAsmConfig; use crate::{prelude::*, BackendConfig}; -struct ModuleCodegenResult(CompiledModule, Option<(WorkProductId, WorkProduct)>); +struct ModuleCodegenResult { + module_regular: CompiledModule, + module_global_asm: Option, + existing_work_product: Option<(WorkProductId, WorkProduct)>, +} -impl HashStable for ModuleCodegenResult { +enum OngoingModuleCodegen { + Sync(Result), + Async(JoinHandle>), +} + +impl HashStable for OngoingModuleCodegen { fn hash_stable(&self, _: &mut HCX, _: &mut StableHasher) { // do nothing } } -fn make_module(sess: &Session, isa: Box, name: String) -> ObjectModule { +pub(crate) struct OngoingCodegen { + modules: Vec, + allocator_module: Option, + metadata_module: Option, + metadata: EncodedMetadata, + crate_info: CrateInfo, + concurrency_limiter: ConcurrencyLimiter, +} + +impl OngoingCodegen { + pub(crate) fn join( + self, + sess: &Session, + backend_config: &BackendConfig, + ) -> (CodegenResults, FxHashMap) { + let mut work_products = FxHashMap::default(); + let mut modules = vec![]; + + for module_codegen in self.modules { + let module_codegen_result = match module_codegen { + OngoingModuleCodegen::Sync(module_codegen_result) => module_codegen_result, + OngoingModuleCodegen::Async(join_handle) => match join_handle.join() { + Ok(module_codegen_result) => module_codegen_result, + Err(panic) => std::panic::resume_unwind(panic), + }, + }; + + let module_codegen_result = match module_codegen_result { + Ok(module_codegen_result) => module_codegen_result, + Err(err) => sess.fatal(&err), + }; + let ModuleCodegenResult { module_regular, module_global_asm, existing_work_product } = + module_codegen_result; + + if let Some((work_product_id, work_product)) = existing_work_product { + work_products.insert(work_product_id, work_product); + } else { + let work_product = if backend_config.disable_incr_cache { + None + } else if let Some(module_global_asm) = &module_global_asm { + rustc_incremental::copy_cgu_workproduct_to_incr_comp_cache_dir( + sess, + &module_regular.name, + &[ + ("o", &module_regular.object.as_ref().unwrap()), + ("asm.o", &module_global_asm.object.as_ref().unwrap()), + ], + ) + } else { + rustc_incremental::copy_cgu_workproduct_to_incr_comp_cache_dir( + sess, + &module_regular.name, + &[("o", &module_regular.object.as_ref().unwrap())], + ) + }; + if let Some((work_product_id, work_product)) = work_product { + work_products.insert(work_product_id, work_product); + } + } + + modules.push(module_regular); + if let Some(module_global_asm) = module_global_asm { + modules.push(module_global_asm); + } + } + + drop(self.concurrency_limiter); + + ( + CodegenResults { + modules, + allocator_module: self.allocator_module, + metadata_module: self.metadata_module, + metadata: self.metadata, + crate_info: self.crate_info, + }, + work_products, + ) + } +} + +fn make_module(sess: &Session, backend_config: &BackendConfig, name: String) -> ObjectModule { + let isa = crate::build_isa(sess, backend_config); + let mut builder = ObjectBuilder::new(isa, name + ".o", cranelift_module::default_libcall_names()).unwrap(); // Unlike cg_llvm, cg_clif defaults to disabling -Zfunction-sections. For cg_llvm binary size @@ -37,15 +133,15 @@ fn make_module(sess: &Session, isa: Box, name: String) -> ObjectM ObjectModule::new(builder) } -fn emit_module( - tcx: TyCtxt<'_>, - backend_config: &BackendConfig, +fn emit_cgu( + output_filenames: &OutputFilenames, + prof: &SelfProfilerRef, name: String, - kind: ModuleKind, module: ObjectModule, - debug: Option>, + debug: Option, unwind_context: UnwindContext, -) -> ModuleCodegenResult { + global_asm_object_file: Option, +) -> Result { let mut product = module.finish(); if let Some(mut debug) = debug { @@ -54,134 +150,191 @@ fn emit_module( unwind_context.emit(&mut product); - let tmp_file = tcx.output_filenames(()).temp_path(OutputType::Object, Some(&name)); - let obj = product.object.write().unwrap(); + let module_regular = + emit_module(output_filenames, prof, product.object, ModuleKind::Regular, name.clone())?; - tcx.sess.prof.artifact_size("object_file", name.clone(), obj.len().try_into().unwrap()); + Ok(ModuleCodegenResult { + module_regular, + module_global_asm: global_asm_object_file.map(|global_asm_object_file| CompiledModule { + name: format!("{name}.asm"), + kind: ModuleKind::Regular, + object: Some(global_asm_object_file), + dwarf_object: None, + bytecode: None, + }), + existing_work_product: None, + }) +} - if let Err(err) = std::fs::write(&tmp_file, obj) { - tcx.sess.fatal(&format!("error writing object file: {}", err)); - } - - let work_product = if backend_config.disable_incr_cache { - None - } else { - rustc_incremental::copy_cgu_workproduct_to_incr_comp_cache_dir( - tcx.sess, - &name, - &[("o", &tmp_file)], - ) +fn emit_module( + output_filenames: &OutputFilenames, + prof: &SelfProfilerRef, + object: cranelift_object::object::write::Object<'_>, + kind: ModuleKind, + name: String, +) -> Result { + let tmp_file = output_filenames.temp_path(OutputType::Object, Some(&name)); + let mut file = match File::create(&tmp_file) { + Ok(file) => file, + Err(err) => return Err(format!("error creating object file: {}", err)), }; - ModuleCodegenResult( - CompiledModule { name, kind, object: Some(tmp_file), dwarf_object: None, bytecode: None }, - work_product, - ) + if let Err(err) = object.write_stream(&mut file) { + return Err(format!("error writing object file: {}", err)); + } + + prof.artifact_size("object_file", &*name, file.metadata().unwrap().len()); + + Ok(CompiledModule { name, kind, object: Some(tmp_file), dwarf_object: None, bytecode: None }) } fn reuse_workproduct_for_cgu( tcx: TyCtxt<'_>, cgu: &CodegenUnit<'_>, - work_products: &mut FxHashMap, -) -> CompiledModule { +) -> Result { let work_product = cgu.previous_work_product(tcx); - let obj_out = tcx.output_filenames(()).temp_path(OutputType::Object, Some(cgu.name().as_str())); - let source_file = rustc_incremental::in_incr_comp_dir_sess( + let obj_out_regular = + tcx.output_filenames(()).temp_path(OutputType::Object, Some(cgu.name().as_str())); + let source_file_regular = rustc_incremental::in_incr_comp_dir_sess( &tcx.sess, &work_product.saved_files.get("o").expect("no saved object file in work product"), ); - if let Err(err) = rustc_fs_util::link_or_copy(&source_file, &obj_out) { - tcx.sess.err(&format!( + + if let Err(err) = rustc_fs_util::link_or_copy(&source_file_regular, &obj_out_regular) { + return Err(format!( "unable to copy {} to {}: {}", - source_file.display(), - obj_out.display(), + source_file_regular.display(), + obj_out_regular.display(), err )); } + let obj_out_global_asm = + crate::global_asm::add_file_stem_postfix(obj_out_regular.clone(), ".asm"); + let has_global_asm = if let Some(asm_o) = work_product.saved_files.get("asm.o") { + let source_file_global_asm = rustc_incremental::in_incr_comp_dir_sess(&tcx.sess, asm_o); + if let Err(err) = rustc_fs_util::link_or_copy(&source_file_global_asm, &obj_out_global_asm) + { + return Err(format!( + "unable to copy {} to {}: {}", + source_file_regular.display(), + obj_out_regular.display(), + err + )); + } + true + } else { + false + }; - work_products.insert(cgu.work_product_id(), work_product); - - CompiledModule { - name: cgu.name().to_string(), - kind: ModuleKind::Regular, - object: Some(obj_out), - dwarf_object: None, - bytecode: None, - } + Ok(ModuleCodegenResult { + module_regular: CompiledModule { + name: cgu.name().to_string(), + kind: ModuleKind::Regular, + object: Some(obj_out_regular), + dwarf_object: None, + bytecode: None, + }, + module_global_asm: if has_global_asm { + Some(CompiledModule { + name: cgu.name().to_string(), + kind: ModuleKind::Regular, + object: Some(obj_out_global_asm), + dwarf_object: None, + bytecode: None, + }) + } else { + None + }, + existing_work_product: Some((cgu.work_product_id(), work_product)), + }) } fn module_codegen( tcx: TyCtxt<'_>, - (backend_config, cgu_name): (BackendConfig, rustc_span::Symbol), -) -> ModuleCodegenResult { - let cgu = tcx.codegen_unit(cgu_name); - let mono_items = cgu.items_in_deterministic_order(tcx); + (backend_config, global_asm_config, cgu_name, token): ( + BackendConfig, + Arc, + rustc_span::Symbol, + ConcurrencyLimiterToken, + ), +) -> OngoingModuleCodegen { + let (cgu_name, mut cx, mut module, codegened_functions) = tcx.sess.time("codegen cgu", || { + let cgu = tcx.codegen_unit(cgu_name); + let mono_items = cgu.items_in_deterministic_order(tcx); - let isa = crate::build_isa(tcx.sess, &backend_config); - let mut module = make_module(tcx.sess, isa, cgu_name.as_str().to_string()); + let mut module = make_module(tcx.sess, &backend_config, cgu_name.as_str().to_string()); - let mut cx = crate::CodegenCx::new( - tcx, - backend_config.clone(), - module.isa(), - tcx.sess.opts.debuginfo != DebugInfo::None, - cgu_name, - ); - super::predefine_mono_items(tcx, &mut module, &mono_items); - for (mono_item, _) in mono_items { - match mono_item { - MonoItem::Fn(inst) => { - cx.tcx - .sess - .time("codegen fn", || crate::base::codegen_fn(&mut cx, &mut module, inst)); - } - MonoItem::Static(def_id) => crate::constant::codegen_static(tcx, &mut module, def_id), - MonoItem::GlobalAsm(item_id) => { - let item = cx.tcx.hir().item(item_id); - if let rustc_hir::ItemKind::GlobalAsm(asm) = item.kind { - if !asm.options.contains(InlineAsmOptions::ATT_SYNTAX) { - cx.global_asm.push_str("\n.intel_syntax noprefix\n"); - } else { - cx.global_asm.push_str("\n.att_syntax\n"); - } - for piece in asm.template { - match *piece { - InlineAsmTemplatePiece::String(ref s) => cx.global_asm.push_str(s), - InlineAsmTemplatePiece::Placeholder { .. } => todo!(), - } - } - cx.global_asm.push_str("\n.att_syntax\n\n"); - } else { - bug!("Expected GlobalAsm found {:?}", item); + let mut cx = crate::CodegenCx::new( + tcx, + backend_config.clone(), + module.isa(), + tcx.sess.opts.debuginfo != DebugInfo::None, + cgu_name, + ); + super::predefine_mono_items(tcx, &mut module, &mono_items); + let mut codegened_functions = vec![]; + for (mono_item, _) in mono_items { + match mono_item { + MonoItem::Fn(inst) => { + tcx.sess.time("codegen fn", || { + let codegened_function = crate::base::codegen_fn( + tcx, + &mut cx, + Function::new(), + &mut module, + inst, + ); + codegened_functions.push(codegened_function); + }); + } + MonoItem::Static(def_id) => { + crate::constant::codegen_static(tcx, &mut module, def_id) + } + MonoItem::GlobalAsm(item_id) => { + crate::global_asm::codegen_global_asm_item(tcx, &mut cx.global_asm, item_id); } } } - } - crate::main_shim::maybe_create_entry_wrapper( - tcx, - &mut module, - &mut cx.unwind_context, - false, - cgu.is_primary(), - ); - - let debug_context = cx.debug_context; - let unwind_context = cx.unwind_context; - let codegen_result = tcx.sess.time("write object file", || { - emit_module( + crate::main_shim::maybe_create_entry_wrapper( tcx, - &backend_config, - cgu.name().as_str().to_string(), - ModuleKind::Regular, - module, - debug_context, - unwind_context, - ) + &mut module, + &mut cx.unwind_context, + false, + cgu.is_primary(), + ); + + let cgu_name = cgu.name().as_str().to_owned(); + + (cgu_name, cx, module, codegened_functions) }); - codegen_global_asm(tcx, cgu.name().as_str(), &cx.global_asm); + OngoingModuleCodegen::Async(std::thread::spawn(move || { + cx.profiler.clone().verbose_generic_activity("compile functions").run(|| { + let mut cached_context = Context::new(); + for codegened_func in codegened_functions { + crate::base::compile_fn(&mut cx, &mut cached_context, &mut module, codegened_func); + } + }); - codegen_result + let global_asm_object_file = + cx.profiler.verbose_generic_activity("compile assembly").run(|| { + crate::global_asm::compile_global_asm(&global_asm_config, &cgu_name, &cx.global_asm) + })?; + + let codegen_result = cx.profiler.verbose_generic_activity("write object file").run(|| { + emit_cgu( + &global_asm_config.output_filenames, + &cx.profiler, + cgu_name, + module, + cx.debug_context, + cx.unwind_context, + global_asm_object_file, + ) + }); + std::mem::drop(token); + codegen_result + })) } pub(crate) fn run_aot( @@ -189,9 +342,7 @@ pub(crate) fn run_aot( backend_config: BackendConfig, metadata: EncodedMetadata, need_metadata_module: bool, -) -> Box<(CodegenResults, FxHashMap)> { - let mut work_products = FxHashMap::default(); - +) -> Box { let cgus = if tcx.sess.opts.output_types.should_codegen() { tcx.collect_and_partition_mono_items(()).1 } else { @@ -206,62 +357,69 @@ pub(crate) fn run_aot( } } + let global_asm_config = Arc::new(crate::global_asm::GlobalAsmConfig::new(tcx)); + + let mut concurrency_limiter = ConcurrencyLimiter::new(tcx.sess, cgus.len()); + let modules = super::time(tcx, backend_config.display_cg_time, "codegen mono items", || { cgus.iter() .map(|cgu| { - let cgu_reuse = determine_cgu_reuse(tcx, cgu); + let cgu_reuse = if backend_config.disable_incr_cache { + CguReuse::No + } else { + determine_cgu_reuse(tcx, cgu) + }; tcx.sess.cgu_reuse_tracker.set_actual_reuse(cgu.name().as_str(), cgu_reuse); match cgu_reuse { - _ if backend_config.disable_incr_cache => {} - CguReuse::No => {} - CguReuse::PreLto => { - return reuse_workproduct_for_cgu(tcx, &*cgu, &mut work_products); + CguReuse::No => { + let dep_node = cgu.codegen_dep_node(tcx); + tcx.dep_graph + .with_task( + dep_node, + tcx, + ( + backend_config.clone(), + global_asm_config.clone(), + cgu.name(), + concurrency_limiter.acquire(), + ), + module_codegen, + Some(rustc_middle::dep_graph::hash_result), + ) + .0 + } + CguReuse::PreLto => unreachable!(), + CguReuse::PostLto => { + concurrency_limiter.job_already_done(); + OngoingModuleCodegen::Sync(reuse_workproduct_for_cgu(tcx, &*cgu)) } - CguReuse::PostLto => unreachable!(), } - - let dep_node = cgu.codegen_dep_node(tcx); - let (ModuleCodegenResult(module, work_product), _) = tcx.dep_graph.with_task( - dep_node, - tcx, - (backend_config.clone(), cgu.name()), - module_codegen, - Some(rustc_middle::dep_graph::hash_result), - ); - - if let Some((id, product)) = work_product { - work_products.insert(id, product); - } - - module }) .collect::>() }); tcx.sess.abort_if_errors(); - let isa = crate::build_isa(tcx.sess, &backend_config); - let mut allocator_module = make_module(tcx.sess, isa, "allocator_shim".to_string()); - assert_eq!(pointer_ty(tcx), allocator_module.target_config().pointer_type()); + let mut allocator_module = make_module(tcx.sess, &backend_config, "allocator_shim".to_string()); let mut allocator_unwind_context = UnwindContext::new(allocator_module.isa(), true); let created_alloc_shim = crate::allocator::codegen(tcx, &mut allocator_module, &mut allocator_unwind_context); let allocator_module = if created_alloc_shim { - let ModuleCodegenResult(module, work_product) = emit_module( - tcx, - &backend_config, - "allocator_shim".to_string(), + let mut product = allocator_module.finish(); + allocator_unwind_context.emit(&mut product); + + match emit_module( + tcx.output_filenames(()), + &tcx.sess.prof, + product.object, ModuleKind::Allocator, - allocator_module, - None, - allocator_unwind_context, - ); - if let Some((id, product)) = work_product { - work_products.insert(id, product); + "allocator_shim".to_owned(), + ) { + Ok(allocator_module) => Some(allocator_module), + Err(err) => tcx.sess.fatal(err), } - Some(module) } else { None }; @@ -308,102 +466,14 @@ pub(crate) fn run_aot( } .to_owned(); - Box::new(( - CodegenResults { - modules, - allocator_module, - metadata_module, - metadata, - crate_info: CrateInfo::new(tcx, target_cpu), - }, - work_products, - )) -} - -fn codegen_global_asm(tcx: TyCtxt<'_>, cgu_name: &str, global_asm: &str) { - use std::io::Write; - use std::process::{Command, Stdio}; - - if global_asm.is_empty() { - return; - } - - if cfg!(not(feature = "inline_asm")) - || tcx.sess.target.is_like_osx - || tcx.sess.target.is_like_windows - { - if global_asm.contains("__rust_probestack") { - return; - } - - // FIXME fix linker error on macOS - if cfg!(not(feature = "inline_asm")) { - tcx.sess.fatal( - "asm! and global_asm! support is disabled while compiling rustc_codegen_cranelift", - ); - } else { - tcx.sess.fatal("asm! and global_asm! are not yet supported on macOS and Windows"); - } - } - - let assembler = crate::toolchain::get_toolchain_binary(tcx.sess, "as"); - let linker = crate::toolchain::get_toolchain_binary(tcx.sess, "ld"); - - // Remove all LLVM style comments - let global_asm = global_asm - .lines() - .map(|line| if let Some(index) = line.find("//") { &line[0..index] } else { line }) - .collect::>() - .join("\n"); - - let output_object_file = tcx.output_filenames(()).temp_path(OutputType::Object, Some(cgu_name)); - - // Assemble `global_asm` - let global_asm_object_file = add_file_stem_postfix(output_object_file.clone(), ".asm"); - let mut child = Command::new(assembler) - .arg("-o") - .arg(&global_asm_object_file) - .stdin(Stdio::piped()) - .spawn() - .expect("Failed to spawn `as`."); - child.stdin.take().unwrap().write_all(global_asm.as_bytes()).unwrap(); - let status = child.wait().expect("Failed to wait for `as`."); - if !status.success() { - tcx.sess.fatal(&format!("Failed to assemble `{}`", global_asm)); - } - - // Link the global asm and main object file together - let main_object_file = add_file_stem_postfix(output_object_file.clone(), ".main"); - std::fs::rename(&output_object_file, &main_object_file).unwrap(); - let status = Command::new(linker) - .arg("-r") // Create a new object file - .arg("-o") - .arg(output_object_file) - .arg(&main_object_file) - .arg(&global_asm_object_file) - .status() - .unwrap(); - if !status.success() { - tcx.sess.fatal(&format!( - "Failed to link `{}` and `{}` together", - main_object_file.display(), - global_asm_object_file.display(), - )); - } - - std::fs::remove_file(global_asm_object_file).unwrap(); - std::fs::remove_file(main_object_file).unwrap(); -} - -fn add_file_stem_postfix(mut path: PathBuf, postfix: &str) -> PathBuf { - let mut new_filename = path.file_stem().unwrap().to_owned(); - new_filename.push(postfix); - if let Some(extension) = path.extension() { - new_filename.push("."); - new_filename.push(extension); - } - path.set_file_name(new_filename); - path + Box::new(OngoingCodegen { + modules, + allocator_module, + metadata_module, + metadata, + crate_info: CrateInfo::new(tcx, target_cpu), + concurrency_limiter, + }) } // Adapted from https://github.com/rust-lang/rust/blob/303d8aff6092709edd4dbd35b1c88e9aa40bf6d8/src/librustc_codegen_ssa/base.rs#L922-L953 @@ -432,5 +502,5 @@ fn determine_cgu_reuse<'tcx>(tcx: TyCtxt<'tcx>, cgu: &CodegenUnit<'tcx>) -> CguR cgu.name() ); - if tcx.try_mark_green(&dep_node) { CguReuse::PreLto } else { CguReuse::No } + if tcx.try_mark_green(&dep_node) { CguReuse::PostLto } else { CguReuse::No } } diff --git a/src/driver/jit.rs b/src/driver/jit.rs index a56a91000596..0e77e4004c0b 100644 --- a/src/driver/jit.rs +++ b/src/driver/jit.rs @@ -61,11 +61,11 @@ impl UnsafeMessage { } } -fn create_jit_module<'tcx>( - tcx: TyCtxt<'tcx>, +fn create_jit_module( + tcx: TyCtxt<'_>, backend_config: &BackendConfig, hotswap: bool, -) -> (JITModule, CodegenCx<'tcx>) { +) -> (JITModule, CodegenCx) { let crate_info = CrateInfo::new(tcx, "dummy_target_cpu".to_string()); let imported_symbols = load_imported_symbols_for_jit(tcx.sess, crate_info); @@ -111,6 +111,7 @@ pub(crate) fn run_jit(tcx: TyCtxt<'_>, backend_config: BackendConfig) -> ! { &backend_config, matches!(backend_config.codegen_mode, CodegenMode::JitLazy), ); + let mut cached_context = Context::new(); let (_, cgus) = tcx.collect_and_partition_mono_items(()); let mono_items = cgus @@ -128,11 +129,19 @@ pub(crate) fn run_jit(tcx: TyCtxt<'_>, backend_config: BackendConfig) -> ! { MonoItem::Fn(inst) => match backend_config.codegen_mode { CodegenMode::Aot => unreachable!(), CodegenMode::Jit => { - cx.tcx.sess.time("codegen fn", || { - crate::base::codegen_fn(&mut cx, &mut jit_module, inst) + tcx.sess.time("codegen fn", || { + crate::base::codegen_and_compile_fn( + tcx, + &mut cx, + &mut cached_context, + &mut jit_module, + inst, + ) }); } - CodegenMode::JitLazy => codegen_shim(&mut cx, &mut jit_module, inst), + CodegenMode::JitLazy => { + codegen_shim(tcx, &mut cx, &mut cached_context, &mut jit_module, inst) + } }, MonoItem::Static(def_id) => { crate::constant::codegen_static(tcx, &mut jit_module, def_id); @@ -259,7 +268,15 @@ fn jit_fn(instance_ptr: *const Instance<'static>, trampoline_ptr: *const u8) -> false, Symbol::intern("dummy_cgu_name"), ); - tcx.sess.time("codegen fn", || crate::base::codegen_fn(&mut cx, jit_module, instance)); + tcx.sess.time("codegen fn", || { + crate::base::codegen_and_compile_fn( + tcx, + &mut cx, + &mut Context::new(), + jit_module, + instance, + ) + }); assert!(cx.global_asm.is_empty()); jit_module.finalize_definitions(); @@ -334,9 +351,13 @@ fn load_imported_symbols_for_jit( imported_symbols } -fn codegen_shim<'tcx>(cx: &mut CodegenCx<'tcx>, module: &mut JITModule, inst: Instance<'tcx>) { - let tcx = cx.tcx; - +fn codegen_shim<'tcx>( + tcx: TyCtxt<'tcx>, + cx: &mut CodegenCx, + cached_context: &mut Context, + module: &mut JITModule, + inst: Instance<'tcx>, +) { let pointer_type = module.target_config().pointer_type(); let name = tcx.symbol_name(inst).name; @@ -357,8 +378,9 @@ fn codegen_shim<'tcx>(cx: &mut CodegenCx<'tcx>, module: &mut JITModule, inst: In ) .unwrap(); - cx.cached_context.clear(); - let trampoline = &mut cx.cached_context.func; + let context = cached_context; + context.clear(); + let trampoline = &mut context.func; trampoline.signature = sig.clone(); let mut builder_ctx = FunctionBuilderContext::new(); @@ -381,5 +403,6 @@ fn codegen_shim<'tcx>(cx: &mut CodegenCx<'tcx>, module: &mut JITModule, inst: In let ret_vals = trampoline_builder.func.dfg.inst_results(call_inst).to_vec(); trampoline_builder.ins().return_(&ret_vals); - module.define_function(func_id, &mut cx.cached_context).unwrap(); + module.define_function(func_id, context).unwrap(); + cx.unwind_context.add_function(func_id, context, module.isa()); } diff --git a/src/global_asm.rs b/src/global_asm.rs new file mode 100644 index 000000000000..dcbcaba30fee --- /dev/null +++ b/src/global_asm.rs @@ -0,0 +1,114 @@ +//! The AOT driver uses [`cranelift_object`] to write object files suitable for linking into a +//! standalone executable. + +use std::io::Write; +use std::path::PathBuf; +use std::process::{Command, Stdio}; +use std::sync::Arc; + +use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; +use rustc_hir::ItemId; +use rustc_session::config::{OutputFilenames, OutputType}; + +use crate::prelude::*; + +pub(crate) fn codegen_global_asm_item(tcx: TyCtxt<'_>, global_asm: &mut String, item_id: ItemId) { + let item = tcx.hir().item(item_id); + if let rustc_hir::ItemKind::GlobalAsm(asm) = item.kind { + if !asm.options.contains(InlineAsmOptions::ATT_SYNTAX) { + global_asm.push_str("\n.intel_syntax noprefix\n"); + } else { + global_asm.push_str("\n.att_syntax\n"); + } + for piece in asm.template { + match *piece { + InlineAsmTemplatePiece::String(ref s) => global_asm.push_str(s), + InlineAsmTemplatePiece::Placeholder { .. } => todo!(), + } + } + global_asm.push_str("\n.att_syntax\n\n"); + } else { + bug!("Expected GlobalAsm found {:?}", item); + } +} + +#[derive(Debug)] +pub(crate) struct GlobalAsmConfig { + asm_enabled: bool, + assembler: PathBuf, + pub(crate) output_filenames: Arc, +} + +impl GlobalAsmConfig { + pub(crate) fn new(tcx: TyCtxt<'_>) -> Self { + let asm_enabled = cfg!(feature = "inline_asm") && !tcx.sess.target.is_like_windows; + + GlobalAsmConfig { + asm_enabled, + assembler: crate::toolchain::get_toolchain_binary(tcx.sess, "as"), + output_filenames: tcx.output_filenames(()).clone(), + } + } +} + +pub(crate) fn compile_global_asm( + config: &GlobalAsmConfig, + cgu_name: &str, + global_asm: &str, +) -> Result, String> { + if global_asm.is_empty() { + return Ok(None); + } + + if !config.asm_enabled { + if global_asm.contains("__rust_probestack") { + return Ok(None); + } + + // FIXME fix linker error on macOS + if cfg!(not(feature = "inline_asm")) { + return Err( + "asm! and global_asm! support is disabled while compiling rustc_codegen_cranelift" + .to_owned(), + ); + } else { + return Err("asm! and global_asm! are not yet supported on Windows".to_owned()); + } + } + + // Remove all LLVM style comments + let global_asm = global_asm + .lines() + .map(|line| if let Some(index) = line.find("//") { &line[0..index] } else { line }) + .collect::>() + .join("\n"); + + let output_object_file = config.output_filenames.temp_path(OutputType::Object, Some(cgu_name)); + + // Assemble `global_asm` + let global_asm_object_file = add_file_stem_postfix(output_object_file.clone(), ".asm"); + let mut child = Command::new(&config.assembler) + .arg("-o") + .arg(&global_asm_object_file) + .stdin(Stdio::piped()) + .spawn() + .expect("Failed to spawn `as`."); + child.stdin.take().unwrap().write_all(global_asm.as_bytes()).unwrap(); + let status = child.wait().expect("Failed to wait for `as`."); + if !status.success() { + return Err(format!("Failed to assemble `{}`", global_asm)); + } + + Ok(Some(global_asm_object_file)) +} + +pub(crate) fn add_file_stem_postfix(mut path: PathBuf, postfix: &str) -> PathBuf { + let mut new_filename = path.file_stem().unwrap().to_owned(); + new_filename.push(postfix); + if let Some(extension) = path.extension() { + new_filename.push("."); + new_filename.push(extension); + } + path.set_file_name(new_filename); + path +} diff --git a/src/inline_asm.rs b/src/inline_asm.rs index 241de5e36530..8b3d475cb180 100644 --- a/src/inline_asm.rs +++ b/src/inline_asm.rs @@ -15,15 +15,19 @@ pub(crate) fn codegen_inline_asm<'tcx>( template: &[InlineAsmTemplatePiece], operands: &[InlineAsmOperand<'tcx>], options: InlineAsmOptions, + destination: Option, ) { // FIXME add .eh_frame unwind info directives if !template.is_empty() { + // Used by panic_abort if template[0] == InlineAsmTemplatePiece::String("int $$0x29".to_string()) { - let true_ = fx.bcx.ins().iconst(types::I32, 1); - fx.bcx.ins().trapnz(true_, TrapCode::User(1)); + fx.bcx.ins().trap(TrapCode::User(1)); return; - } else if template[0] == InlineAsmTemplatePiece::String("movq %rbx, ".to_string()) + } + + // Used by stdarch + if template[0] == InlineAsmTemplatePiece::String("movq %rbx, ".to_string()) && matches!( template[1], InlineAsmTemplatePiece::Placeholder { @@ -47,51 +51,46 @@ pub(crate) fn codegen_inline_asm<'tcx>( { assert_eq!(operands.len(), 4); let (leaf, eax_place) = match operands[1] { - InlineAsmOperand::InOut { reg, late: true, ref in_value, out_place } => { - assert_eq!( - reg, - InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::ax)) - ); - ( - crate::base::codegen_operand(fx, in_value).load_scalar(fx), - crate::base::codegen_place(fx, out_place.unwrap()), - ) - } + InlineAsmOperand::InOut { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::ax)), + late: true, + ref in_value, + out_place: Some(out_place), + } => ( + crate::base::codegen_operand(fx, in_value).load_scalar(fx), + crate::base::codegen_place(fx, out_place), + ), _ => unreachable!(), }; let ebx_place = match operands[0] { - InlineAsmOperand::Out { reg, late: true, place } => { - assert_eq!( - reg, + InlineAsmOperand::Out { + reg: InlineAsmRegOrRegClass::RegClass(InlineAsmRegClass::X86( - X86InlineAsmRegClass::reg - )) - ); - crate::base::codegen_place(fx, place.unwrap()) - } + X86InlineAsmRegClass::reg, + )), + late: true, + place: Some(place), + } => crate::base::codegen_place(fx, place), _ => unreachable!(), }; let (sub_leaf, ecx_place) = match operands[2] { - InlineAsmOperand::InOut { reg, late: true, ref in_value, out_place } => { - assert_eq!( - reg, - InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::cx)) - ); - ( - crate::base::codegen_operand(fx, in_value).load_scalar(fx), - crate::base::codegen_place(fx, out_place.unwrap()), - ) - } + InlineAsmOperand::InOut { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::cx)), + late: true, + ref in_value, + out_place: Some(out_place), + } => ( + crate::base::codegen_operand(fx, in_value).load_scalar(fx), + crate::base::codegen_place(fx, out_place), + ), _ => unreachable!(), }; let edx_place = match operands[3] { - InlineAsmOperand::Out { reg, late: true, place } => { - assert_eq!( - reg, - InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::dx)) - ); - crate::base::codegen_place(fx, place.unwrap()) - } + InlineAsmOperand::Out { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::dx)), + late: true, + place: Some(place), + } => crate::base::codegen_place(fx, place), _ => unreachable!(), }; @@ -101,12 +100,99 @@ pub(crate) fn codegen_inline_asm<'tcx>( ebx_place.write_cvalue(fx, CValue::by_val(ebx, fx.layout_of(fx.tcx.types.u32))); ecx_place.write_cvalue(fx, CValue::by_val(ecx, fx.layout_of(fx.tcx.types.u32))); edx_place.write_cvalue(fx, CValue::by_val(edx, fx.layout_of(fx.tcx.types.u32))); + let destination_block = fx.get_block(destination.unwrap()); + fx.bcx.ins().jump(destination_block, &[]); return; - } else if fx.tcx.symbol_name(fx.instance).name.starts_with("___chkstk") { + } + + // Used by compiler-builtins + if fx.tcx.symbol_name(fx.instance).name.starts_with("___chkstk") { // ___chkstk, ___chkstk_ms and __alloca are only used on Windows crate::trap::trap_unimplemented(fx, "Stack probes are not supported"); + return; } else if fx.tcx.symbol_name(fx.instance).name == "__alloca" { crate::trap::trap_unimplemented(fx, "Alloca is not supported"); + return; + } + + // Used by measureme + if template[0] == InlineAsmTemplatePiece::String("xor %eax, %eax".to_string()) + && template[1] == InlineAsmTemplatePiece::String("\n".to_string()) + && template[2] == InlineAsmTemplatePiece::String("mov %rbx, ".to_string()) + && matches!( + template[3], + InlineAsmTemplatePiece::Placeholder { + operand_idx: 0, + modifier: Some('r'), + span: _ + } + ) + && template[4] == InlineAsmTemplatePiece::String("\n".to_string()) + && template[5] == InlineAsmTemplatePiece::String("cpuid".to_string()) + && template[6] == InlineAsmTemplatePiece::String("\n".to_string()) + && template[7] == InlineAsmTemplatePiece::String("mov ".to_string()) + && matches!( + template[8], + InlineAsmTemplatePiece::Placeholder { + operand_idx: 0, + modifier: Some('r'), + span: _ + } + ) + && template[9] == InlineAsmTemplatePiece::String(", %rbx".to_string()) + { + let destination_block = fx.get_block(destination.unwrap()); + fx.bcx.ins().jump(destination_block, &[]); + return; + } else if template[0] == InlineAsmTemplatePiece::String("rdpmc".to_string()) { + // Return zero dummy values for all performance counters + match operands[0] { + InlineAsmOperand::In { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::cx)), + value: _, + } => {} + _ => unreachable!(), + }; + let lo = match operands[1] { + InlineAsmOperand::Out { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::ax)), + late: true, + place: Some(place), + } => crate::base::codegen_place(fx, place), + _ => unreachable!(), + }; + let hi = match operands[2] { + InlineAsmOperand::Out { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::dx)), + late: true, + place: Some(place), + } => crate::base::codegen_place(fx, place), + _ => unreachable!(), + }; + + let u32_layout = fx.layout_of(fx.tcx.types.u32); + let zero = fx.bcx.ins().iconst(types::I32, 0); + lo.write_cvalue(fx, CValue::by_val(zero, u32_layout)); + hi.write_cvalue(fx, CValue::by_val(zero, u32_layout)); + + let destination_block = fx.get_block(destination.unwrap()); + fx.bcx.ins().jump(destination_block, &[]); + return; + } else if template[0] == InlineAsmTemplatePiece::String("lock xadd ".to_string()) + && matches!( + template[1], + InlineAsmTemplatePiece::Placeholder { operand_idx: 1, modifier: None, span: _ } + ) + && template[2] == InlineAsmTemplatePiece::String(", (".to_string()) + && matches!( + template[3], + InlineAsmTemplatePiece::Placeholder { operand_idx: 0, modifier: None, span: _ } + ) + && template[4] == InlineAsmTemplatePiece::String(")".to_string()) + { + let destination_block = fx.get_block(destination.unwrap()); + fx.bcx.ins().jump(destination_block, &[]); + return; } } @@ -175,6 +261,16 @@ pub(crate) fn codegen_inline_asm<'tcx>( } call_inline_asm(fx, &asm_name, asm_gen.stack_slot_size, inputs, outputs); + + match destination { + Some(destination) => { + let destination_block = fx.get_block(destination); + fx.bcx.ins().jump(destination_block, &[]); + } + None => { + fx.bcx.ins().trap(TrapCode::UnreachableCodeReached); + } + } } struct InlineAssemblyGenerator<'a, 'tcx> { @@ -637,7 +733,7 @@ fn call_inline_asm<'tcx>( inputs: Vec<(Size, Value)>, outputs: Vec<(Size, CPlace<'tcx>)>, ) { - let stack_slot = fx.bcx.func.create_stack_slot(StackSlotData { + let stack_slot = fx.bcx.func.create_sized_stack_slot(StackSlotData { kind: StackSlotKind::ExplicitSlot, size: u32::try_from(slot_size.bytes()).unwrap(), }); diff --git a/src/intrinsics/cpuid.rs b/src/intrinsics/cpuid.rs index d02dfd93c3ee..5120b89c4e8b 100644 --- a/src/intrinsics/cpuid.rs +++ b/src/intrinsics/cpuid.rs @@ -62,7 +62,7 @@ pub(crate) fn codegen_cpuid_call<'tcx>( fx.bcx.ins().jump(dest, &[zero, zero, proc_info_ecx, proc_info_edx]); fx.bcx.switch_to_block(unsupported_leaf); - crate::trap::trap_unreachable( + crate::trap::trap_unimplemented( fx, "__cpuid_count arch intrinsic doesn't yet support specified leaf", ); diff --git a/src/intrinsics/llvm.rs b/src/intrinsics/llvm.rs index 869670c8cfac..a799dca938e2 100644 --- a/src/intrinsics/llvm.rs +++ b/src/intrinsics/llvm.rs @@ -139,6 +139,7 @@ pub(crate) fn codegen_llvm_intrinsic_call<'tcx>( .sess .warn(&format!("unsupported llvm intrinsic {}; replacing with trap", intrinsic)); crate::trap::trap_unimplemented(fx, intrinsic); + return; } } diff --git a/src/intrinsics/mod.rs b/src/intrinsics/mod.rs index b2a83e1d4ebc..ef3d5ccea8a2 100644 --- a/src/intrinsics/mod.rs +++ b/src/intrinsics/mod.rs @@ -44,7 +44,7 @@ fn report_atomic_type_validation_error<'tcx>( ), ); // Prevent verifier error - crate::trap::trap_unreachable(fx, "compilation should not have succeeded"); + fx.bcx.ins().trap(TrapCode::UnreachableCodeReached); } pub(crate) fn clif_vector_type<'tcx>(tcx: TyCtxt<'tcx>, layout: TyAndLayout<'tcx>) -> Option { @@ -53,7 +53,7 @@ pub(crate) fn clif_vector_type<'tcx>(tcx: TyCtxt<'tcx>, layout: TyAndLayout<'tcx _ => unreachable!(), }; - match scalar_to_clif_type(tcx, element).by(u16::try_from(count).unwrap()) { + match scalar_to_clif_type(tcx, element).by(u32::try_from(count).unwrap()) { // Cranelift currently only implements icmp for 128bit vectors. Some(vector_ty) if vector_ty.bits() == 128 => Some(vector_ty), _ => None, @@ -301,7 +301,44 @@ fn codegen_float_intrinsic_call<'tcx>( _ => unreachable!(), }; - let res = fx.easy_call(name, &args, ty); + let layout = fx.layout_of(ty); + let res = match intrinsic { + sym::fmaf32 | sym::fmaf64 => { + let a = args[0].load_scalar(fx); + let b = args[1].load_scalar(fx); + let c = args[2].load_scalar(fx); + CValue::by_val(fx.bcx.ins().fma(a, b, c), layout) + } + sym::copysignf32 | sym::copysignf64 => { + let a = args[0].load_scalar(fx); + let b = args[1].load_scalar(fx); + CValue::by_val(fx.bcx.ins().fcopysign(a, b), layout) + } + sym::fabsf32 + | sym::fabsf64 + | sym::floorf32 + | sym::floorf64 + | sym::ceilf32 + | sym::ceilf64 + | sym::truncf32 + | sym::truncf64 => { + let a = args[0].load_scalar(fx); + + let val = match intrinsic { + sym::fabsf32 | sym::fabsf64 => fx.bcx.ins().fabs(a), + sym::floorf32 | sym::floorf64 => fx.bcx.ins().floor(a), + sym::ceilf32 | sym::ceilf64 => fx.bcx.ins().ceil(a), + sym::truncf32 | sym::truncf64 => fx.bcx.ins().trunc(a), + _ => unreachable!(), + }; + + CValue::by_val(val, layout) + } + // These intrinsics aren't supported natively by Cranelift. + // Lower them to a libcall. + _ => fx.easy_call(name, &args, ty), + }; + ret.write_cvalue(fx, res); true @@ -818,8 +855,6 @@ fn codegen_regular_intrinsic_call<'tcx>( if fx.tcx.is_compiler_builtins(LOCAL_CRATE) { // special case for compiler-builtins to avoid having to patch it crate::trap::trap_unimplemented(fx, "128bit atomics not yet supported"); - let ret_block = fx.get_block(destination.unwrap()); - fx.bcx.ins().jump(ret_block, &[]); return; } else { fx.tcx @@ -851,8 +886,6 @@ fn codegen_regular_intrinsic_call<'tcx>( if fx.tcx.is_compiler_builtins(LOCAL_CRATE) { // special case for compiler-builtins to avoid having to patch it crate::trap::trap_unimplemented(fx, "128bit atomics not yet supported"); - let ret_block = fx.get_block(destination.unwrap()); - fx.bcx.ins().jump(ret_block, &[]); return; } else { fx.tcx diff --git a/src/intrinsics/simd.rs b/src/intrinsics/simd.rs index 30e3d112594a..a32b413d45f9 100644 --- a/src/intrinsics/simd.rs +++ b/src/intrinsics/simd.rs @@ -14,7 +14,7 @@ fn report_simd_type_validation_error( ) { fx.tcx.sess.span_err(span, &format!("invalid monomorphization of `{}` intrinsic: expected SIMD input type, found non-SIMD `{}`", intrinsic, ty)); // Prevent verifier error - crate::trap::trap_unreachable(fx, "compilation should not have succeeded"); + fx.bcx.ins().trap(TrapCode::UnreachableCodeReached); } pub(super) fn codegen_simd_intrinsic_call<'tcx>( @@ -157,7 +157,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( ), ); // Prevent verifier error - crate::trap::trap_unreachable(fx, "compilation should not have succeeded"); + fx.bcx.ins().trap(TrapCode::UnreachableCodeReached); return; } } @@ -274,12 +274,17 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( idx_const } else { fx.tcx.sess.span_warn(span, "Index argument for `simd_extract` is not a constant"); - let res = crate::trap::trap_unimplemented_ret_value( + let trap_block = fx.bcx.create_block(); + let dummy_block = fx.bcx.create_block(); + let true_ = fx.bcx.ins().iconst(types::I8, 1); + fx.bcx.ins().brnz(true_, trap_block, &[]); + fx.bcx.ins().jump(dummy_block, &[]); + fx.bcx.switch_to_block(trap_block); + crate::trap::trap_unimplemented( fx, - ret.layout(), "Index argument for `simd_extract` is not a constant", ); - ret.write_cvalue(fx, res); + fx.bcx.switch_to_block(dummy_block); return; }; @@ -392,21 +397,15 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( let layout = a.layout(); let (lane_count, lane_ty) = layout.ty.simd_size_and_type(fx.tcx); + let res_lane_layout = fx.layout_of(lane_ty); for lane in 0..lane_count { - let a_lane = a.value_lane(fx, lane); - let b_lane = b.value_lane(fx, lane); - let c_lane = c.value_lane(fx, lane); + let a_lane = a.value_lane(fx, lane).load_scalar(fx); + let b_lane = b.value_lane(fx, lane).load_scalar(fx); + let c_lane = c.value_lane(fx, lane).load_scalar(fx); - let res_lane = match lane_ty.kind() { - ty::Float(FloatTy::F32) => { - fx.easy_call("fmaf", &[a_lane, b_lane, c_lane], lane_ty) - } - ty::Float(FloatTy::F64) => { - fx.easy_call("fma", &[a_lane, b_lane, c_lane], lane_ty) - } - _ => unreachable!(), - }; + let res_lane = fx.bcx.ins().fma(a_lane, b_lane, c_lane); + let res_lane = CValue::by_val(res_lane, res_lane_layout); ret.place_lane(fx, lane).write_cvalue(fx, res_lane); } diff --git a/src/lib.rs b/src/lib.rs index bb0793b1deb2..913414e76182 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,6 +4,7 @@ #![warn(unused_lifetimes)] #![warn(unreachable_pub)] +extern crate jobserver; #[macro_use] extern crate rustc_middle; extern crate rustc_ast; @@ -25,10 +26,12 @@ extern crate rustc_target; extern crate rustc_driver; use std::any::Any; -use std::cell::Cell; +use std::cell::{Cell, RefCell}; +use std::sync::Arc; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_codegen_ssa::CodegenResults; +use rustc_data_structures::profiling::SelfProfilerRef; use rustc_errors::ErrorGuaranteed; use rustc_metadata::EncodedMetadata; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; @@ -51,11 +54,13 @@ mod cast; mod codegen_i128; mod common; mod compiler_builtins; +mod concurrency_limiter; mod config; mod constant; mod debuginfo; mod discriminant; mod driver; +mod global_asm; mod inline_asm; mod intrinsics; mod linkage; @@ -119,19 +124,20 @@ impl String> Drop for PrintOnPanic { /// The codegen context holds any information shared between the codegen of individual functions /// inside a single codegen unit with the exception of the Cranelift [`Module`](cranelift_module::Module). -struct CodegenCx<'tcx> { - tcx: TyCtxt<'tcx>, +struct CodegenCx { + profiler: SelfProfilerRef, + output_filenames: Arc, + should_write_ir: bool, global_asm: String, inline_asm_index: Cell, - cached_context: Context, - debug_context: Option>, + debug_context: Option, unwind_context: UnwindContext, cgu_name: Symbol, } -impl<'tcx> CodegenCx<'tcx> { +impl CodegenCx { fn new( - tcx: TyCtxt<'tcx>, + tcx: TyCtxt<'_>, backend_config: BackendConfig, isa: &dyn TargetIsa, debug_info: bool, @@ -147,10 +153,11 @@ impl<'tcx> CodegenCx<'tcx> { None }; CodegenCx { - tcx, + profiler: tcx.prof.clone(), + output_filenames: tcx.output_filenames(()).clone(), + should_write_ir: crate::pretty_clif::should_write_ir(tcx), global_asm: String::new(), inline_asm_index: Cell::new(0), - cached_context: Context::new(), debug_context, unwind_context, cgu_name, @@ -159,7 +166,7 @@ impl<'tcx> CodegenCx<'tcx> { } pub struct CraneliftCodegenBackend { - pub config: Option, + pub config: RefCell>, } impl CodegenBackend for CraneliftCodegenBackend { @@ -169,6 +176,13 @@ impl CodegenBackend for CraneliftCodegenBackend { Lto::No | Lto::ThinLocal => {} Lto::Thin | Lto::Fat => sess.warn("LTO is not supported. You may get a linker error."), } + + let mut config = self.config.borrow_mut(); + if config.is_none() { + let new_config = BackendConfig::from_opts(&sess.opts.cg.llvm_args) + .unwrap_or_else(|err| sess.fatal(&err)); + *config = Some(new_config); + } } fn target_features(&self, _sess: &Session, _allow_unstable: bool) -> Vec { @@ -186,15 +200,7 @@ impl CodegenBackend for CraneliftCodegenBackend { need_metadata_module: bool, ) -> Box { tcx.sess.abort_if_errors(); - let config = if let Some(config) = self.config.clone() { - config - } else { - if !tcx.sess.unstable_options() && !tcx.sess.opts.cg.llvm_args.is_empty() { - tcx.sess.fatal("`-Z unstable-options` must be passed to allow configuring cg_clif"); - } - BackendConfig::from_opts(&tcx.sess.opts.cg.llvm_args) - .unwrap_or_else(|err| tcx.sess.fatal(&err)) - }; + let config = self.config.borrow().clone().unwrap(); match config.codegen_mode { CodegenMode::Aot => driver::aot::run_aot(tcx, config, metadata, need_metadata_module), CodegenMode::Jit | CodegenMode::JitLazy => { @@ -210,12 +216,13 @@ impl CodegenBackend for CraneliftCodegenBackend { fn join_codegen( &self, ongoing_codegen: Box, - _sess: &Session, + sess: &Session, _outputs: &OutputFilenames, ) -> Result<(CodegenResults, FxHashMap), ErrorGuaranteed> { - Ok(*ongoing_codegen - .downcast::<(CodegenResults, FxHashMap)>() - .unwrap()) + Ok(ongoing_codegen + .downcast::() + .unwrap() + .join(sess, self.config.borrow().as_ref().unwrap())) } fn link( @@ -312,5 +319,5 @@ fn build_isa(sess: &Session, backend_config: &BackendConfig) -> Box Box { - Box::new(CraneliftCodegenBackend { config: None }) + Box::new(CraneliftCodegenBackend { config: RefCell::new(None) }) } diff --git a/src/optimize/mod.rs b/src/optimize/mod.rs index d1f89adb3bb9..0df7e82294bd 100644 --- a/src/optimize/mod.rs +++ b/src/optimize/mod.rs @@ -1,20 +1,3 @@ //! Various optimizations specific to cg_clif -use cranelift_codegen::isa::TargetIsa; - -use crate::prelude::*; - pub(crate) mod peephole; - -pub(crate) fn optimize_function<'tcx>( - tcx: TyCtxt<'tcx>, - isa: &dyn TargetIsa, - instance: Instance<'tcx>, - ctx: &mut Context, - clif_comments: &mut crate::pretty_clif::CommentWriter, -) { - // FIXME classify optimizations over opt levels once we have more - - crate::pretty_clif::write_clif_file(tcx, "preopt", isa, instance, &ctx.func, &*clif_comments); - crate::base::verify_func(tcx, &*clif_comments, &ctx.func); -} diff --git a/src/pretty_clif.rs b/src/pretty_clif.rs index 1d1ec21680e3..a7af162687c3 100644 --- a/src/pretty_clif.rs +++ b/src/pretty_clif.rs @@ -62,7 +62,7 @@ use cranelift_codegen::{ }; use rustc_middle::ty::layout::FnAbiOf; -use rustc_session::config::OutputType; +use rustc_session::config::{OutputFilenames, OutputType}; use crate::prelude::*; @@ -205,15 +205,11 @@ pub(crate) fn should_write_ir(tcx: TyCtxt<'_>) -> bool { } pub(crate) fn write_ir_file( - tcx: TyCtxt<'_>, - name: impl FnOnce() -> String, + output_filenames: &OutputFilenames, + name: &str, write: impl FnOnce(&mut dyn Write) -> std::io::Result<()>, ) { - if !should_write_ir(tcx) { - return; - } - - let clif_output_dir = tcx.output_filenames(()).with_extension("clif"); + let clif_output_dir = output_filenames.with_extension("clif"); match std::fs::create_dir(&clif_output_dir) { Ok(()) => {} @@ -221,44 +217,43 @@ pub(crate) fn write_ir_file( res @ Err(_) => res.unwrap(), } - let clif_file_name = clif_output_dir.join(name()); + let clif_file_name = clif_output_dir.join(name); let res = std::fs::File::create(clif_file_name).and_then(|mut file| write(&mut file)); if let Err(err) = res { - tcx.sess.warn(&format!("error writing ir file: {}", err)); + // Using early_warn as no Session is available here + rustc_session::early_warn( + rustc_session::config::ErrorOutputType::default(), + &format!("error writing ir file: {}", err), + ); } } -pub(crate) fn write_clif_file<'tcx>( - tcx: TyCtxt<'tcx>, +pub(crate) fn write_clif_file( + output_filenames: &OutputFilenames, + symbol_name: &str, postfix: &str, isa: &dyn cranelift_codegen::isa::TargetIsa, - instance: Instance<'tcx>, func: &cranelift_codegen::ir::Function, mut clif_comments: &CommentWriter, ) { // FIXME work around filename too long errors - write_ir_file( - tcx, - || format!("{}.{}.clif", tcx.symbol_name(instance).name, postfix), - |file| { - let mut clif = String::new(); - cranelift_codegen::write::decorate_function(&mut clif_comments, &mut clif, func) - .unwrap(); + write_ir_file(output_filenames, &format!("{}.{}.clif", symbol_name, postfix), |file| { + let mut clif = String::new(); + cranelift_codegen::write::decorate_function(&mut clif_comments, &mut clif, func).unwrap(); - for flag in isa.flags().iter() { - writeln!(file, "set {}", flag)?; - } - write!(file, "target {}", isa.triple().architecture.to_string())?; - for isa_flag in isa.isa_flags().iter() { - write!(file, " {}", isa_flag)?; - } - writeln!(file, "\n")?; - writeln!(file)?; - file.write_all(clif.as_bytes())?; - Ok(()) - }, - ); + for flag in isa.flags().iter() { + writeln!(file, "set {}", flag)?; + } + write!(file, "target {}", isa.triple().architecture.to_string())?; + for isa_flag in isa.isa_flags().iter() { + write!(file, " {}", isa_flag)?; + } + writeln!(file, "\n")?; + writeln!(file)?; + file.write_all(clif.as_bytes())?; + Ok(()) + }); } impl fmt::Debug for FunctionCx<'_, '_, '_> { diff --git a/src/toolchain.rs b/src/toolchain.rs index f86236ef3eaf..b6b465e1f4e0 100644 --- a/src/toolchain.rs +++ b/src/toolchain.rs @@ -8,10 +8,8 @@ use rustc_session::Session; /// Tries to infer the path of a binary for the target toolchain from the linker name. pub(crate) fn get_toolchain_binary(sess: &Session, tool: &str) -> PathBuf { let (mut linker, _linker_flavor) = linker_and_flavor(sess); - let linker_file_name = linker - .file_name() - .and_then(|name| name.to_str()) - .unwrap_or_else(|| sess.fatal("couldn't extract file name from specified linker")); + let linker_file_name = + linker.file_name().unwrap().to_str().expect("linker filename should be valid UTF-8"); if linker_file_name == "ld.lld" { if tool != "ld" { diff --git a/src/trap.rs b/src/trap.rs index 923269c4de9a..82a2ec579549 100644 --- a/src/trap.rs +++ b/src/trap.rs @@ -25,33 +25,10 @@ fn codegen_print(fx: &mut FunctionCx<'_, '_, '_>, msg: &str) { fx.bcx.ins().call(puts, &[msg_ptr]); } -/// Use this for example when a function call should never return. This will fill the current block, -/// so you can **not** add instructions to it afterwards. -/// -/// Trap code: user65535 -pub(crate) fn trap_unreachable(fx: &mut FunctionCx<'_, '_, '_>, msg: impl AsRef) { - codegen_print(fx, msg.as_ref()); - fx.bcx.ins().trap(TrapCode::UnreachableCodeReached); -} /// Use this when something is unimplemented, but `libcore` or `libstd` requires it to codegen. -/// Unlike `trap_unreachable` this will not fill the current block, so you **must** add instructions -/// to it afterwards. /// /// Trap code: user65535 pub(crate) fn trap_unimplemented(fx: &mut FunctionCx<'_, '_, '_>, msg: impl AsRef) { codegen_print(fx, msg.as_ref()); - let true_ = fx.bcx.ins().iconst(types::I32, 1); - fx.bcx.ins().trapnz(true_, TrapCode::User(!0)); -} - -/// Like `trap_unimplemented` but returns a fake value of the specified type. -/// -/// Trap code: user65535 -pub(crate) fn trap_unimplemented_ret_value<'tcx>( - fx: &mut FunctionCx<'_, '_, 'tcx>, - dest_layout: TyAndLayout<'tcx>, - msg: impl AsRef, -) -> CValue<'tcx> { - trap_unimplemented(fx, msg); - CValue::by_ref(Pointer::const_addr(fx, 0), dest_layout) + fx.bcx.ins().trap(TrapCode::User(!0)); } diff --git a/src/value_and_place.rs b/src/value_and_place.rs index 45ae2bd8f07c..2ee98546c992 100644 --- a/src/value_and_place.rs +++ b/src/value_and_place.rs @@ -122,7 +122,7 @@ impl<'tcx> CValue<'tcx> { let clif_ty = match layout.abi { Abi::Scalar(scalar) => scalar_to_clif_type(fx.tcx, scalar), Abi::Vector { element, count } => scalar_to_clif_type(fx.tcx, element) - .by(u16::try_from(count).unwrap()) + .by(u32::try_from(count).unwrap()) .unwrap(), _ => unreachable!("{:?}", layout.ty), }; @@ -330,7 +330,7 @@ impl<'tcx> CPlace<'tcx> { .fatal(&format!("values of type {} are too big to store on the stack", layout.ty)); } - let stack_slot = fx.bcx.create_stack_slot(StackSlotData { + let stack_slot = fx.bcx.create_sized_stack_slot(StackSlotData { kind: StackSlotKind::ExplicitSlot, // FIXME Don't force the size to a multiple of 16 bytes once Cranelift gets a way to // specify stack slot alignment. @@ -472,7 +472,7 @@ impl<'tcx> CPlace<'tcx> { } _ if src_ty.is_vector() || dst_ty.is_vector() => { // FIXME do something more efficient for transmutes between vectors and integers. - let stack_slot = fx.bcx.create_stack_slot(StackSlotData { + let stack_slot = fx.bcx.create_sized_stack_slot(StackSlotData { kind: StackSlotKind::ExplicitSlot, // FIXME Don't force the size to a multiple of 16 bytes once Cranelift gets a way to // specify stack slot alignment. @@ -519,7 +519,7 @@ impl<'tcx> CPlace<'tcx> { if let ty::Array(element, len) = dst_layout.ty.kind() { // Can only happen for vector types let len = - u16::try_from(len.eval_usize(fx.tcx, ParamEnv::reveal_all())).unwrap(); + u32::try_from(len.eval_usize(fx.tcx, ParamEnv::reveal_all())).unwrap(); let vector_ty = fx.clif_type(*element).unwrap().by(len).unwrap(); let data = match from.0 { @@ -614,7 +614,7 @@ impl<'tcx> CPlace<'tcx> { dst_align, src_align, true, - MemFlags::trusted(), + flags, ); } CValueInner::ByRef(_, Some(_)) => todo!(), diff --git a/test.sh b/test.sh index a10924628bb0..3d929a1d50ce 100755 --- a/test.sh +++ b/test.sh @@ -1,13 +1,2 @@ #!/usr/bin/env bash -set -e - -./y.rs build --sysroot none "$@" - -rm -r target/out || true - -scripts/tests.sh no_sysroot - -./y.rs build "$@" - -scripts/tests.sh base_sysroot -scripts/tests.sh extended_sysroot +exec ./y.rs test From d9bbac069b1c8f235bcf2415a47b783fb945b4a7 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 24 Aug 2022 17:06:47 +0000 Subject: [PATCH 009/309] Rustfmt --- build_system/tests.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build_system/tests.rs b/build_system/tests.rs index dc83b10958e0..e21397cece8b 100644 --- a/build_system/tests.rs +++ b/build_system/tests.rs @@ -465,7 +465,8 @@ impl TestRunner { out_dir.push("out"); let is_native = host_triple == target_triple; - let jit_supported = target_triple.contains("x86_64") && is_native && !host_triple.contains("windows"); + let jit_supported = + target_triple.contains("x86_64") && is_native && !host_triple.contains("windows"); let mut rust_flags = env::var("RUSTFLAGS").ok().unwrap_or("".to_string()); let mut run_wrapper = Vec::new(); From 8c93170965d026962b59052ee579dd436883d195 Mon Sep 17 00:00:00 2001 From: Nathan Stocks Date: Wed, 24 Aug 2022 16:52:46 -0600 Subject: [PATCH 010/309] adjust to new error value --- src/base.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/base.rs b/src/base.rs index 44c34d6c8cb7..3a2875e5d27d 100644 --- a/src/base.rs +++ b/src/base.rs @@ -925,8 +925,11 @@ pub(crate) fn codegen_panic_inner<'tcx>( args: &[Value], span: Span, ) { - let def_id = - fx.tcx.lang_items().require(lang_item).unwrap_or_else(|s| fx.tcx.sess.span_fatal(span, &s)); + let def_id = fx + .tcx + .lang_items() + .require(lang_item) + .unwrap_or_else(|e| fx.tcx.sess.span_fatal(span, e.to_string())); let instance = Instance::mono(fx.tcx, def_id).polymorphize(fx.tcx); let symbol_name = fx.tcx.symbol_name(instance).name; From 1c989472e41f0b6033bcf62b76dcd28f75e7b25b Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 25 Aug 2022 17:52:37 +1000 Subject: [PATCH 011/309] Box `CastTarget` within `PassMode`. Because `PassMode::Cast` is by far the largest variant, but is relatively rare. This requires making `PassMode` not impl `Copy`, and `Clone` is no longer necessary. This causes lots of sigil adjusting, but nothing very notable. --- src/abi/comments.rs | 2 +- src/abi/pass_mode.rs | 20 ++++++++++++-------- src/abi/returning.rs | 6 +++--- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/abi/comments.rs b/src/abi/comments.rs index 37d2679c10d7..7f4619b5c940 100644 --- a/src/abi/comments.rs +++ b/src/abi/comments.rs @@ -24,7 +24,7 @@ pub(super) fn add_arg_comment<'tcx>( local: Option, local_field: Option, params: &[Value], - arg_abi_mode: PassMode, + arg_abi_mode: &PassMode, arg_layout: TyAndLayout<'tcx>, ) { if !fx.clif_comments.enabled() { diff --git a/src/abi/pass_mode.rs b/src/abi/pass_mode.rs index 6c10baa53d41..058dee176e24 100644 --- a/src/abi/pass_mode.rs +++ b/src/abi/pass_mode.rs @@ -38,7 +38,7 @@ fn apply_arg_attrs_to_abi_param(mut param: AbiParam, arg_attrs: ArgAttributes) - param } -fn cast_target_to_abi_params(cast: CastTarget) -> SmallVec<[AbiParam; 2]> { +fn cast_target_to_abi_params(cast: &CastTarget) -> SmallVec<[AbiParam; 2]> { let (rest_count, rem_bytes) = if cast.rest.unit.size.bytes() == 0 { (0, 0) } else { @@ -100,7 +100,7 @@ impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> { } _ => unreachable!("{:?}", self.layout.abi), }, - PassMode::Cast(cast) => cast_target_to_abi_params(cast), + PassMode::Cast(ref cast) => cast_target_to_abi_params(cast), PassMode::Indirect { attrs, extra_attrs: None, on_stack } => { if on_stack { // Abi requires aligning struct size to pointer size @@ -145,7 +145,9 @@ impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> { } _ => unreachable!("{:?}", self.layout.abi), }, - PassMode::Cast(cast) => (None, cast_target_to_abi_params(cast).into_iter().collect()), + PassMode::Cast(ref cast) => { + (None, cast_target_to_abi_params(cast).into_iter().collect()) + } PassMode::Indirect { attrs: _, extra_attrs: None, on_stack } => { assert!(!on_stack); (Some(AbiParam::special(pointer_ty(tcx), ArgumentPurpose::StructReturn)), vec![]) @@ -160,7 +162,7 @@ impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> { pub(super) fn to_casted_value<'tcx>( fx: &mut FunctionCx<'_, '_, 'tcx>, arg: CValue<'tcx>, - cast: CastTarget, + cast: &CastTarget, ) -> SmallVec<[Value; 2]> { let (ptr, meta) = arg.force_stack(fx); assert!(meta.is_none()); @@ -179,7 +181,7 @@ pub(super) fn from_casted_value<'tcx>( fx: &mut FunctionCx<'_, '_, 'tcx>, block_params: &[Value], layout: TyAndLayout<'tcx>, - cast: CastTarget, + cast: &CastTarget, ) -> CValue<'tcx> { let abi_params = cast_target_to_abi_params(cast); let abi_param_size: u32 = abi_params.iter().map(|param| param.value_type.bytes()).sum(); @@ -224,7 +226,7 @@ pub(super) fn adjust_arg_for_abi<'tcx>( let (a, b) = arg.load_scalar_pair(fx); smallvec![a, b] } - PassMode::Cast(cast) => to_casted_value(fx, arg, cast), + PassMode::Cast(ref cast) => to_casted_value(fx, arg, cast), PassMode::Indirect { .. } => { if is_owned { match arg.force_stack(fx) { @@ -268,7 +270,7 @@ pub(super) fn cvalue_for_param<'tcx>( local, local_field, &block_params, - arg_abi.mode, + &arg_abi.mode, arg_abi.layout, ); @@ -282,7 +284,9 @@ pub(super) fn cvalue_for_param<'tcx>( assert_eq!(block_params.len(), 2, "{:?}", block_params); Some(CValue::by_val_pair(block_params[0], block_params[1], arg_abi.layout)) } - PassMode::Cast(cast) => Some(from_casted_value(fx, &block_params, arg_abi.layout, cast)), + PassMode::Cast(ref cast) => { + Some(from_casted_value(fx, &block_params, arg_abi.layout, cast)) + } PassMode::Indirect { attrs: _, extra_attrs: None, on_stack: _ } => { assert_eq!(block_params.len(), 1, "{:?}", block_params); Some(CValue::by_ref(Pointer::new(block_params[0]), arg_abi.layout)) diff --git a/src/abi/returning.rs b/src/abi/returning.rs index ff3bb2dfd000..29ef5e2dfdae 100644 --- a/src/abi/returning.rs +++ b/src/abi/returning.rs @@ -44,7 +44,7 @@ pub(super) fn codegen_return_param<'tcx>( Some(RETURN_PLACE), None, &ret_param, - fx.fn_abi.as_ref().unwrap().ret.mode, + &fx.fn_abi.as_ref().unwrap().ret.mode, fx.fn_abi.as_ref().unwrap().ret.layout, ); @@ -92,7 +92,7 @@ pub(super) fn codegen_with_call_return_arg<'tcx>( ret_place .write_cvalue(fx, CValue::by_val_pair(ret_val_a, ret_val_b, ret_arg_abi.layout)); } - PassMode::Cast(cast) => { + PassMode::Cast(ref cast) => { let results = fx.bcx.inst_results(call_inst).iter().copied().collect::>(); let result = @@ -131,7 +131,7 @@ pub(crate) fn codegen_return(fx: &mut FunctionCx<'_, '_, '_>) { let (ret_val_a, ret_val_b) = place.to_cvalue(fx).load_scalar_pair(fx); fx.bcx.ins().return_(&[ret_val_a, ret_val_b]); } - PassMode::Cast(cast) => { + PassMode::Cast(ref cast) => { let place = fx.get_local_place(RETURN_PLACE); let ret_val = place.to_cvalue(fx); let ret_vals = super::pass_mode::to_casted_value(fx, ret_val, cast); From 2d2a3be651db8da69c391e0bdfe0189fe1d504d5 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 25 Aug 2022 22:19:38 +1000 Subject: [PATCH 012/309] Move `ArgAbi::pad_i32` into `PassMode::Cast`. Because it's only needed for that variant. This shrinks the types and clarifies the logic. --- src/abi/pass_mode.rs | 11 +++++++---- src/abi/returning.rs | 8 ++++---- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/abi/pass_mode.rs b/src/abi/pass_mode.rs index 058dee176e24..165f15bb3f12 100644 --- a/src/abi/pass_mode.rs +++ b/src/abi/pass_mode.rs @@ -100,7 +100,10 @@ impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> { } _ => unreachable!("{:?}", self.layout.abi), }, - PassMode::Cast(ref cast) => cast_target_to_abi_params(cast), + PassMode::Cast(ref cast, pad_i32) => { + assert!(!pad_i32, "padding support not yet implemented"); + cast_target_to_abi_params(cast) + } PassMode::Indirect { attrs, extra_attrs: None, on_stack } => { if on_stack { // Abi requires aligning struct size to pointer size @@ -145,7 +148,7 @@ impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> { } _ => unreachable!("{:?}", self.layout.abi), }, - PassMode::Cast(ref cast) => { + PassMode::Cast(ref cast, _) => { (None, cast_target_to_abi_params(cast).into_iter().collect()) } PassMode::Indirect { attrs: _, extra_attrs: None, on_stack } => { @@ -226,7 +229,7 @@ pub(super) fn adjust_arg_for_abi<'tcx>( let (a, b) = arg.load_scalar_pair(fx); smallvec![a, b] } - PassMode::Cast(ref cast) => to_casted_value(fx, arg, cast), + PassMode::Cast(ref cast, _) => to_casted_value(fx, arg, cast), PassMode::Indirect { .. } => { if is_owned { match arg.force_stack(fx) { @@ -284,7 +287,7 @@ pub(super) fn cvalue_for_param<'tcx>( assert_eq!(block_params.len(), 2, "{:?}", block_params); Some(CValue::by_val_pair(block_params[0], block_params[1], arg_abi.layout)) } - PassMode::Cast(ref cast) => { + PassMode::Cast(ref cast, _) => { Some(from_casted_value(fx, &block_params, arg_abi.layout, cast)) } PassMode::Indirect { attrs: _, extra_attrs: None, on_stack: _ } => { diff --git a/src/abi/returning.rs b/src/abi/returning.rs index 29ef5e2dfdae..aaa1418767a3 100644 --- a/src/abi/returning.rs +++ b/src/abi/returning.rs @@ -13,7 +13,7 @@ pub(super) fn codegen_return_param<'tcx>( block_params_iter: &mut impl Iterator, ) -> CPlace<'tcx> { let (ret_place, ret_param): (_, SmallVec<[_; 2]>) = match fx.fn_abi.as_ref().unwrap().ret.mode { - PassMode::Ignore | PassMode::Direct(_) | PassMode::Pair(_, _) | PassMode::Cast(_) => { + PassMode::Ignore | PassMode::Direct(_) | PassMode::Pair(_, _) | PassMode::Cast(..) => { let is_ssa = ssa_analyzed[RETURN_PLACE] == crate::analyze::SsaKind::Ssa; ( super::make_local_place( @@ -75,7 +75,7 @@ pub(super) fn codegen_with_call_return_arg<'tcx>( PassMode::Indirect { attrs: _, extra_attrs: Some(_), on_stack: _ } => { unreachable!("unsized return value") } - PassMode::Direct(_) | PassMode::Pair(_, _) | PassMode::Cast(_) => (None, None), + PassMode::Direct(_) | PassMode::Pair(_, _) | PassMode::Cast(..) => (None, None), }; let call_inst = f(fx, return_ptr); @@ -92,7 +92,7 @@ pub(super) fn codegen_with_call_return_arg<'tcx>( ret_place .write_cvalue(fx, CValue::by_val_pair(ret_val_a, ret_val_b, ret_arg_abi.layout)); } - PassMode::Cast(ref cast) => { + PassMode::Cast(ref cast, _) => { let results = fx.bcx.inst_results(call_inst).iter().copied().collect::>(); let result = @@ -131,7 +131,7 @@ pub(crate) fn codegen_return(fx: &mut FunctionCx<'_, '_, '_>) { let (ret_val_a, ret_val_b) = place.to_cvalue(fx).load_scalar_pair(fx); fx.bcx.ins().return_(&[ret_val_a, ret_val_b]); } - PassMode::Cast(ref cast) => { + PassMode::Cast(ref cast, _) => { let place = fx.get_local_place(RETURN_PLACE); let ret_val = place.to_cvalue(fx); let ret_vals = super::pass_mode::to_casted_value(fx, ret_val, cast); From 1071c4c10bb24f12b65bfface2859c1fd8898575 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Tue, 5 Jul 2022 00:00:00 +0000 Subject: [PATCH 013/309] Replace `Body::basic_blocks()` with field access --- src/analyze.rs | 2 +- src/base.rs | 4 ++-- src/constant.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/analyze.rs b/src/analyze.rs index 35b89358b198..0cbb9f3ec2d8 100644 --- a/src/analyze.rs +++ b/src/analyze.rs @@ -26,7 +26,7 @@ pub(crate) fn analyze(fx: &FunctionCx<'_, '_, '_>) -> IndexVec { }) .collect::>(); - for bb in fx.mir.basic_blocks().iter() { + for bb in fx.mir.basic_blocks.iter() { for stmt in bb.statements.iter() { match &stmt.kind { Assign(place_and_rval) => match &place_and_rval.1 { diff --git a/src/base.rs b/src/base.rs index 44c34d6c8cb7..3011813c7035 100644 --- a/src/base.rs +++ b/src/base.rs @@ -73,7 +73,7 @@ pub(crate) fn codegen_fn<'tcx>( // Predefine blocks let start_block = bcx.create_block(); let block_map: IndexVec = - (0..mir.basic_blocks().len()).map(|_| bcx.create_block()).collect(); + (0..mir.basic_blocks.len()).map(|_| bcx.create_block()).collect(); // Make FunctionCx let target_config = module.target_config(); @@ -271,7 +271,7 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) { } fx.tcx.sess.time("codegen prelude", || crate::abi::codegen_fn_prelude(fx, start_block)); - for (bb, bb_data) in fx.mir.basic_blocks().iter_enumerated() { + for (bb, bb_data) in fx.mir.basic_blocks.iter_enumerated() { let block = fx.get_block(bb); fx.bcx.switch_to_block(block); diff --git a/src/constant.rs b/src/constant.rs index 7f7fd0e9c579..e2b68f24a21d 100644 --- a/src/constant.rs +++ b/src/constant.rs @@ -505,7 +505,7 @@ pub(crate) fn mir_operand_get_const_val<'tcx>( return None; } let mut computed_const_val = None; - for bb_data in fx.mir.basic_blocks() { + for bb_data in fx.mir.basic_blocks.iter() { for stmt in &bb_data.statements { match &stmt.kind { StatementKind::Assign(local_and_rvalue) if &local_and_rvalue.0 == place => { From 0644a8c8582205b065aa902aeacfc45e87ee053d Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 27 Aug 2022 14:11:19 -0400 Subject: [PATCH 014/309] =?UTF-8?q?interpret:=20rename=20relocation=20?= =?UTF-8?q?=E2=86=92=20provenance?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/constant.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/constant.rs b/src/constant.rs index 7f7fd0e9c579..4f1531139322 100644 --- a/src/constant.rs +++ b/src/constant.rs @@ -430,7 +430,7 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant let bytes = alloc.inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len()).to_vec(); data_ctx.define(bytes.into_boxed_slice()); - for &(offset, alloc_id) in alloc.relocations().iter() { + for &(offset, alloc_id) in alloc.provenance().iter() { let addend = { let endianness = tcx.data_layout.endian; let offset = offset.bytes() as usize; From 94f2fef4ccadb716f4662475afdb9905143d53f2 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 27 Aug 2022 14:54:02 -0400 Subject: [PATCH 015/309] interpret: make read-pointer-as-bytes *always* work in Miri and show some extra information when it happens in CTFE --- src/intrinsics/simd.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/intrinsics/simd.rs b/src/intrinsics/simd.rs index a32b413d45f9..1f358b1bbb96 100644 --- a/src/intrinsics/simd.rs +++ b/src/intrinsics/simd.rs @@ -186,7 +186,10 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( let size = Size::from_bytes( 4 * ret_lane_count, /* size_of([u32; ret_lane_count]) */ ); - alloc.inner().get_bytes(fx, alloc_range(offset, size)).unwrap() + alloc + .inner() + .get_bytes_strip_provenance(fx, alloc_range(offset, size)) + .unwrap() } _ => unreachable!("{:?}", idx_const), }; From a7de42f61b38dd69a4a58dcf2c7ee69bd4aed50f Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun, 28 Aug 2022 10:43:19 +0200 Subject: [PATCH 016/309] Use pull instead of push based model for getting dylib symbols in the jit This avoids having to parse the dylibs to get all symbols and matches the way the dynamic linker resolves symbols. Furthermore it fixes the jit on Windows. --- build_system/tests.rs | 3 +-- src/driver/jit.rs | 51 +++++++++++++++---------------------------- 2 files changed, 18 insertions(+), 36 deletions(-) diff --git a/build_system/tests.rs b/build_system/tests.rs index e21397cece8b..3f225b4efa2b 100644 --- a/build_system/tests.rs +++ b/build_system/tests.rs @@ -465,8 +465,7 @@ impl TestRunner { out_dir.push("out"); let is_native = host_triple == target_triple; - let jit_supported = - target_triple.contains("x86_64") && is_native && !host_triple.contains("windows"); + let jit_supported = target_triple.contains("x86_64") && is_native; let mut rust_flags = env::var("RUSTFLAGS").ok().unwrap_or("".to_string()); let mut run_wrapper = Vec::new(); diff --git a/src/driver/jit.rs b/src/driver/jit.rs index 0e77e4004c0b..4ad859b6d837 100644 --- a/src/driver/jit.rs +++ b/src/driver/jit.rs @@ -67,13 +67,12 @@ fn create_jit_module( hotswap: bool, ) -> (JITModule, CodegenCx) { let crate_info = CrateInfo::new(tcx, "dummy_target_cpu".to_string()); - let imported_symbols = load_imported_symbols_for_jit(tcx.sess, crate_info); let isa = crate::build_isa(tcx.sess, backend_config); let mut jit_builder = JITBuilder::with_isa(isa, cranelift_module::default_libcall_names()); jit_builder.hotswap(hotswap); crate::compiler_builtins::register_functions_for_jit(&mut jit_builder); - jit_builder.symbols(imported_symbols); + jit_builder.symbol_lookup_fn(dep_symbol_lookup_fn(tcx.sess, crate_info)); jit_builder.symbol("__clif_jit_fn", clif_jit_fn as *const u8); let mut jit_module = JITModule::new(jit_builder); @@ -286,10 +285,10 @@ fn jit_fn(instance_ptr: *const Instance<'static>, trampoline_ptr: *const u8) -> }) } -fn load_imported_symbols_for_jit( +fn dep_symbol_lookup_fn( sess: &Session, crate_info: CrateInfo, -) -> Vec<(String, *const u8)> { +) -> Box Option<*const u8>> { use rustc_middle::middle::dependency_format::Linkage; let mut dylib_paths = Vec::new(); @@ -316,39 +315,23 @@ fn load_imported_symbols_for_jit( } } - let mut imported_symbols = Vec::new(); - for path in dylib_paths { - use object::{Object, ObjectSymbol}; - let lib = libloading::Library::new(&path).unwrap(); - let obj = std::fs::read(path).unwrap(); - let obj = object::File::parse(&*obj).unwrap(); - imported_symbols.extend(obj.dynamic_symbols().filter_map(|symbol| { - let name = symbol.name().unwrap().to_string(); - if name.is_empty() || !symbol.is_global() || symbol.is_undefined() { - return None; - } - if name.starts_with("rust_metadata_") { - // The metadata is part of a section that is not loaded by the dynamic linker in - // case of cg_llvm. - return None; - } - let dlsym_name = if cfg!(target_os = "macos") { - // On macOS `dlsym` expects the name without leading `_`. - assert!(name.starts_with('_'), "{:?}", name); - &name[1..] - } else { - &name - }; - let symbol: libloading::Symbol<'_, *const u8> = - unsafe { lib.get(dlsym_name.as_bytes()) }.unwrap(); - Some((name, *symbol)) - })); - std::mem::forget(lib) - } + let imported_dylibs = Box::leak( + dylib_paths + .into_iter() + .map(|path| libloading::Library::new(&path).unwrap()) + .collect::>(), + ); sess.abort_if_errors(); - imported_symbols + Box::new(move |sym_name| { + for dylib in &*imported_dylibs { + if let Ok(sym) = unsafe { dylib.get::<*const u8>(sym_name.as_bytes()) } { + return Some(*sym); + } + } + None + }) } fn codegen_shim<'tcx>( From 4b3aa91b03c3dbfd05becbd890b4b06574092aab Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Tue, 5 Jul 2022 19:56:22 +0200 Subject: [PATCH 017/309] Support `#[unix_sigpipe = "inherit|sig_dfl|sig_ign"]` on `fn main()` This makes it possible to instruct libstd to never touch the signal handler for `SIGPIPE`, which makes programs pipeable by default (e.g. with `./your-program | head -n 1`) without `ErrorKind::BrokenPipe` errors. --- src/main_shim.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/main_shim.rs b/src/main_shim.rs index c67b6e98b32c..3c024a84d909 100644 --- a/src/main_shim.rs +++ b/src/main_shim.rs @@ -1,7 +1,7 @@ use rustc_hir::LangItem; use rustc_middle::ty::subst::GenericArg; use rustc_middle::ty::AssocKind; -use rustc_session::config::EntryFnType; +use rustc_session::config::{sigpipe, EntryFnType}; use rustc_span::symbol::Ident; use crate::prelude::*; @@ -15,12 +15,12 @@ pub(crate) fn maybe_create_entry_wrapper( is_jit: bool, is_primary_cgu: bool, ) { - let (main_def_id, is_main_fn) = match tcx.entry_fn(()) { + let (main_def_id, (is_main_fn, sigpipe)) = match tcx.entry_fn(()) { Some((def_id, entry_ty)) => ( def_id, match entry_ty { - EntryFnType::Main => true, - EntryFnType::Start => false, + EntryFnType::Main { sigpipe } => (true, sigpipe), + EntryFnType::Start => (false, sigpipe::DEFAULT), }, ), None => return, @@ -35,7 +35,7 @@ pub(crate) fn maybe_create_entry_wrapper( return; } - create_entry_fn(tcx, module, unwind_context, main_def_id, is_jit, is_main_fn); + create_entry_fn(tcx, module, unwind_context, main_def_id, is_jit, is_main_fn, sigpipe); fn create_entry_fn( tcx: TyCtxt<'_>, @@ -44,6 +44,7 @@ pub(crate) fn maybe_create_entry_wrapper( rust_main_def_id: DefId, ignore_lang_start_wrapper: bool, is_main_fn: bool, + sigpipe: u8, ) { let main_ret_ty = tcx.fn_sig(rust_main_def_id).output(); // Given that `main()` has no arguments, @@ -83,6 +84,7 @@ pub(crate) fn maybe_create_entry_wrapper( bcx.switch_to_block(block); let arg_argc = bcx.append_block_param(block, m.target_config().pointer_type()); let arg_argv = bcx.append_block_param(block, m.target_config().pointer_type()); + let arg_sigpipe = bcx.ins().iconst(types::I8, sigpipe as i64); let main_func_ref = m.declare_func_in_func(main_func_id, &mut bcx.func); @@ -143,7 +145,8 @@ pub(crate) fn maybe_create_entry_wrapper( let main_val = bcx.ins().func_addr(m.target_config().pointer_type(), main_func_ref); let func_ref = m.declare_func_in_func(start_func_id, &mut bcx.func); - let call_inst = bcx.ins().call(func_ref, &[main_val, arg_argc, arg_argv]); + let call_inst = + bcx.ins().call(func_ref, &[main_val, arg_argc, arg_argv, arg_sigpipe]); bcx.inst_results(call_inst)[0] } else { // using user-defined start fn From eb9fb29395af3ca0258b70c9f99ec9d029f2ace5 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Tue, 30 Aug 2022 10:14:55 +0200 Subject: [PATCH 018/309] Rustup to rustc 1.65.0-nightly (bc4b39c27 2022-08-29) --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index 14f2746ecb19..acc86748867b 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2022-08-24" +channel = "nightly-2022-08-30" components = ["rust-src", "rustc-dev", "llvm-tools-preview"] From 8d76b2ffb9df53225e7ac08abcf90b1f9eefccae Mon Sep 17 00:00:00 2001 From: Dezhi Wu Date: Thu, 18 Aug 2022 10:13:37 +0800 Subject: [PATCH 019/309] Fix a bunch of typo This PR will fix some typos detected by [typos]. I only picked the ones I was sure were spelling errors to fix, mostly in the comments. [typos]: https://github.com/crate-ci/typos --- src/abi/mod.rs | 2 +- src/constant.rs | 2 +- src/intrinsics/mod.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/abi/mod.rs b/src/abi/mod.rs index 815450f689e4..0497c2570e62 100644 --- a/src/abi/mod.rs +++ b/src/abi/mod.rs @@ -342,7 +342,7 @@ pub(crate) fn codegen_terminator_call<'tcx>( let ret_place = codegen_place(fx, destination); - // Handle special calls like instrinsics and empty drop glue. + // Handle special calls like intrinsics and empty drop glue. let instance = if let ty::FnDef(def_id, substs) = *fn_ty.kind() { let instance = ty::Instance::resolve(fx.tcx, ty::ParamEnv::reveal_all(), def_id, substs) .unwrap() diff --git a/src/constant.rs b/src/constant.rs index cb5d73a7e0ba..9224f499339c 100644 --- a/src/constant.rs +++ b/src/constant.rs @@ -59,7 +59,7 @@ pub(crate) fn check_constants(fx: &mut FunctionCx<'_, '_, '_>) -> bool { ErrorHandled::TooGeneric => { span_bug!( constant.span, - "codgen encountered polymorphic constant: {:?}", + "codegen encountered polymorphic constant: {:?}", err ); } diff --git a/src/intrinsics/mod.rs b/src/intrinsics/mod.rs index 95239f415a99..4aeb1e3aab95 100644 --- a/src/intrinsics/mod.rs +++ b/src/intrinsics/mod.rs @@ -203,7 +203,7 @@ pub(crate) fn codegen_intrinsic_call<'tcx>( sym::transmute => { crate::base::codegen_panic(fx, "Transmuting to uninhabited type.", source_info); } - _ => unimplemented!("unsupported instrinsic {}", intrinsic), + _ => unimplemented!("unsupported intrinsics {}", intrinsic), } return; }; From 3d9cee13dd4b6cf7fac4d8489ef33d8b669aa2b8 Mon Sep 17 00:00:00 2001 From: Dezhi Wu Date: Thu, 18 Aug 2022 16:36:49 +0800 Subject: [PATCH 020/309] Correct typo --- src/intrinsics/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/intrinsics/mod.rs b/src/intrinsics/mod.rs index 4aeb1e3aab95..39e9e784a478 100644 --- a/src/intrinsics/mod.rs +++ b/src/intrinsics/mod.rs @@ -203,7 +203,7 @@ pub(crate) fn codegen_intrinsic_call<'tcx>( sym::transmute => { crate::base::codegen_panic(fx, "Transmuting to uninhabited type.", source_info); } - _ => unimplemented!("unsupported intrinsics {}", intrinsic), + _ => unimplemented!("unsupported intrinsic {}", intrinsic), } return; }; From 096611855c90561188cb73d0e4a656e27a710d91 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 1 Sep 2022 11:53:35 +0200 Subject: [PATCH 021/309] Update abi-checker This updates a test expectation for s390x --- build_system/prepare.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build_system/prepare.rs b/build_system/prepare.rs index d23b7f00dcf1..eb032fb8bcb5 100644 --- a/build_system/prepare.rs +++ b/build_system/prepare.rs @@ -18,7 +18,7 @@ pub(crate) fn prepare() { "abi-checker", "Gankra", "abi-checker", - "a2232d45f202846f5c02203c9f27355360f9a2ff", + "4c6dc8c9c687e2b3a760ff2176ce236872b37212", ); apply_patches("abi-checker", Path::new("abi-checker")); From 2bcc936a5aeea322cec6d1f04d5323d11e033723 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 1 Sep 2022 13:53:47 +0200 Subject: [PATCH 022/309] Remove all uses of Function::with_name_signature The current main branch of Cranelift changed it's signature. Removing it's use is the easiest way to deal with this. --- src/allocator.rs | 4 ++-- src/main_shim.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/allocator.rs b/src/allocator.rs index 6d321c7b298a..bad8a87b9bee 100644 --- a/src/allocator.rs +++ b/src/allocator.rs @@ -78,7 +78,7 @@ fn codegen_inner( let callee_func_id = module.declare_function(&callee_name, Linkage::Import, &sig).unwrap(); let mut ctx = Context::new(); - ctx.func = Function::with_name_signature(ExternalName::user(0, 0), sig.clone()); + ctx.func.signature = sig.clone(); { let mut func_ctx = FunctionBuilderContext::new(); let mut bcx = FunctionBuilder::new(&mut ctx.func, &mut func_ctx); @@ -116,7 +116,7 @@ fn codegen_inner( let callee_func_id = module.declare_function(callee_name, Linkage::Import, &sig).unwrap(); let mut ctx = Context::new(); - ctx.func = Function::with_name_signature(ExternalName::user(0, 0), sig); + ctx.func.signature = sig; { let mut func_ctx = FunctionBuilderContext::new(); let mut bcx = FunctionBuilder::new(&mut ctx.func, &mut func_ctx); diff --git a/src/main_shim.rs b/src/main_shim.rs index c67b6e98b32c..b5a17ee153d1 100644 --- a/src/main_shim.rs +++ b/src/main_shim.rs @@ -74,7 +74,7 @@ pub(crate) fn maybe_create_entry_wrapper( let main_func_id = m.declare_function(main_name, Linkage::Import, &main_sig).unwrap(); let mut ctx = Context::new(); - ctx.func = Function::with_name_signature(ExternalName::user(0, 0), cmain_sig); + ctx.func.signature = cmain_sig; { let mut func_ctx = FunctionBuilderContext::new(); let mut bcx = FunctionBuilder::new(&mut ctx.func, &mut func_ctx); From 2231545ebf83901ced90d469b891b04a1fe89b97 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 1 Sep 2022 14:00:21 +0200 Subject: [PATCH 023/309] Use pointer_ty instead of func.dfg.value_type This fixes a borrowck error with the current main branch of Cranelift. --- src/intrinsics/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/intrinsics/mod.rs b/src/intrinsics/mod.rs index 95239f415a99..186772d807d1 100644 --- a/src/intrinsics/mod.rs +++ b/src/intrinsics/mod.rs @@ -1216,7 +1216,7 @@ fn codegen_regular_intrinsic_call<'tcx>( // FIXME once unwinding is supported, change this to actually catch panics let f_sig = fx.bcx.func.import_signature(Signature { call_conv: fx.target_config.default_call_conv, - params: vec![AbiParam::new(fx.bcx.func.dfg.value_type(data))], + params: vec![AbiParam::new(pointer_ty(fx.tcx))], returns: vec![], }); From df6221adc6ad721af59acd501dbfce426b1298da Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Tue, 26 Jul 2022 17:40:48 -0400 Subject: [PATCH 024/309] Sort tests at compile time, not at startup Recently, another Miri user was trying to run `cargo miri test` on the crate `iced-x86` with `--features=code_asm,mvex`. This configuration has a startup time of ~18 minutes. That's ~18 minutes before any tests even start to run. The fact that this crate has over 26,000 tests and Miri is slow makes a lot of code which is otherwise a bit sloppy but fine into a huge runtime issue. Sorting the tests when the test harness is created instead of at startup time knocks just under 4 minutes out of those ~18 minutes. I have ways to remove most of the rest of the startup time, but this change requires coordinating changes of both the compiler and libtest, so I'm sending it separately. --- compiler/rustc_builtin_macros/src/test.rs | 37 ++++++---- .../rustc_builtin_macros/src/test_harness.rs | 15 ++-- compiler/rustc_feature/src/builtin_attrs.rs | 2 +- library/test/src/console.rs | 2 +- library/test/src/lib.rs | 17 ++--- library/test/src/tests.rs | 27 -------- src/librustdoc/doctest.rs | 3 +- src/test/pretty/tests-are-sorted.pp | 69 +++++++++++++++++++ src/test/pretty/tests-are-sorted.rs | 13 ++++ src/tools/compiletest/src/main.rs | 2 + 10 files changed, 130 insertions(+), 57 deletions(-) create mode 100644 src/test/pretty/tests-are-sorted.pp create mode 100644 src/test/pretty/tests-are-sorted.rs diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs index 7eee80733666..92e70bfc779e 100644 --- a/compiler/rustc_builtin_macros/src/test.rs +++ b/compiler/rustc_builtin_macros/src/test.rs @@ -36,13 +36,22 @@ pub fn expand_test_case( let sp = ecx.with_def_site_ctxt(attr_sp); let mut item = anno_item.expect_item(); item = item.map(|mut item| { + let test_path_symbol = Symbol::intern(&item_path( + // skip the name of the root module + &ecx.current_expansion.module.mod_path[1..], + &item.ident, + )); item.vis = ast::Visibility { span: item.vis.span, kind: ast::VisibilityKind::Public, tokens: None, }; item.ident.span = item.ident.span.with_ctxt(sp.ctxt()); - item.attrs.push(ecx.attribute(ecx.meta_word(sp, sym::rustc_test_marker))); + item.attrs.push(ecx.attribute(attr::mk_name_value_item_str( + Ident::new(sym::rustc_test_marker, sp), + test_path_symbol, + sp, + ))); item }); @@ -215,6 +224,12 @@ pub fn expand_test_or_bench( ) }; + let test_path_symbol = Symbol::intern(&item_path( + // skip the name of the root module + &cx.current_expansion.module.mod_path[1..], + &item.ident, + )); + let mut test_const = cx.item( sp, Ident::new(item.ident.name, sp), @@ -224,9 +239,14 @@ pub fn expand_test_or_bench( Ident::new(sym::cfg, attr_sp), vec![attr::mk_nested_word_item(Ident::new(sym::test, attr_sp))], )), - // #[rustc_test_marker] - cx.attribute(cx.meta_word(attr_sp, sym::rustc_test_marker)), - ], + // #[rustc_test_marker = "test_case_sort_key"] + cx.attribute(attr::mk_name_value_item_str( + Ident::new(sym::rustc_test_marker, attr_sp), + test_path_symbol, + attr_sp, + )), + ] + .into(), // const $ident: test::TestDescAndFn = ast::ItemKind::Const( ast::Defaultness::Final, @@ -250,14 +270,7 @@ pub fn expand_test_or_bench( cx.expr_call( sp, cx.expr_path(test_path("StaticTestName")), - vec![cx.expr_str( - sp, - Symbol::intern(&item_path( - // skip the name of the root module - &cx.current_expansion.module.mod_path[1..], - &item.ident, - )), - )], + vec![cx.expr_str(sp, test_path_symbol)], ), ), // ignore: true | false diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index 89b0d2cc9be2..a06c2e641931 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -19,9 +19,11 @@ use tracing::debug; use std::{iter, mem}; +#[derive(Clone)] struct Test { span: Span, ident: Ident, + name: Symbol, } struct TestCtxt<'a> { @@ -121,10 +123,10 @@ impl<'a> MutVisitor for TestHarnessGenerator<'a> { fn flat_map_item(&mut self, i: P) -> SmallVec<[P; 1]> { let mut item = i.into_inner(); - if is_test_case(&self.cx.ext_cx.sess, &item) { + if let Some(name) = get_test_name(&self.cx.ext_cx.sess, &item) { debug!("this is a test item"); - let test = Test { span: item.span, ident: item.ident }; + let test = Test { span: item.span, ident: item.ident, name }; self.tests.push(test); } @@ -355,9 +357,12 @@ fn mk_tests_slice(cx: &TestCtxt<'_>, sp: Span) -> P { debug!("building test vector from {} tests", cx.test_cases.len()); let ecx = &cx.ext_cx; + let mut tests = cx.test_cases.clone(); + tests.sort_by(|a, b| a.name.as_str().cmp(&b.name.as_str())); + ecx.expr_array_ref( sp, - cx.test_cases + tests .iter() .map(|test| { ecx.expr_addr_of(test.span, ecx.expr_path(ecx.path(test.span, vec![test.ident]))) @@ -366,8 +371,8 @@ fn mk_tests_slice(cx: &TestCtxt<'_>, sp: Span) -> P { ) } -fn is_test_case(sess: &Session, i: &ast::Item) -> bool { - sess.contains_name(&i.attrs, sym::rustc_test_marker) +fn get_test_name(sess: &Session, i: &ast::Item) -> Option { + sess.first_attr_value_str_by_name(&i.attrs, sym::rustc_test_marker) } fn get_test_runner( diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 0487270b52a9..7c063cdec529 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -727,7 +727,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ for reserving for `for From for T` impl" ), rustc_attr!( - rustc_test_marker, Normal, template!(Word), WarnFollowing, + rustc_test_marker, Normal, template!(NameValueStr: "name"), WarnFollowing, "the `#[rustc_test_marker]` attribute is used internally to track tests", ), rustc_attr!( diff --git a/library/test/src/console.rs b/library/test/src/console.rs index e9dda98966de..b1270c272710 100644 --- a/library/test/src/console.rs +++ b/library/test/src/console.rs @@ -147,7 +147,7 @@ pub fn list_tests_console(opts: &TestOpts, tests: Vec) -> io::Res let mut ntest = 0; let mut nbench = 0; - for test in filter_tests(&opts, tests) { + for test in filter_tests(&opts, tests).into_iter() { use crate::TestFn::*; let TestDescAndFn { desc: TestDesc { name, .. }, testfn } = test; diff --git a/library/test/src/lib.rs b/library/test/src/lib.rs index 3b7193adcc75..e39d383728e8 100644 --- a/library/test/src/lib.rs +++ b/library/test/src/lib.rs @@ -242,7 +242,7 @@ where let event = TestEvent::TeFiltered(filtered_descs, shuffle_seed); notify_about_test_event(event)?; - let (filtered_tests, filtered_benchs): (Vec<_>, _) = filtered_tests + let (mut filtered_tests, filtered_benchs): (Vec<_>, _) = filtered_tests .into_iter() .enumerate() .map(|(i, e)| (TestId(i), e)) @@ -250,12 +250,12 @@ where let concurrency = opts.test_threads.unwrap_or_else(get_concurrency); - let mut remaining = filtered_tests; if let Some(shuffle_seed) = shuffle_seed { - shuffle_tests(shuffle_seed, &mut remaining); - } else { - remaining.reverse(); + shuffle_tests(shuffle_seed, &mut filtered_tests); } + // Store the tests in a VecDeque so we can efficiently remove the first element to run the + // tests in the order they were passed (unless shuffled). + let mut remaining = VecDeque::from(filtered_tests); let mut pending = 0; let (tx, rx) = channel::(); @@ -295,7 +295,7 @@ where if concurrency == 1 { while !remaining.is_empty() { - let (id, test) = remaining.pop().unwrap(); + let (id, test) = remaining.pop_front().unwrap(); let event = TestEvent::TeWait(test.desc.clone()); notify_about_test_event(event)?; let join_handle = @@ -309,7 +309,7 @@ where } else { while pending > 0 || !remaining.is_empty() { while pending < concurrency && !remaining.is_empty() { - let (id, test) = remaining.pop().unwrap(); + let (id, test) = remaining.pop_front().unwrap(); let timeout = time::get_default_test_timeout(); let desc = test.desc.clone(); @@ -421,9 +421,6 @@ pub fn filter_tests(opts: &TestOpts, tests: Vec) -> Vec {} } - // Sort the tests alphabetically - filtered.sort_by(|t1, t2| t1.desc.name.as_slice().cmp(t2.desc.name.as_slice())); - filtered } diff --git a/library/test/src/tests.rs b/library/test/src/tests.rs index 0b81aff5907e..79489c40f4e0 100644 --- a/library/test/src/tests.rs +++ b/library/test/src/tests.rs @@ -600,33 +600,6 @@ fn sample_tests() -> Vec { tests } -#[test] -pub fn sort_tests() { - let mut opts = TestOpts::new(); - opts.run_tests = true; - - let tests = sample_tests(); - let filtered = filter_tests(&opts, tests); - - let expected = vec![ - "isize::test_pow".to_string(), - "isize::test_to_str".to_string(), - "sha1::test".to_string(), - "test::do_not_run_ignored_tests".to_string(), - "test::filter_for_ignored_option".to_string(), - "test::first_free_arg_should_be_a_filter".to_string(), - "test::ignored_tests_result_in_ignored".to_string(), - "test::parse_ignored_flag".to_string(), - "test::parse_include_ignored_flag".to_string(), - "test::run_include_ignored_option".to_string(), - "test::sort_tests".to_string(), - ]; - - for (a, b) in expected.iter().zip(filtered) { - assert_eq!(*a, b.desc.name.to_string()); - } -} - #[test] pub fn shuffle_tests() { let mut opts = TestOpts::new(); diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 20ae102bc27d..6201f3d9ce1f 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -210,12 +210,13 @@ pub(crate) fn run(options: RustdocOptions) -> Result<(), ErrorGuaranteed> { pub(crate) fn run_tests( mut test_args: Vec, nocapture: bool, - tests: Vec, + mut tests: Vec, ) { test_args.insert(0, "rustdoctest".to_string()); if nocapture { test_args.push("--nocapture".to_string()); } + tests.sort_by(|a, b| a.desc.name.as_slice().cmp(&b.desc.name.as_slice())); test::test_main(&test_args, tests, None); } diff --git a/src/test/pretty/tests-are-sorted.pp b/src/test/pretty/tests-are-sorted.pp new file mode 100644 index 000000000000..15dcd4ed97df --- /dev/null +++ b/src/test/pretty/tests-are-sorted.pp @@ -0,0 +1,69 @@ +#![feature(prelude_import)] +#![no_std] +#[prelude_import] +use ::std::prelude::rust_2015::*; +#[macro_use] +extern crate std; +// compile-flags: --crate-type=lib --test +// pretty-compare-only +// pretty-mode:expanded +// pp-exact:tests-are-sorted.pp + +extern crate test; +#[cfg(test)] +#[rustc_test_marker = "m_test"] +pub const m_test: test::TestDescAndFn = + test::TestDescAndFn { + desc: test::TestDesc { + name: test::StaticTestName("m_test"), + ignore: false, + ignore_message: ::core::option::Option::None, + compile_fail: false, + no_run: false, + should_panic: test::ShouldPanic::No, + test_type: test::TestType::Unknown, + }, + testfn: test::StaticTestFn(|| test::assert_test_result(m_test())), + }; +fn m_test() {} + +extern crate test; +#[cfg(test)] +#[rustc_test_marker = "z_test"] +pub const z_test: test::TestDescAndFn = + test::TestDescAndFn { + desc: test::TestDesc { + name: test::StaticTestName("z_test"), + ignore: false, + ignore_message: ::core::option::Option::None, + compile_fail: false, + no_run: false, + should_panic: test::ShouldPanic::No, + test_type: test::TestType::Unknown, + }, + testfn: test::StaticTestFn(|| test::assert_test_result(z_test())), + }; +fn z_test() {} + +extern crate test; +#[cfg(test)] +#[rustc_test_marker = "a_test"] +pub const a_test: test::TestDescAndFn = + test::TestDescAndFn { + desc: test::TestDesc { + name: test::StaticTestName("a_test"), + ignore: false, + ignore_message: ::core::option::Option::None, + compile_fail: false, + no_run: false, + should_panic: test::ShouldPanic::No, + test_type: test::TestType::Unknown, + }, + testfn: test::StaticTestFn(|| test::assert_test_result(a_test())), + }; +fn a_test() {} +#[rustc_main] +pub fn main() -> () { + extern crate test; + test::test_main_static(&[&a_test, &m_test, &z_test]) +} diff --git a/src/test/pretty/tests-are-sorted.rs b/src/test/pretty/tests-are-sorted.rs new file mode 100644 index 000000000000..1f737d547193 --- /dev/null +++ b/src/test/pretty/tests-are-sorted.rs @@ -0,0 +1,13 @@ +// compile-flags: --crate-type=lib --test +// pretty-compare-only +// pretty-mode:expanded +// pp-exact:tests-are-sorted.pp + +#[test] +fn m_test() {} + +#[test] +fn z_test() {} + +#[test] +fn a_test() {} diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs index 0e2cc52a645b..f3f06bde6f16 100644 --- a/src/tools/compiletest/src/main.rs +++ b/src/tools/compiletest/src/main.rs @@ -401,6 +401,8 @@ pub fn run_tests(config: Config) { make_tests(c, &mut tests); } + tests.sort_by(|a, b| a.desc.name.as_slice().cmp(&b.desc.name.as_slice())); + let res = test::run_tests_console(&opts, tests); match res { Ok(true) => {} From eef75dde3ba669044e1f2c85d83ebd448efb97c7 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 1 Sep 2022 15:36:12 +0000 Subject: [PATCH 025/309] Update libloading to 0.7.3 This was previously done in bfcf97bd8360931eb088d65f247fc9e1016f8199, but got reverted due to a bug. The bug seems to be fixed now. Fixes #1137 --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- src/driver/jit.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index edae7e471578..b4c607d482e5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -232,9 +232,9 @@ checksum = "505e71a4706fa491e9b1b55f51b95d4037d0821ee40131190475f692b35b009b" [[package]] name = "libloading" -version = "0.6.7" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "351a32417a12d5f7e82c368a66781e307834dae04c6ce0cd4456d52989229883" +checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd" dependencies = [ "cfg-if", "winapi", diff --git a/Cargo.toml b/Cargo.toml index e7c342748548..7a9e8f5d8e0c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ object = { version = "0.29.0", default-features = false, features = ["std", "rea ar = { git = "https://github.com/bjorn3/rust-ar.git", branch = "do_not_remove_cg_clif_ranlib" } indexmap = "1.9.1" -libloading = { version = "0.6.0", optional = true } +libloading = { version = "0.7.3", optional = true } once_cell = "1.10.0" smallvec = "1.8.1" diff --git a/src/driver/jit.rs b/src/driver/jit.rs index 4ad859b6d837..6a430b5215e3 100644 --- a/src/driver/jit.rs +++ b/src/driver/jit.rs @@ -318,7 +318,7 @@ fn dep_symbol_lookup_fn( let imported_dylibs = Box::leak( dylib_paths .into_iter() - .map(|path| libloading::Library::new(&path).unwrap()) + .map(|path| unsafe { libloading::Library::new(&path).unwrap() }) .collect::>(), ); From de7342b4446476a2d9375a7f93be54dc9f8707d1 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 1 Sep 2022 16:18:39 +0000 Subject: [PATCH 026/309] Implement core::hint::spin_loop() on AArch64 This is used in the futex based mutex implementation of libstd --- src/intrinsics/llvm.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/intrinsics/llvm.rs b/src/intrinsics/llvm.rs index a799dca938e2..598c13e757ef 100644 --- a/src/intrinsics/llvm.rs +++ b/src/intrinsics/llvm.rs @@ -14,6 +14,10 @@ pub(crate) fn codegen_llvm_intrinsic_call<'tcx>( target: Option, ) { match intrinsic { + "llvm.x86.sse2.pause" | "llvm.aarch64.isb" => { + // Spin loop hint + } + // Used by `_mm_movemask_epi8` and `_mm256_movemask_epi8` "llvm.x86.sse2.pmovmskb.128" | "llvm.x86.avx2.pmovmskb" | "llvm.x86.sse2.movmsk.pd" => { intrinsic_args!(fx, args => (a); intrinsic); From 244455d8e3137f3bd3e2ee6a49ebb787e7d7f792 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 1 Sep 2022 16:41:01 +0000 Subject: [PATCH 027/309] Apply sysroot patches to rustc bootstrap and rustc test suite tests This is necessary on AArch64 as 128bit atomics aren't yet supported by Cranelift. --- scripts/setup_rust_fork.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/setup_rust_fork.sh b/scripts/setup_rust_fork.sh index 091bfa1e9926..6ae8b14f4c01 100644 --- a/scripts/setup_rust_fork.sh +++ b/scripts/setup_rust_fork.sh @@ -10,6 +10,8 @@ git fetch git checkout -- . git checkout "$(rustc -V | cut -d' ' -f3 | tr -d '(')" +git am ../patches/*-sysroot-*.patch + git apply - < Date: Fri, 2 Sep 2022 09:34:37 +0000 Subject: [PATCH 028/309] Fix panic in ConcurrencyLimiter when unwinding Fixes #1275 --- src/concurrency_limiter.rs | 17 +++++++++++++---- src/driver/aot.rs | 2 +- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/concurrency_limiter.rs b/src/concurrency_limiter.rs index dfde97920461..f855e20e0a1a 100644 --- a/src/concurrency_limiter.rs +++ b/src/concurrency_limiter.rs @@ -10,6 +10,7 @@ pub(super) struct ConcurrencyLimiter { helper_thread: Option, state: Arc>, available_token_condvar: Arc, + finished: bool, } impl ConcurrencyLimiter { @@ -32,6 +33,7 @@ impl ConcurrencyLimiter { helper_thread: Some(helper_thread), state, available_token_condvar: Arc::new(Condvar::new()), + finished: false, } } @@ -56,16 +58,23 @@ impl ConcurrencyLimiter { let mut state = self.state.lock().unwrap(); state.job_already_done(); } -} -impl Drop for ConcurrencyLimiter { - fn drop(&mut self) { - // + pub(crate) fn finished(mut self) { self.helper_thread.take(); // Assert that all jobs have finished let state = Mutex::get_mut(Arc::get_mut(&mut self.state).unwrap()).unwrap(); state.assert_done(); + + self.finished = true; + } +} + +impl Drop for ConcurrencyLimiter { + fn drop(&mut self) { + if !self.finished && !std::thread::panicking() { + panic!("Forgot to call finished() on ConcurrencyLimiter"); + } } } diff --git a/src/driver/aot.rs b/src/driver/aot.rs index 8eabe1cbcb15..f873561c1713 100644 --- a/src/driver/aot.rs +++ b/src/driver/aot.rs @@ -106,7 +106,7 @@ impl OngoingCodegen { } } - drop(self.concurrency_limiter); + self.concurrency_limiter.finished(); ( CodegenResults { From 7f260a953b583fdca533d57f92639d69024c262b Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun, 4 Sep 2022 13:35:58 +0200 Subject: [PATCH 029/309] Rustup to rustc 1.65.0-nightly (84f0c3f79 2022-09-03) --- example/mini_core_hello_world.rs | 1 + rust-toolchain | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/example/mini_core_hello_world.rs b/example/mini_core_hello_world.rs index e83be3a3df5c..215d3556a17c 100644 --- a/example/mini_core_hello_world.rs +++ b/example/mini_core_hello_world.rs @@ -93,6 +93,7 @@ fn start( main: fn() -> T, argc: isize, argv: *const *const u8, + _sigpipe: u8, ) -> isize { if argc == 3 { unsafe { puts(*argv as *const i8); } diff --git a/rust-toolchain b/rust-toolchain index acc86748867b..f17897e48adf 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2022-08-30" +channel = "nightly-2022-09-04" components = ["rust-src", "rustc-dev", "llvm-tools-preview"] From c4c393c78ede150fbdf693e23689d472fc54bfa6 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Mon, 5 Sep 2022 12:01:37 +0000 Subject: [PATCH 030/309] Update portable-simd --- build_system/prepare.rs | 2 +- ...table-simd-Disable-unsupported-tests.patch | 231 +++++++++++++----- src/intrinsics/simd.rs | 2 +- 3 files changed, 171 insertions(+), 64 deletions(-) diff --git a/build_system/prepare.rs b/build_system/prepare.rs index eb032fb8bcb5..b7368e8ffaba 100644 --- a/build_system/prepare.rs +++ b/build_system/prepare.rs @@ -41,7 +41,7 @@ pub(crate) fn prepare() { "portable-simd", "rust-lang", "portable-simd", - "b8d6b6844602f80af79cd96401339ec594d472d8", + "d5cd4a8112d958bd3a252327e0d069a6363249bd", ); apply_patches("portable-simd", Path::new("portable-simd")); diff --git a/patches/0001-portable-simd-Disable-unsupported-tests.patch b/patches/0001-portable-simd-Disable-unsupported-tests.patch index 54e13b090abd..a1824f86ce2d 100644 --- a/patches/0001-portable-simd-Disable-unsupported-tests.patch +++ b/patches/0001-portable-simd-Disable-unsupported-tests.patch @@ -1,80 +1,188 @@ -From 97c473937382a5b5858d9cce3c947855d23b2dc5 Mon Sep 17 00:00:00 2001 +From 82f597cf81b169b0e72a576ac8751f598c059c48 Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Thu, 18 Nov 2021 19:28:40 +0100 Subject: [PATCH] Disable unsupported tests --- - crates/core_simd/src/math.rs | 6 ++++++ - crates/core_simd/src/vector.rs | 2 ++ - crates/core_simd/tests/masks.rs | 2 ++ - crates/core_simd/tests/ops_macros.rs | 4 ++++ - 4 files changed, 14 insertions(+) + crates/core_simd/src/elements/int.rs | 8 ++++++++ + crates/core_simd/src/elements/uint.rs | 4 ++++ + crates/core_simd/src/masks/full_masks.rs | 9 +++++++++ + crates/core_simd/src/vector.rs | 2 ++ + crates/core_simd/tests/masks.rs | 2 ++ + 5 files changed, 25 insertions(+) -diff --git a/crates/core_simd/src/math.rs b/crates/core_simd/src/math.rs -index 2bae414..2f87499 100644 ---- a/crates/core_simd/src/math.rs -+++ b/crates/core_simd/src/math.rs -@@ -5,6 +5,7 @@ macro_rules! impl_uint_arith { - ($($ty:ty),+) => { - $( impl Simd<$ty, LANES> where LaneCount: SupportedLaneCount { +diff --git a/crates/core_simd/src/elements/int.rs b/crates/core_simd/src/elements/int.rs +index 9b8c37e..ea95f08 100644 +--- a/crates/core_simd/src/elements/int.rs ++++ b/crates/core_simd/src/elements/int.rs +@@ -11,6 +11,7 @@ pub trait SimdInt: Copy + Sealed { + /// Scalar type contained by this SIMD vector type. + type Scalar; + ++ /* + /// Lanewise saturating add. + /// + /// # Examples +@@ -45,6 +46,7 @@ pub trait SimdInt: Copy + Sealed { + /// assert_eq!(unsat, Simd::from_array([1, MAX, MIN, 0])); + /// assert_eq!(sat, Simd::from_array([MIN, MIN, MIN, 0])); + fn saturating_sub(self, second: Self) -> Self; ++ */ + + /// Lanewise absolute value, implemented in Rust. + /// Every lane becomes its absolute value. +@@ -61,6 +63,7 @@ pub trait SimdInt: Copy + Sealed { + /// ``` + fn abs(self) -> Self; + ++ /* + /// Lanewise saturating absolute value, implemented in Rust. + /// As abs(), except the MIN value becomes MAX instead of itself. + /// +@@ -96,6 +99,7 @@ pub trait SimdInt: Copy + Sealed { + /// assert_eq!(sat, Simd::from_array([MAX, 2, -3, MIN + 1])); + /// ``` + fn saturating_neg(self) -> Self; ++ */ + + /// Returns true for each positive lane and false if it is zero or negative. + fn is_positive(self) -> Self::Mask; +@@ -199,6 +203,7 @@ macro_rules! impl_trait { + type Mask = Mask<<$ty as SimdElement>::Mask, LANES>; + type Scalar = $ty; + /* - /// Lanewise saturating add. - /// - /// # Examples -@@ -43,6 +44,7 @@ macro_rules! impl_uint_arith { - pub fn saturating_sub(self, second: Self) -> Self { - unsafe { simd_saturating_sub(self, second) } - } -+ */ - })+ - } - } -@@ -51,6 +53,7 @@ macro_rules! impl_int_arith { - ($($ty:ty),+) => { - $( impl Simd<$ty, LANES> where LaneCount: SupportedLaneCount { - -+ /* - /// Lanewise saturating add. - /// - /// # Examples -@@ -89,6 +92,7 @@ macro_rules! impl_int_arith { - pub fn saturating_sub(self, second: Self) -> Self { - unsafe { simd_saturating_sub(self, second) } + #[inline] + fn saturating_add(self, second: Self) -> Self { + // Safety: `self` is a vector +@@ -210,6 +215,7 @@ macro_rules! impl_trait { + // Safety: `self` is a vector + unsafe { intrinsics::simd_saturating_sub(self, second) } } + */ - /// Lanewise absolute value, implemented in Rust. - /// Every lane becomes its absolute value. -@@ -109,6 +113,7 @@ macro_rules! impl_int_arith { + #[inline] + fn abs(self) -> Self { +@@ -218,6 +224,7 @@ macro_rules! impl_trait { (self^m) - m } + /* - /// Lanewise saturating absolute value, implemented in Rust. - /// As abs(), except the MIN value becomes MAX instead of itself. - /// -@@ -151,6 +156,7 @@ macro_rules! impl_int_arith { - pub fn saturating_neg(self) -> Self { + #[inline] + fn saturating_abs(self) -> Self { + // arith shift for -1 or 0 mask based on sign bit, giving 2s complement +@@ -230,6 +237,7 @@ macro_rules! impl_trait { + fn saturating_neg(self) -> Self { Self::splat(0).saturating_sub(self) } + */ - })+ + + #[inline] + fn is_positive(self) -> Self::Mask { +diff --git a/crates/core_simd/src/elements/uint.rs b/crates/core_simd/src/elements/uint.rs +index 21e7e76..0d6dee2 100644 +--- a/crates/core_simd/src/elements/uint.rs ++++ b/crates/core_simd/src/elements/uint.rs +@@ -6,6 +6,7 @@ pub trait SimdUint: Copy + Sealed { + /// Scalar type contained by this SIMD vector type. + type Scalar; + ++ /* + /// Lanewise saturating add. + /// + /// # Examples +@@ -40,6 +41,7 @@ pub trait SimdUint: Copy + Sealed { + /// assert_eq!(unsat, Simd::from_array([3, 2, 1, 0])); + /// assert_eq!(sat, Simd::splat(0)); + fn saturating_sub(self, second: Self) -> Self; ++ */ + + /// Returns the sum of the lanes of the vector, with wrapping addition. + fn reduce_sum(self) -> Self::Scalar; +@@ -78,6 +80,7 @@ macro_rules! impl_trait { + { + type Scalar = $ty; + ++ /* + #[inline] + fn saturating_add(self, second: Self) -> Self { + // Safety: `self` is a vector +@@ -89,6 +92,7 @@ macro_rules! impl_trait { + // Safety: `self` is a vector + unsafe { intrinsics::simd_saturating_sub(self, second) } + } ++ */ + + #[inline] + fn reduce_sum(self) -> Self::Scalar { +diff --git a/crates/core_simd/src/masks/full_masks.rs b/crates/core_simd/src/masks/full_masks.rs +index adf0fcb..5b10292 100644 +--- a/crates/core_simd/src/masks/full_masks.rs ++++ b/crates/core_simd/src/masks/full_masks.rs +@@ -150,6 +150,7 @@ where + super::Mask: ToBitMaskArray, + [(); as ToBitMaskArray>::BYTES]: Sized, + { ++ /* + assert_eq!( as ToBitMaskArray>::BYTES, N); + + // Safety: N is the correct bitmask size +@@ -170,6 +171,8 @@ where + + bitmask + } ++ */ ++ panic!(); } - } + + #[cfg(feature = "generic_const_exprs")] +@@ -209,6 +212,7 @@ where + where + super::Mask: ToBitMask, + { ++ /* + // Safety: U is required to be the appropriate bitmask type + let bitmask: U = unsafe { intrinsics::simd_bitmask(self.0) }; + +@@ -218,6 +222,8 @@ where + } else { + bitmask + } ++ */ ++ panic!(); + } + + #[inline] +@@ -225,6 +231,7 @@ where + where + super::Mask: ToBitMask, + { ++ /* + // LLVM assumes bit order should match endianness + let bitmask = if cfg!(target_endian = "big") { + bitmask.reverse_bits(LANES) +@@ -240,6 +247,8 @@ where + Self::splat(false).to_int(), + )) + } ++ */ ++ panic!(); + } + + #[inline] diff --git a/crates/core_simd/src/vector.rs b/crates/core_simd/src/vector.rs -index 7c5ec2b..c8631e8 100644 +index e8e8f68..7173c24 100644 --- a/crates/core_simd/src/vector.rs +++ b/crates/core_simd/src/vector.rs -@@ -75,6 +75,7 @@ where - Self(array) +@@ -250,6 +250,7 @@ where + unsafe { intrinsics::simd_cast(self) } } + /* /// Reads from potentially discontiguous indices in `slice` to construct a SIMD vector. /// If an index is out-of-bounds, the lane is instead selected from the `or` vector. /// -@@ -297,6 +298,7 @@ where +@@ -473,6 +474,7 @@ where // Cleared ☢️ *mut T Zone } } @@ -83,25 +191,24 @@ index 7c5ec2b..c8631e8 100644 impl Copy for Simd diff --git a/crates/core_simd/tests/masks.rs b/crates/core_simd/tests/masks.rs -index 6a8ecd3..68fcb49 100644 +index 673d0db..0d68b01 100644 --- a/crates/core_simd/tests/masks.rs +++ b/crates/core_simd/tests/masks.rs -@@ -68,6 +68,7 @@ macro_rules! test_mask_api { - assert_eq!(core_simd::Mask::<$type, 8>::from_int(int), mask); +@@ -59,6 +59,7 @@ macro_rules! test_mask_api { + assert!(!v.all()); } + /* - #[cfg(feature = "generic_const_exprs")] #[test] - fn roundtrip_bitmask_conversion() { -@@ -80,6 +81,7 @@ macro_rules! test_mask_api { - assert_eq!(bitmask, [0b01001001, 0b10000011]); - assert_eq!(core_simd::Mask::<$type, 16>::from_bitmask(bitmask), mask); + fn roundtrip_int_conversion() { + let values = [true, false, false, true, false, false, true, false]; +@@ -99,6 +100,7 @@ macro_rules! test_mask_api { + assert_eq!(bitmask, 0b01); + assert_eq!(core_simd::Mask::<$type, 2>::from_bitmask(bitmask), mask); } + */ - } - } - } + + #[test] + fn cast() { -- -2.26.2.7.g19db9cfb68 - +2.25.1 diff --git a/src/intrinsics/simd.rs b/src/intrinsics/simd.rs index 1f358b1bbb96..a8aefcf30b5e 100644 --- a/src/intrinsics/simd.rs +++ b/src/intrinsics/simd.rs @@ -26,7 +26,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( span: Span, ) { match intrinsic { - sym::simd_cast => { + sym::simd_as | sym::simd_cast => { intrinsic_args!(fx, args => (a); intrinsic); if !a.layout().ty.is_simd() { From 0bb9bdf8e337b494c5ea385a309425c46f6aa383 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Mon, 5 Sep 2022 15:31:14 +0000 Subject: [PATCH 031/309] Use value_lane instead of value_field in simd/llvm.rs --- src/intrinsics/llvm.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/intrinsics/llvm.rs b/src/intrinsics/llvm.rs index 598c13e757ef..783d426c30bc 100644 --- a/src/intrinsics/llvm.rs +++ b/src/intrinsics/llvm.rs @@ -29,8 +29,7 @@ pub(crate) fn codegen_llvm_intrinsic_call<'tcx>( let mut res = fx.bcx.ins().iconst(types::I32, 0); for lane in (0..lane_count).rev() { - let a_lane = - a.value_field(fx, mir::Field::new(lane.try_into().unwrap())).load_scalar(fx); + let a_lane = a.value_lane(fx, lane).load_scalar(fx); // cast float to int let a_lane = match lane_ty { From f200fbca10b4cc066120c4d19b58d2f244c6e638 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Mon, 5 Sep 2022 15:37:56 +0000 Subject: [PATCH 032/309] Implement simd_bitmask --- ...table-simd-Disable-unsupported-tests.patch | 65 ++++++-------- src/intrinsics/simd.rs | 86 ++++++++++++++++++- 2 files changed, 111 insertions(+), 40 deletions(-) diff --git a/patches/0001-portable-simd-Disable-unsupported-tests.patch b/patches/0001-portable-simd-Disable-unsupported-tests.patch index a1824f86ce2d..c75d5dda5caf 100644 --- a/patches/0001-portable-simd-Disable-unsupported-tests.patch +++ b/patches/0001-portable-simd-Disable-unsupported-tests.patch @@ -1,4 +1,4 @@ -From 82f597cf81b169b0e72a576ac8751f598c059c48 Mon Sep 17 00:00:00 2001 +From b742f03694b920cc14400727d54424e8e1b60928 Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Thu, 18 Nov 2021 19:28:40 +0100 Subject: [PATCH] Disable unsupported tests @@ -6,10 +6,10 @@ Subject: [PATCH] Disable unsupported tests --- crates/core_simd/src/elements/int.rs | 8 ++++++++ crates/core_simd/src/elements/uint.rs | 4 ++++ - crates/core_simd/src/masks/full_masks.rs | 9 +++++++++ + crates/core_simd/src/masks/full_masks.rs | 6 ++++++ crates/core_simd/src/vector.rs | 2 ++ - crates/core_simd/tests/masks.rs | 2 ++ - 5 files changed, 25 insertions(+) + crates/core_simd/tests/masks.rs | 3 --- + 5 files changed, 20 insertions(+), 3 deletions(-) diff --git a/crates/core_simd/src/elements/int.rs b/crates/core_simd/src/elements/int.rs index 9b8c37e..ea95f08 100644 @@ -116,10 +116,10 @@ index 21e7e76..0d6dee2 100644 #[inline] fn reduce_sum(self) -> Self::Scalar { diff --git a/crates/core_simd/src/masks/full_masks.rs b/crates/core_simd/src/masks/full_masks.rs -index adf0fcb..5b10292 100644 +index adf0fcb..e7e657e 100644 --- a/crates/core_simd/src/masks/full_masks.rs +++ b/crates/core_simd/src/masks/full_masks.rs -@@ -150,6 +150,7 @@ where +@@ -180,6 +180,7 @@ where super::Mask: ToBitMaskArray, [(); as ToBitMaskArray>::BYTES]: Sized, { @@ -127,33 +127,16 @@ index adf0fcb..5b10292 100644 assert_eq!( as ToBitMaskArray>::BYTES, N); // Safety: N is the correct bitmask size -@@ -170,6 +171,8 @@ where - - bitmask - } -+ */ -+ panic!(); - } - - #[cfg(feature = "generic_const_exprs")] -@@ -209,6 +212,7 @@ where - where - super::Mask: ToBitMask, - { -+ /* - // Safety: U is required to be the appropriate bitmask type - let bitmask: U = unsafe { intrinsics::simd_bitmask(self.0) }; - -@@ -218,6 +222,8 @@ where - } else { - bitmask +@@ -202,6 +203,8 @@ where + Self::splat(false).to_int(), + )) } + */ + panic!(); } #[inline] -@@ -225,6 +231,7 @@ where +@@ -225,6 +228,7 @@ where where super::Mask: ToBitMask, { @@ -161,7 +144,7 @@ index adf0fcb..5b10292 100644 // LLVM assumes bit order should match endianness let bitmask = if cfg!(target_endian = "big") { bitmask.reverse_bits(LANES) -@@ -240,6 +247,8 @@ where +@@ -240,6 +244,8 @@ where Self::splat(false).to_int(), )) } @@ -191,24 +174,30 @@ index e8e8f68..7173c24 100644 impl Copy for Simd diff --git a/crates/core_simd/tests/masks.rs b/crates/core_simd/tests/masks.rs -index 673d0db..0d68b01 100644 +index 673d0db..3ebfcd1 100644 --- a/crates/core_simd/tests/masks.rs +++ b/crates/core_simd/tests/masks.rs -@@ -59,6 +59,7 @@ macro_rules! test_mask_api { - assert!(!v.all()); +@@ -78,7 +78,6 @@ macro_rules! test_mask_api { + let mask = core_simd::Mask::<$type, 16>::from_array(values); + let bitmask = mask.to_bitmask(); + assert_eq!(bitmask, 0b1000001101001001); +- assert_eq!(core_simd::Mask::<$type, 16>::from_bitmask(bitmask), mask); } -+ /* #[test] - fn roundtrip_int_conversion() { - let values = [true, false, false, true, false, false, true, false]; -@@ -99,6 +100,7 @@ macro_rules! test_mask_api { +@@ -91,13 +90,11 @@ macro_rules! test_mask_api { + let mask = core_simd::Mask::<$type, 4>::from_array(values); + let bitmask = mask.to_bitmask(); + assert_eq!(bitmask, 0b1000); +- assert_eq!(core_simd::Mask::<$type, 4>::from_bitmask(bitmask), mask); + + let values = [true, false]; + let mask = core_simd::Mask::<$type, 2>::from_array(values); + let bitmask = mask.to_bitmask(); assert_eq!(bitmask, 0b01); - assert_eq!(core_simd::Mask::<$type, 2>::from_bitmask(bitmask), mask); +- assert_eq!(core_simd::Mask::<$type, 2>::from_bitmask(bitmask), mask); } -+ */ #[test] - fn cast() { -- 2.25.1 diff --git a/src/intrinsics/simd.rs b/src/intrinsics/simd.rs index a8aefcf30b5e..4d77370dfc52 100644 --- a/src/intrinsics/simd.rs +++ b/src/intrinsics/simd.rs @@ -2,6 +2,7 @@ use rustc_middle::ty::subst::SubstsRef; use rustc_span::Symbol; +use rustc_target::abi::Endian; use super::*; use crate::prelude::*; @@ -162,6 +163,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( } } } else { + // FIXME remove this case intrinsic.as_str()["simd_shuffle".len()..].parse().unwrap() }; @@ -650,10 +652,90 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( } } - // simd_saturating_* - // simd_bitmask + sym::simd_bitmask => { + intrinsic_args!(fx, args => (a); intrinsic); + + let (lane_count, lane_ty) = a.layout().ty.simd_size_and_type(fx.tcx); + let lane_clif_ty = fx.clif_type(lane_ty).unwrap(); + + // The `fn simd_bitmask(vector) -> unsigned integer` intrinsic takes a + // vector mask and returns the most significant bit (MSB) of each lane in the form + // of either: + // * an unsigned integer + // * an array of `u8` + // If the vector has less than 8 lanes, a u8 is returned with zeroed trailing bits. + // + // The bit order of the result depends on the byte endianness, LSB-first for little + // endian and MSB-first for big endian. + let expected_int_bits = lane_count.max(8); + let expected_bytes = expected_int_bits / 8 + ((expected_int_bits % 8 > 0) as u64); + + match lane_ty.kind() { + ty::Int(_) | ty::Uint(_) => {} + _ => { + fx.tcx.sess.span_fatal( + span, + &format!( + "invalid monomorphization of `simd_bitmask` intrinsic: \ + vector argument `{}`'s element type `{}`, expected integer element \ + type", + a.layout().ty, + lane_ty + ), + ); + } + } + + let res_type = + Type::int_with_byte_size(u16::try_from(expected_bytes).unwrap()).unwrap(); + let mut res = fx.bcx.ins().iconst(res_type, 0); + + let lanes = match fx.tcx.sess.target.endian { + Endian::Big => Box::new(0..lane_count) as Box>, + Endian::Little => Box::new((0..lane_count).rev()) as Box>, + }; + for lane in lanes { + let a_lane = a.value_lane(fx, lane).load_scalar(fx); + + // extract sign bit of an int + let a_lane_sign = fx.bcx.ins().ushr_imm(a_lane, i64::from(lane_clif_ty.bits() - 1)); + + // shift sign bit into result + let a_lane_sign = clif_intcast(fx, a_lane_sign, res_type, false); + res = fx.bcx.ins().ishl_imm(res, 1); + res = fx.bcx.ins().bor(res, a_lane_sign); + } + + match ret.layout().ty.kind() { + ty::Uint(i) if i.bit_width() == Some(expected_int_bits) => {} + ty::Array(elem, len) + if matches!(elem.kind(), ty::Uint(ty::UintTy::U8)) + && len.try_eval_usize(fx.tcx, ty::ParamEnv::reveal_all()) + == Some(expected_bytes) => {} + _ => { + fx.tcx.sess.span_fatal( + span, + &format!( + "invalid monomorphization of `simd_bitmask` intrinsic: \ + cannot return `{}`, expected `u{}` or `[u8; {}]`", + ret.layout().ty, + expected_int_bits, + expected_bytes + ), + ); + } + } + + let res = CValue::by_val(res, ret.layout()); + ret.write_cvalue(fx, res); + } + + // simd_arith_offset + // simd_saturating_add + // simd_saturating_sub // simd_scatter // simd_gather + // simd_select_bitmask _ => { fx.tcx.sess.span_fatal(span, &format!("Unknown SIMD intrinsic {}", intrinsic)); } From 0980596271fd99665921052c8f666f7fb718a3cc Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Mon, 5 Sep 2022 16:13:08 +0000 Subject: [PATCH 033/309] Extract codegen_saturating_int_binop function --- src/intrinsics/mod.rs | 32 +------------------------------- src/num.rs | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 31 deletions(-) diff --git a/src/intrinsics/mod.rs b/src/intrinsics/mod.rs index b0863e096540..e06166d0ab7c 100644 --- a/src/intrinsics/mod.rs +++ b/src/intrinsics/mod.rs @@ -507,37 +507,7 @@ fn codegen_regular_intrinsic_call<'tcx>( _ => unreachable!(), }; - let signed = type_sign(lhs.layout().ty); - - let checked_res = crate::num::codegen_checked_int_binop(fx, bin_op, lhs, rhs); - - let (val, has_overflow) = checked_res.load_scalar_pair(fx); - let clif_ty = fx.clif_type(lhs.layout().ty).unwrap(); - - let (min, max) = type_min_max_value(&mut fx.bcx, clif_ty, signed); - - let val = match (intrinsic, signed) { - (sym::saturating_add, false) => fx.bcx.ins().select(has_overflow, max, val), - (sym::saturating_sub, false) => fx.bcx.ins().select(has_overflow, min, val), - (sym::saturating_add, true) => { - let rhs = rhs.load_scalar(fx); - let rhs_ge_zero = - fx.bcx.ins().icmp_imm(IntCC::SignedGreaterThanOrEqual, rhs, 0); - let sat_val = fx.bcx.ins().select(rhs_ge_zero, max, min); - fx.bcx.ins().select(has_overflow, sat_val, val) - } - (sym::saturating_sub, true) => { - let rhs = rhs.load_scalar(fx); - let rhs_ge_zero = - fx.bcx.ins().icmp_imm(IntCC::SignedGreaterThanOrEqual, rhs, 0); - let sat_val = fx.bcx.ins().select(rhs_ge_zero, min, max); - fx.bcx.ins().select(has_overflow, sat_val, val) - } - _ => unreachable!(), - }; - - let res = CValue::by_val(val, lhs.layout()); - + let res = crate::num::codegen_saturating_int_binop(fx, bin_op, lhs, rhs); ret.write_cvalue(fx, res); } sym::rotate_left => { diff --git a/src/num.rs b/src/num.rs index 4ce8adb182e0..4fadbf24d8a8 100644 --- a/src/num.rs +++ b/src/num.rs @@ -309,6 +309,42 @@ pub(crate) fn codegen_checked_int_binop<'tcx>( CValue::by_val_pair(res, has_overflow, out_layout) } +pub(crate) fn codegen_saturating_int_binop<'tcx>( + fx: &mut FunctionCx<'_, '_, 'tcx>, + bin_op: BinOp, + lhs: CValue<'tcx>, + rhs: CValue<'tcx>, +) -> CValue<'tcx> { + assert_eq!(lhs.layout().ty, rhs.layout().ty); + + let signed = type_sign(lhs.layout().ty); + let clif_ty = fx.clif_type(lhs.layout().ty).unwrap(); + let (min, max) = type_min_max_value(&mut fx.bcx, clif_ty, signed); + + let checked_res = crate::num::codegen_checked_int_binop(fx, bin_op, lhs, rhs); + let (val, has_overflow) = checked_res.load_scalar_pair(fx); + + let val = match (bin_op, signed) { + (BinOp::Add, false) => fx.bcx.ins().select(has_overflow, max, val), + (BinOp::Sub, false) => fx.bcx.ins().select(has_overflow, min, val), + (BinOp::Add, true) => { + let rhs = rhs.load_scalar(fx); + let rhs_ge_zero = fx.bcx.ins().icmp_imm(IntCC::SignedGreaterThanOrEqual, rhs, 0); + let sat_val = fx.bcx.ins().select(rhs_ge_zero, max, min); + fx.bcx.ins().select(has_overflow, sat_val, val) + } + (BinOp::Sub, true) => { + let rhs = rhs.load_scalar(fx); + let rhs_ge_zero = fx.bcx.ins().icmp_imm(IntCC::SignedGreaterThanOrEqual, rhs, 0); + let sat_val = fx.bcx.ins().select(rhs_ge_zero, min, max); + fx.bcx.ins().select(has_overflow, sat_val, val) + } + _ => unreachable!(), + }; + + CValue::by_val(val, lhs.layout()) +} + pub(crate) fn codegen_float_binop<'tcx>( fx: &mut FunctionCx<'_, '_, 'tcx>, bin_op: BinOp, From 782b5fe7ac97554f7bf4b9121e985871e650ca41 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Mon, 5 Sep 2022 16:13:36 +0000 Subject: [PATCH 034/309] Implement simd_saturating_{add,sub} --- ...table-simd-Disable-unsupported-tests.patch | 104 ------------------ src/intrinsics/mod.rs | 24 ++++ src/intrinsics/simd.rs | 17 ++- 3 files changed, 39 insertions(+), 106 deletions(-) diff --git a/patches/0001-portable-simd-Disable-unsupported-tests.patch b/patches/0001-portable-simd-Disable-unsupported-tests.patch index c75d5dda5caf..e23dea976944 100644 --- a/patches/0001-portable-simd-Disable-unsupported-tests.patch +++ b/patches/0001-portable-simd-Disable-unsupported-tests.patch @@ -11,110 +11,6 @@ Subject: [PATCH] Disable unsupported tests crates/core_simd/tests/masks.rs | 3 --- 5 files changed, 20 insertions(+), 3 deletions(-) -diff --git a/crates/core_simd/src/elements/int.rs b/crates/core_simd/src/elements/int.rs -index 9b8c37e..ea95f08 100644 ---- a/crates/core_simd/src/elements/int.rs -+++ b/crates/core_simd/src/elements/int.rs -@@ -11,6 +11,7 @@ pub trait SimdInt: Copy + Sealed { - /// Scalar type contained by this SIMD vector type. - type Scalar; - -+ /* - /// Lanewise saturating add. - /// - /// # Examples -@@ -45,6 +46,7 @@ pub trait SimdInt: Copy + Sealed { - /// assert_eq!(unsat, Simd::from_array([1, MAX, MIN, 0])); - /// assert_eq!(sat, Simd::from_array([MIN, MIN, MIN, 0])); - fn saturating_sub(self, second: Self) -> Self; -+ */ - - /// Lanewise absolute value, implemented in Rust. - /// Every lane becomes its absolute value. -@@ -61,6 +63,7 @@ pub trait SimdInt: Copy + Sealed { - /// ``` - fn abs(self) -> Self; - -+ /* - /// Lanewise saturating absolute value, implemented in Rust. - /// As abs(), except the MIN value becomes MAX instead of itself. - /// -@@ -96,6 +99,7 @@ pub trait SimdInt: Copy + Sealed { - /// assert_eq!(sat, Simd::from_array([MAX, 2, -3, MIN + 1])); - /// ``` - fn saturating_neg(self) -> Self; -+ */ - - /// Returns true for each positive lane and false if it is zero or negative. - fn is_positive(self) -> Self::Mask; -@@ -199,6 +203,7 @@ macro_rules! impl_trait { - type Mask = Mask<<$ty as SimdElement>::Mask, LANES>; - type Scalar = $ty; - -+ /* - #[inline] - fn saturating_add(self, second: Self) -> Self { - // Safety: `self` is a vector -@@ -210,6 +215,7 @@ macro_rules! impl_trait { - // Safety: `self` is a vector - unsafe { intrinsics::simd_saturating_sub(self, second) } - } -+ */ - - #[inline] - fn abs(self) -> Self { -@@ -218,6 +224,7 @@ macro_rules! impl_trait { - (self^m) - m - } - -+ /* - #[inline] - fn saturating_abs(self) -> Self { - // arith shift for -1 or 0 mask based on sign bit, giving 2s complement -@@ -230,6 +237,7 @@ macro_rules! impl_trait { - fn saturating_neg(self) -> Self { - Self::splat(0).saturating_sub(self) - } -+ */ - - #[inline] - fn is_positive(self) -> Self::Mask { -diff --git a/crates/core_simd/src/elements/uint.rs b/crates/core_simd/src/elements/uint.rs -index 21e7e76..0d6dee2 100644 ---- a/crates/core_simd/src/elements/uint.rs -+++ b/crates/core_simd/src/elements/uint.rs -@@ -6,6 +6,7 @@ pub trait SimdUint: Copy + Sealed { - /// Scalar type contained by this SIMD vector type. - type Scalar; - -+ /* - /// Lanewise saturating add. - /// - /// # Examples -@@ -40,6 +41,7 @@ pub trait SimdUint: Copy + Sealed { - /// assert_eq!(unsat, Simd::from_array([3, 2, 1, 0])); - /// assert_eq!(sat, Simd::splat(0)); - fn saturating_sub(self, second: Self) -> Self; -+ */ - - /// Returns the sum of the lanes of the vector, with wrapping addition. - fn reduce_sum(self) -> Self::Scalar; -@@ -78,6 +80,7 @@ macro_rules! impl_trait { - { - type Scalar = $ty; - -+ /* - #[inline] - fn saturating_add(self, second: Self) -> Self { - // Safety: `self` is a vector -@@ -89,6 +92,7 @@ macro_rules! impl_trait { - // Safety: `self` is a vector - unsafe { intrinsics::simd_saturating_sub(self, second) } - } -+ */ - - #[inline] - fn reduce_sum(self) -> Self::Scalar { diff --git a/crates/core_simd/src/masks/full_masks.rs b/crates/core_simd/src/masks/full_masks.rs index adf0fcb..e7e657e 100644 --- a/crates/core_simd/src/masks/full_masks.rs diff --git a/src/intrinsics/mod.rs b/src/intrinsics/mod.rs index e06166d0ab7c..1e47ccbd49fa 100644 --- a/src/intrinsics/mod.rs +++ b/src/intrinsics/mod.rs @@ -84,6 +84,30 @@ fn simd_for_each_lane<'tcx>( } } +fn simd_pair_for_each_lane_typed<'tcx>( + fx: &mut FunctionCx<'_, '_, 'tcx>, + x: CValue<'tcx>, + y: CValue<'tcx>, + ret: CPlace<'tcx>, + f: &dyn Fn(&mut FunctionCx<'_, '_, 'tcx>, CValue<'tcx>, CValue<'tcx>) -> CValue<'tcx>, +) { + assert_eq!(x.layout(), y.layout()); + let layout = x.layout(); + + let (lane_count, _lane_ty) = layout.ty.simd_size_and_type(fx.tcx); + let (ret_lane_count, _ret_lane_ty) = ret.layout().ty.simd_size_and_type(fx.tcx); + assert_eq!(lane_count, ret_lane_count); + + for lane_idx in 0..lane_count { + let x_lane = x.value_lane(fx, lane_idx); + let y_lane = y.value_lane(fx, lane_idx); + + let res_lane = f(fx, x_lane, y_lane); + + ret.place_lane(fx, lane_idx).write_cvalue(fx, res_lane); + } +} + fn simd_pair_for_each_lane<'tcx>( fx: &mut FunctionCx<'_, '_, 'tcx>, x: CValue<'tcx>, diff --git a/src/intrinsics/simd.rs b/src/intrinsics/simd.rs index 4d77370dfc52..a70ced7472f5 100644 --- a/src/intrinsics/simd.rs +++ b/src/intrinsics/simd.rs @@ -730,9 +730,22 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( ret.write_cvalue(fx, res); } + sym::simd_saturating_add | sym::simd_saturating_sub => { + intrinsic_args!(fx, args => (x, y); intrinsic); + + let bin_op = match intrinsic { + sym::simd_saturating_add => BinOp::Add, + sym::simd_saturating_sub => BinOp::Sub, + _ => unreachable!(), + }; + + // FIXME use vector instructions when possible + simd_pair_for_each_lane_typed(fx, x, y, ret, &|fx, x_lane, y_lane| { + crate::num::codegen_saturating_int_binop(fx, bin_op, x_lane, y_lane) + }); + } + // simd_arith_offset - // simd_saturating_add - // simd_saturating_sub // simd_scatter // simd_gather // simd_select_bitmask From 13fcf476001ff511103d9bec44e5be4e24eb0c96 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Mon, 5 Sep 2022 16:37:22 +0000 Subject: [PATCH 035/309] Implement simd_select_bitmask --- ...table-simd-Disable-unsupported-tests.patch | 64 ------------------- src/intrinsics/simd.rs | 29 ++++++++- 2 files changed, 28 insertions(+), 65 deletions(-) diff --git a/patches/0001-portable-simd-Disable-unsupported-tests.patch b/patches/0001-portable-simd-Disable-unsupported-tests.patch index e23dea976944..89e2b61c1fc8 100644 --- a/patches/0001-portable-simd-Disable-unsupported-tests.patch +++ b/patches/0001-portable-simd-Disable-unsupported-tests.patch @@ -11,44 +11,6 @@ Subject: [PATCH] Disable unsupported tests crates/core_simd/tests/masks.rs | 3 --- 5 files changed, 20 insertions(+), 3 deletions(-) -diff --git a/crates/core_simd/src/masks/full_masks.rs b/crates/core_simd/src/masks/full_masks.rs -index adf0fcb..e7e657e 100644 ---- a/crates/core_simd/src/masks/full_masks.rs -+++ b/crates/core_simd/src/masks/full_masks.rs -@@ -180,6 +180,7 @@ where - super::Mask: ToBitMaskArray, - [(); as ToBitMaskArray>::BYTES]: Sized, - { -+ /* - assert_eq!( as ToBitMaskArray>::BYTES, N); - - // Safety: N is the correct bitmask size -@@ -202,6 +203,8 @@ where - Self::splat(false).to_int(), - )) - } -+ */ -+ panic!(); - } - - #[inline] -@@ -225,6 +228,7 @@ where - where - super::Mask: ToBitMask, - { -+ /* - // LLVM assumes bit order should match endianness - let bitmask = if cfg!(target_endian = "big") { - bitmask.reverse_bits(LANES) -@@ -240,6 +244,8 @@ where - Self::splat(false).to_int(), - )) - } -+ */ -+ panic!(); - } - - #[inline] diff --git a/crates/core_simd/src/vector.rs b/crates/core_simd/src/vector.rs index e8e8f68..7173c24 100644 --- a/crates/core_simd/src/vector.rs @@ -69,31 +31,5 @@ index e8e8f68..7173c24 100644 } impl Copy for Simd -diff --git a/crates/core_simd/tests/masks.rs b/crates/core_simd/tests/masks.rs -index 673d0db..3ebfcd1 100644 ---- a/crates/core_simd/tests/masks.rs -+++ b/crates/core_simd/tests/masks.rs -@@ -78,7 +78,6 @@ macro_rules! test_mask_api { - let mask = core_simd::Mask::<$type, 16>::from_array(values); - let bitmask = mask.to_bitmask(); - assert_eq!(bitmask, 0b1000001101001001); -- assert_eq!(core_simd::Mask::<$type, 16>::from_bitmask(bitmask), mask); - } - - #[test] -@@ -91,13 +90,11 @@ macro_rules! test_mask_api { - let mask = core_simd::Mask::<$type, 4>::from_array(values); - let bitmask = mask.to_bitmask(); - assert_eq!(bitmask, 0b1000); -- assert_eq!(core_simd::Mask::<$type, 4>::from_bitmask(bitmask), mask); - - let values = [true, false]; - let mask = core_simd::Mask::<$type, 2>::from_array(values); - let bitmask = mask.to_bitmask(); - assert_eq!(bitmask, 0b01); -- assert_eq!(core_simd::Mask::<$type, 2>::from_bitmask(bitmask), mask); - } - - #[test] -- 2.25.1 diff --git a/src/intrinsics/simd.rs b/src/intrinsics/simd.rs index a70ced7472f5..51fce8c854bd 100644 --- a/src/intrinsics/simd.rs +++ b/src/intrinsics/simd.rs @@ -652,6 +652,34 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( } } + sym::simd_select_bitmask => { + intrinsic_args!(fx, args => (m, a, b); intrinsic); + + if !a.layout().ty.is_simd() { + report_simd_type_validation_error(fx, intrinsic, span, a.layout().ty); + return; + } + assert_eq!(a.layout(), b.layout()); + + let (lane_count, lane_ty) = a.layout().ty.simd_size_and_type(fx.tcx); + let lane_layout = fx.layout_of(lane_ty); + + let m = m.load_scalar(fx); + + for lane in 0..lane_count { + let m_lane = fx.bcx.ins().ushr_imm(m, u64::from(lane) as i64); + let m_lane = fx.bcx.ins().band_imm(m_lane, 1); + let a_lane = a.value_lane(fx, lane).load_scalar(fx); + let b_lane = b.value_lane(fx, lane).load_scalar(fx); + + let m_lane = fx.bcx.ins().icmp_imm(IntCC::Equal, m_lane, 0); + let res_lane = + CValue::by_val(fx.bcx.ins().select(m_lane, b_lane, a_lane), lane_layout); + + ret.place_lane(fx, lane).write_cvalue(fx, res_lane); + } + } + sym::simd_bitmask => { intrinsic_args!(fx, args => (a); intrinsic); @@ -748,7 +776,6 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( // simd_arith_offset // simd_scatter // simd_gather - // simd_select_bitmask _ => { fx.tcx.sess.span_fatal(span, &format!("Unknown SIMD intrinsic {}", intrinsic)); } From 088e03fe224e4e7adf3fe1c3d5365a699418b7d9 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 30 Jun 2022 08:16:05 +0000 Subject: [PATCH 036/309] Lower the assume intrinsic to a MIR statement --- src/base.rs | 2 ++ src/constant.rs | 1 + src/intrinsics/mod.rs | 3 --- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/base.rs b/src/base.rs index c412e451a033..1f49ead93a3e 100644 --- a/src/base.rs +++ b/src/base.rs @@ -791,6 +791,8 @@ fn codegen_stmt<'tcx>( | StatementKind::Nop | StatementKind::FakeRead(..) | StatementKind::Retag { .. } + // We ignore `assume` intrinsics, they are only useful for optimizations + | StatementKind::Assume(..) | StatementKind::AscribeUserType(..) => {} StatementKind::Coverage { .. } => fx.tcx.sess.fatal("-Zcoverage is unimplemented"), diff --git a/src/constant.rs b/src/constant.rs index 9224f499339c..f75de9096f3a 100644 --- a/src/constant.rs +++ b/src/constant.rs @@ -540,6 +540,7 @@ pub(crate) fn mir_operand_get_const_val<'tcx>( return None; } // conservative handling StatementKind::Assign(_) + | StatementKind::Assume(_) | StatementKind::FakeRead(_) | StatementKind::SetDiscriminant { .. } | StatementKind::Deinit(_) diff --git a/src/intrinsics/mod.rs b/src/intrinsics/mod.rs index 39e9e784a478..0cd9332a58be 100644 --- a/src/intrinsics/mod.rs +++ b/src/intrinsics/mod.rs @@ -357,9 +357,6 @@ fn codegen_regular_intrinsic_call<'tcx>( let usize_layout = fx.layout_of(fx.tcx.types.usize); match intrinsic { - sym::assume => { - intrinsic_args!(fx, args => (_a); intrinsic); - } sym::likely | sym::unlikely => { intrinsic_args!(fx, args => (a); intrinsic); From 104168a0f8ec9f218af84c315c0006c2d72450fe Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 12 Jul 2022 10:05:00 +0000 Subject: [PATCH 037/309] Generalize the Assume intrinsic statement to a general Intrinsic statement --- src/base.rs | 41 +++++++++++++++++++++++++---------------- src/constant.rs | 9 +++++---- 2 files changed, 30 insertions(+), 20 deletions(-) diff --git a/src/base.rs b/src/base.rs index 1f49ead93a3e..2aa11ac2eeaa 100644 --- a/src/base.rs +++ b/src/base.rs @@ -791,25 +791,34 @@ fn codegen_stmt<'tcx>( | StatementKind::Nop | StatementKind::FakeRead(..) | StatementKind::Retag { .. } - // We ignore `assume` intrinsics, they are only useful for optimizations - | StatementKind::Assume(..) | StatementKind::AscribeUserType(..) => {} StatementKind::Coverage { .. } => fx.tcx.sess.fatal("-Zcoverage is unimplemented"), - StatementKind::CopyNonOverlapping(inner) => { - let dst = codegen_operand(fx, &inner.dst); - let pointee = dst - .layout() - .pointee_info_at(fx, rustc_target::abi::Size::ZERO) - .expect("Expected pointer"); - let dst = dst.load_scalar(fx); - let src = codegen_operand(fx, &inner.src).load_scalar(fx); - let count = codegen_operand(fx, &inner.count).load_scalar(fx); - let elem_size: u64 = pointee.size.bytes(); - let bytes = - if elem_size != 1 { fx.bcx.ins().imul_imm(count, elem_size as i64) } else { count }; - fx.bcx.call_memcpy(fx.target_config, dst, src, bytes); - } + StatementKind::Intrinsic(ref intrinsic) => match &**intrinsic { + // We ignore `assume` intrinsics, they are only useful for optimizations + NonDivergingIntrinsic::Assume(_) => {} + NonDivergingIntrinsic::CopyNonOverlapping(mir::CopyNonOverlapping { + src, + dst, + count, + }) => { + let dst = codegen_operand(fx, dst); + let pointee = dst + .layout() + .pointee_info_at(fx, rustc_target::abi::Size::ZERO) + .expect("Expected pointer"); + let dst = dst.load_scalar(fx); + let src = codegen_operand(fx, src).load_scalar(fx); + let count = codegen_operand(fx, count).load_scalar(fx); + let elem_size: u64 = pointee.size.bytes(); + let bytes = if elem_size != 1 { + fx.bcx.ins().imul_imm(count, elem_size as i64) + } else { + count + }; + fx.bcx.call_memcpy(fx.target_config, dst, src, bytes); + } + }, } } diff --git a/src/constant.rs b/src/constant.rs index f75de9096f3a..0305341da784 100644 --- a/src/constant.rs +++ b/src/constant.rs @@ -536,11 +536,12 @@ pub(crate) fn mir_operand_get_const_val<'tcx>( { return None; } - StatementKind::CopyNonOverlapping(_) => { - return None; - } // conservative handling + StatementKind::Intrinsic(ref intrinsic) => match **intrinsic { + NonDivergingIntrinsic::CopyNonOverlapping(..) => return None, + NonDivergingIntrinsic::Assume(..) => {} + }, + // conservative handling StatementKind::Assign(_) - | StatementKind::Assume(_) | StatementKind::FakeRead(_) | StatementKind::SetDiscriminant { .. } | StatementKind::Deinit(_) From 1cf9be98cdc2d2753b11dd71d83c03c82897b006 Mon Sep 17 00:00:00 2001 From: David Wood Date: Thu, 21 Jul 2022 16:19:22 +0100 Subject: [PATCH 038/309] ssa: implement `#[collapse_debuginfo]` Debuginfo line information for macro invocations are collapsed by default - line information are replaced by the line of the outermost expansion site. Using `-Zdebug-macros` disables this behaviour. When the `collapse_debuginfo` feature is enabled, the default behaviour is reversed so that debuginfo is not collapsed by default. In addition, the `#[collapse_debuginfo]` attribute is available and can be applied to macro definitions which will then have their line information collapsed. Signed-off-by: David Wood --- src/debuginfo/line_info.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/debuginfo/line_info.rs b/src/debuginfo/line_info.rs index 3ad0c420eaf0..463de6a91c74 100644 --- a/src/debuginfo/line_info.rs +++ b/src/debuginfo/line_info.rs @@ -68,9 +68,9 @@ impl DebugContext { ) -> (Lrc, u64, u64) { // Based on https://github.com/rust-lang/rust/blob/e369d87b015a84653343032833d65d0545fd3f26/src/librustc_codegen_ssa/mir/mod.rs#L116-L131 // In order to have a good line stepping behavior in debugger, we overwrite debug - // locations of macro expansions with that of the outermost expansion site - // (unless the crate is being compiled with `-Z debug-macros`). - let span = if !span.from_expansion() || tcx.sess.opts.unstable_opts.debug_macros { + // locations of macro expansions with that of the outermost expansion site (when the macro is + // annotated with `#[collapse_debuginfo]` or when `-Zdebug-macros` is provided). + let span = if tcx.should_collapse_debuginfo(span) { span } else { // Walk up the macro expansion chain until we reach a non-expanded span. From df8a62d4f380d656444a6fc4e704bcd3693f4b9a Mon Sep 17 00:00:00 2001 From: Chase Wilson Date: Wed, 7 Sep 2022 10:27:42 -0500 Subject: [PATCH 039/309] Use `CURRENT_RUSTC_VERSION` --- library/core/src/option.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 1d6784af2a5c..df5a20869a3f 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -1718,7 +1718,7 @@ impl Option<(T, U)> { /// assert_eq!(y.unzip(), (None, None)); /// ``` #[inline] - #[stable(feature = "unzip_option", since = "1.63.0")] + #[stable(feature = "unzip_option", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_unstable(feature = "const_option", issue = "67441")] pub const fn unzip(self) -> (Option, Option) where From 944a142bfae7ee0d10cb12e9c3383b05e311f57e Mon Sep 17 00:00:00 2001 From: Michael Benfield Date: Thu, 25 Aug 2022 01:14:23 +0000 Subject: [PATCH 040/309] Change name of "dataful" variant to "untagged" This is in anticipation of a new enum layout, in which the niche optimization may be applied even when multiple variants have data. --- src/discriminant.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/discriminant.rs b/src/discriminant.rs index e41ae1fbdbac..97b395bcd051 100644 --- a/src/discriminant.rs +++ b/src/discriminant.rs @@ -42,10 +42,10 @@ pub(crate) fn codegen_set_discriminant<'tcx>( Variants::Multiple { tag: _, tag_field, - tag_encoding: TagEncoding::Niche { dataful_variant, ref niche_variants, niche_start }, + tag_encoding: TagEncoding::Niche { untagged_variant, ref niche_variants, niche_start }, variants: _, } => { - if variant_index != dataful_variant { + if variant_index != untagged_variant { let niche = place.place_field(fx, mir::Field::new(tag_field)); let niche_value = variant_index.as_u32() - niche_variants.start().as_u32(); let niche_value = ty::ScalarInt::try_from_uint( @@ -113,7 +113,7 @@ pub(crate) fn codegen_get_discriminant<'tcx>( let res = CValue::by_val(val, dest_layout); dest.write_cvalue(fx, res); } - TagEncoding::Niche { dataful_variant, ref niche_variants, niche_start } => { + TagEncoding::Niche { untagged_variant, ref niche_variants, niche_start } => { // Rebase from niche values to discriminants, and check // whether the result is in range for the niche variants. @@ -169,8 +169,9 @@ pub(crate) fn codegen_get_discriminant<'tcx>( fx.bcx.ins().iadd_imm(relative_discr, i64::from(niche_variants.start().as_u32())) }; - let dataful_variant = fx.bcx.ins().iconst(cast_to, i64::from(dataful_variant.as_u32())); - let discr = fx.bcx.ins().select(is_niche, niche_discr, dataful_variant); + let untagged_variant = + fx.bcx.ins().iconst(cast_to, i64::from(untagged_variant.as_u32())); + let discr = fx.bcx.ins().select(is_niche, niche_discr, untagged_variant); let res = CValue::by_val(discr, dest_layout); dest.write_cvalue(fx, res); } From 931c07c94f5e6d2e24c08a1c4941612aab69872a Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 6 Sep 2022 14:08:59 +0000 Subject: [PATCH 041/309] The `<*const T>::guaranteed_*` methods now return an option for the unknown case --- src/intrinsics/mod.rs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/intrinsics/mod.rs b/src/intrinsics/mod.rs index 39e9e784a478..586c9489dd44 100644 --- a/src/intrinsics/mod.rs +++ b/src/intrinsics/mod.rs @@ -819,20 +819,13 @@ fn codegen_regular_intrinsic_call<'tcx>( ret.write_cvalue(fx, val); } - sym::ptr_guaranteed_eq => { + sym::ptr_guaranteed_cmp => { intrinsic_args!(fx, args => (a, b); intrinsic); let val = crate::num::codegen_ptr_binop(fx, BinOp::Eq, a, b); ret.write_cvalue(fx, val); } - sym::ptr_guaranteed_ne => { - intrinsic_args!(fx, args => (a, b); intrinsic); - - let val = crate::num::codegen_ptr_binop(fx, BinOp::Ne, a, b); - ret.write_cvalue(fx, val); - } - sym::caller_location => { intrinsic_args!(fx, args => (); intrinsic); From 0958f9486ba0cb8ecd0093ff3100af45d19529f3 Mon Sep 17 00:00:00 2001 From: kraktus Date: Mon, 3 Oct 2022 13:57:42 +0200 Subject: [PATCH 042/309] Add `manual_filter` lint for `Option` Share much of its implementation with `manual_map` and should greatly benefit from its previous feedback. --- CHANGELOG.md | 1 + clippy_lints/src/lib.register_all.rs | 1 + clippy_lints/src/lib.register_complexity.rs | 1 + clippy_lints/src/lib.register_lints.rs | 1 + clippy_lints/src/matches/manual_filter.rs | 181 +++++++++++++++ clippy_lints/src/matches/manual_map.rs | 213 +++++++++++------ clippy_lints/src/matches/mod.rs | 39 ++++ clippy_utils/src/sugg.rs | 6 +- src/docs.rs | 1 + src/docs/manual_filter.txt | 21 ++ tests/ui/manual_filter.fixed | 119 ++++++++++ tests/ui/manual_filter.rs | 243 ++++++++++++++++++++ tests/ui/manual_filter.stderr | 191 +++++++++++++++ 13 files changed, 951 insertions(+), 67 deletions(-) create mode 100644 clippy_lints/src/matches/manual_filter.rs create mode 100644 src/docs/manual_filter.txt create mode 100644 tests/ui/manual_filter.fixed create mode 100644 tests/ui/manual_filter.rs create mode 100644 tests/ui/manual_filter.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f12a6735962..cab2808ee3b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3986,6 +3986,7 @@ Released 2018-09-13 [`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn [`manual_bits`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits [`manual_clamp`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_clamp +[`manual_filter`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter [`manual_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map [`manual_find`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find [`manual_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find_map diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 9ad2a88eb26c..21d686fcdf54 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -135,6 +135,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(match_result_ok::MATCH_RESULT_OK), LintId::of(matches::COLLAPSIBLE_MATCH), LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH), + LintId::of(matches::MANUAL_FILTER), LintId::of(matches::MANUAL_MAP), LintId::of(matches::MANUAL_UNWRAP_OR), LintId::of(matches::MATCH_AS_REF), diff --git a/clippy_lints/src/lib.register_complexity.rs b/clippy_lints/src/lib.register_complexity.rs index a58d066fa6b6..e3849e5a626b 100644 --- a/clippy_lints/src/lib.register_complexity.rs +++ b/clippy_lints/src/lib.register_complexity.rs @@ -27,6 +27,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(manual_strip::MANUAL_STRIP), LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN), LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN), + LintId::of(matches::MANUAL_FILTER), LintId::of(matches::MANUAL_UNWRAP_OR), LintId::of(matches::MATCH_AS_REF), LintId::of(matches::MATCH_SINGLE_BINDING), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 307ec40f40b3..70defe3bcbd5 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -257,6 +257,7 @@ store.register_lints(&[ match_result_ok::MATCH_RESULT_OK, matches::COLLAPSIBLE_MATCH, matches::INFALLIBLE_DESTRUCTURING_MATCH, + matches::MANUAL_FILTER, matches::MANUAL_MAP, matches::MANUAL_UNWRAP_OR, matches::MATCH_AS_REF, diff --git a/clippy_lints/src/matches/manual_filter.rs b/clippy_lints/src/matches/manual_filter.rs new file mode 100644 index 000000000000..9931f1268ab3 --- /dev/null +++ b/clippy_lints/src/matches/manual_filter.rs @@ -0,0 +1,181 @@ +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, path_to_local_id}; +use rustc_hir::intravisit::{walk_expr, Visitor}; +use rustc_hir::LangItem::OptionSome; +use rustc_hir::{Arm, Block, BlockCheckMode, Expr, ExprKind, HirId, Pat, PatKind, UnsafeSource}; +use rustc_lint::LateContext; +use rustc_span::{sym, SyntaxContext}; + +use super::manual_map::{check_with, SomeExpr}; +use super::MANUAL_FILTER; + +#[derive(Default)] +struct NeedsUnsafeBlock(pub bool); + +impl<'tcx> Visitor<'tcx> for NeedsUnsafeBlock { + fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { + match expr.kind { + ExprKind::Block( + Block { + rules: BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided), + .. + }, + _, + ) => { + self.0 = true; + }, + _ => walk_expr(self, expr), + } + } +} + +// Function called on the `expr` of `[&+]Some((ref | ref mut) x) => ` +// Need to check if it's of the `if {} else {}` +// AND that only one `then/else_expr` resolves to `Some(x)` while the other resolves to `None` +// return `cond` if +fn get_cond_expr<'tcx>( + cx: &LateContext<'tcx>, + pat: &Pat<'_>, + expr: &'tcx Expr<'_>, + ctxt: SyntaxContext, +) -> Option> { + if_chain! { + if let Some(block_expr) = peels_blocks_incl_unsafe_opt(expr); + if let ExprKind::If(cond, then_expr, Some(else_expr)) = block_expr.kind; + if let PatKind::Binding(_,target, ..) = pat.kind; + if let (then_visitor, else_visitor) + = (handle_if_or_else_expr(cx, target, ctxt, then_expr), + handle_if_or_else_expr(cx, target, ctxt, else_expr)); + if then_visitor != else_visitor; // check that one expr resolves to `Some(x)`, the other to `None` + then { + let mut needs_unsafe_block = NeedsUnsafeBlock::default(); + needs_unsafe_block.visit_expr(expr); + return Some(SomeExpr { + expr: peels_blocks_incl_unsafe(cond.peel_drop_temps()), + needs_unsafe_block: needs_unsafe_block.0, + needs_negated: !then_visitor // if the `then_expr` resolves to `None`, need to negate the cond + }) + } + }; + None +} + +fn peels_blocks_incl_unsafe_opt<'a>(expr: &'a Expr<'a>) -> Option<&'a Expr<'a>> { + // we don't want to use `peel_blocks` here because we don't care if the block is unsafe, it's + // checked by `NeedsUnsafeBlock` + if let ExprKind::Block(block, None) = expr.kind { + if block.stmts.is_empty() { + return block.expr; + } + }; + None +} + +fn peels_blocks_incl_unsafe<'a>(expr: &'a Expr<'a>) -> &'a Expr<'a> { + peels_blocks_incl_unsafe_opt(expr).unwrap_or(expr) +} + +// function called for each expression: +// Some(x) => if { +// +// } else { +// +// } +// Returns true if resolves to `Some(x)`, `false` otherwise +fn handle_if_or_else_expr<'tcx>( + cx: &LateContext<'_>, + target: HirId, + ctxt: SyntaxContext, + if_or_else_expr: &'tcx Expr<'_>, +) -> bool { + if let Some(inner_expr) = peels_blocks_incl_unsafe_opt(if_or_else_expr) { + // there can be not statements in the block as they would be removed when switching to `.filter` + if let ExprKind::Call(callee, [arg]) = inner_expr.kind { + return ctxt == if_or_else_expr.span.ctxt() + && is_res_lang_ctor(cx, path_res(cx, callee), OptionSome) + && path_to_local_id(arg, target); + } + }; + false +} + +// given the closure: `|| ` +// returns `|&| ` +fn add_ampersand_if_copy(body_str: String, has_copy_trait: bool) -> String { + if has_copy_trait { + let mut with_ampersand = body_str; + with_ampersand.insert(1, '&'); + with_ampersand + } else { + body_str + } +} + +pub(super) fn check_match<'tcx>( + cx: &LateContext<'tcx>, + scrutinee: &'tcx Expr<'_>, + arms: &'tcx [Arm<'_>], + expr: &'tcx Expr<'_>, +) { + let ty = cx.typeck_results().expr_ty(expr); + if_chain! { + if is_type_diagnostic_item(cx, ty, sym::Option); + if arms.len() == 2; + if arms[0].guard.is_none(); + if arms[1].guard.is_none(); + then { + check(cx, expr, scrutinee, arms[0].pat, arms[0].body, Some(arms[1].pat), arms[1].body) + } + } +} + +pub(super) fn check_if_let<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + let_pat: &'tcx Pat<'_>, + let_expr: &'tcx Expr<'_>, + then_expr: &'tcx Expr<'_>, + else_expr: &'tcx Expr<'_>, +) { + check(cx, expr, let_expr, let_pat, then_expr, None, else_expr); +} + +fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + scrutinee: &'tcx Expr<'_>, + then_pat: &'tcx Pat<'_>, + then_body: &'tcx Expr<'_>, + else_pat: Option<&'tcx Pat<'_>>, + else_body: &'tcx Expr<'_>, +) { + if let Some(sugg_info) = check_with( + cx, + expr, + scrutinee, + then_pat, + then_body, + else_pat, + else_body, + get_cond_expr, + ) { + let body_str = add_ampersand_if_copy(sugg_info.body_str, sugg_info.scrutinee_impl_copy); + span_lint_and_sugg( + cx, + MANUAL_FILTER, + expr.span, + "manual implementation of `Option::filter`", + "try this", + if sugg_info.needs_brackets { + format!( + "{{ {}{}.filter({body_str}) }}", + sugg_info.scrutinee_str, sugg_info.as_ref_str + ) + } else { + format!("{}{}.filter({body_str})", sugg_info.scrutinee_str, sugg_info.as_ref_str) + }, + sugg_info.app, + ); + } +} diff --git a/clippy_lints/src/matches/manual_map.rs b/clippy_lints/src/matches/manual_map.rs index 76f5e1c941c7..2b6a07c5d744 100644 --- a/clippy_lints/src/matches/manual_map.rs +++ b/clippy_lints/src/matches/manual_map.rs @@ -1,10 +1,11 @@ +use super::MANUAL_MAP; use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; -use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable, type_is_unsafe_function}; +use clippy_utils::ty::{is_copy, is_type_diagnostic_item, peel_mid_ty_refs_is_mutable, type_is_unsafe_function}; use clippy_utils::{ can_move_expr_to_closure, 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, + peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, sugg::Sugg, CaptureKind, }; use rustc_ast::util::parser::PREC_POSTFIX; use rustc_errors::Applicability; @@ -16,8 +17,6 @@ use rustc_hir::{ use rustc_lint::LateContext; use rustc_span::{sym, SyntaxContext}; -use super::MANUAL_MAP; - pub(super) fn check_match<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, @@ -43,7 +42,6 @@ pub(super) fn check_if_let<'tcx>( check(cx, expr, let_expr, let_pat, then_expr, None, else_expr); } -#[expect(clippy::too_many_lines)] fn check<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, @@ -53,12 +51,59 @@ fn check<'tcx>( else_pat: Option<&'tcx Pat<'_>>, else_body: &'tcx Expr<'_>, ) { + if let Some(sugg_info) = check_with( + cx, + expr, + scrutinee, + then_pat, + then_body, + else_pat, + else_body, + get_some_expr, + ) { + span_lint_and_sugg( + cx, + MANUAL_MAP, + expr.span, + "manual implementation of `Option::map`", + "try this", + if sugg_info.needs_brackets { + format!( + "{{ {}{}.map({}) }}", + sugg_info.scrutinee_str, sugg_info.as_ref_str, sugg_info.body_str + ) + } else { + format!( + "{}{}.map({})", + sugg_info.scrutinee_str, sugg_info.as_ref_str, sugg_info.body_str + ) + }, + sugg_info.app, + ); + } +} + +#[expect(clippy::too_many_arguments)] +#[expect(clippy::too_many_lines)] +pub(super) fn check_with<'tcx, F>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + scrutinee: &'tcx Expr<'_>, + then_pat: &'tcx Pat<'_>, + then_body: &'tcx Expr<'_>, + else_pat: Option<&'tcx Pat<'_>>, + else_body: &'tcx Expr<'_>, + get_some_expr_fn: F, +) -> Option> +where + F: Fn(&LateContext<'tcx>, &'tcx Pat<'_>, &'tcx Expr<'_>, SyntaxContext) -> Option>, +{ let (scrutinee_ty, ty_ref_count, ty_mutability) = peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(scrutinee)); if !(is_type_diagnostic_item(cx, scrutinee_ty, sym::Option) && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Option)) { - return; + return None; } let expr_ctxt = expr.span.ctxt(); @@ -78,29 +123,29 @@ fn check<'tcx>( (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::None)) if is_none_expr(cx, else_body) => { (then_body, pattern, ref_count, false) }, - _ => return, + _ => return None, }; // Top level or patterns aren't allowed in closures. if matches!(some_pat.kind, PatKind::Or(_)) { - return; + return None; } - let some_expr = match get_some_expr(cx, some_expr, false, expr_ctxt) { + let some_expr = match get_some_expr_fn(cx, some_pat, some_expr, expr_ctxt) { Some(expr) => expr, - None => return, + None => return None, }; // These two lints will go back and forth with each other. if cx.typeck_results().expr_ty(some_expr.expr) == cx.tcx.types.unit && !is_lint_allowed(cx, OPTION_MAP_UNIT_FN, expr.hir_id) { - return; + return None; } // `map` won't perform any adjustments. if !cx.typeck_results().expr_adjustments(some_expr.expr).is_empty() { - return; + return None; } // Determine which binding mode to use. @@ -125,16 +170,16 @@ fn check<'tcx>( }); if let ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(l), .. })) = e.kind { match captures.get(l) { - Some(CaptureKind::Value | CaptureKind::Ref(Mutability::Mut)) => return, + Some(CaptureKind::Value | CaptureKind::Ref(Mutability::Mut)) => return None, Some(CaptureKind::Ref(Mutability::Not)) if binding_ref_mutability == Mutability::Mut => { - return; + return None; }, Some(CaptureKind::Ref(Mutability::Not)) | None => (), } } } }, - None => return, + None => return None, }; let mut app = Applicability::MachineApplicable; @@ -149,6 +194,7 @@ fn check<'tcx>( scrutinee_str.into() }; + let closure_expr_snip = some_expr.to_snippet_with_context(cx, expr_ctxt, &mut app); let body_str = if let PatKind::Binding(annotation, id, some_binding, None) = some_pat.kind { if_chain! { if !some_expr.needs_unsafe_block; @@ -161,7 +207,7 @@ fn check<'tcx>( && !is_lint_allowed(cx, MATCH_AS_REF, expr.hir_id) && binding_ref.is_some() { - return; + return None; } // `ref` and `ref mut` annotations were handled earlier. @@ -170,41 +216,47 @@ fn check<'tcx>( } else { "" }; - let expr_snip = snippet_with_context(cx, some_expr.expr.span, expr_ctxt, "..", &mut app).0; + if some_expr.needs_unsafe_block { - format!("|{annotation}{some_binding}| unsafe {{ {expr_snip} }}") + format!("|{annotation}{some_binding}| unsafe {{ {closure_expr_snip} }}") } else { - format!("|{annotation}{some_binding}| {expr_snip}") + format!("|{annotation}{some_binding}| {closure_expr_snip}") } } } } else if !is_wild_none && explicit_ref.is_none() { // TODO: handle explicit reference annotations. let pat_snip = snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app).0; - let expr_snip = snippet_with_context(cx, some_expr.expr.span, expr_ctxt, "..", &mut app).0; if some_expr.needs_unsafe_block { - format!("|{pat_snip}| unsafe {{ {expr_snip} }}") + format!("|{pat_snip}| unsafe {{ {closure_expr_snip} }}") } else { - format!("|{pat_snip}| {expr_snip}") + format!("|{pat_snip}| {closure_expr_snip}") } } else { // Refutable bindings and mixed reference annotations can't be handled by `map`. - return; + return None; }; - span_lint_and_sugg( - cx, - MANUAL_MAP, - expr.span, - "manual implementation of `Option::map`", - "try this", - if else_pat.is_none() && is_else_clause(cx.tcx, expr) { - format!("{{ {scrutinee_str}{as_ref_str}.map({body_str}) }}") - } else { - format!("{scrutinee_str}{as_ref_str}.map({body_str})") - }, + // relies on the fact that Option: Copy where T: copy + let scrutinee_impl_copy = is_copy(cx, scrutinee_ty); + + Some(SuggInfo { + needs_brackets: else_pat.is_none() && is_else_clause(cx.tcx, expr), + scrutinee_impl_copy, + scrutinee_str, + as_ref_str, + body_str, app, - ); + }) +} + +pub struct SuggInfo<'a> { + pub needs_brackets: bool, + pub scrutinee_impl_copy: bool, + pub scrutinee_str: String, + pub as_ref_str: &'a str, + pub body_str: String, + pub app: Applicability, } // Checks whether the expression could be passed as a function, or whether a closure is needed. @@ -222,7 +274,8 @@ fn can_pass_as_func<'tcx>(cx: &LateContext<'tcx>, binding: HirId, expr: &'tcx Ex } } -enum OptionPat<'a> { +#[derive(Debug)] +pub(super) enum OptionPat<'a> { Wild, None, Some { @@ -234,14 +287,39 @@ enum OptionPat<'a> { }, } -struct SomeExpr<'tcx> { - expr: &'tcx Expr<'tcx>, - needs_unsafe_block: bool, +pub(super) struct SomeExpr<'tcx> { + pub expr: &'tcx Expr<'tcx>, + pub needs_unsafe_block: bool, + pub needs_negated: bool, // for `manual_filter` lint +} + +impl<'tcx> SomeExpr<'tcx> { + pub fn new_no_negated(expr: &'tcx Expr<'tcx>, needs_unsafe_block: bool) -> Self { + Self { + expr, + needs_unsafe_block, + needs_negated: false, + } + } + + pub fn to_snippet_with_context( + &self, + cx: &LateContext<'tcx>, + ctxt: SyntaxContext, + app: &mut Applicability, + ) -> Sugg<'tcx> { + let sugg = Sugg::hir_with_context(cx, self.expr, ctxt, "..", app); + if self.needs_negated { !sugg } else { sugg } + } } // Try to parse into a recognized `Option` pattern. // i.e. `_`, `None`, `Some(..)`, or a reference to any of those. -fn try_parse_pattern<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: SyntaxContext) -> Option> { +pub(super) fn try_parse_pattern<'tcx>( + cx: &LateContext<'tcx>, + pat: &'tcx Pat<'_>, + ctxt: SyntaxContext, +) -> Option> { fn f<'tcx>( cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, @@ -268,36 +346,41 @@ fn try_parse_pattern<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: Syn // Checks for an expression wrapped by the `Some` constructor. Returns the contained expression. fn get_some_expr<'tcx>( cx: &LateContext<'tcx>, + _: &'tcx Pat<'_>, expr: &'tcx Expr<'_>, - needs_unsafe_block: bool, ctxt: SyntaxContext, ) -> Option> { - // 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) => - { - Some(SomeExpr { - expr: arg, - needs_unsafe_block, - }) - }, - ExprKind::Block( - Block { - stmts: [], - expr: Some(expr), - rules, - .. + fn get_some_expr_internal<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + needs_unsafe_block: bool, + ctxt: SyntaxContext, + ) -> Option> { + // 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) => + { + Some(SomeExpr::new_no_negated(arg, needs_unsafe_block)) }, - _, - ) => get_some_expr( - cx, - expr, - needs_unsafe_block || *rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided), - ctxt, - ), - _ => None, + ExprKind::Block( + Block { + stmts: [], + expr: Some(expr), + rules, + .. + }, + _, + ) => get_some_expr_internal( + cx, + expr, + needs_unsafe_block || *rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided), + ctxt, + ), + _ => None, + } } + get_some_expr_internal(cx, expr, false, ctxt) } // Checks for the `None` value. diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index e6b183fc05f2..cf1bd7a12ade 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -1,5 +1,6 @@ mod collapsible_match; mod infallible_destructuring_match; +mod manual_filter; mod manual_map; mod manual_unwrap_or; mod match_as_ref; @@ -898,6 +899,34 @@ declare_clippy_lint! { "reimplementation of `map`" } +declare_clippy_lint! { + /// ### What it does + /// Checks for usages of `match` which could be implemented using `filter` + /// + /// ### Why is this bad? + /// Using the `filter` method is clearer and more concise. + /// + /// ### Example + /// ```rust + /// match Some(0) { + /// Some(x) => if x % 2 == 0 { + /// Some(x) + /// } else { + /// None + /// }, + /// None => None, + /// }; + /// ``` + /// Use instead: + /// ```rust + /// Some(0).filter(|&x| x % 2 == 0); + /// ``` + #[clippy::version = "1.65.0"] + pub MANUAL_FILTER, + complexity, + "reimplentation of `filter`" +} + #[derive(Default)] pub struct Matches { msrv: Option, @@ -939,6 +968,7 @@ impl_lint_pass!(Matches => [ SIGNIFICANT_DROP_IN_SCRUTINEE, TRY_ERR, MANUAL_MAP, + MANUAL_FILTER, ]); impl<'tcx> LateLintPass<'tcx> for Matches { @@ -988,6 +1018,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { if !in_constant(cx, expr.hir_id) { manual_unwrap_or::check(cx, expr, ex, arms); manual_map::check_match(cx, expr, ex, arms); + manual_filter::check_match(cx, ex, arms, expr); } if self.infallible_destructuring_match_linted { @@ -1014,6 +1045,14 @@ impl<'tcx> LateLintPass<'tcx> for Matches { } if !in_constant(cx, expr.hir_id) { manual_map::check_if_let(cx, expr, if_let.let_pat, if_let.let_expr, if_let.if_then, else_expr); + manual_filter::check_if_let( + cx, + expr, + if_let.let_pat, + if_let.let_expr, + if_let.if_then, + else_expr, + ); } } redundant_pattern_match::check_if_let( diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index ef836e84829b..e88542b77a67 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -1,7 +1,9 @@ //! Contains utility functions to generate suggestions. #![deny(clippy::missing_docs_in_private_items)] -use crate::source::{snippet, snippet_opt, snippet_with_applicability, snippet_with_macro_callsite}; +use crate::source::{ + snippet, snippet_opt, snippet_with_applicability, snippet_with_context, snippet_with_macro_callsite, +}; use crate::ty::expr_sig; use crate::{get_parent_expr_for_hir, higher}; use rustc_ast::util::parser::AssocOp; @@ -110,7 +112,7 @@ impl<'a> Sugg<'a> { if expr.span.ctxt() == ctxt { Self::hir_from_snippet(expr, |span| snippet(cx, span, default)) } else { - let snip = snippet_with_applicability(cx, expr.span, default, applicability); + let snip = snippet_with_context(cx, expr.span, ctxt, default, applicability).0; Sugg::NonParen(snip) } } diff --git a/src/docs.rs b/src/docs.rs index 39540e4b0489..d24342076b69 100644 --- a/src/docs.rs +++ b/src/docs.rs @@ -256,6 +256,7 @@ docs! { "manual_async_fn", "manual_bits", "manual_clamp", + "manual_filter", "manual_filter_map", "manual_find", "manual_find_map", diff --git a/src/docs/manual_filter.txt b/src/docs/manual_filter.txt new file mode 100644 index 000000000000..19a4d9319d94 --- /dev/null +++ b/src/docs/manual_filter.txt @@ -0,0 +1,21 @@ +### What it does +Checks for usages of `match` which could be implemented using `filter` + +### Why is this bad? +Using the `filter` method is clearer and more concise. + +### Example +``` +match Some(0) { + Some(x) => if x % 2 == 0 { + Some(x) + } else { + None + }, + None => None, +}; +``` +Use instead: +``` +Some(0).filter(|&x| x % 2 == 0); +``` \ No newline at end of file diff --git a/tests/ui/manual_filter.fixed b/tests/ui/manual_filter.fixed new file mode 100644 index 000000000000..3553291b87df --- /dev/null +++ b/tests/ui/manual_filter.fixed @@ -0,0 +1,119 @@ +// run-rustfix + +#![warn(clippy::manual_filter)] +#![allow(unused_variables, dead_code)] + +fn main() { + Some(0).filter(|&x| x <= 0); + + Some(1).filter(|&x| x <= 0); + + Some(2).filter(|&x| x <= 0); + + Some(3).filter(|&x| x > 0); + + let y = Some(4); + y.filter(|&x| x <= 0); + + Some(5).filter(|&x| x > 0); + + Some(6).as_ref().filter(|&x| x > &0); + + let external_cond = true; + Some(String::new()).filter(|x| external_cond); + + Some(7).filter(|&x| external_cond); + + Some(8).filter(|&x| x != 0); + + Some(9).filter(|&x| x > 10 && x < 100); + + const fn f1() { + // Don't lint, `.filter` is not const + match Some(10) { + Some(x) => { + if x > 10 && x < 100 { + Some(x) + } else { + None + } + }, + None => None, + }; + } + + #[allow(clippy::blocks_in_if_conditions)] + Some(11).filter(|&x| { + println!("foo"); + x > 10 && x < 100 + }); + + match Some(12) { + // Don't Lint, statement is lost by `.filter` + Some(x) => { + if x > 10 && x < 100 { + println!("foo"); + Some(x) + } else { + None + } + }, + None => None, + }; + + match Some(13) { + // Don't Lint, because of `None => Some(1)` + Some(x) => { + if x > 10 && x < 100 { + println!("foo"); + Some(x) + } else { + None + } + }, + None => Some(1), + }; + + unsafe fn f(x: u32) -> bool { + true + } + let _ = Some(14).filter(|&x| unsafe { f(x) }); + let _ = Some(15).filter(|&x| unsafe { f(x) }); + + #[allow(clippy::redundant_pattern_matching)] + if let Some(_) = Some(16) { + Some(16) + } else { Some(16).filter(|&x| x % 2 == 0) }; + + match Some((17, 17)) { + // Not linted for now could be + Some((x, y)) => { + if y != x { + Some((x, y)) + } else { + None + } + }, + None => None, + }; + + struct NamedTuple { + pub x: u8, + pub y: (i32, u32), + } + + match Some(NamedTuple { + // Not linted for now could be + x: 17, + y: (18, 19), + }) { + Some(NamedTuple { x, y }) => { + if y.1 != x as u32 { + Some(NamedTuple { x, y }) + } else { + None + } + }, + None => None, + }; +} diff --git a/tests/ui/manual_filter.rs b/tests/ui/manual_filter.rs new file mode 100644 index 000000000000..aa9f90f752b1 --- /dev/null +++ b/tests/ui/manual_filter.rs @@ -0,0 +1,243 @@ +// run-rustfix + +#![warn(clippy::manual_filter)] +#![allow(unused_variables, dead_code)] + +fn main() { + match Some(0) { + None => None, + Some(x) => { + if x > 0 { + None + } else { + Some(x) + } + }, + }; + + match Some(1) { + Some(x) => { + if x > 0 { + None + } else { + Some(x) + } + }, + None => None, + }; + + match Some(2) { + Some(x) => { + if x > 0 { + None + } else { + Some(x) + } + }, + _ => None, + }; + + match Some(3) { + Some(x) => { + if x > 0 { + Some(x) + } else { + None + } + }, + None => None, + }; + + let y = Some(4); + match y { + // Some(4) + None => None, + Some(x) => { + if x > 0 { + None + } else { + Some(x) + } + }, + }; + + match Some(5) { + Some(x) => { + if x > 0 { + Some(x) + } else { + None + } + }, + _ => None, + }; + + match Some(6) { + Some(ref x) => { + if x > &0 { + Some(x) + } else { + None + } + }, + _ => None, + }; + + let external_cond = true; + match Some(String::new()) { + Some(x) => { + if external_cond { + Some(x) + } else { + None + } + }, + _ => None, + }; + + if let Some(x) = Some(7) { + if external_cond { Some(x) } else { None } + } else { + None + }; + + match &Some(8) { + &Some(x) => { + if x != 0 { + Some(x) + } else { + None + } + }, + _ => None, + }; + + match Some(9) { + Some(x) => { + if x > 10 && x < 100 { + Some(x) + } else { + None + } + }, + None => None, + }; + + const fn f1() { + // Don't lint, `.filter` is not const + match Some(10) { + Some(x) => { + if x > 10 && x < 100 { + Some(x) + } else { + None + } + }, + None => None, + }; + } + + #[allow(clippy::blocks_in_if_conditions)] + match Some(11) { + // Lint, statement is preserved by `.filter` + Some(x) => { + if { + println!("foo"); + x > 10 && x < 100 + } { + Some(x) + } else { + None + } + }, + None => None, + }; + + match Some(12) { + // Don't Lint, statement is lost by `.filter` + Some(x) => { + if x > 10 && x < 100 { + println!("foo"); + Some(x) + } else { + None + } + }, + None => None, + }; + + match Some(13) { + // Don't Lint, because of `None => Some(1)` + Some(x) => { + if x > 10 && x < 100 { + println!("foo"); + Some(x) + } else { + None + } + }, + None => Some(1), + }; + + unsafe fn f(x: u32) -> bool { + true + } + let _ = match Some(14) { + Some(x) => { + if unsafe { f(x) } { + Some(x) + } else { + None + } + }, + None => None, + }; + let _ = match Some(15) { + Some(x) => unsafe { + if f(x) { Some(x) } else { None } + }, + None => None, + }; + + #[allow(clippy::redundant_pattern_matching)] + if let Some(_) = Some(16) { + Some(16) + } else if let Some(x) = Some(16) { + // Lint starting from here + if x % 2 == 0 { Some(x) } else { None } + } else { + None + }; + + match Some((17, 17)) { + // Not linted for now could be + Some((x, y)) => { + if y != x { + Some((x, y)) + } else { + None + } + }, + None => None, + }; + + struct NamedTuple { + pub x: u8, + pub y: (i32, u32), + } + + match Some(NamedTuple { + // Not linted for now could be + x: 17, + y: (18, 19), + }) { + Some(NamedTuple { x, y }) => { + if y.1 != x as u32 { + Some(NamedTuple { x, y }) + } else { + None + } + }, + None => None, + }; +} diff --git a/tests/ui/manual_filter.stderr b/tests/ui/manual_filter.stderr new file mode 100644 index 000000000000..53dea9229306 --- /dev/null +++ b/tests/ui/manual_filter.stderr @@ -0,0 +1,191 @@ +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:7:5 + | +LL | / match Some(0) { +LL | | None => None, +LL | | Some(x) => { +LL | | if x > 0 { +... | +LL | | }, +LL | | }; + | |_____^ help: try this: `Some(0).filter(|&x| x <= 0)` + | + = note: `-D clippy::manual-filter` implied by `-D warnings` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:18:5 + | +LL | / match Some(1) { +LL | | Some(x) => { +LL | | if x > 0 { +LL | | None +... | +LL | | None => None, +LL | | }; + | |_____^ help: try this: `Some(1).filter(|&x| x <= 0)` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:29:5 + | +LL | / match Some(2) { +LL | | Some(x) => { +LL | | if x > 0 { +LL | | None +... | +LL | | _ => None, +LL | | }; + | |_____^ help: try this: `Some(2).filter(|&x| x <= 0)` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:40:5 + | +LL | / match Some(3) { +LL | | Some(x) => { +LL | | if x > 0 { +LL | | Some(x) +... | +LL | | None => None, +LL | | }; + | |_____^ help: try this: `Some(3).filter(|&x| x > 0)` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:52:5 + | +LL | / match y { +LL | | // Some(4) +LL | | None => None, +LL | | Some(x) => { +... | +LL | | }, +LL | | }; + | |_____^ help: try this: `y.filter(|&x| x <= 0)` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:64:5 + | +LL | / match Some(5) { +LL | | Some(x) => { +LL | | if x > 0 { +LL | | Some(x) +... | +LL | | _ => None, +LL | | }; + | |_____^ help: try this: `Some(5).filter(|&x| x > 0)` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:75:5 + | +LL | / match Some(6) { +LL | | Some(ref x) => { +LL | | if x > &0 { +LL | | Some(x) +... | +LL | | _ => None, +LL | | }; + | |_____^ help: try this: `Some(6).as_ref().filter(|&x| x > &0)` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:87:5 + | +LL | / match Some(String::new()) { +LL | | Some(x) => { +LL | | if external_cond { +LL | | Some(x) +... | +LL | | _ => None, +LL | | }; + | |_____^ help: try this: `Some(String::new()).filter(|x| external_cond)` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:98:5 + | +LL | / if let Some(x) = Some(7) { +LL | | if external_cond { Some(x) } else { None } +LL | | } else { +LL | | None +LL | | }; + | |_____^ help: try this: `Some(7).filter(|&x| external_cond)` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:104:5 + | +LL | / match &Some(8) { +LL | | &Some(x) => { +LL | | if x != 0 { +LL | | Some(x) +... | +LL | | _ => None, +LL | | }; + | |_____^ help: try this: `Some(8).filter(|&x| x != 0)` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:115:5 + | +LL | / match Some(9) { +LL | | Some(x) => { +LL | | if x > 10 && x < 100 { +LL | | Some(x) +... | +LL | | None => None, +LL | | }; + | |_____^ help: try this: `Some(9).filter(|&x| x > 10 && x < 100)` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:141:5 + | +LL | / match Some(11) { +LL | | // Lint, statement is preserved by `.filter` +LL | | Some(x) => { +LL | | if { +... | +LL | | None => None, +LL | | }; + | |_____^ + | +help: try this + | +LL ~ Some(11).filter(|&x| { +LL + println!("foo"); +LL + x > 10 && x < 100 +LL ~ }); + | + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:185:13 + | +LL | let _ = match Some(14) { + | _____________^ +LL | | Some(x) => { +LL | | if unsafe { f(x) } { +LL | | Some(x) +... | +LL | | None => None, +LL | | }; + | |_____^ help: try this: `Some(14).filter(|&x| unsafe { f(x) })` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:195:13 + | +LL | let _ = match Some(15) { + | _____________^ +LL | | Some(x) => unsafe { +LL | | if f(x) { Some(x) } else { None } +LL | | }, +LL | | None => None, +LL | | }; + | |_____^ help: try this: `Some(15).filter(|&x| unsafe { f(x) })` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:205:12 + | +LL | } else if let Some(x) = Some(16) { + | ____________^ +LL | | // Lint starting from here +LL | | if x % 2 == 0 { Some(x) } else { None } +LL | | } else { +LL | | None +LL | | }; + | |_____^ help: try this: `{ Some(16).filter(|&x| x % 2 == 0) }` + +error: aborting due to 15 previous errors + From 87bbc2d4135b0ee8c5fd5f33db7411029b5484e5 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Mon, 12 Sep 2022 12:28:14 +0200 Subject: [PATCH 043/309] Rustup to rustc 1.65.0-nightly (59e7a308e 2022-09-11) --- build_sysroot/Cargo.lock | 4 ++-- rust-toolchain | 2 +- src/intrinsics/mod.rs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build_sysroot/Cargo.lock b/build_sysroot/Cargo.lock index 6c5043bb6f8e..17338aeaadaa 100644 --- a/build_sysroot/Cargo.lock +++ b/build_sysroot/Cargo.lock @@ -123,9 +123,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897cd85af6387be149f55acf168e41be176a02de7872403aaab184afc2f327e6" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" dependencies = [ "compiler_builtins", "libc", diff --git a/rust-toolchain b/rust-toolchain index f17897e48adf..4a228e3f8a87 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2022-09-04" +channel = "nightly-2022-09-12" components = ["rust-src", "rustc-dev", "llvm-tools-preview"] diff --git a/src/intrinsics/mod.rs b/src/intrinsics/mod.rs index 971cd9f63a4a..0302b843aa22 100644 --- a/src/intrinsics/mod.rs +++ b/src/intrinsics/mod.rs @@ -813,8 +813,8 @@ fn codegen_regular_intrinsic_call<'tcx>( sym::ptr_guaranteed_cmp => { intrinsic_args!(fx, args => (a, b); intrinsic); - let val = crate::num::codegen_ptr_binop(fx, BinOp::Eq, a, b); - ret.write_cvalue(fx, val); + let val = crate::num::codegen_ptr_binop(fx, BinOp::Eq, a, b).load_scalar(fx); + ret.write_cvalue(fx, CValue::by_val(val, fx.layout_of(fx.tcx.types.u8))); } sym::caller_location => { From 917be273ab827c45e3b46e24157525339844e121 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun, 28 Aug 2022 16:05:45 +0000 Subject: [PATCH 044/309] Minor improvement to apply_patches --- build_system/prepare.rs | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/build_system/prepare.rs b/build_system/prepare.rs index b7368e8ffaba..9b89525395fc 100644 --- a/build_system/prepare.rs +++ b/build_system/prepare.rs @@ -1,8 +1,7 @@ use std::env; use std::ffi::OsStr; -use std::ffi::OsString; use std::fs; -use std::path::Path; +use std::path::{Path, PathBuf}; use std::process::Command; use super::rustc_info::{get_file_name, get_rustc_path, get_rustc_version}; @@ -156,14 +155,20 @@ fn init_git_repo(repo_dir: &Path) { spawn_and_wait(git_commit_cmd); } -fn get_patches(crate_name: &str) -> Vec { - let mut patches: Vec<_> = fs::read_dir("patches") +fn get_patches(source_dir: &Path, crate_name: &str) -> Vec { + let mut patches: Vec<_> = fs::read_dir(source_dir.join("patches")) .unwrap() .map(|entry| entry.unwrap().path()) .filter(|path| path.extension() == Some(OsStr::new("patch"))) - .map(|path| path.file_name().unwrap().to_owned()) - .filter(|file_name| { - file_name.to_str().unwrap().split_once("-").unwrap().1.starts_with(crate_name) + .filter(|path| { + path.file_name() + .unwrap() + .to_str() + .unwrap() + .split_once("-") + .unwrap() + .1 + .starts_with(crate_name) }) .collect(); patches.sort(); @@ -171,11 +176,14 @@ fn get_patches(crate_name: &str) -> Vec { } fn apply_patches(crate_name: &str, target_dir: &Path) { - for patch in get_patches(crate_name) { - eprintln!("[PATCH] {:?} <- {:?}", target_dir.file_name().unwrap(), patch); - let patch_arg = env::current_dir().unwrap().join("patches").join(patch); + for patch in get_patches(&std::env::current_dir().unwrap(), crate_name) { + eprintln!( + "[PATCH] {:?} <- {:?}", + target_dir.file_name().unwrap(), + patch.file_name().unwrap() + ); let mut apply_patch_cmd = Command::new("git"); - apply_patch_cmd.arg("am").arg(patch_arg).arg("-q").current_dir(target_dir); + apply_patch_cmd.arg("am").arg(patch).arg("-q").current_dir(target_dir); spawn_and_wait(apply_patch_cmd); } } From b12286fec3334238e899420a6586b3c1c7498a98 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun, 28 Aug 2022 16:46:09 +0000 Subject: [PATCH 045/309] Let abi-checker take the full path to the cg_clif dylib --- build_system/abi_checker.rs | 11 +++-------- build_system/build_backend.rs | 6 +++++- build_system/build_sysroot.rs | 7 +++---- build_system/mod.rs | 8 ++++---- build_system/tests.rs | 6 +++--- 5 files changed, 18 insertions(+), 20 deletions(-) diff --git a/build_system/abi_checker.rs b/build_system/abi_checker.rs index 67dbd0a38a4f..faff081745f8 100644 --- a/build_system/abi_checker.rs +++ b/build_system/abi_checker.rs @@ -10,7 +10,7 @@ pub(crate) fn run( channel: &str, sysroot_kind: SysrootKind, target_dir: &Path, - cg_clif_build_dir: &Path, + cg_clif_dylib: &Path, host_triple: &str, target_triple: &str, ) { @@ -29,7 +29,7 @@ pub(crate) fn run( channel, sysroot_kind, target_dir, - cg_clif_build_dir, + cg_clif_dylib, host_triple, target_triple, ); @@ -39,11 +39,6 @@ pub(crate) fn run( abi_checker_path.push("abi-checker"); env::set_current_dir(abi_checker_path.clone()).unwrap(); - let build_dir = abi_checker_path.parent().unwrap().join("build"); - let cg_clif_dylib_path = build_dir.join(if cfg!(windows) { "bin" } else { "lib" }).join( - env::consts::DLL_PREFIX.to_string() + "rustc_codegen_cranelift" + env::consts::DLL_SUFFIX, - ); - let pairs = ["rustc_calls_cgclif", "cgclif_calls_rustc", "cgclif_calls_cc", "cc_calls_cgclif"]; let mut cmd = Command::new("cargo"); @@ -54,7 +49,7 @@ pub(crate) fn run( cmd.arg("--pairs"); cmd.args(pairs); cmd.arg("--add-rustc-codegen-backend"); - cmd.arg(format!("cgclif:{}", cg_clif_dylib_path.display())); + cmd.arg(format!("cgclif:{}", cg_clif_dylib.display())); spawn_and_wait(cmd); } diff --git a/build_system/build_backend.rs b/build_system/build_backend.rs index 9e59b8199b41..d199d9906c34 100644 --- a/build_system/build_backend.rs +++ b/build_system/build_backend.rs @@ -2,6 +2,7 @@ use std::env; use std::path::{Path, PathBuf}; use std::process::Command; +use super::rustc_info::get_file_name; use super::utils::is_ci; pub(crate) fn build_backend( @@ -41,5 +42,8 @@ pub(crate) fn build_backend( eprintln!("[BUILD] rustc_codegen_cranelift"); super::utils::spawn_and_wait(cmd); - Path::new("target").join(host_triple).join(channel) + Path::new("target") + .join(host_triple) + .join(channel) + .join(get_file_name("rustc_codegen_cranelift", "dylib")) } diff --git a/build_system/build_sysroot.rs b/build_system/build_sysroot.rs index 7e205b0fd0b3..5b18982fa7ef 100644 --- a/build_system/build_sysroot.rs +++ b/build_system/build_sysroot.rs @@ -10,7 +10,7 @@ pub(crate) fn build_sysroot( channel: &str, sysroot_kind: SysrootKind, target_dir: &Path, - cg_clif_build_dir: &Path, + cg_clif_dylib_src: &Path, host_triple: &str, target_triple: &str, ) { @@ -23,7 +23,6 @@ pub(crate) fn build_sysroot( fs::create_dir_all(target_dir.join("lib")).unwrap(); // Copy the backend - let cg_clif_dylib = get_file_name("rustc_codegen_cranelift", "dylib"); let cg_clif_dylib_path = target_dir .join(if cfg!(windows) { // Windows doesn't have rpath support, so the cg_clif dylib needs to be next to the @@ -32,8 +31,8 @@ pub(crate) fn build_sysroot( } else { "lib" }) - .join(&cg_clif_dylib); - try_hard_link(cg_clif_build_dir.join(cg_clif_dylib), &cg_clif_dylib_path); + .join(get_file_name("rustc_codegen_cranelift", "dylib")); + try_hard_link(cg_clif_dylib_src, &cg_clif_dylib_path); // Build and copy rustc and cargo wrappers for wrapper in ["rustc-clif", "cargo-clif"] { diff --git a/build_system/mod.rs b/build_system/mod.rs index c3706dc6f820..c665d1ef71c2 100644 --- a/build_system/mod.rs +++ b/build_system/mod.rs @@ -130,7 +130,7 @@ pub fn main() { process::exit(1); } - let cg_clif_build_dir = + let cg_clif_dylib = build_backend::build_backend(channel, &host_triple, use_unstable_features); match command { Command::Test => { @@ -138,7 +138,7 @@ pub fn main() { channel, sysroot_kind, &target_dir, - &cg_clif_build_dir, + &cg_clif_dylib, &host_triple, &target_triple, ); @@ -147,7 +147,7 @@ pub fn main() { channel, sysroot_kind, &target_dir, - &cg_clif_build_dir, + &cg_clif_dylib, &host_triple, &target_triple, ); @@ -157,7 +157,7 @@ pub fn main() { channel, sysroot_kind, &target_dir, - &cg_clif_build_dir, + &cg_clif_dylib, &host_triple, &target_triple, ); diff --git a/build_system/tests.rs b/build_system/tests.rs index 3f225b4efa2b..d3296c6fda3d 100644 --- a/build_system/tests.rs +++ b/build_system/tests.rs @@ -397,7 +397,7 @@ pub(crate) fn run_tests( channel: &str, sysroot_kind: SysrootKind, target_dir: &Path, - cg_clif_build_dir: &Path, + cg_clif_dylib: &Path, host_triple: &str, target_triple: &str, ) { @@ -408,7 +408,7 @@ pub(crate) fn run_tests( channel, SysrootKind::None, &target_dir, - cg_clif_build_dir, + cg_clif_dylib, &host_triple, &target_triple, ); @@ -427,7 +427,7 @@ pub(crate) fn run_tests( channel, sysroot_kind, &target_dir, - cg_clif_build_dir, + cg_clif_dylib, &host_triple, &target_triple, ); From 721668f13364f8d7b288b4dc6919bc2f9fdbdfb4 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun, 28 Aug 2022 17:14:47 +0000 Subject: [PATCH 046/309] Tiny cleanup --- build_system/tests.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/build_system/tests.rs b/build_system/tests.rs index d3296c6fda3d..e6348624c7bd 100644 --- a/build_system/tests.rs +++ b/build_system/tests.rs @@ -249,11 +249,7 @@ const EXTENDED_SYSROOT_SUITE: &[TestCase] = &[ bench_compile.arg("--prepare"); bench_compile.arg(format!("{:?}", runner.cargo_command(["clean"]))); - if cfg!(windows) { - bench_compile.arg("cmd /C \"set RUSTFLAGS= && cargo build\""); - } else { - bench_compile.arg("RUSTFLAGS='' cargo build"); - } + bench_compile.arg("cargo build"); bench_compile.arg(format!("{:?}", runner.cargo_command(["build"]))); spawn_and_wait(bench_compile); From c677cba06b69510585e7ce1f21523857679f08b8 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun, 28 Aug 2022 17:53:36 +0000 Subject: [PATCH 047/309] More uniform imports --- build_system/abi_checker.rs | 9 +++++---- build_system/config.rs | 3 ++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/build_system/abi_checker.rs b/build_system/abi_checker.rs index faff081745f8..053198b69931 100644 --- a/build_system/abi_checker.rs +++ b/build_system/abi_checker.rs @@ -1,11 +1,12 @@ -use super::build_sysroot; -use super::config; -use super::utils::spawn_and_wait; -use build_system::SysrootKind; use std::env; use std::path::Path; use std::process::Command; +use super::build_sysroot; +use super::config; +use super::utils::spawn_and_wait; +use super::SysrootKind; + pub(crate) fn run( channel: &str, sysroot_kind: SysrootKind, diff --git a/build_system/config.rs b/build_system/config.rs index ef540cf1f822..c31784e1097d 100644 --- a/build_system/config.rs +++ b/build_system/config.rs @@ -1,4 +1,5 @@ -use std::{fs, process}; +use std::fs; +use std::process; fn load_config_file() -> Vec<(String, Option)> { fs::read_to_string("config.txt") From be305c2b1f4be31c66c3f646a85dc459d6ee5807 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun, 28 Aug 2022 16:38:09 +0000 Subject: [PATCH 048/309] Introduce cargo_command helper --- build_system/abi_checker.rs | 10 +--- build_system/build_backend.rs | 12 ++-- build_system/build_sysroot.rs | 5 +- build_system/prepare.rs | 5 +- build_system/tests.rs | 100 ++++++++++++++++------------------ build_system/utils.rs | 20 +++++++ 6 files changed, 81 insertions(+), 71 deletions(-) diff --git a/build_system/abi_checker.rs b/build_system/abi_checker.rs index 053198b69931..177b44d3141f 100644 --- a/build_system/abi_checker.rs +++ b/build_system/abi_checker.rs @@ -1,10 +1,9 @@ use std::env; use std::path::Path; -use std::process::Command; use super::build_sysroot; use super::config; -use super::utils::spawn_and_wait; +use super::utils::{cargo_command, spawn_and_wait}; use super::SysrootKind; pub(crate) fn run( @@ -38,14 +37,11 @@ pub(crate) fn run( eprintln!("Running abi-checker"); let mut abi_checker_path = env::current_dir().unwrap(); abi_checker_path.push("abi-checker"); - env::set_current_dir(abi_checker_path.clone()).unwrap(); + env::set_current_dir(&abi_checker_path.clone()).unwrap(); let pairs = ["rustc_calls_cgclif", "cgclif_calls_rustc", "cgclif_calls_cc", "cc_calls_cgclif"]; - let mut cmd = Command::new("cargo"); - cmd.arg("run"); - cmd.arg("--target"); - cmd.arg(target_triple); + let mut cmd = cargo_command("cargo", "run", Some(target_triple), &abi_checker_path); cmd.arg("--"); cmd.arg("--pairs"); cmd.args(pairs); diff --git a/build_system/build_backend.rs b/build_system/build_backend.rs index d199d9906c34..cda468bcfa2d 100644 --- a/build_system/build_backend.rs +++ b/build_system/build_backend.rs @@ -1,17 +1,16 @@ use std::env; -use std::path::{Path, PathBuf}; -use std::process::Command; +use std::path::PathBuf; use super::rustc_info::get_file_name; -use super::utils::is_ci; +use super::utils::{cargo_command, is_ci}; pub(crate) fn build_backend( channel: &str, host_triple: &str, use_unstable_features: bool, ) -> PathBuf { - let mut cmd = Command::new("cargo"); - cmd.arg("build").arg("--target").arg(host_triple); + let source_dir = std::env::current_dir().unwrap(); + let mut cmd = cargo_command("cargo", "build", Some(host_triple), &source_dir); cmd.env("CARGO_BUILD_INCREMENTAL", "true"); // Force incr comp even in release mode @@ -42,7 +41,8 @@ pub(crate) fn build_backend( eprintln!("[BUILD] rustc_codegen_cranelift"); super::utils::spawn_and_wait(cmd); - Path::new("target") + source_dir + .join("target") .join(host_triple) .join(channel) .join(get_file_name("rustc_codegen_cranelift", "dylib")) diff --git a/build_system/build_sysroot.rs b/build_system/build_sysroot.rs index 5b18982fa7ef..c2c81feb25a6 100644 --- a/build_system/build_sysroot.rs +++ b/build_system/build_sysroot.rs @@ -3,7 +3,7 @@ use std::path::{Path, PathBuf}; use std::process::{self, Command}; use super::rustc_info::{get_file_name, get_rustc_version, get_wrapper_file_name}; -use super::utils::{spawn_and_wait, try_hard_link}; +use super::utils::{cargo_command, spawn_and_wait, try_hard_link}; use super::SysrootKind; pub(crate) fn build_sysroot( @@ -185,8 +185,7 @@ fn build_clif_sysroot_for_triple( } // Build sysroot - let mut build_cmd = Command::new("cargo"); - build_cmd.arg("build").arg("--target").arg(triple).current_dir("build_sysroot"); + let mut build_cmd = cargo_command("cargo", "build", Some(triple), Path::new("build_sysroot")); let mut rustflags = "-Zforce-unstable-if-unmarked -Cpanic=abort".to_string(); rustflags.push_str(&format!(" -Zcodegen-backend={}", cg_clif_dylib_path.to_str().unwrap())); if channel == "release" { diff --git a/build_system/prepare.rs b/build_system/prepare.rs index 9b89525395fc..83a76d3591d6 100644 --- a/build_system/prepare.rs +++ b/build_system/prepare.rs @@ -5,7 +5,7 @@ use std::path::{Path, PathBuf}; use std::process::Command; use super::rustc_info::{get_file_name, get_rustc_path, get_rustc_version}; -use super::utils::{copy_dir_recursively, spawn_and_wait}; +use super::utils::{cargo_command, copy_dir_recursively, spawn_and_wait}; pub(crate) fn prepare() { prepare_sysroot(); @@ -52,8 +52,7 @@ pub(crate) fn prepare() { ); eprintln!("[LLVM BUILD] simple-raytracer"); - let mut build_cmd = Command::new("cargo"); - build_cmd.arg("build").env_remove("CARGO_TARGET_DIR").current_dir("simple-raytracer"); + let build_cmd = cargo_command("cargo", "build", None, Path::new("simple-raytracer")); spawn_and_wait(build_cmd); fs::copy( Path::new("simple-raytracer/target/debug").join(get_file_name("main", "bin")), diff --git a/build_system/tests.rs b/build_system/tests.rs index e6348624c7bd..3f0d461fbe6c 100644 --- a/build_system/tests.rs +++ b/build_system/tests.rs @@ -1,7 +1,7 @@ use super::build_sysroot; use super::config; use super::rustc_info::get_wrapper_file_name; -use super::utils::{spawn_and_wait, spawn_and_wait_with_input}; +use super::utils::{cargo_command, spawn_and_wait, spawn_and_wait_with_input}; use build_system::SysrootKind; use std::env; use std::ffi::OsStr; @@ -218,20 +218,14 @@ const BASE_SYSROOT_SUITE: &[TestCase] = &[ const EXTENDED_SYSROOT_SUITE: &[TestCase] = &[ TestCase::new("test.rust-random/rand", &|runner| { runner.in_dir(["rand"], |runner| { - runner.run_cargo(["clean"]); + runner.run_cargo("clean", []); if runner.host_triple == runner.target_triple { eprintln!("[TEST] rust-random/rand"); - runner.run_cargo(["test", "--workspace"]); + runner.run_cargo("test", ["--workspace"]); } else { eprintln!("[AOT] rust-random/rand"); - runner.run_cargo([ - "build", - "--workspace", - "--target", - &runner.target_triple, - "--tests", - ]); + runner.run_cargo("build", ["--workspace", "--tests"]); } }); }), @@ -247,11 +241,19 @@ const EXTENDED_SYSROOT_SUITE: &[TestCase] = &[ bench_compile.arg("--warmup"); bench_compile.arg("1"); bench_compile.arg("--prepare"); - bench_compile.arg(format!("{:?}", runner.cargo_command(["clean"]))); + bench_compile.arg(format!("{:?}", runner.cargo_command("clean", []))); bench_compile.arg("cargo build"); - bench_compile.arg(format!("{:?}", runner.cargo_command(["build"]))); + let cargo_clif = runner + .root_dir + .clone() + .join("build") + .join(get_wrapper_file_name("cargo-clif", "bin")); + let mut clif_build_cmd = cargo_command(cargo_clif, "build", None, Path::new(".")); + clif_build_cmd.env("RUSTFLAGS", &runner.rust_flags); + bench_compile.arg(format!("{:?}", clif_build_cmd)); + spawn_and_wait(bench_compile); eprintln!("[BENCH RUN] ebobby/simple-raytracer"); @@ -265,51 +267,39 @@ const EXTENDED_SYSROOT_SUITE: &[TestCase] = &[ bench_run.arg(PathBuf::from("./raytracer_cg_clif")); spawn_and_wait(bench_run); } else { - runner.run_cargo(["clean"]); + runner.run_cargo("clean", []); eprintln!("[BENCH COMPILE] ebobby/simple-raytracer (skipped)"); eprintln!("[COMPILE] ebobby/simple-raytracer"); - runner.run_cargo(["build", "--target", &runner.target_triple]); + runner.run_cargo("build", []); eprintln!("[BENCH RUN] ebobby/simple-raytracer (skipped)"); } }); }), TestCase::new("test.libcore", &|runner| { runner.in_dir(["build_sysroot", "sysroot_src", "library", "core", "tests"], |runner| { - runner.run_cargo(["clean"]); + runner.run_cargo("clean", []); if runner.host_triple == runner.target_triple { - runner.run_cargo(["test"]); + runner.run_cargo("test", []); } else { eprintln!("Cross-Compiling: Not running tests"); - runner.run_cargo(["build", "--target", &runner.target_triple, "--tests"]); + runner.run_cargo("build", ["--tests"]); } }); }), TestCase::new("test.regex-shootout-regex-dna", &|runner| { runner.in_dir(["regex"], |runner| { - runner.run_cargo(["clean"]); + runner.run_cargo("clean", []); // newer aho_corasick versions throw a deprecation warning let lint_rust_flags = format!("{} --cap-lints warn", runner.rust_flags); - let mut build_cmd = runner.cargo_command([ - "build", - "--example", - "shootout-regex-dna", - "--target", - &runner.target_triple, - ]); + let mut build_cmd = runner.cargo_command("build", ["--example", "shootout-regex-dna"]); build_cmd.env("RUSTFLAGS", lint_rust_flags.clone()); spawn_and_wait(build_cmd); if runner.host_triple == runner.target_triple { - let mut run_cmd = runner.cargo_command([ - "run", - "--example", - "shootout-regex-dna", - "--target", - &runner.target_triple, - ]); + let mut run_cmd = runner.cargo_command("run", ["--example", "shootout-regex-dna"]); run_cmd.env("RUSTFLAGS", lint_rust_flags); let input = @@ -350,28 +340,30 @@ const EXTENDED_SYSROOT_SUITE: &[TestCase] = &[ }), TestCase::new("test.regex", &|runner| { runner.in_dir(["regex"], |runner| { - runner.run_cargo(["clean"]); + runner.run_cargo("clean", []); // newer aho_corasick versions throw a deprecation warning let lint_rust_flags = format!("{} --cap-lints warn", runner.rust_flags); if runner.host_triple == runner.target_triple { - let mut run_cmd = runner.cargo_command([ + let mut run_cmd = runner.cargo_command( "test", - "--tests", - "--", - "--exclude-should-panic", - "--test-threads", - "1", - "-Zunstable-options", - "-q", - ]); + [ + "--tests", + "--", + "--exclude-should-panic", + "--test-threads", + "1", + "-Zunstable-options", + "-q", + ], + ); run_cmd.env("RUSTFLAGS", lint_rust_flags); spawn_and_wait(run_cmd); } else { eprintln!("Cross-Compiling: Not running tests"); let mut build_cmd = - runner.cargo_command(["build", "--tests", "--target", &runner.target_triple]); + runner.cargo_command("build", ["--tests", "--target", &runner.target_triple]); build_cmd.env("RUSTFLAGS", lint_rust_flags.clone()); spawn_and_wait(build_cmd); } @@ -379,11 +371,11 @@ const EXTENDED_SYSROOT_SUITE: &[TestCase] = &[ }), TestCase::new("test.portable-simd", &|runner| { runner.in_dir(["portable-simd"], |runner| { - runner.run_cargo(["clean"]); - runner.run_cargo(["build", "--all-targets", "--target", &runner.target_triple]); + runner.run_cargo("clean", []); + runner.run_cargo("build", ["--all-targets", "--target", &runner.target_triple]); if runner.host_triple == runner.target_triple { - runner.run_cargo(["test", "-q"]); + runner.run_cargo("test", ["-q"]); } }); }), @@ -590,25 +582,29 @@ impl TestRunner { spawn_and_wait(cmd); } - fn cargo_command(&self, args: I) -> Command + fn cargo_command<'a, I>(&self, subcommand: &str, args: I) -> Command where - I: IntoIterator, - S: AsRef, + I: IntoIterator, { let mut cargo_clif = self.root_dir.clone(); cargo_clif.push("build"); cargo_clif.push(get_wrapper_file_name("cargo-clif", "bin")); - let mut cmd = Command::new(cargo_clif); + let mut cmd = cargo_command( + cargo_clif, + subcommand, + if subcommand == "clean" { None } else { Some(&self.target_triple) }, + Path::new("."), + ); cmd.args(args); cmd.env("RUSTFLAGS", &self.rust_flags); cmd } - fn run_cargo<'a, I>(&self, args: I) + fn run_cargo<'a, I>(&self, subcommand: &str, args: I) where I: IntoIterator, { - spawn_and_wait(self.cargo_command(args)); + spawn_and_wait(self.cargo_command(subcommand, args)); } } diff --git a/build_system/utils.rs b/build_system/utils.rs index bdf8f8ecd997..4015a2beaba2 100644 --- a/build_system/utils.rs +++ b/build_system/utils.rs @@ -4,6 +4,26 @@ use std::io::Write; use std::path::Path; use std::process::{self, Command, Stdio}; +pub(crate) fn cargo_command( + cargo: impl AsRef, + subcommand: &str, + triple: Option<&str>, + source_dir: &Path, +) -> Command { + let mut cmd = Command::new(cargo.as_ref()); + cmd.arg(subcommand) + .arg("--manifest-path") + .arg(source_dir.join("Cargo.toml")) + .arg("--target-dir") + .arg(source_dir.join("target")); + + if let Some(triple) = triple { + cmd.arg("--target").arg(triple); + } + + cmd +} + #[track_caller] pub(crate) fn try_hard_link(src: impl AsRef, dst: impl AsRef) { let src = src.as_ref(); From a65c881aa3c3c065277375c592bea539ebaa3d4d Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun, 28 Aug 2022 17:41:36 +0000 Subject: [PATCH 049/309] Introduce hyperfine_command helper --- build_system/tests.rs | 33 +++++++++++++++------------------ build_system/utils.rs | 26 ++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 18 deletions(-) diff --git a/build_system/tests.rs b/build_system/tests.rs index 3f0d461fbe6c..74042bc92183 100644 --- a/build_system/tests.rs +++ b/build_system/tests.rs @@ -1,7 +1,7 @@ use super::build_sysroot; use super::config; use super::rustc_info::get_wrapper_file_name; -use super::utils::{cargo_command, spawn_and_wait, spawn_and_wait_with_input}; +use super::utils::{cargo_command, hyperfine_command, spawn_and_wait, spawn_and_wait_with_input}; use build_system::SysrootKind; use std::env; use std::ffi::OsStr; @@ -231,28 +231,23 @@ const EXTENDED_SYSROOT_SUITE: &[TestCase] = &[ }), TestCase::new("bench.simple-raytracer", &|runner| { runner.in_dir(["simple-raytracer"], |runner| { - let run_runs = env::var("RUN_RUNS").unwrap_or("10".to_string()); + let run_runs = env::var("RUN_RUNS").unwrap_or("10".to_string()).parse().unwrap(); if runner.host_triple == runner.target_triple { eprintln!("[BENCH COMPILE] ebobby/simple-raytracer"); - let mut bench_compile = Command::new("hyperfine"); - bench_compile.arg("--runs"); - bench_compile.arg(&run_runs); - bench_compile.arg("--warmup"); - bench_compile.arg("1"); - bench_compile.arg("--prepare"); - bench_compile.arg(format!("{:?}", runner.cargo_command("clean", []))); + let prepare = runner.cargo_command("clean", []); - bench_compile.arg("cargo build"); + let llvm_build_cmd = cargo_command("cargo", "build", None, Path::new(".")); let cargo_clif = runner .root_dir .clone() .join("build") .join(get_wrapper_file_name("cargo-clif", "bin")); - let mut clif_build_cmd = cargo_command(cargo_clif, "build", None, Path::new(".")); - clif_build_cmd.env("RUSTFLAGS", &runner.rust_flags); - bench_compile.arg(format!("{:?}", clif_build_cmd)); + let clif_build_cmd = cargo_command(cargo_clif, "build", None, Path::new(".")); + + let bench_compile = + hyperfine_command(1, run_runs, Some(prepare), llvm_build_cmd, clif_build_cmd); spawn_and_wait(bench_compile); @@ -260,11 +255,13 @@ const EXTENDED_SYSROOT_SUITE: &[TestCase] = &[ fs::copy(PathBuf::from("./target/debug/main"), PathBuf::from("raytracer_cg_clif")) .unwrap(); - let mut bench_run = Command::new("hyperfine"); - bench_run.arg("--runs"); - bench_run.arg(&run_runs); - bench_run.arg(PathBuf::from("./raytracer_cg_llvm")); - bench_run.arg(PathBuf::from("./raytracer_cg_clif")); + let bench_run = hyperfine_command( + 0, + run_runs, + None, + Command::new("./raytracer_cg_llvm"), + Command::new("./raytracer_cg_clif"), + ); spawn_and_wait(bench_run); } else { runner.run_cargo("clean", []); diff --git a/build_system/utils.rs b/build_system/utils.rs index 4015a2beaba2..48da64906e2a 100644 --- a/build_system/utils.rs +++ b/build_system/utils.rs @@ -24,6 +24,32 @@ pub(crate) fn cargo_command( cmd } +pub(crate) fn hyperfine_command( + warmup: u64, + runs: u64, + prepare: Option, + a: Command, + b: Command, +) -> Command { + let mut bench = Command::new("hyperfine"); + + if warmup != 0 { + bench.arg("--warmup").arg(warmup.to_string()); + } + + if runs != 0 { + bench.arg("--runs").arg(runs.to_string()); + } + + if let Some(prepare) = prepare { + bench.arg("--prepare").arg(format!("{:?}", prepare)); + } + + bench.arg(format!("{:?}", a)).arg(format!("{:?}", b)); + + bench +} + #[track_caller] pub(crate) fn try_hard_link(src: impl AsRef, dst: impl AsRef) { let src = src.as_ref(); From 02219239525517322c6e90339db2c8ca234f097f Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Tue, 30 Aug 2022 12:39:28 -0700 Subject: [PATCH 050/309] Make x.py check work --- src/base.rs | 4 ++++ src/value_and_place.rs | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/base.rs b/src/base.rs index 2aa11ac2eeaa..399474d79e3b 100644 --- a/src/base.rs +++ b/src/base.rs @@ -701,6 +701,10 @@ fn codegen_stmt<'tcx>( let operand = codegen_operand(fx, operand); operand.unsize_value(fx, lval); } + Rvalue::Cast(CastKind::DynStar, _, _) => { + // FIXME(dyn-star) + unimplemented!() + } Rvalue::Discriminant(place) => { let place = codegen_place(fx, place); let value = place.to_cvalue(fx); diff --git a/src/value_and_place.rs b/src/value_and_place.rs index 2ee98546c992..d58b52851ac0 100644 --- a/src/value_and_place.rs +++ b/src/value_and_place.rs @@ -815,7 +815,7 @@ pub(crate) fn assert_assignable<'tcx>( ); // fn(&T) -> for<'l> fn(&'l T) is allowed } - (&ty::Dynamic(from_traits, _), &ty::Dynamic(to_traits, _)) => { + (&ty::Dynamic(from_traits, _, _from_kind), &ty::Dynamic(to_traits, _, _to_kind)) => { for (from, to) in from_traits.iter().zip(to_traits) { let from = fx.tcx.normalize_erasing_late_bound_regions(ParamEnv::reveal_all(), from); From da373e3ea83186c93f2902efac445a155807d5e6 Mon Sep 17 00:00:00 2001 From: b-naber Date: Mon, 27 Jun 2022 16:32:47 +0200 Subject: [PATCH 051/309] use ty::Unevaluated<'tcx, ()> in type system --- src/constant.rs | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/constant.rs b/src/constant.rs index 0305341da784..7bf578b6a4e6 100644 --- a/src/constant.rs +++ b/src/constant.rs @@ -124,6 +124,22 @@ pub(crate) fn codegen_constant<'tcx>( ) -> CValue<'tcx> { let const_ = match fx.monomorphize(constant.literal) { ConstantKind::Ty(ct) => ct, + ConstantKind::Unevaluated(mir::Unevaluated { def, substs, promoted }) + if fx.tcx.is_static(def.did) => + { + assert!(substs.is_empty()); + assert!(promoted.is_none()); + + return codegen_static_ref(fx, def.did, fx.layout_of(const_.ty())).to_cvalue(fx); + } + ConstantKind::Unevaluated(unevaluated) => { + match fx.tcx.const_eval_resolve(ParamEnv::reveal_all(), unevaluated, None) { + Ok(const_val) => const_val, + Err(_) => { + span_bug!(constant.span, "erroneous constant not captured by required_consts"); + } + } + } ConstantKind::Val(val, ty) => return codegen_const_value(fx, val, ty), }; let const_val = match const_.kind() { @@ -133,17 +149,8 @@ pub(crate) fn codegen_constant<'tcx>( { assert!(substs.is_empty()); assert!(promoted.is_none()); - return codegen_static_ref(fx, def.did, fx.layout_of(const_.ty())).to_cvalue(fx); } - ConstKind::Unevaluated(unevaluated) => { - match fx.tcx.const_eval_resolve(ParamEnv::reveal_all(), unevaluated, None) { - Ok(const_val) => const_val, - Err(_) => { - span_bug!(constant.span, "erroneous constant not captured by required_consts"); - } - } - } ConstKind::Param(_) | ConstKind::Infer(_) | ConstKind::Bound(_, _) From fa35afe8dca1e73169246101cba56b933a7ea132 Mon Sep 17 00:00:00 2001 From: b-naber Date: Thu, 30 Jun 2022 16:54:10 +0200 Subject: [PATCH 052/309] cranelift changes --- src/constant.rs | 108 ++++++++++++++++++++++++++---------------------- 1 file changed, 59 insertions(+), 49 deletions(-) diff --git a/src/constant.rs b/src/constant.rs index 7bf578b6a4e6..a04b38ae33f3 100644 --- a/src/constant.rs +++ b/src/constant.rs @@ -41,36 +41,30 @@ impl ConstantCx { pub(crate) fn check_constants(fx: &mut FunctionCx<'_, '_, '_>) -> bool { let mut all_constants_ok = true; for constant in &fx.mir.required_consts { - let const_ = match fx.monomorphize(constant.literal) { - ConstantKind::Ty(ct) => ct, + let unevaluated = match fx.monomorphize(constant.literal) { + ConstantKind::Ty(ct) => match ct.kind() { + ConstKind::Unevaluated(uv) => uv.expand(), + ConstKind::Value(_) => continue, + ConstKind::Param(_) + | ConstKind::Infer(_) + | ConstKind::Bound(_, _) + | ConstKind::Placeholder(_) + | ConstKind::Error(_) => unreachable!("{:?}", ct), + }, + ConstantKind::Unevaluated(uv, _) => uv, ConstantKind::Val(..) => continue, }; - match const_.kind() { - ConstKind::Value(_) => {} - ConstKind::Unevaluated(unevaluated) => { - if let Err(err) = - fx.tcx.const_eval_resolve(ParamEnv::reveal_all(), unevaluated, None) - { - all_constants_ok = false; - match err { - ErrorHandled::Reported(_) | ErrorHandled::Linted => { - fx.tcx.sess.span_err(constant.span, "erroneous constant encountered"); - } - ErrorHandled::TooGeneric => { - span_bug!( - constant.span, - "codegen encountered polymorphic constant: {:?}", - err - ); - } - } + + if let Err(err) = fx.tcx.const_eval_resolve(ParamEnv::reveal_all(), unevaluated, None) { + all_constants_ok = false; + match err { + ErrorHandled::Reported(_) | ErrorHandled::Linted => { + fx.tcx.sess.span_err(constant.span, "erroneous constant encountered"); + } + ErrorHandled::TooGeneric => { + span_bug!(constant.span, "codegen encountered polymorphic constant: {:?}", err); } } - ConstKind::Param(_) - | ConstKind::Infer(_) - | ConstKind::Bound(_, _) - | ConstKind::Placeholder(_) - | ConstKind::Error(_) => unreachable!("{:?}", const_), } } all_constants_ok @@ -122,43 +116,56 @@ pub(crate) fn codegen_constant<'tcx>( fx: &mut FunctionCx<'_, '_, 'tcx>, constant: &Constant<'tcx>, ) -> CValue<'tcx> { - let const_ = match fx.monomorphize(constant.literal) { - ConstantKind::Ty(ct) => ct, - ConstantKind::Unevaluated(mir::Unevaluated { def, substs, promoted }) + let (const_val, ty) = match fx.monomorphize(constant.literal) { + ConstantKind::Ty(const_) => match const_.kind() { + ConstKind::Value(valtree) => { + (fx.tcx.valtree_to_const_val((const_.ty(), valtree)), const_.ty()) + } + ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted }) + if fx.tcx.is_static(def.did) => + { + assert!(substs.is_empty()); + assert_eq!(promoted, ()); + return codegen_static_ref(fx, def.did, fx.layout_of(const_.ty())).to_cvalue(fx); + } + ConstKind::Unevaluated(unevaluated) => { + match fx.tcx.const_eval_resolve(ParamEnv::reveal_all(), unevaluated.expand(), None) + { + Ok(const_val) => (const_val, const_.ty()), + Err(_) => { + span_bug!( + constant.span, + "erroneous constant not captured by required_consts" + ); + } + } + } + ConstKind::Param(_) + | ConstKind::Infer(_) + | ConstKind::Bound(_, _) + | ConstKind::Placeholder(_) + | ConstKind::Error(_) => unreachable!("{:?}", const_), + }, + ConstantKind::Unevaluated(ty::Unevaluated { def, substs, promoted }, ty) if fx.tcx.is_static(def.did) => { assert!(substs.is_empty()); assert!(promoted.is_none()); - return codegen_static_ref(fx, def.did, fx.layout_of(const_.ty())).to_cvalue(fx); + return codegen_static_ref(fx, def.did, fx.layout_of(ty)).to_cvalue(fx); } - ConstantKind::Unevaluated(unevaluated) => { + ConstantKind::Unevaluated(unevaluated, ty) => { match fx.tcx.const_eval_resolve(ParamEnv::reveal_all(), unevaluated, None) { - Ok(const_val) => const_val, + Ok(const_val) => (const_val, ty), Err(_) => { span_bug!(constant.span, "erroneous constant not captured by required_consts"); } } } - ConstantKind::Val(val, ty) => return codegen_const_value(fx, val, ty), - }; - let const_val = match const_.kind() { - ConstKind::Value(valtree) => fx.tcx.valtree_to_const_val((const_.ty(), valtree)), - ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted }) - if fx.tcx.is_static(def.did) => - { - assert!(substs.is_empty()); - assert!(promoted.is_none()); - return codegen_static_ref(fx, def.did, fx.layout_of(const_.ty())).to_cvalue(fx); - } - ConstKind::Param(_) - | ConstKind::Infer(_) - | ConstKind::Bound(_, _) - | ConstKind::Placeholder(_) - | ConstKind::Error(_) => unreachable!("{:?}", const_), + ConstantKind::Val(val, ty) => (val, ty), }; - codegen_const_value(fx, const_val, const_.ty()) + codegen_const_value(fx, const_val, ty) } pub(crate) fn codegen_const_value<'tcx>( @@ -503,6 +510,9 @@ pub(crate) fn mir_operand_get_const_val<'tcx>( .eval_for_mir(fx.tcx, ParamEnv::reveal_all()) .try_to_value(fx.tcx), ConstantKind::Val(val, _) => Some(val), + ConstantKind::Unevaluated(uv, _) => { + fx.tcx.const_eval_resolve(ParamEnv::reveal_all(), uv, None).ok() + } }, // FIXME(rust-lang/rust#85105): Casts like `IMM8 as u32` result in the const being stored // inside a temporary before being passed to the intrinsic requiring the const argument. From 6d544a8a4c9ab5cd4b228532e2501980881801b7 Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Tue, 30 Aug 2022 12:44:00 -0700 Subject: [PATCH 053/309] Address code review comments --- src/value_and_place.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/value_and_place.rs b/src/value_and_place.rs index d58b52851ac0..cfaadca94910 100644 --- a/src/value_and_place.rs +++ b/src/value_and_place.rs @@ -816,6 +816,7 @@ pub(crate) fn assert_assignable<'tcx>( // fn(&T) -> for<'l> fn(&'l T) is allowed } (&ty::Dynamic(from_traits, _, _from_kind), &ty::Dynamic(to_traits, _, _to_kind)) => { + // FIXME(dyn-star): Do the right thing with DynKinds for (from, to) in from_traits.iter().zip(to_traits) { let from = fx.tcx.normalize_erasing_late_bound_regions(ParamEnv::reveal_all(), from); From a10dd1f340c425660557c042836da9e1e52e7453 Mon Sep 17 00:00:00 2001 From: b-naber Date: Wed, 14 Sep 2022 15:35:24 +0200 Subject: [PATCH 054/309] address review again --- src/constant.rs | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/src/constant.rs b/src/constant.rs index a04b38ae33f3..bc34802fa728 100644 --- a/src/constant.rs +++ b/src/constant.rs @@ -121,25 +121,7 @@ pub(crate) fn codegen_constant<'tcx>( ConstKind::Value(valtree) => { (fx.tcx.valtree_to_const_val((const_.ty(), valtree)), const_.ty()) } - ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted }) - if fx.tcx.is_static(def.did) => - { - assert!(substs.is_empty()); - assert_eq!(promoted, ()); - return codegen_static_ref(fx, def.did, fx.layout_of(const_.ty())).to_cvalue(fx); - } - ConstKind::Unevaluated(unevaluated) => { - match fx.tcx.const_eval_resolve(ParamEnv::reveal_all(), unevaluated.expand(), None) - { - Ok(const_val) => (const_val, const_.ty()), - Err(_) => { - span_bug!( - constant.span, - "erroneous constant not captured by required_consts" - ); - } - } - } + ConstKind::Unevaluated(_) => bug!("expected constant to be evaluated at this stage"), ConstKind::Param(_) | ConstKind::Infer(_) | ConstKind::Bound(_, _) From f68742a03cf460d8e319830acc6f371238d6bf13 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 15 Sep 2022 14:59:14 +0200 Subject: [PATCH 055/309] Rustup to rustc 1.65.0-nightly (750bd1a7f 2022-09-14) --- build_sysroot/Cargo.lock | 4 ++-- rust-toolchain | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build_sysroot/Cargo.lock b/build_sysroot/Cargo.lock index 17338aeaadaa..641891a95c4b 100644 --- a/build_sysroot/Cargo.lock +++ b/build_sysroot/Cargo.lock @@ -301,9 +301,9 @@ dependencies = [ [[package]] name = "unicode-width" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" dependencies = [ "compiler_builtins", "rustc-std-workspace-core", diff --git a/rust-toolchain b/rust-toolchain index 4a228e3f8a87..ba03a47fac2e 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2022-09-12" +channel = "nightly-2022-09-15" components = ["rust-src", "rustc-dev", "llvm-tools-preview"] From 879c86ff3088c70725dcbeed0430e497f970b8bd Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 15 Sep 2022 14:08:30 +0000 Subject: [PATCH 056/309] Implement dyn* support --- src/abi/mod.rs | 54 ++++++++++++++++++++++++++++++++++++++++-- src/base.rs | 6 ++--- src/unsize.rs | 16 +++++++++++++ src/value_and_place.rs | 4 ++++ src/vtable.rs | 26 +++++++++++++++----- 5 files changed, 95 insertions(+), 11 deletions(-) diff --git a/src/abi/mod.rs b/src/abi/mod.rs index 0497c2570e62..04e39954dd3c 100644 --- a/src/abi/mod.rs +++ b/src/abi/mod.rs @@ -465,7 +465,7 @@ pub(crate) fn codegen_terminator_call<'tcx>( let sig = clif_sig_from_fn_abi(fx.tcx, fx.target_config.default_call_conv, &fn_abi); let sig = fx.bcx.import_signature(sig); - (CallTarget::Indirect(sig, method), Some(ptr)) + (CallTarget::Indirect(sig, method), Some(ptr.get_addr(fx))) } // Normal call @@ -560,7 +560,19 @@ pub(crate) fn codegen_drop<'tcx>( // we don't actually need to drop anything } else { match ty.kind() { - ty::Dynamic(..) => { + ty::Dynamic(_, _, ty::Dyn) => { + // IN THIS ARM, WE HAVE: + // ty = *mut (dyn Trait) + // which is: exists ( *mut T, Vtable ) + // args[0] args[1] + // + // args = ( Data, Vtable ) + // | + // v + // /-------\ + // | ... | + // \-------/ + // let (ptr, vtable) = drop_place.to_ptr_maybe_unsized(); let ptr = ptr.get_addr(fx); let drop_fn = crate::vtable::drop_fn_of_obj(fx, vtable.unwrap()); @@ -578,6 +590,44 @@ pub(crate) fn codegen_drop<'tcx>( let sig = fx.bcx.import_signature(sig); fx.bcx.ins().call_indirect(sig, drop_fn, &[ptr]); } + ty::Dynamic(_, _, ty::DynStar) => { + // IN THIS ARM, WE HAVE: + // ty = *mut (dyn* Trait) + // which is: *mut exists (T, Vtable) + // + // args = [ * ] + // | + // v + // ( Data, Vtable ) + // | + // v + // /-------\ + // | ... | + // \-------/ + // + // + // WE CAN CONVERT THIS INTO THE ABOVE LOGIC BY DOING + // + // data = &(*args[0]).0 // gives a pointer to Data above (really the same pointer) + // vtable = (*args[0]).1 // loads the vtable out + // (data, vtable) // an equivalent Rust `*mut dyn Trait` + // + // SO THEN WE CAN USE THE ABOVE CODE. + let dyn_star = drop_place.to_cvalue(fx); + let (data, vtable) = dyn_star.load_scalar_pair(fx); + let drop_fn = crate::vtable::drop_fn_of_obj(fx, vtable); + + let virtual_drop = Instance { + def: ty::InstanceDef::Virtual(drop_instance.def_id(), 0), + substs: drop_instance.substs, + }; + let fn_abi = + RevealAllLayoutCx(fx.tcx).fn_abi_of_instance(virtual_drop, ty::List::empty()); + + let sig = clif_sig_from_fn_abi(fx.tcx, fx.target_config.default_call_conv, &fn_abi); + let sig = fx.bcx.import_signature(sig); + fx.bcx.ins().call_indirect(sig, drop_fn, &[data]); + } _ => { assert!(!matches!(drop_instance.def, InstanceDef::Virtual(_, _))); diff --git a/src/base.rs b/src/base.rs index 399474d79e3b..317aaa21c8e5 100644 --- a/src/base.rs +++ b/src/base.rs @@ -701,9 +701,9 @@ fn codegen_stmt<'tcx>( let operand = codegen_operand(fx, operand); operand.unsize_value(fx, lval); } - Rvalue::Cast(CastKind::DynStar, _, _) => { - // FIXME(dyn-star) - unimplemented!() + Rvalue::Cast(CastKind::DynStar, ref operand, _) => { + let operand = codegen_operand(fx, operand); + operand.coerce_dyn_star(fx, lval); } Rvalue::Discriminant(place) => { let place = codegen_place(fx, place); diff --git a/src/unsize.rs b/src/unsize.rs index dd9d891ddbde..b194a7c8b0dd 100644 --- a/src/unsize.rs +++ b/src/unsize.rs @@ -147,6 +147,22 @@ pub(crate) fn coerce_unsized_into<'tcx>( } } +pub(crate) fn coerce_dyn_star<'tcx>( + fx: &mut FunctionCx<'_, '_, 'tcx>, + src: CValue<'tcx>, + dst: CPlace<'tcx>, +) { + let data = src.load_scalar(fx); + + let vtable = if let ty::Dynamic(data, _, ty::DynStar) = dst.layout().ty.kind() { + crate::vtable::get_vtable(fx, src.layout().ty, data.principal()) + } else { + bug!("Only valid to do a DynStar cast into a DynStar type") + }; + + dst.write_cvalue(fx, CValue::by_val_pair(data, vtable, dst.layout())); +} + // Adapted from https://github.com/rust-lang/rust/blob/2a663555ddf36f6b041445894a8c175cd1bc718c/src/librustc_codegen_ssa/glue.rs pub(crate) fn size_and_align_of_dst<'tcx>( diff --git a/src/value_and_place.rs b/src/value_and_place.rs index cfaadca94910..91fb421a9ee1 100644 --- a/src/value_and_place.rs +++ b/src/value_and_place.rs @@ -236,6 +236,10 @@ impl<'tcx> CValue<'tcx> { crate::unsize::coerce_unsized_into(fx, self, dest); } + pub(crate) fn coerce_dyn_star(self, fx: &mut FunctionCx<'_, '_, 'tcx>, dest: CPlace<'tcx>) { + crate::unsize::coerce_dyn_star(fx, self, dest); + } + /// If `ty` is signed, `const_val` must already be sign extended. pub(crate) fn const_val( fx: &mut FunctionCx<'_, '_, 'tcx>, diff --git a/src/vtable.rs b/src/vtable.rs index 36b3725ef42b..f04fb82de8c8 100644 --- a/src/vtable.rs +++ b/src/vtable.rs @@ -45,12 +45,26 @@ pub(crate) fn get_ptr_and_method_ref<'tcx>( fx: &mut FunctionCx<'_, '_, 'tcx>, arg: CValue<'tcx>, idx: usize, -) -> (Value, Value) { - let (ptr, vtable) = if let Abi::ScalarPair(_, _) = arg.layout().abi { - arg.load_scalar_pair(fx) - } else { - let (ptr, vtable) = arg.try_to_ptr().unwrap(); - (ptr.get_addr(fx), vtable.unwrap()) +) -> (Pointer, Value) { + let (ptr, vtable) = 'block: { + if let ty::Ref(_, ty, _) = arg.layout().ty.kind() { + if ty.is_dyn_star() { + let inner_layout = fx.layout_of(arg.layout().ty.builtin_deref(true).unwrap().ty); + let dyn_star = CPlace::for_ptr(Pointer::new(arg.load_scalar(fx)), inner_layout); + let ptr = dyn_star.place_field(fx, mir::Field::new(0)).to_ptr(); + let vtable = + dyn_star.place_field(fx, mir::Field::new(1)).to_cvalue(fx).load_scalar(fx); + break 'block (ptr, vtable); + } + } + + if let Abi::ScalarPair(_, _) = arg.layout().abi { + let (ptr, vtable) = arg.load_scalar_pair(fx); + (Pointer::new(ptr), vtable) + } else { + let (ptr, vtable) = arg.try_to_ptr().unwrap(); + (ptr, vtable.unwrap()) + } }; let usize_size = fx.layout_of(fx.tcx.types.usize).size.bytes(); From b023e2f7d04d6f7707e4dd05ab133e56e9f4ff51 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 15 Sep 2022 16:05:27 +0000 Subject: [PATCH 057/309] Fix bundled static libraries --- src/archive.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/archive.rs b/src/archive.rs index b4c790961707..31d3d0e06156 100644 --- a/src/archive.rs +++ b/src/archive.rs @@ -159,6 +159,8 @@ impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> { let err = err.to_string(); if err == "Unknown file magic" { // Not an object file; skip it. + } else if object::read::archive::ArchiveFile::parse(&*data).is_ok() { + // Nested archive file; skip it. } else { sess.fatal(&format!( "error parsing `{}` during archive creation: {}", From d82b696594c39d5f463e4560b68d7a2d4e33b2f0 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 15 Sep 2022 16:14:29 +0000 Subject: [PATCH 058/309] Update for latests rustc test suite changes --- scripts/setup_rust_fork.sh | 4 ++++ scripts/test_rustc_tests.sh | 2 ++ 2 files changed, 6 insertions(+) diff --git a/scripts/setup_rust_fork.sh b/scripts/setup_rust_fork.sh index 6ae8b14f4c01..d6a37789599f 100644 --- a/scripts/setup_rust_fork.sh +++ b/scripts/setup_rust_fork.sh @@ -68,3 +68,7 @@ popd # FIXME remove once inline asm is fully supported export RUSTFLAGS="$RUSTFLAGS --cfg=rustix_use_libc" + +# Allow the testsuite to use llvm tools +host_triple=$(rustc -vV | grep host | cut -d: -f2 | tr -d " ") +export LLVM_BIN_DIR="$(rustc --print sysroot)/lib/rustlib/$host_triple/bin" diff --git a/scripts/test_rustc_tests.sh b/scripts/test_rustc_tests.sh index 944787612d8b..ace0cd76d81a 100755 --- a/scripts/test_rustc_tests.sh +++ b/scripts/test_rustc_tests.sh @@ -116,6 +116,8 @@ rm src/test/ui/test-attrs/test-type.rs # TODO panic message on stderr. correct s # not sure if this is actually a bug in the test suite, but the symbol list shows the function without leading _ for some reason rm -r src/test/run-make/native-link-modifier-bundle +rm src/test/ui/stdio-is-blocking.rs # really slow with unoptimized libstd + echo "[TEST] rustc test suite" RUST_TEST_NOCAPTURE=1 COMPILETEST_FORCE_STAGE0=1 ./x.py test --stage 0 src/test/{codegen-units,run-make,run-pass-valgrind,ui,incremental} popd From d7c77313cb108b8a8fbeb61dffb74adcc36a82ca Mon Sep 17 00:00:00 2001 From: b-naber Date: Thu, 15 Sep 2022 22:27:41 +0200 Subject: [PATCH 059/309] nits --- src/constant.rs | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/src/constant.rs b/src/constant.rs index bc34802fa728..6b4ed9b9d405 100644 --- a/src/constant.rs +++ b/src/constant.rs @@ -117,17 +117,7 @@ pub(crate) fn codegen_constant<'tcx>( constant: &Constant<'tcx>, ) -> CValue<'tcx> { let (const_val, ty) = match fx.monomorphize(constant.literal) { - ConstantKind::Ty(const_) => match const_.kind() { - ConstKind::Value(valtree) => { - (fx.tcx.valtree_to_const_val((const_.ty(), valtree)), const_.ty()) - } - ConstKind::Unevaluated(_) => bug!("expected constant to be evaluated at this stage"), - ConstKind::Param(_) - | ConstKind::Infer(_) - | ConstKind::Bound(_, _) - | ConstKind::Placeholder(_) - | ConstKind::Error(_) => unreachable!("{:?}", const_), - }, + ConstantKind::Ty(const_) => unreachable!("{:?}", const_), ConstantKind::Unevaluated(ty::Unevaluated { def, substs, promoted }, ty) if fx.tcx.is_static(def.did) => { From 93af5f599922bd14c2156b26b277fa642cbceac5 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 27 Jul 2022 11:58:34 +0000 Subject: [PATCH 060/309] Revert "Revert "Rollup merge of #98582 - oli-obk:unconstrained_opaque_type, r=estebank"" This reverts commit 4a742a691e7dd2522bad68b86fe2fd5a199d5561. --- src/base.rs | 1 + src/value_and_place.rs | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/src/base.rs b/src/base.rs index 399474d79e3b..11540d800816 100644 --- a/src/base.rs +++ b/src/base.rs @@ -850,6 +850,7 @@ pub(crate) fn codegen_place<'tcx>( PlaceElem::Deref => { cplace = cplace.place_deref(fx); } + PlaceElem::OpaqueCast(ty) => cplace = cplace.place_opaque_cast(fx, ty), PlaceElem::Field(field, _ty) => { cplace = cplace.place_field(fx, field); } diff --git a/src/value_and_place.rs b/src/value_and_place.rs index cfaadca94910..3fa3e3657cb6 100644 --- a/src/value_and_place.rs +++ b/src/value_and_place.rs @@ -621,6 +621,14 @@ impl<'tcx> CPlace<'tcx> { } } + pub(crate) fn place_opaque_cast( + self, + fx: &mut FunctionCx<'_, '_, 'tcx>, + ty: Ty<'tcx>, + ) -> CPlace<'tcx> { + CPlace { inner: self.inner, layout: fx.layout_of(ty) } + } + pub(crate) fn place_field( self, fx: &mut FunctionCx<'_, '_, 'tcx>, From 908bdea178065aceb0dd46f91c93d6c347b49ab4 Mon Sep 17 00:00:00 2001 From: Tim Hutt Date: Sat, 10 Sep 2022 12:02:54 +0100 Subject: [PATCH 061/309] Document surprising and dangerous fs::Permissions behaviour on Unix This documents the very surprising behaviour that `set_readonly(false)` will make a file *world writable* on Unix. I would go so far as to say that this function should be deprecated on Unix, or maybe even entirely. But documenting the bad behaviour is a good first step. --- library/std/src/fs.rs | 67 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 3 deletions(-) diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index c6c78dc3939e..188ff00e1f8d 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -1365,6 +1365,34 @@ impl FileTimes { impl Permissions { /// Returns `true` if these permissions describe a readonly (unwritable) file. /// + /// # Note + /// + /// This function does not take Access Control Lists (ACLs) or Unix group + /// membership into account. + /// + /// # Windows + /// + /// On Windows this returns [`FILE_ATTRIBUTE_READONLY`](https://docs.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants). + /// If `FILE_ATTRIBUTE_READONLY` is set then writes to the file will fail + /// but the user may still have permission to change this flag. If + /// `FILE_ATTRIBUTE_READONLY` is *not* set then writes may still fail due + /// to lack of write permission. + /// The behavior of this attribute for directories depends on the Windows + /// version. + /// + /// # Unix (including macOS) + /// + /// On Unix-based platforms this checks if *any* of the owner, group or others + /// write permission bits are set. It does not check if the current + /// user is in the file's assigned group. It also does not check ACLs. + /// Therefore even if this returns true you may not be able to write to the + /// file, and vice versa. The [`PermissionsExt`] trait gives direct access + /// to the permission bits but also does not read ACLs. If you need to + /// accurately know whether or not a file is writable use the `access()` + /// function from libc. + /// + /// [`PermissionsExt`]: crate::os::unix::fs::PermissionsExt + /// /// # Examples /// /// ```no_run @@ -1390,8 +1418,40 @@ impl Permissions { /// using the resulting `Permission` will update file permissions to allow /// writing. /// - /// This operation does **not** modify the filesystem. To modify the - /// filesystem use the [`set_permissions`] function. + /// This operation does **not** modify the files attributes. This only + /// changes the in-memory value of these attributes for this `Permissions` + /// instance. To modify the files attributes use the [`set_permissions`] + /// function which commits these attribute changes to the file. + /// + /// # Note + /// + /// `set_readonly(false)` makes the file *world-writable* on Unix. + /// You can use the [`PermissionsExt`] trait on Unix to avoid this issue. + /// + /// It also does not take Access Control Lists (ACLs) or Unix group + /// membership into account. + /// + /// # Windows + /// + /// On Windows this sets or clears [`FILE_ATTRIBUTE_READONLY`](https://docs.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants). + /// If `FILE_ATTRIBUTE_READONLY` is set then writes to the file will fail + /// but the user may still have permission to change this flag. If + /// `FILE_ATTRIBUTE_READONLY` is *not* set then the write may still fail if + /// the user does not have permission to write to the file. + /// + /// In Windows 7 and earlier this attribute prevents deleting empty + /// directories. It does not prevent modifying the directory contents. + /// On later versions of Windows this attribute is ignored for directories. + /// + /// # Unix (including macOS) + /// + /// On Unix-based platforms this sets or clears the write access bit for + /// the owner, group *and* others, equivalent to `chmod a+w ` + /// or `chmod a-w ` respectively. The latter will grant write access + /// to all users! You can use the [`PermissionsExt`] trait on Unix + /// to avoid this issue. + /// + /// [`PermissionsExt`]: crate::os::unix::fs::PermissionsExt /// /// # Examples /// @@ -1405,7 +1465,8 @@ impl Permissions { /// /// permissions.set_readonly(true); /// - /// // filesystem doesn't change + /// // filesystem doesn't change, only the in memory state of the + /// // readonly permission /// assert_eq!(false, metadata.permissions().readonly()); /// /// // just this particular `permissions`. From 3c5882018f7f686a5a0b3a631cad33503987e5aa Mon Sep 17 00:00:00 2001 From: b-naber Date: Mon, 19 Sep 2022 19:46:53 +0200 Subject: [PATCH 062/309] introduce mir::Unevaluated --- src/constant.rs | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/constant.rs b/src/constant.rs index 6b4ed9b9d405..aac64d854a6b 100644 --- a/src/constant.rs +++ b/src/constant.rs @@ -5,7 +5,6 @@ use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir::interpret::{ read_target_uint, AllocId, ConstAllocation, ConstValue, ErrorHandled, GlobalAlloc, Scalar, }; -use rustc_middle::ty::ConstKind; use rustc_span::DUMMY_SP; use cranelift_codegen::ir::GlobalValueData; @@ -42,15 +41,7 @@ pub(crate) fn check_constants(fx: &mut FunctionCx<'_, '_, '_>) -> bool { let mut all_constants_ok = true; for constant in &fx.mir.required_consts { let unevaluated = match fx.monomorphize(constant.literal) { - ConstantKind::Ty(ct) => match ct.kind() { - ConstKind::Unevaluated(uv) => uv.expand(), - ConstKind::Value(_) => continue, - ConstKind::Param(_) - | ConstKind::Infer(_) - | ConstKind::Bound(_, _) - | ConstKind::Placeholder(_) - | ConstKind::Error(_) => unreachable!("{:?}", ct), - }, + ConstantKind::Ty(_) => unreachable!(), ConstantKind::Unevaluated(uv, _) => uv, ConstantKind::Val(..) => continue, }; @@ -118,7 +109,7 @@ pub(crate) fn codegen_constant<'tcx>( ) -> CValue<'tcx> { let (const_val, ty) = match fx.monomorphize(constant.literal) { ConstantKind::Ty(const_) => unreachable!("{:?}", const_), - ConstantKind::Unevaluated(ty::Unevaluated { def, substs, promoted }, ty) + ConstantKind::Unevaluated(mir::Unevaluated { def, substs, promoted }, ty) if fx.tcx.is_static(def.did) => { assert!(substs.is_empty()); From 7e250da20fd6ac20c554397bcb0e69bc6c235dac Mon Sep 17 00:00:00 2001 From: b-naber Date: Thu, 22 Sep 2022 12:34:23 +0200 Subject: [PATCH 063/309] rename Unevaluated to UnevaluatedConst --- src/constant.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/constant.rs b/src/constant.rs index aac64d854a6b..e12805b093cc 100644 --- a/src/constant.rs +++ b/src/constant.rs @@ -109,7 +109,7 @@ pub(crate) fn codegen_constant<'tcx>( ) -> CValue<'tcx> { let (const_val, ty) = match fx.monomorphize(constant.literal) { ConstantKind::Ty(const_) => unreachable!("{:?}", const_), - ConstantKind::Unevaluated(mir::Unevaluated { def, substs, promoted }, ty) + ConstantKind::Unevaluated(mir::UnevaluatedConst { def, substs, promoted }, ty) if fx.tcx.is_static(def.did) => { assert!(substs.is_empty()); From b3d3ba9928820465d6ca08e1068280006453d291 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun, 25 Sep 2022 14:11:30 +0200 Subject: [PATCH 064/309] Rustup to rustc 1.66.0-nightly (3f83906b3 2022-09-24) --- build_sysroot/Cargo.lock | 32 +++++++++++++++++++++----------- rust-toolchain | 2 +- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/build_sysroot/Cargo.lock b/build_sysroot/Cargo.lock index 641891a95c4b..d5a65c3b7217 100644 --- a/build_sysroot/Cargo.lock +++ b/build_sysroot/Cargo.lock @@ -55,10 +55,20 @@ dependencies = [ ] [[package]] -name = "compiler_builtins" -version = "0.1.79" +name = "cfg-if" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f873ce2bd3550b0b565f878b3d04ea8253f4259dc3d20223af2e1ba86f5ecca" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +dependencies = [ + "compiler_builtins", + "rustc-std-workspace-core", +] + +[[package]] +name = "compiler_builtins" +version = "0.1.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3feb824e4dde07fb32acc53c8cc1f9e537bb4d7e0a331198000840979d410d3" dependencies = [ "rustc-std-workspace-core", ] @@ -135,9 +145,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.132" +version = "0.2.133" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5" +checksum = "c0f80d65747a3e43d1596c7c5492d95d5edddaabd45a7fcdb02b95f644164966" dependencies = [ "rustc-std-workspace-core", ] @@ -182,7 +192,7 @@ name = "panic_abort" version = "0.0.0" dependencies = [ "alloc", - "cfg-if", + "cfg-if 0.1.10", "compiler_builtins", "core", "libc", @@ -193,7 +203,7 @@ name = "panic_unwind" version = "0.0.0" dependencies = [ "alloc", - "cfg-if", + "cfg-if 0.1.10", "compiler_builtins", "core", "libc", @@ -245,7 +255,7 @@ version = "0.0.0" dependencies = [ "addr2line", "alloc", - "cfg-if", + "cfg-if 1.0.0", "compiler_builtins", "core", "dlmalloc", @@ -267,7 +277,7 @@ dependencies = [ name = "std_detect" version = "0.1.5" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "compiler_builtins", "libc", "rustc-std-workspace-alloc", @@ -289,7 +299,7 @@ dependencies = [ name = "test" version = "0.0.0" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", "core", "getopts", "libc", @@ -315,7 +325,7 @@ name = "unwind" version = "0.0.0" dependencies = [ "cc", - "cfg-if", + "cfg-if 0.1.10", "compiler_builtins", "core", "libc", diff --git a/rust-toolchain b/rust-toolchain index ba03a47fac2e..06e9106e685a 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2022-09-15" +channel = "nightly-2022-09-25" components = ["rust-src", "rustc-dev", "llvm-tools-preview"] From 5ed4377677f071489ad24860ac2c6c3fac324921 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun, 25 Sep 2022 14:35:21 +0200 Subject: [PATCH 065/309] Fix ConstantKind::Ty codegen --- src/base.rs | 2 +- src/constant.rs | 74 ++++++++++++++++++++++--------------------------- 2 files changed, 34 insertions(+), 42 deletions(-) diff --git a/src/base.rs b/src/base.rs index de74a91dd8fb..1aeb9ff25703 100644 --- a/src/base.rs +++ b/src/base.rs @@ -917,7 +917,7 @@ pub(crate) fn codegen_operand<'tcx>( let cplace = codegen_place(fx, *place); cplace.to_cvalue(fx) } - Operand::Constant(const_) => crate::constant::codegen_constant(fx, const_), + Operand::Constant(const_) => crate::constant::codegen_constant_operand(fx, const_), } } diff --git a/src/constant.rs b/src/constant.rs index 9fbb08a9cb99..d1610b4005fa 100644 --- a/src/constant.rs +++ b/src/constant.rs @@ -7,7 +7,6 @@ use rustc_middle::mir::interpret::{ }; use rustc_span::DUMMY_SP; -use cranelift_codegen::ir::GlobalValueData; use cranelift_module::*; use crate::prelude::*; @@ -81,52 +80,45 @@ pub(crate) fn codegen_tls_ref<'tcx>( CValue::by_val(tls_ptr, layout) } -fn codegen_static_ref<'tcx>( +pub(crate) fn eval_mir_constant<'tcx>( fx: &mut FunctionCx<'_, '_, 'tcx>, - def_id: DefId, - layout: TyAndLayout<'tcx>, -) -> CPlace<'tcx> { - let data_id = data_id_for_static(fx.tcx, fx.module, def_id, false); - let local_data_id = fx.module.declare_data_in_func(data_id, &mut fx.bcx.func); - if fx.clif_comments.enabled() { - fx.add_comment(local_data_id, format!("{:?}", def_id)); - } - let global_ptr = fx.bcx.ins().global_value(fx.pointer_type, local_data_id); - assert!(!layout.is_unsized(), "unsized statics aren't supported"); - assert!( - matches!( - fx.bcx.func.global_values[local_data_id], - GlobalValueData::Symbol { tls: false, .. } - ), - "tls static referenced without Rvalue::ThreadLocalRef" - ); - CPlace::for_ptr(crate::pointer::Pointer::new(global_ptr), layout) + constant: &Constant<'tcx>, +) -> (ConstValue<'tcx>, Ty<'tcx>) { + let constant_kind = fx.monomorphize(constant.literal); + let uv = match constant_kind { + ConstantKind::Ty(const_) => match const_.kind() { + ty::ConstKind::Unevaluated(uv) => uv.expand(), + ty::ConstKind::Value(val) => { + return (fx.tcx.valtree_to_const_val((const_.ty(), val)), const_.ty()); + } + err => span_bug!( + constant.span, + "encountered bad ConstKind after monomorphizing: {:?}", + err + ), + }, + ConstantKind::Unevaluated(mir::UnevaluatedConst { def, .. }, _) + if fx.tcx.is_static(def.did) => + { + span_bug!(constant.span, "MIR constant refers to static"); + } + ConstantKind::Unevaluated(uv, _) => uv, + ConstantKind::Val(val, _) => return (val, constant_kind.ty()), + }; + + ( + fx.tcx.const_eval_resolve(ty::ParamEnv::reveal_all(), uv, None).unwrap_or_else(|_err| { + span_bug!(constant.span, "erroneous constant not captured by required_consts"); + }), + constant_kind.ty(), + ) } -pub(crate) fn codegen_constant<'tcx>( +pub(crate) fn codegen_constant_operand<'tcx>( fx: &mut FunctionCx<'_, '_, 'tcx>, constant: &Constant<'tcx>, ) -> CValue<'tcx> { - let (const_val, ty) = match fx.monomorphize(constant.literal) { - ConstantKind::Ty(const_) => unreachable!("{:?}", const_), - ConstantKind::Unevaluated(mir::UnevaluatedConst { def, substs, promoted }, ty) - if fx.tcx.is_static(def.did) => - { - assert!(substs.is_empty()); - assert!(promoted.is_none()); - - return codegen_static_ref(fx, def.did, fx.layout_of(ty)).to_cvalue(fx); - } - ConstantKind::Unevaluated(unevaluated, ty) => { - match fx.tcx.const_eval_resolve(ParamEnv::reveal_all(), unevaluated, None) { - Ok(const_val) => (const_val, ty), - Err(_) => { - span_bug!(constant.span, "erroneous constant not captured by required_consts"); - } - } - } - ConstantKind::Val(val, ty) => (val, ty), - }; + let (const_val, ty) = eval_mir_constant(fx, constant); codegen_const_value(fx, const_val, ty) } From 241eae76e209b578b75b916e0e9a7d6ea24deb43 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun, 25 Sep 2022 14:44:51 +0200 Subject: [PATCH 066/309] Monomorphize ConstantKind::Unevaluated in mir_operand_get_const_val --- src/constant.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/constant.rs b/src/constant.rs index d1610b4005fa..da4a96c4acc8 100644 --- a/src/constant.rs +++ b/src/constant.rs @@ -459,14 +459,13 @@ pub(crate) fn mir_operand_get_const_val<'tcx>( operand: &Operand<'tcx>, ) -> Option> { match operand { - Operand::Constant(const_) => match const_.literal { - ConstantKind::Ty(const_) => fx - .monomorphize(const_) - .eval_for_mir(fx.tcx, ParamEnv::reveal_all()) - .try_to_value(fx.tcx), + Operand::Constant(const_) => match fx.monomorphize(const_.literal) { + ConstantKind::Ty(const_) => Some( + const_.eval_for_mir(fx.tcx, ParamEnv::reveal_all()).try_to_value(fx.tcx).unwrap(), + ), ConstantKind::Val(val, _) => Some(val), ConstantKind::Unevaluated(uv, _) => { - fx.tcx.const_eval_resolve(ParamEnv::reveal_all(), uv, None).ok() + Some(fx.tcx.const_eval_resolve(ParamEnv::reveal_all(), uv, None).unwrap()) } }, // FIXME(rust-lang/rust#85105): Casts like `IMM8 as u32` result in the const being stored From f82016891a26d7cd940f5e266c5fa24edee2f208 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun, 25 Sep 2022 14:58:33 +0200 Subject: [PATCH 067/309] Fix cpuid replacement shim --- src/inline_asm.rs | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/inline_asm.rs b/src/inline_asm.rs index 8b3d475cb180..3fcc84d39295 100644 --- a/src/inline_asm.rs +++ b/src/inline_asm.rs @@ -27,7 +27,7 @@ pub(crate) fn codegen_inline_asm<'tcx>( } // Used by stdarch - if template[0] == InlineAsmTemplatePiece::String("movq %rbx, ".to_string()) + if template[0] == InlineAsmTemplatePiece::String("mov ".to_string()) && matches!( template[1], InlineAsmTemplatePiece::Placeholder { @@ -36,24 +36,26 @@ pub(crate) fn codegen_inline_asm<'tcx>( span: _ } ) - && template[2] == InlineAsmTemplatePiece::String("\n".to_string()) - && template[3] == InlineAsmTemplatePiece::String("cpuid".to_string()) - && template[4] == InlineAsmTemplatePiece::String("\n".to_string()) - && template[5] == InlineAsmTemplatePiece::String("xchgq %rbx, ".to_string()) + && template[2] == InlineAsmTemplatePiece::String(", rbx".to_string()) + && template[3] == InlineAsmTemplatePiece::String("\n".to_string()) + && template[4] == InlineAsmTemplatePiece::String("cpuid".to_string()) + && template[5] == InlineAsmTemplatePiece::String("\n".to_string()) + && template[6] == InlineAsmTemplatePiece::String("xchg ".to_string()) && matches!( - template[6], + template[7], InlineAsmTemplatePiece::Placeholder { operand_idx: 0, modifier: Some('r'), span: _ } ) + && template[8] == InlineAsmTemplatePiece::String(", rbx".to_string()) { assert_eq!(operands.len(), 4); let (leaf, eax_place) = match operands[1] { InlineAsmOperand::InOut { reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::ax)), - late: true, + late: _, ref in_value, out_place: Some(out_place), } => ( @@ -68,7 +70,7 @@ pub(crate) fn codegen_inline_asm<'tcx>( InlineAsmRegOrRegClass::RegClass(InlineAsmRegClass::X86( X86InlineAsmRegClass::reg, )), - late: true, + late: _, place: Some(place), } => crate::base::codegen_place(fx, place), _ => unreachable!(), @@ -76,7 +78,7 @@ pub(crate) fn codegen_inline_asm<'tcx>( let (sub_leaf, ecx_place) = match operands[2] { InlineAsmOperand::InOut { reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::cx)), - late: true, + late: _, ref in_value, out_place: Some(out_place), } => ( @@ -88,7 +90,7 @@ pub(crate) fn codegen_inline_asm<'tcx>( let edx_place = match operands[3] { InlineAsmOperand::Out { reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::dx)), - late: true, + late: _, place: Some(place), } => crate::base::codegen_place(fx, place), _ => unreachable!(), From d422d506511d8665d16e1f396ef316dddb537ad0 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun, 25 Sep 2022 15:14:59 +0200 Subject: [PATCH 068/309] Pass --sysroot when building the new sysroot To avoid accidentally loading the original libcore --- build_system/build_sysroot.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/build_system/build_sysroot.rs b/build_system/build_sysroot.rs index c2c81feb25a6..856aecc49fd1 100644 --- a/build_system/build_sysroot.rs +++ b/build_system/build_sysroot.rs @@ -188,6 +188,7 @@ fn build_clif_sysroot_for_triple( let mut build_cmd = cargo_command("cargo", "build", Some(triple), Path::new("build_sysroot")); let mut rustflags = "-Zforce-unstable-if-unmarked -Cpanic=abort".to_string(); rustflags.push_str(&format!(" -Zcodegen-backend={}", cg_clif_dylib_path.to_str().unwrap())); + rustflags.push_str(&format!(" --sysroot={}", target_dir.to_str().unwrap())); if channel == "release" { build_cmd.arg("--release"); rustflags.push_str(" -Zmir-opt-level=3"); From 94d52158c6ac7b1ce0350a334b28c9e3ccf00060 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun, 25 Sep 2022 15:16:45 +0200 Subject: [PATCH 069/309] Work around rust-lang/libc#2924 --- build_sysroot/Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build_sysroot/Cargo.lock b/build_sysroot/Cargo.lock index d5a65c3b7217..a8077feaaaba 100644 --- a/build_sysroot/Cargo.lock +++ b/build_sysroot/Cargo.lock @@ -145,9 +145,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.133" +version = "0.2.132" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0f80d65747a3e43d1596c7c5492d95d5edddaabd45a7fcdb02b95f644164966" +checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5" dependencies = [ "rustc-std-workspace-core", ] From 322ff0ba0589202df43f4749745c503529daacfc Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun, 25 Sep 2022 14:05:01 +0000 Subject: [PATCH 070/309] Work around rustbuild bug --- scripts/setup_rust_fork.sh | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/scripts/setup_rust_fork.sh b/scripts/setup_rust_fork.sh index d6a37789599f..25103a2fd503 100644 --- a/scripts/setup_rust_fork.sh +++ b/scripts/setup_rust_fork.sh @@ -27,6 +27,21 @@ index d95b5b7f17f..00b6f0e3635 100644 [dev-dependencies] rand = "0.7" rand_xorshift = "0.2" +diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs +index a6333976f2a..c9a872e876c 100644 +--- a/src/bootstrap/config.rs ++++ b/src/bootstrap/config.rs +@@ -917,6 +917,10 @@ pub fn parse(args: &[String]) -> Config { + config.initial_cargo = config.out.join(config.build.triple).join("stage0/bin/cargo"); + } + ++ // Workaround for rustbuild bug ++ config.initial_rustc = PathBuf::from("$(pwd)/../build/rustc-clif"); ++ config.initial_cargo = PathBuf::from("$(rustup which cargo)"); ++ + // NOTE: it's important this comes *after* we set \`initial_rustc\` just above. + if config.dry_run { + let dir = config.out.join("tmp-dry-run"); diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 8431aa7b818..a3ff7e68ce5 100644 --- a/src/tools/compiletest/src/runtest.rs From 887ca1fd2a5c027e0369435c5087a4ad269658d2 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun, 25 Sep 2022 14:06:07 +0000 Subject: [PATCH 071/309] Update rustc test suite failure list --- scripts/test_rustc_tests.sh | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/scripts/test_rustc_tests.sh b/scripts/test_rustc_tests.sh index ace0cd76d81a..293dff133864 100755 --- a/scripts/test_rustc_tests.sh +++ b/scripts/test_rustc_tests.sh @@ -30,9 +30,6 @@ rm src/test/incremental/issue-80691-bad-eval-cache.rs # -Cpanic=abort causes abo # requires compiling with -Cpanic=unwind rm src/test/ui/test-attrs/test-fn-signature-verification-for-explicit-return-type.rs # "Cannot run dynamic test fn out-of-process" -rm src/test/ui/async-await/async-fn-size-moved-locals.rs # -Cpanic=abort shrinks some generator by one byte -rm src/test/ui/async-await/async-fn-size-uninit-locals.rs # same -rm src/test/ui/generator/size-moved-locals.rs # same rm -r src/test/ui/macros/rfc-2011-nicer-assert-messages/ # vendor intrinsics @@ -67,6 +64,7 @@ rm src/test/ui/target-feature/missing-plusminus.rs # error not implemented rm src/test/ui/fn/dyn-fn-alignment.rs # wants a 256 byte alignment rm -r src/test/run-make/emit-named-files # requires full --emit support rm src/test/ui/abi/stack-probes.rs # stack probes not yet implemented +rm src/test/ui/simd/intrinsic/ptr-cast.rs # simd_expose_addr intrinsic unimplemented # optimization tests # ================== From 7dccb51fe367a21af01ce9095d4e06082dfba56d Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Mon, 26 Sep 2022 13:56:18 +0200 Subject: [PATCH 072/309] Abi-checker got renamed to abi-cafe --- .gitignore | 1 + build_system/{abi_checker.rs => abi_cafe.rs} | 18 +++++++++--------- build_system/mod.rs | 4 ++-- build_system/prepare.rs | 6 +++--- clean_all.sh | 2 +- config.txt | 2 +- ... 0001-abi-cafe-Disable-failing-tests.patch} | 2 +- 7 files changed, 18 insertions(+), 17 deletions(-) rename build_system/{abi_checker.rs => abi_cafe.rs} (67%) rename patches/{0001-abi-checker-Disable-failing-tests.patch => 0001-abi-cafe-Disable-failing-tests.patch} (96%) diff --git a/.gitignore b/.gitignore index 6fd3e4443de5..9a4c54f08ca8 100644 --- a/.gitignore +++ b/.gitignore @@ -19,4 +19,5 @@ perf.data.old /regex /simple-raytracer /portable-simd +/abi-cafe /abi-checker diff --git a/build_system/abi_checker.rs b/build_system/abi_cafe.rs similarity index 67% rename from build_system/abi_checker.rs rename to build_system/abi_cafe.rs index 177b44d3141f..c2944ce62623 100644 --- a/build_system/abi_checker.rs +++ b/build_system/abi_cafe.rs @@ -14,17 +14,17 @@ pub(crate) fn run( host_triple: &str, target_triple: &str, ) { - if !config::get_bool("testsuite.abi-checker") { - eprintln!("[SKIP] abi-checker"); + if !config::get_bool("testsuite.abi-cafe") { + eprintln!("[SKIP] abi-cafe"); return; } if host_triple != target_triple { - eprintln!("[SKIP] abi-checker (cross-compilation not supported)"); + eprintln!("[SKIP] abi-cafe (cross-compilation not supported)"); return; } - eprintln!("Building sysroot for abi-checker"); + eprintln!("Building sysroot for abi-cafe"); build_sysroot::build_sysroot( channel, sysroot_kind, @@ -34,14 +34,14 @@ pub(crate) fn run( target_triple, ); - eprintln!("Running abi-checker"); - let mut abi_checker_path = env::current_dir().unwrap(); - abi_checker_path.push("abi-checker"); - env::set_current_dir(&abi_checker_path.clone()).unwrap(); + eprintln!("Running abi-cafe"); + let mut abi_cafe_path = env::current_dir().unwrap(); + abi_cafe_path.push("abi-cafe"); + env::set_current_dir(&abi_cafe_path.clone()).unwrap(); let pairs = ["rustc_calls_cgclif", "cgclif_calls_rustc", "cgclif_calls_cc", "cc_calls_cgclif"]; - let mut cmd = cargo_command("cargo", "run", Some(target_triple), &abi_checker_path); + let mut cmd = cargo_command("cargo", "run", Some(target_triple), &abi_cafe_path); cmd.arg("--"); cmd.arg("--pairs"); cmd.args(pairs); diff --git a/build_system/mod.rs b/build_system/mod.rs index c665d1ef71c2..03c8f58c724a 100644 --- a/build_system/mod.rs +++ b/build_system/mod.rs @@ -4,7 +4,7 @@ use std::process; use self::utils::is_ci; -mod abi_checker; +mod abi_cafe; mod build_backend; mod build_sysroot; mod config; @@ -143,7 +143,7 @@ pub fn main() { &target_triple, ); - abi_checker::run( + abi_cafe::run( channel, sysroot_kind, &target_dir, diff --git a/build_system/prepare.rs b/build_system/prepare.rs index 83a76d3591d6..3ad9f87e3186 100644 --- a/build_system/prepare.rs +++ b/build_system/prepare.rs @@ -14,12 +14,12 @@ pub(crate) fn prepare() { Command::new("cargo").arg("install").arg("hyperfine").spawn().unwrap().wait().unwrap(); clone_repo_shallow_github( - "abi-checker", + "abi-cafe", "Gankra", - "abi-checker", + "abi-cafe", "4c6dc8c9c687e2b3a760ff2176ce236872b37212", ); - apply_patches("abi-checker", Path::new("abi-checker")); + apply_patches("abi-cafe", Path::new("abi-cafe")); clone_repo_shallow_github( "rand", diff --git a/clean_all.sh b/clean_all.sh index 62e52bd19580..0fd9b9455a3f 100755 --- a/clean_all.sh +++ b/clean_all.sh @@ -3,4 +3,4 @@ set -e rm -rf build_sysroot/{sysroot_src/,target/,compiler-builtins/,rustc_version} rm -rf target/ build/ perf.data{,.old} y.bin -rm -rf rand/ regex/ simple-raytracer/ portable-simd/ abi-checker/ +rm -rf rand/ regex/ simple-raytracer/ portable-simd/ abi-checker/ abi-cafe/ diff --git a/config.txt b/config.txt index 2264d301d592..0d539191b12f 100644 --- a/config.txt +++ b/config.txt @@ -49,4 +49,4 @@ test.regex-shootout-regex-dna test.regex test.portable-simd -testsuite.abi-checker +testsuite.abi-cafe diff --git a/patches/0001-abi-checker-Disable-failing-tests.patch b/patches/0001-abi-cafe-Disable-failing-tests.patch similarity index 96% rename from patches/0001-abi-checker-Disable-failing-tests.patch rename to patches/0001-abi-cafe-Disable-failing-tests.patch index 526366a75987..511d2f536fda 100644 --- a/patches/0001-abi-checker-Disable-failing-tests.patch +++ b/patches/0001-abi-cafe-Disable-failing-tests.patch @@ -1,7 +1,7 @@ From 1a315ba225577dbbd1f449d9609f16f984f68708 Mon Sep 17 00:00:00 2001 From: Afonso Bordado Date: Fri, 12 Aug 2022 22:51:58 +0000 -Subject: [PATCH] Disable abi-checker tests +Subject: [PATCH] Disable abi-cafe tests --- src/report.rs | 14 ++++++++++++++ From 72992c483c02974897aab5be3144befd896d5472 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun, 25 Sep 2022 15:49:44 +0000 Subject: [PATCH 073/309] Update to Cranelift 0.88.0 --- Cargo.lock | 58 +++++++++++++++++++++++++++++++++-------------------- Cargo.toml | 12 +++++------ src/base.rs | 4 +++- src/lib.rs | 4 ++-- 4 files changed, 47 insertions(+), 31 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b4c607d482e5..c0f1dfd3e9d9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -24,6 +24,12 @@ name = "ar" version = "0.8.0" source = "git+https://github.com/bjorn3/rust-ar.git?branch=do_not_remove_cg_clif_ranlib#de9ab0e56bf3a208381d342aa5b60f9ff2891648" +[[package]] +name = "arrayvec" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" + [[package]] name = "autocfg" version = "1.1.0" @@ -36,6 +42,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bumpalo" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d" + [[package]] name = "byteorder" version = "1.4.3" @@ -50,19 +62,21 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cranelift-bforest" -version = "0.87.0" +version = "0.88.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93945adbccc8d731503d3038814a51e8317497c9e205411820348132fa01a358" +checksum = "b27bbd3e6c422cf6282b047bcdd51ecd9ca9f3497a3be0132ffa08e509b824b0" dependencies = [ "cranelift-entity", ] [[package]] name = "cranelift-codegen" -version = "0.87.0" +version = "0.88.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b482acc9d0d0d1ad3288a90a8150ee648be3dce8dc8c8669ff026f72debdc31" +checksum = "872f5d4557a411b087bd731df6347c142ae1004e6467a144a7e33662e5715a01" dependencies = [ + "arrayvec", + "bumpalo", "cranelift-bforest", "cranelift-codegen-meta", "cranelift-codegen-shared", @@ -77,30 +91,30 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" -version = "0.87.0" +version = "0.88.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9ec188d71e663192ef9048f204e410a7283b609942efc9fcc77da6d496edbb8" +checksum = "21b49fdebb29c62c1fc4da1eeebd609e9d530ecde24a9876def546275f73a244" dependencies = [ "cranelift-codegen-shared", ] [[package]] name = "cranelift-codegen-shared" -version = "0.87.0" +version = "0.88.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ad794b1b1c2c7bd9f7b76cfe0f084eaf7753e55d56191c3f7d89e8fa4978b99" +checksum = "5fc0c091e2db055d4d7f6b7cec2d2ead286bcfaea3357c6a52c2a2613a8cb5ac" [[package]] name = "cranelift-entity" -version = "0.87.0" +version = "0.88.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "342da0d5056f4119d3c311c4aab2460ceb6ee6e127bb395b76dd2279a09ea7a5" +checksum = "354a9597be87996c9b278655e68b8447f65dd907256855ad773864edee8d985c" [[package]] name = "cranelift-frontend" -version = "0.87.0" +version = "0.88.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfff792f775b07d4d9cfe9f1c767ce755c6cbadda1bbd6db18a1c75ff9f7376a" +checksum = "0cd8dd3fb8b82c772f4172e87ae1677b971676fffa7c4e3398e3047e650a266b" dependencies = [ "cranelift-codegen", "log", @@ -110,15 +124,15 @@ dependencies = [ [[package]] name = "cranelift-isle" -version = "0.87.0" +version = "0.88.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d51089478849f2ac8ef60a8a2d5346c8d4abfec0e45ac5b24530ef9f9499e1e" +checksum = "b82527802b1f7d8da288adc28f1dc97ea52943f5871c041213f7b5035ac698a7" [[package]] name = "cranelift-jit" -version = "0.87.0" +version = "0.88.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "095936e41720f86004b4c57ce88e6a13af28646bb3a6fb4afbebd5ae90c50029" +checksum = "54a6d007394ef18312fc6616fb99618844f13dff01edbbbc4a451c2b7cd5c8f4" dependencies = [ "anyhow", "cranelift-codegen", @@ -134,9 +148,9 @@ dependencies = [ [[package]] name = "cranelift-module" -version = "0.87.0" +version = "0.88.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "704a1aea4723d97eafe0fb7af110f6f6868b1ac95f5380bbc9adb2a3b8cf97e8" +checksum = "a106dd90ab4071f9e16b9fd3c2057d61c37f35af7c8f079c9ec59d6ede5b2cef" dependencies = [ "anyhow", "cranelift-codegen", @@ -144,9 +158,9 @@ dependencies = [ [[package]] name = "cranelift-native" -version = "0.87.0" +version = "0.88.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "885debe62f2078638d6585f54c9f05f5c2008f22ce5a2a9100ada785fc065dbd" +checksum = "c30ba8b910f1be023af0c39109cb28a8809734942a6b3eecbf2de8993052ea5e" dependencies = [ "cranelift-codegen", "libc", @@ -155,9 +169,9 @@ dependencies = [ [[package]] name = "cranelift-object" -version = "0.87.0" +version = "0.88.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aac1310cf1081ae8eca916c92cd163b977c77cab6e831fa812273c26ff921816" +checksum = "506ac7a483c765a83795166ffe689f656afc0f4eb57fafa3f9e60b64bfa2caa6" dependencies = [ "anyhow", "cranelift-codegen", diff --git a/Cargo.toml b/Cargo.toml index 7a9e8f5d8e0c..46fed4380e52 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,12 +8,12 @@ crate-type = ["dylib"] [dependencies] # These have to be in sync with each other -cranelift-codegen = { version = "0.87.0", features = ["unwind", "all-arch"] } -cranelift-frontend = "0.87.0" -cranelift-module = "0.87.0" -cranelift-native = "0.87.0" -cranelift-jit = { version = "0.87.0", optional = true } -cranelift-object = "0.87.0" +cranelift-codegen = { version = "0.88.0", features = ["unwind", "all-arch"] } +cranelift-frontend = "0.88.0" +cranelift-module = "0.88.0" +cranelift-native = "0.88.0" +cranelift-jit = { version = "0.88.0", optional = true } +cranelift-object = "0.88.0" target-lexicon = "0.12.0" gimli = { version = "0.26.0", default-features = false, features = ["write"]} object = { version = "0.29.0", default-features = false, features = ["std", "read_core", "write", "archive", "coff", "elf", "macho", "pe"] } diff --git a/src/base.rs b/src/base.rs index 1aeb9ff25703..491525993fd1 100644 --- a/src/base.rs +++ b/src/base.rs @@ -6,6 +6,8 @@ use rustc_middle::ty::adjustment::PointerCast; use rustc_middle::ty::layout::FnAbiOf; use rustc_middle::ty::print::with_no_trimmed_paths; +use cranelift_codegen::ir::UserFuncName; + use crate::constant::ConstantCx; use crate::debuginfo::FunctionDebugContext; use crate::prelude::*; @@ -64,7 +66,7 @@ pub(crate) fn codegen_fn<'tcx>( let mut func_ctx = FunctionBuilderContext::new(); let mut func = cached_func; func.clear(); - func.name = ExternalName::user(0, func_id.as_u32()); + func.name = UserFuncName::user(0, func_id.as_u32()); func.signature = sig; func.collect_debug_info(); diff --git a/src/lib.rs b/src/lib.rs index 913414e76182..74bd47365353 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -96,8 +96,8 @@ mod prelude { pub(crate) use cranelift_codegen::ir::function::Function; pub(crate) use cranelift_codegen::ir::types; pub(crate) use cranelift_codegen::ir::{ - AbiParam, Block, ExternalName, FuncRef, Inst, InstBuilder, MemFlags, Signature, SourceLoc, - StackSlot, StackSlotData, StackSlotKind, TrapCode, Type, Value, + AbiParam, Block, FuncRef, Inst, InstBuilder, MemFlags, Signature, SourceLoc, StackSlot, + StackSlotData, StackSlotKind, TrapCode, Type, Value, }; pub(crate) use cranelift_codegen::isa::{self, CallConv}; pub(crate) use cranelift_codegen::Context; From 0035f09c5a25d230b594374239a0c61301137946 Mon Sep 17 00:00:00 2001 From: Afonso Bordado Date: Sun, 21 Aug 2022 17:42:50 +0100 Subject: [PATCH 074/309] Avoid masking shift amounts Cranelift 0.87 now follows its own documentation regarding shift amounts, and implicitly masks them if the arch requires it. [0] [0]: https://github.com/bytecodealliance/wasmtime/commit/05089321740a07757dff0a285176b2651a49aae2 --- src/num.rs | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/src/num.rs b/src/num.rs index 4fadbf24d8a8..ecbab408ded9 100644 --- a/src/num.rs +++ b/src/num.rs @@ -150,18 +150,12 @@ pub(crate) fn codegen_int_binop<'tcx>( BinOp::BitXor => b.bxor(lhs, rhs), BinOp::BitAnd => b.band(lhs, rhs), BinOp::BitOr => b.bor(lhs, rhs), - BinOp::Shl => { - let lhs_ty = fx.bcx.func.dfg.value_type(lhs); - let actual_shift = fx.bcx.ins().band_imm(rhs, i64::from(lhs_ty.bits() - 1)); - fx.bcx.ins().ishl(lhs, actual_shift) - } + BinOp::Shl => b.ishl(lhs, rhs), BinOp::Shr => { - let lhs_ty = fx.bcx.func.dfg.value_type(lhs); - let actual_shift = fx.bcx.ins().band_imm(rhs, i64::from(lhs_ty.bits() - 1)); if signed { - fx.bcx.ins().sshr(lhs, actual_shift) + b.sshr(lhs, rhs) } else { - fx.bcx.ins().ushr(lhs, actual_shift) + b.ushr(lhs, rhs) } } // Compare binops handles by `codegen_binop`. @@ -279,22 +273,15 @@ pub(crate) fn codegen_checked_int_binop<'tcx>( } } BinOp::Shl => { - let lhs_ty = fx.bcx.func.dfg.value_type(lhs); - let masked_shift = fx.bcx.ins().band_imm(rhs, i64::from(lhs_ty.bits() - 1)); - let val = fx.bcx.ins().ishl(lhs, masked_shift); + let val = fx.bcx.ins().ishl(lhs, rhs); let ty = fx.bcx.func.dfg.value_type(val); let max_shift = i64::from(ty.bits()) - 1; let has_overflow = fx.bcx.ins().icmp_imm(IntCC::UnsignedGreaterThan, rhs, max_shift); (val, has_overflow) } BinOp::Shr => { - let lhs_ty = fx.bcx.func.dfg.value_type(lhs); - let masked_shift = fx.bcx.ins().band_imm(rhs, i64::from(lhs_ty.bits() - 1)); - let val = if !signed { - fx.bcx.ins().ushr(lhs, masked_shift) - } else { - fx.bcx.ins().sshr(lhs, masked_shift) - }; + let val = + if !signed { fx.bcx.ins().ushr(lhs, rhs) } else { fx.bcx.ins().sshr(lhs, rhs) }; let ty = fx.bcx.func.dfg.value_type(val); let max_shift = i64::from(ty.bits()) - 1; let has_overflow = fx.bcx.ins().icmp_imm(IntCC::UnsignedGreaterThan, rhs, max_shift); From f90bc3009ab7284772a7abc961fa746aaec5ffcd Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun, 25 Sep 2022 15:54:15 +0000 Subject: [PATCH 075/309] Enable stack probing on x86_64 Cranelift now supports inline stack probes on x86_64 --- src/lib.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 74bd47365353..629d79d50124 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -251,7 +251,6 @@ fn build_isa(sess: &Session, backend_config: &BackendConfig) -> Box Box Date: Sun, 25 Sep 2022 16:01:43 +0000 Subject: [PATCH 076/309] Remove stabilized feature gate --- example/issue-91827-extern-types.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/example/issue-91827-extern-types.rs b/example/issue-91827-extern-types.rs index 2ecc8b8238b1..039100696331 100644 --- a/example/issue-91827-extern-types.rs +++ b/example/issue-91827-extern-types.rs @@ -5,7 +5,6 @@ // Test that we can handle unsized types with an extern type tail part. // Regression test for issue #91827. -#![feature(const_ptr_offset_from)] #![feature(extern_types)] use std::ptr::addr_of; From 88522158b2a7462418b89ddd7424502ee60535b4 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Mon, 26 Sep 2022 16:21:57 +0000 Subject: [PATCH 077/309] Re-enable structs abi-cafe test on AArch64 --- patches/0001-abi-cafe-Disable-failing-tests.patch | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/patches/0001-abi-cafe-Disable-failing-tests.patch b/patches/0001-abi-cafe-Disable-failing-tests.patch index 511d2f536fda..a6bcade8eb85 100644 --- a/patches/0001-abi-cafe-Disable-failing-tests.patch +++ b/patches/0001-abi-cafe-Disable-failing-tests.patch @@ -11,7 +11,7 @@ diff --git a/src/report.rs b/src/report.rs index 7346f5e..8347762 100644 --- a/src/report.rs +++ b/src/report.rs -@@ -45,6 +45,20 @@ pub fn get_test_rules(test: &TestKey, caller: &dyn AbiImpl, callee: &dyn AbiImpl +@@ -45,6 +45,13 @@ pub fn get_test_rules(test: &TestKey, caller: &dyn AbiImpl, callee: &dyn AbiImpl // // THIS AREA RESERVED FOR VENDORS TO APPLY PATCHES @@ -21,13 +21,6 @@ index 7346f5e..8347762 100644 + result.run = Link; + result.check = Pass(Link); + } -+ -+ // structs is broken in the current release of cranelift for aarch64. -+ // It has been fixed for cranelift 0.88: https://github.com/bytecodealliance/wasmtime/pull/4634 -+ if cfg!(target_arch = "aarch64") && test.test_name == "structs" { -+ result.run = Link; -+ result.check = Pass(Link); -+ } + // END OF VENDOR RESERVED AREA // From ba56e8f313895eefc3e0f7de38c9cde3145b0c04 Mon Sep 17 00:00:00 2001 From: Afonso Bordado Date: Mon, 26 Sep 2022 09:45:00 +0100 Subject: [PATCH 078/309] Remove MSVC Check --- build_system/mod.rs | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/build_system/mod.rs b/build_system/mod.rs index 03c8f58c724a..b25270d832ce 100644 --- a/build_system/mod.rs +++ b/build_system/mod.rs @@ -122,16 +122,7 @@ pub fn main() { host_triple.clone() }; - if target_triple.ends_with("-msvc") { - eprintln!("The MSVC toolchain is not yet supported by rustc_codegen_cranelift."); - eprintln!("Switch to the MinGW toolchain for Windows support."); - eprintln!("Hint: You can use `rustup set default-host x86_64-pc-windows-gnu` to"); - eprintln!("set the global default target to MinGW"); - process::exit(1); - } - - let cg_clif_dylib = - build_backend::build_backend(channel, &host_triple, use_unstable_features); + let cg_clif_dylib = build_backend::build_backend(channel, &host_triple, use_unstable_features); match command { Command::Test => { tests::run_tests( From 481484c1410c360acd309ed3ba7fe214a6f61454 Mon Sep 17 00:00:00 2001 From: Afonso Bordado Date: Tue, 2 Aug 2022 08:31:50 +0100 Subject: [PATCH 079/309] Windows MinGW & MSVC Matrix CI --- .github/workflows/main.yml | 42 +++++++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e8897e9ae814..029e83f6aa4e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -122,10 +122,21 @@ jobs: name: cg_clif-${{ runner.os }}-cross-x86_64-mingw path: cg_clif.tar.xz - build_windows: - runs-on: windows-latest + windows: + runs-on: ${{ matrix.os }} timeout-minutes: 60 + strategy: + fail-fast: false + matrix: + include: + # Native Windows build with MSVC + - os: windows-latest + # cross-compile from Windows to Windows MinGW + - os: windows-latest + env: + TARGET_TRIPLE: x86_64-pc-windows-gnu + steps: - uses: actions/checkout@v3 @@ -149,29 +160,40 @@ jobs: # path: target # key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('rust-toolchain', '**/Cargo.lock') }} + - name: Set MinGW as the default toolchain + if: matrix.env.TARGET_TRIPLE == 'x86_64-pc-windows-gnu' + run: rustup set default-host x86_64-pc-windows-gnu + - name: Prepare dependencies run: | git config --global user.email "user@example.com" git config --global user.name "User" git config --global core.autocrlf false - rustup set default-host x86_64-pc-windows-gnu rustc y.rs -o y.exe -g ./y.exe prepare + - name: Build without unstable features + env: + TARGET_TRIPLE: ${{ matrix.env.TARGET_TRIPLE }} + # This is the config rust-lang/rust uses for builds + run: ./y.rs build --no-unstable-features + - name: Build - #name: Test + run: ./y.rs build --sysroot none + + - name: Test run: | # Enable backtraces for easier debugging - #$Env:RUST_BACKTRACE=1 + $Env:RUST_BACKTRACE=1 # Reduce amount of benchmark runs as they are slow - #$Env:COMPILE_RUNS=2 - #$Env:RUN_RUNS=2 + $Env:COMPILE_RUNS=2 + $Env:RUN_RUNS=2 # Enable extra checks - #$Env:CG_CLIF_ENABLE_VERIFIER=1 + $Env:CG_CLIF_ENABLE_VERIFIER=1 - ./y.exe build + ./y.exe test - name: Package prebuilt cg_clif # don't use compression as xzip isn't supported by tar on windows and bzip2 hangs @@ -180,5 +202,5 @@ jobs: - name: Upload prebuilt cg_clif uses: actions/upload-artifact@v2 with: - name: cg_clif-${{ runner.os }} + name: cg_clif-${{ runner.os }}-${{ matrix.env.TARGET_TRIPLE }} path: cg_clif.tar From 1878ca61bcce43276fa723921b7a579482293ff0 Mon Sep 17 00:00:00 2001 From: Afonso Bordado Date: Mon, 26 Sep 2022 13:38:53 +0100 Subject: [PATCH 080/309] Disable JIT Tests for windows --- build_system/tests.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build_system/tests.rs b/build_system/tests.rs index 74042bc92183..d4f393fc7ff9 100644 --- a/build_system/tests.rs +++ b/build_system/tests.rs @@ -450,7 +450,8 @@ impl TestRunner { out_dir.push("out"); let is_native = host_triple == target_triple; - let jit_supported = target_triple.contains("x86_64") && is_native; + let jit_supported = + target_triple.contains("x86_64") && is_native && !host_triple.contains("windows"); let mut rust_flags = env::var("RUSTFLAGS").ok().unwrap_or("".to_string()); let mut run_wrapper = Vec::new(); From 01d3e1a3ade98bbf636cf38289ed50c2d3d66ef0 Mon Sep 17 00:00:00 2001 From: Afonso Bordado Date: Mon, 26 Sep 2022 13:44:55 +0100 Subject: [PATCH 081/309] Disable failing windows CI tests --- .github/workflows/main.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 029e83f6aa4e..91240cca8b48 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -192,6 +192,18 @@ jobs: # Enable extra checks $Env:CG_CLIF_ENABLE_VERIFIER=1 + + # WIP Disable some tests + + # This fails due to some weird argument handling by hyperfine, not an actual regression + # more of a build system issue + (Get-Content config.txt) -replace '(bench.simple-raytracer)', '# $1' | Out-File config.txt + + # This fails with a different output than expected + (Get-Content config.txt) -replace '(test.regex-shootout-regex-dna)', '# $1' | Out-File config.txt + + # This only fails on x86_64-pc-windows-gnu when run from a windows host + (Get-Content config.txt) -replace '(test.rust-random/rand)', '# $1' | Out-File config.txt ./y.exe test From 8072dec7a5f75861fc152310eb1bd6f171103835 Mon Sep 17 00:00:00 2001 From: Afonso Bordado Date: Mon, 26 Sep 2022 15:00:53 +0100 Subject: [PATCH 082/309] Drop abi cafe patch --- .../0001-abi-cafe-Disable-failing-tests.patch | 29 ------------------- 1 file changed, 29 deletions(-) delete mode 100644 patches/0001-abi-cafe-Disable-failing-tests.patch diff --git a/patches/0001-abi-cafe-Disable-failing-tests.patch b/patches/0001-abi-cafe-Disable-failing-tests.patch deleted file mode 100644 index a6bcade8eb85..000000000000 --- a/patches/0001-abi-cafe-Disable-failing-tests.patch +++ /dev/null @@ -1,29 +0,0 @@ -From 1a315ba225577dbbd1f449d9609f16f984f68708 Mon Sep 17 00:00:00 2001 -From: Afonso Bordado -Date: Fri, 12 Aug 2022 22:51:58 +0000 -Subject: [PATCH] Disable abi-cafe tests - ---- - src/report.rs | 14 ++++++++++++++ - 1 file changed, 14 insertions(+) - -diff --git a/src/report.rs b/src/report.rs -index 7346f5e..8347762 100644 ---- a/src/report.rs -+++ b/src/report.rs -@@ -45,6 +45,13 @@ pub fn get_test_rules(test: &TestKey, caller: &dyn AbiImpl, callee: &dyn AbiImpl - // - // THIS AREA RESERVED FOR VENDORS TO APPLY PATCHES - -+ // Currently MSVC has some broken ABI issues. Furthermore, they cause -+ // a STATUS_ACCESS_VIOLATION, so we can't even run them. Ensure that they compile and link. -+ if cfg!(windows) && (test.test_name == "bool" || test.test_name == "ui128") { -+ result.run = Link; -+ result.check = Pass(Link); -+ } -+ - // END OF VENDOR RESERVED AREA - // - // --- -2.34.1 From 738dd2b70b6ab8366ae4620e5c0597c209e0235c Mon Sep 17 00:00:00 2001 From: Afonso Bordado Date: Tue, 27 Sep 2022 07:26:12 +0100 Subject: [PATCH 083/309] Windows CI Cache --- .github/workflows/main.yml | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 91240cca8b48..2d63881b7ab1 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -140,25 +140,25 @@ jobs: steps: - uses: actions/checkout@v3 - #- name: Cache cargo installed crates - # uses: actions/cache@v2 - # with: - # path: ~/.cargo/bin - # key: ${{ runner.os }}-cargo-installed-crates + - name: Cache cargo installed crates + uses: actions/cache@v2 + with: + path: ~/.cargo/bin + key: ${{ runner.os }}-${{ matrix.env.TARGET_TRIPLE }}-cargo-installed-crates - #- name: Cache cargo registry and index - # uses: actions/cache@v2 - # with: - # path: | - # ~/.cargo/registry - # ~/.cargo/git - # key: ${{ runner.os }}-cargo-registry-and-index-${{ hashFiles('**/Cargo.lock') }} + - name: Cache cargo registry and index + uses: actions/cache@v2 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + key: ${{ runner.os }}-${{ matrix.env.TARGET_TRIPLE }}-cargo-registry-and-index-${{ hashFiles('**/Cargo.lock') }} - #- name: Cache cargo target dir - # uses: actions/cache@v2 - # with: - # path: target - # key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('rust-toolchain', '**/Cargo.lock') }} + - name: Cache cargo target dir + uses: actions/cache@v2 + with: + path: target + key: ${{ runner.os }}-${{ matrix.env.TARGET_TRIPLE }}-cargo-build-target-${{ hashFiles('rust-toolchain', '**/Cargo.lock') }} - name: Set MinGW as the default toolchain if: matrix.env.TARGET_TRIPLE == 'x86_64-pc-windows-gnu' From cd8becbc03916b2c1db31ce1063c40f7d0d2b38e Mon Sep 17 00:00:00 2001 From: Afonso Bordado Date: Tue, 27 Sep 2022 07:38:54 +0100 Subject: [PATCH 084/309] Disable some ABI tests on MinGW --- ...e-some-test-on-x86_64-pc-windows-gnu.patch | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 patches/0001-abi-cafe-Disable-some-test-on-x86_64-pc-windows-gnu.patch diff --git a/patches/0001-abi-cafe-Disable-some-test-on-x86_64-pc-windows-gnu.patch b/patches/0001-abi-cafe-Disable-some-test-on-x86_64-pc-windows-gnu.patch new file mode 100644 index 000000000000..0e5e7cdfcdf1 --- /dev/null +++ b/patches/0001-abi-cafe-Disable-some-test-on-x86_64-pc-windows-gnu.patch @@ -0,0 +1,29 @@ +From 2b15fee2bb5fd14e34c7e17e44d99cb34f4c555d Mon Sep 17 00:00:00 2001 +From: Afonso Bordado +Date: Tue, 27 Sep 2022 07:55:17 +0100 +Subject: [PATCH] Disable some test on x86_64-pc-windows-gnu + +--- + src/report.rs | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/src/report.rs b/src/report.rs +index eeec614..f582867 100644 +--- a/src/report.rs ++++ b/src/report.rs +@@ -48,6 +48,12 @@ pub fn get_test_rules(test: &TestKey, caller: &dyn AbiImpl, callee: &dyn AbiImpl + // + // THIS AREA RESERVED FOR VENDORS TO APPLY PATCHES + ++ // x86_64-pc-windows-gnu has some broken i128 tests that aren't disabled by default ++ if cfg!(all(target_os = "windows", target_env = "gnu")) && test.test_name == "ui128" { ++ result.run = Link; ++ result.check = Pass(Link); ++ } ++ + // END OF VENDOR RESERVED AREA + // + // +-- +2.30.1.windows.1 + From fb71d8abea5d2e512dd0bd3d9b3694d1421172cb Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Tue, 27 Sep 2022 08:31:22 +0000 Subject: [PATCH 085/309] Update to Cranelift 0.88.1 This fixes a regression of Atomic*::compare_exchange on AArch64 --- Cargo.lock | 44 ++++++++++++++++++++++---------------------- Cargo.toml | 12 ++++++------ 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c0f1dfd3e9d9..3fa9d56cd01a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -62,18 +62,18 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cranelift-bforest" -version = "0.88.0" +version = "0.88.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b27bbd3e6c422cf6282b047bcdd51ecd9ca9f3497a3be0132ffa08e509b824b0" +checksum = "44409ccf2d0f663920cab563d2b79fcd6b2e9a2bcc6e929fef76c8f82ad6c17a" dependencies = [ "cranelift-entity", ] [[package]] name = "cranelift-codegen" -version = "0.88.0" +version = "0.88.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "872f5d4557a411b087bd731df6347c142ae1004e6467a144a7e33662e5715a01" +checksum = "98de2018ad96eb97f621f7d6b900a0cc661aec8d02ea4a50e56ecb48e5a2fcaf" dependencies = [ "arrayvec", "bumpalo", @@ -91,30 +91,30 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" -version = "0.88.0" +version = "0.88.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b49fdebb29c62c1fc4da1eeebd609e9d530ecde24a9876def546275f73a244" +checksum = "5287ce36e6c4758fbaf298bd1a8697ad97a4f2375a3d1b61142ea538db4877e5" dependencies = [ "cranelift-codegen-shared", ] [[package]] name = "cranelift-codegen-shared" -version = "0.88.0" +version = "0.88.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fc0c091e2db055d4d7f6b7cec2d2ead286bcfaea3357c6a52c2a2613a8cb5ac" +checksum = "2855c24219e2f08827f3f4ffb2da92e134ae8d8ecc185b11ec8f9878cf5f588e" [[package]] name = "cranelift-entity" -version = "0.88.0" +version = "0.88.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "354a9597be87996c9b278655e68b8447f65dd907256855ad773864edee8d985c" +checksum = "0b65673279d75d34bf11af9660ae2dbd1c22e6d28f163f5c72f4e1dc56d56103" [[package]] name = "cranelift-frontend" -version = "0.88.0" +version = "0.88.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cd8dd3fb8b82c772f4172e87ae1677b971676fffa7c4e3398e3047e650a266b" +checksum = "3ed2b3d7a4751163f6c4a349205ab1b7d9c00eecf19dcea48592ef1f7688eefc" dependencies = [ "cranelift-codegen", "log", @@ -124,15 +124,15 @@ dependencies = [ [[package]] name = "cranelift-isle" -version = "0.88.0" +version = "0.88.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b82527802b1f7d8da288adc28f1dc97ea52943f5871c041213f7b5035ac698a7" +checksum = "3be64cecea9d90105fc6a2ba2d003e98c867c1d6c4c86cc878f97ad9fb916293" [[package]] name = "cranelift-jit" -version = "0.88.0" +version = "0.88.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54a6d007394ef18312fc6616fb99618844f13dff01edbbbc4a451c2b7cd5c8f4" +checksum = "f98ed42a70a0c9c388e34ec9477f57fc7300f541b1e5136a0e2ea02b1fac6015" dependencies = [ "anyhow", "cranelift-codegen", @@ -148,9 +148,9 @@ dependencies = [ [[package]] name = "cranelift-module" -version = "0.88.0" +version = "0.88.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a106dd90ab4071f9e16b9fd3c2057d61c37f35af7c8f079c9ec59d6ede5b2cef" +checksum = "d658ac7f156708bfccb647216cc8b9387469f50d352ba4ad80150541e4ae2d49" dependencies = [ "anyhow", "cranelift-codegen", @@ -158,9 +158,9 @@ dependencies = [ [[package]] name = "cranelift-native" -version = "0.88.0" +version = "0.88.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c30ba8b910f1be023af0c39109cb28a8809734942a6b3eecbf2de8993052ea5e" +checksum = "c4a03a6ac1b063e416ca4b93f6247978c991475e8271465340caa6f92f3c16a4" dependencies = [ "cranelift-codegen", "libc", @@ -169,9 +169,9 @@ dependencies = [ [[package]] name = "cranelift-object" -version = "0.88.0" +version = "0.88.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "506ac7a483c765a83795166ffe689f656afc0f4eb57fafa3f9e60b64bfa2caa6" +checksum = "eef0b4119b645b870a43a036d76c0ada3a076b1f82e8b8487659304c8b09049b" dependencies = [ "anyhow", "cranelift-codegen", diff --git a/Cargo.toml b/Cargo.toml index 46fed4380e52..09cf5b4a1edd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,12 +8,12 @@ crate-type = ["dylib"] [dependencies] # These have to be in sync with each other -cranelift-codegen = { version = "0.88.0", features = ["unwind", "all-arch"] } -cranelift-frontend = "0.88.0" -cranelift-module = "0.88.0" -cranelift-native = "0.88.0" -cranelift-jit = { version = "0.88.0", optional = true } -cranelift-object = "0.88.0" +cranelift-codegen = { version = "0.88.1", features = ["unwind", "all-arch"] } +cranelift-frontend = "0.88.1" +cranelift-module = "0.88.1" +cranelift-native = "0.88.1" +cranelift-jit = { version = "0.88.1", optional = true } +cranelift-object = "0.88.1" target-lexicon = "0.12.0" gimli = { version = "0.26.0", default-features = false, features = ["write"]} object = { version = "0.29.0", default-features = false, features = ["std", "read_core", "write", "archive", "coff", "elf", "macho", "pe"] } From e96badc97fde074e64c300c35d0acdd68cddc59b Mon Sep 17 00:00:00 2001 From: Afonso Bordado Date: Tue, 27 Sep 2022 08:15:41 +0100 Subject: [PATCH 086/309] Disable some rand tests on MinGW --- .github/workflows/main.yml | 3 -- ...001-rand-Disable-rand-tests-on-mingw.patch | 47 +++++++++++++++++++ 2 files changed, 47 insertions(+), 3 deletions(-) create mode 100644 patches/0001-rand-Disable-rand-tests-on-mingw.patch diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 2d63881b7ab1..7f6cfd2ccc56 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -201,9 +201,6 @@ jobs: # This fails with a different output than expected (Get-Content config.txt) -replace '(test.regex-shootout-regex-dna)', '# $1' | Out-File config.txt - - # This only fails on x86_64-pc-windows-gnu when run from a windows host - (Get-Content config.txt) -replace '(test.rust-random/rand)', '# $1' | Out-File config.txt ./y.exe test diff --git a/patches/0001-rand-Disable-rand-tests-on-mingw.patch b/patches/0001-rand-Disable-rand-tests-on-mingw.patch new file mode 100644 index 000000000000..108f882408df --- /dev/null +++ b/patches/0001-rand-Disable-rand-tests-on-mingw.patch @@ -0,0 +1,47 @@ +From eea3bbaed34ab210868c324219a862a9d5f4681c Mon Sep 17 00:00:00 2001 +From: Afonso Bordado +Date: Tue, 27 Sep 2022 08:13:58 +0100 +Subject: [PATCH] Disable rand tests on mingw + +--- + rand_distr/src/pareto.rs | 2 ++ + rand_distr/tests/value_stability.rs | 4 ++++ + 2 files changed, 6 insertions(+) + +diff --git a/rand_distr/src/pareto.rs b/rand_distr/src/pareto.rs +index 217899ed9a..5f8e2c9bbb 100644 +--- a/rand_distr/src/pareto.rs ++++ b/rand_distr/src/pareto.rs +@@ -107,6 +107,8 @@ mod tests { + } + + #[test] ++ // This is broken on x86_64-pc-windows-gnu presumably due to a broken powf implementation ++ #[cfg(not(all(target_os = "windows", target_env = "gnu")))] + fn value_stability() { + fn test_samples>( + distr: D, zero: F, expected: &[F], +diff --git a/rand_distr/tests/value_stability.rs b/rand_distr/tests/value_stability.rs +index 192ba748b7..10c6ac24f6 100644 +--- a/rand_distr/tests/value_stability.rs ++++ b/rand_distr/tests/value_stability.rs +@@ -72,6 +72,8 @@ fn unit_disc_stability() { + } + + #[test] ++// This is broken on x86_64-pc-windows-gnu ++#[cfg(not(all(target_os = "windows", target_env = "gnu")))] + fn pareto_stability() { + test_samples(213, Pareto::new(1.0, 1.0).unwrap(), &[ + 1.0423688f32, 2.1235929, 4.132709, 1.4679428, +@@ -143,6 +145,8 @@ fn inverse_gaussian_stability() { + } + + #[test] ++// This is broken on x86_64-pc-windows-gnu ++#[cfg(not(all(target_os = "windows", target_env = "gnu")))] + fn gamma_stability() { + // Gamma has 3 cases: shape == 1, shape < 1, shape > 1 + test_samples(223, Gamma::new(1.0, 5.0).unwrap(), &[ +-- +2.30.1.windows.1 From a83ec3b6a451cd40f654f0606228564b9fa21a04 Mon Sep 17 00:00:00 2001 From: Afonso Bordado Date: Tue, 27 Sep 2022 12:26:23 +0100 Subject: [PATCH 087/309] Ignore rand tests --- ... 0003-rand-Disable-rand-tests-on-mingw.patch} | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) rename patches/{0001-rand-Disable-rand-tests-on-mingw.patch => 0003-rand-Disable-rand-tests-on-mingw.patch} (79%) diff --git a/patches/0001-rand-Disable-rand-tests-on-mingw.patch b/patches/0003-rand-Disable-rand-tests-on-mingw.patch similarity index 79% rename from patches/0001-rand-Disable-rand-tests-on-mingw.patch rename to patches/0003-rand-Disable-rand-tests-on-mingw.patch index 108f882408df..d8775e2d022a 100644 --- a/patches/0001-rand-Disable-rand-tests-on-mingw.patch +++ b/patches/0003-rand-Disable-rand-tests-on-mingw.patch @@ -1,4 +1,4 @@ -From eea3bbaed34ab210868c324219a862a9d5f4681c Mon Sep 17 00:00:00 2001 +From eec874c889b8d24e5ad50faded24288150f057b1 Mon Sep 17 00:00:00 2001 From: Afonso Bordado Date: Tue, 27 Sep 2022 08:13:58 +0100 Subject: [PATCH] Disable rand tests on mingw @@ -9,7 +9,7 @@ Subject: [PATCH] Disable rand tests on mingw 2 files changed, 6 insertions(+) diff --git a/rand_distr/src/pareto.rs b/rand_distr/src/pareto.rs -index 217899ed9a..5f8e2c9bbb 100644 +index 217899e..9cedeb7 100644 --- a/rand_distr/src/pareto.rs +++ b/rand_distr/src/pareto.rs @@ -107,6 +107,8 @@ mod tests { @@ -17,12 +17,12 @@ index 217899ed9a..5f8e2c9bbb 100644 #[test] + // This is broken on x86_64-pc-windows-gnu presumably due to a broken powf implementation -+ #[cfg(not(all(target_os = "windows", target_env = "gnu")))] ++ #[cfg_attr(all(target_os = "windows", target_env = "gnu"), ignore)] fn value_stability() { fn test_samples>( distr: D, zero: F, expected: &[F], diff --git a/rand_distr/tests/value_stability.rs b/rand_distr/tests/value_stability.rs -index 192ba748b7..10c6ac24f6 100644 +index 192ba74..0101ace 100644 --- a/rand_distr/tests/value_stability.rs +++ b/rand_distr/tests/value_stability.rs @@ -72,6 +72,8 @@ fn unit_disc_stability() { @@ -30,7 +30,7 @@ index 192ba748b7..10c6ac24f6 100644 #[test] +// This is broken on x86_64-pc-windows-gnu -+#[cfg(not(all(target_os = "windows", target_env = "gnu")))] ++#[cfg_attr(all(target_os = "windows", target_env = "gnu"), ignore)] fn pareto_stability() { test_samples(213, Pareto::new(1.0, 1.0).unwrap(), &[ 1.0423688f32, 2.1235929, 4.132709, 1.4679428, @@ -39,9 +39,9 @@ index 192ba748b7..10c6ac24f6 100644 #[test] +// This is broken on x86_64-pc-windows-gnu -+#[cfg(not(all(target_os = "windows", target_env = "gnu")))] ++#[cfg_attr(all(target_os = "windows", target_env = "gnu"), ignore)] fn gamma_stability() { // Gamma has 3 cases: shape == 1, shape < 1, shape > 1 test_samples(223, Gamma::new(1.0, 5.0).unwrap(), &[ --- -2.30.1.windows.1 +-- +2.25.1 From 4edf14520aaa7e2d506afa470d9b8b1f9999b8f8 Mon Sep 17 00:00:00 2001 From: Afonso Bordado Date: Tue, 27 Sep 2022 12:29:52 +0100 Subject: [PATCH 088/309] Use target triple artifact names --- .github/workflows/main.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7f6cfd2ccc56..5655f466d749 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -29,7 +29,11 @@ jobs: matrix: include: - os: ubuntu-latest + env: + TARGET_TRIPLE: x86_64-unknown-linux-gnu - os: macos-latest + env: + TARGET_TRIPLE: x86_64-apple-darwin # cross-compile from Linux to Windows using mingw - os: ubuntu-latest env: @@ -112,7 +116,7 @@ jobs: if: matrix.env.TARGET_TRIPLE != 'x86_64-pc-windows-gnu' uses: actions/upload-artifact@v2 with: - name: cg_clif-${{ runner.os }} + name: ${{ matrix.env.TARGET_TRIPLE }} path: cg_clif.tar.xz - name: Upload prebuilt cg_clif (cross compile) @@ -132,6 +136,8 @@ jobs: include: # Native Windows build with MSVC - os: windows-latest + env: + TARGET_TRIPLE: x86_64-pc-windows-msvc # cross-compile from Windows to Windows MinGW - os: windows-latest env: @@ -211,5 +217,5 @@ jobs: - name: Upload prebuilt cg_clif uses: actions/upload-artifact@v2 with: - name: cg_clif-${{ runner.os }}-${{ matrix.env.TARGET_TRIPLE }} + name: ${{ matrix.env.TARGET_TRIPLE }} path: cg_clif.tar From 6909219a2945b9867250459e4722eae4ce359721 Mon Sep 17 00:00:00 2001 From: Afonso Bordado Date: Tue, 27 Sep 2022 12:42:27 +0100 Subject: [PATCH 089/309] Prefix artifact names with `cg_clif` Co-authored-by: bjorn3 <17426603+bjorn3@users.noreply.github.com> --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5655f466d749..5061010c86cd 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -116,7 +116,7 @@ jobs: if: matrix.env.TARGET_TRIPLE != 'x86_64-pc-windows-gnu' uses: actions/upload-artifact@v2 with: - name: ${{ matrix.env.TARGET_TRIPLE }} + name: cg_clif-${{ matrix.env.TARGET_TRIPLE }} path: cg_clif.tar.xz - name: Upload prebuilt cg_clif (cross compile) @@ -217,5 +217,5 @@ jobs: - name: Upload prebuilt cg_clif uses: actions/upload-artifact@v2 with: - name: ${{ matrix.env.TARGET_TRIPLE }} + name: cg_clif-${{ matrix.env.TARGET_TRIPLE }} path: cg_clif.tar From 102a577bb3ec92b8fddc155975d2d9980d25ecdc Mon Sep 17 00:00:00 2001 From: Urgau Date: Sat, 24 Sep 2022 12:34:56 +0200 Subject: [PATCH 090/309] Stabilize bench_black_box --- example/std_example.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/std_example.rs b/example/std_example.rs index 0b5b6cd55d72..ad108c34992e 100644 --- a/example/std_example.rs +++ b/example/std_example.rs @@ -1,4 +1,4 @@ -#![feature(core_intrinsics, generators, generator_trait, is_sorted, bench_black_box)] +#![feature(core_intrinsics, generators, generator_trait, is_sorted)] #[cfg(target_arch = "x86_64")] use std::arch::x86_64::*; From 0fd3bbe6cf5100ff3bcddc4daffbaa02f9de04d5 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Sat, 1 Oct 2022 01:06:35 -0700 Subject: [PATCH 091/309] Shorten the `lookup_line` code slightly The `match` looks like it's exactly the same as `checked_sub(1)`, so we might as well see if perf says we can just do that to save a couple lines. --- compiler/rustc_span/src/lib.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 366fd9d2cd1c..d8ba3d57864d 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -1628,10 +1628,7 @@ impl SourceFile { /// number. If the source_file is empty or the position is located before the /// first line, `None` is returned. pub fn lookup_line(&self, pos: BytePos) -> Option { - self.lines(|lines| match lines.partition_point(|x| x <= &pos) { - 0 => None, - i => Some(i - 1), - }) + self.lines(|lines| lines.partition_point(|x| x <= &pos).checked_sub(1)) } pub fn line_bounds(&self, line_index: usize) -> Range { From b91dc035106f4c203d67d2a0db0077c7a887224b Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Sat, 1 Oct 2022 18:59:17 +0200 Subject: [PATCH 092/309] Add `as_ptr_cast_mut` lint This lint detects calls to a `&self`-taking `as_ptr` method, where the result is then immediately cast to a `*mut T`. Code like this is probably invalid, as that pointer will not have write permissions, and `*mut T` is usually used to write through. --- CHANGELOG.md | 1 + clippy_lints/src/casts/as_ptr_cast_mut.rs | 38 +++++++++++++++++++++++ clippy_lints/src/casts/mod.rs | 31 +++++++++++++++++- clippy_lints/src/lib.register_lints.rs | 1 + clippy_lints/src/lib.register_nursery.rs | 1 + src/docs.rs | 1 + src/docs/as_ptr_cast_mut.txt | 19 ++++++++++++ tests/ui/as_ptr_cast_mut.rs | 37 ++++++++++++++++++++++ tests/ui/as_ptr_cast_mut.stderr | 16 ++++++++++ 9 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 clippy_lints/src/casts/as_ptr_cast_mut.rs create mode 100644 src/docs/as_ptr_cast_mut.txt create mode 100644 tests/ui/as_ptr_cast_mut.rs create mode 100644 tests/ui/as_ptr_cast_mut.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index ef6140152ffc..754d5f506d4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3735,6 +3735,7 @@ Released 2018-09-13 [`approx_constant`]: https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant [`arithmetic_side_effects`]: https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic_side_effects [`as_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_conversions +[`as_ptr_cast_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_ptr_cast_mut [`as_underscore`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_underscore [`assertions_on_constants`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_constants [`assertions_on_result_states`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_result_states diff --git a/clippy_lints/src/casts/as_ptr_cast_mut.rs b/clippy_lints/src/casts/as_ptr_cast_mut.rs new file mode 100644 index 000000000000..9409f4844f54 --- /dev/null +++ b/clippy_lints/src/casts/as_ptr_cast_mut.rs @@ -0,0 +1,38 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_opt; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_middle::{ + mir::Mutability, + ty::{self, Ty, TypeAndMut}, +}; + +use super::AS_PTR_CAST_MUT; + +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_to: Ty<'_>) { + if let ty::RawPtr(ptrty @ TypeAndMut { mutbl: Mutability::Mut, .. }) = cast_to.kind() + && let ty::RawPtr(TypeAndMut { mutbl: Mutability::Not, .. }) = + cx.typeck_results().node_type(cast_expr.hir_id).kind() + && let ExprKind::MethodCall(method_name, receiver, [], _) = cast_expr.peel_blocks().kind + && method_name.ident.name == rustc_span::sym::as_ptr + && let Some(as_ptr_did) = cx.typeck_results().type_dependent_def_id(cast_expr.peel_blocks().hir_id) + && let as_ptr_sig = cx.tcx.fn_sig(as_ptr_did) + && let Some(first_param_ty) = as_ptr_sig.skip_binder().inputs().iter().next() + && let ty::Ref(_, _, Mutability::Not) = first_param_ty.kind() + && let Some(recv) = snippet_opt(cx, receiver.span) + { + // `as_mut_ptr` might not exist + let applicability = Applicability::MaybeIncorrect; + + span_lint_and_sugg( + cx, + AS_PTR_CAST_MUT, + expr.span, + &format!("casting the result of `as_ptr` to *{ptrty}"), + "replace with", + format!("{recv}.as_mut_ptr()"), + applicability + ); + } +} diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index cc5d346b954e..fb0bd4d30f76 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -1,3 +1,4 @@ +mod as_ptr_cast_mut; mod as_underscore; mod borrow_as_ptr; mod cast_abs_to_unsigned; @@ -596,6 +597,32 @@ declare_clippy_lint! { "casting a slice created from a pointer and length to a slice pointer" } +declare_clippy_lint! { + /// ### What it does + /// Checks for the result of a `&self`-taking `as_ptr` being cast to a mutable pointer + /// + /// ### Why is this bad? + /// Since `as_ptr` took a `&self`, the pointer won't have write permissions, making it + /// unlikely that having it as a mutable pointer is correct. + /// + /// ### Example + /// ```rust + /// let string = String::with_capacity(1); + /// let ptr = string.as_ptr() as *mut _; + /// unsafe { ptr.write(4) }; // UNDEFINED BEHAVIOUR + /// ``` + /// Use instead: + /// ```rust + /// let mut string = String::with_capacity(1); + /// let string = string.as_mut_ptr(); + /// unsafe { ptr.write(4) }; + /// ``` + #[clippy::version = "1.66.0"] + pub AS_PTR_CAST_MUT, + nursery, + "casting the result of the `&self`-taking as_ptr to a mutabe point" +} + pub struct Casts { msrv: Option, } @@ -627,7 +654,8 @@ impl_lint_pass!(Casts => [ CAST_ABS_TO_UNSIGNED, AS_UNDERSCORE, BORROW_AS_PTR, - CAST_SLICE_FROM_RAW_PARTS + CAST_SLICE_FROM_RAW_PARTS, + AS_PTR_CAST_MUT, ]); impl<'tcx> LateLintPass<'tcx> for Casts { @@ -653,6 +681,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts { return; } cast_slice_from_raw_parts::check(cx, expr, cast_expr, cast_to, self.msrv); + as_ptr_cast_mut::check(cx, expr, cast_expr, cast_to); fn_to_numeric_cast_any::check(cx, expr, cast_expr, cast_from, cast_to); fn_to_numeric_cast::check(cx, expr, cast_expr, cast_from, cast_to); fn_to_numeric_cast_with_truncation::check(cx, expr, cast_expr, cast_from, cast_to); diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index ee08d802ccfb..2c8497013655 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -66,6 +66,7 @@ store.register_lints(&[ cargo::NEGATIVE_FEATURE_NAMES, cargo::REDUNDANT_FEATURE_NAMES, cargo::WILDCARD_DEPENDENCIES, + casts::AS_PTR_CAST_MUT, casts::AS_UNDERSCORE, casts::BORROW_AS_PTR, casts::CAST_ABS_TO_UNSIGNED, diff --git a/clippy_lints/src/lib.register_nursery.rs b/clippy_lints/src/lib.register_nursery.rs index 87be0052028f..a75bc81b2222 100644 --- a/clippy_lints/src/lib.register_nursery.rs +++ b/clippy_lints/src/lib.register_nursery.rs @@ -4,6 +4,7 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ LintId::of(attrs::EMPTY_LINE_AFTER_OUTER_ATTR), + LintId::of(casts::AS_PTR_CAST_MUT), LintId::of(cognitive_complexity::COGNITIVE_COMPLEXITY), LintId::of(copies::BRANCHES_SHARING_CODE), LintId::of(derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ), diff --git a/src/docs.rs b/src/docs.rs index 166be0618ffd..c3cbd2942d86 100644 --- a/src/docs.rs +++ b/src/docs.rs @@ -28,6 +28,7 @@ docs! { "approx_constant", "arithmetic_side_effects", "as_conversions", + "as_ptr_cast_mut", "as_underscore", "assertions_on_constants", "assertions_on_result_states", diff --git a/src/docs/as_ptr_cast_mut.txt b/src/docs/as_ptr_cast_mut.txt new file mode 100644 index 000000000000..0192a0b686fb --- /dev/null +++ b/src/docs/as_ptr_cast_mut.txt @@ -0,0 +1,19 @@ +### What it does +Checks for the result of a `&self`-taking `as_ptr` being cast to a mutable pointer + +### Why is this bad? +Since `as_ptr` took a `&self`, the pointer won't have write permissions, making it +unlikely that having it as a mutable pointer is correct. + +### Example +``` +let string = String::with_capacity(1); +let ptr = string.as_ptr() as *mut _; +unsafe { ptr.write(4) }; // UNDEFINED BEHAVIOUR +``` +Use instead: +``` +let mut string = String::with_capacity(1); +let string = string.as_mut_ptr(); +unsafe { ptr.write(4) }; +``` \ No newline at end of file diff --git a/tests/ui/as_ptr_cast_mut.rs b/tests/ui/as_ptr_cast_mut.rs new file mode 100644 index 000000000000..0d1d9258433b --- /dev/null +++ b/tests/ui/as_ptr_cast_mut.rs @@ -0,0 +1,37 @@ +#![allow(unused)] +#![warn(clippy::as_ptr_cast_mut)] +#![allow(clippy::wrong_self_convention)] + +struct MutPtrWrapper(Vec); +impl MutPtrWrapper { + fn as_ptr(&mut self) -> *const u8 { + self.0.as_mut_ptr() as *const u8 + } +} + +struct Covariant(*const T); +impl Covariant { + fn as_ptr(self) -> *const T { + self.0 + } +} + +fn main() { + let mut string = String::new(); + let _ = string.as_ptr() as *mut u8; + let _: *mut i8 = string.as_ptr() as *mut _; + let _ = string.as_ptr() as *const i8; + let _ = string.as_mut_ptr(); + let _ = string.as_mut_ptr() as *mut u8; + let _ = string.as_mut_ptr() as *const u8; + + let nn = std::ptr::NonNull::new(4 as *mut u8).unwrap(); + let _ = nn.as_ptr() as *mut u8; + + let mut wrap = MutPtrWrapper(Vec::new()); + let _ = wrap.as_ptr() as *mut u8; + + let mut local = 4; + let ref_with_write_perm = Covariant(std::ptr::addr_of_mut!(local) as *const _); + let _ = ref_with_write_perm.as_ptr() as *mut u8; +} diff --git a/tests/ui/as_ptr_cast_mut.stderr b/tests/ui/as_ptr_cast_mut.stderr new file mode 100644 index 000000000000..2189c3d2f855 --- /dev/null +++ b/tests/ui/as_ptr_cast_mut.stderr @@ -0,0 +1,16 @@ +error: casting the result of `as_ptr` to *mut u8 + --> $DIR/as_ptr_cast_mut.rs:21:13 + | +LL | let _ = string.as_ptr() as *mut u8; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `string.as_mut_ptr()` + | + = note: `-D clippy::as-ptr-cast-mut` implied by `-D warnings` + +error: casting the result of `as_ptr` to *mut i8 + --> $DIR/as_ptr_cast_mut.rs:22:22 + | +LL | let _: *mut i8 = string.as_ptr() as *mut _; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `string.as_mut_ptr()` + +error: aborting due to 2 previous errors + From 169ef781f9ddc9954a2e1aa58351cd14983c9ae6 Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Sat, 1 Oct 2022 20:10:43 +0200 Subject: [PATCH 093/309] Improve wording --- clippy_lints/src/casts/mod.rs | 6 +++--- src/docs/as_ptr_cast_mut.txt | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index fb0bd4d30f76..9a48e8d064a5 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -602,8 +602,8 @@ declare_clippy_lint! { /// Checks for the result of a `&self`-taking `as_ptr` being cast to a mutable pointer /// /// ### Why is this bad? - /// Since `as_ptr` took a `&self`, the pointer won't have write permissions, making it - /// unlikely that having it as a mutable pointer is correct. + /// Since `as_ptr` takes a `&self`, the pointer won't have write permissions unless interior + /// mutability is used, making it unlikely that having it as a mutable pointer is correct. /// /// ### Example /// ```rust @@ -620,7 +620,7 @@ declare_clippy_lint! { #[clippy::version = "1.66.0"] pub AS_PTR_CAST_MUT, nursery, - "casting the result of the `&self`-taking as_ptr to a mutabe point" + "casting the result of the `&self`-taking `as_ptr` to a mutabe pointer" } pub struct Casts { diff --git a/src/docs/as_ptr_cast_mut.txt b/src/docs/as_ptr_cast_mut.txt index 0192a0b686fb..83332a2559d1 100644 --- a/src/docs/as_ptr_cast_mut.txt +++ b/src/docs/as_ptr_cast_mut.txt @@ -2,8 +2,8 @@ Checks for the result of a `&self`-taking `as_ptr` being cast to a mutable pointer ### Why is this bad? -Since `as_ptr` took a `&self`, the pointer won't have write permissions, making it -unlikely that having it as a mutable pointer is correct. +Since `as_ptr` takes a `&self`, the pointer won't have write permissions unless interior +mutability is used, making it unlikely that having it as a mutable pointer is correct. ### Example ``` From 2b944d0c38ebfb99252ccfcb6200db28a217ee07 Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Sat, 1 Oct 2022 20:31:54 +0200 Subject: [PATCH 094/309] Fix example --- clippy_lints/src/casts/mod.rs | 4 ++-- src/docs/as_ptr_cast_mut.txt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index 9a48e8d064a5..b8f0dedf5ed9 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -608,13 +608,13 @@ declare_clippy_lint! { /// ### Example /// ```rust /// let string = String::with_capacity(1); - /// let ptr = string.as_ptr() as *mut _; + /// let ptr = string.as_ptr() as *mut u8; /// unsafe { ptr.write(4) }; // UNDEFINED BEHAVIOUR /// ``` /// Use instead: /// ```rust /// let mut string = String::with_capacity(1); - /// let string = string.as_mut_ptr(); + /// let ptr = string.as_mut_ptr(); /// unsafe { ptr.write(4) }; /// ``` #[clippy::version = "1.66.0"] diff --git a/src/docs/as_ptr_cast_mut.txt b/src/docs/as_ptr_cast_mut.txt index 83332a2559d1..228dde996bb2 100644 --- a/src/docs/as_ptr_cast_mut.txt +++ b/src/docs/as_ptr_cast_mut.txt @@ -8,12 +8,12 @@ mutability is used, making it unlikely that having it as a mutable pointer is co ### Example ``` let string = String::with_capacity(1); -let ptr = string.as_ptr() as *mut _; +let ptr = string.as_ptr() as *mut u8; unsafe { ptr.write(4) }; // UNDEFINED BEHAVIOUR ``` Use instead: ``` let mut string = String::with_capacity(1); -let string = string.as_mut_ptr(); +let ptr = string.as_mut_ptr(); unsafe { ptr.write(4) }; ``` \ No newline at end of file From b89ac0cefcc19b99a5af902ced053344a8054568 Mon Sep 17 00:00:00 2001 From: kraktus Date: Mon, 3 Oct 2022 14:11:54 +0200 Subject: [PATCH 095/309] refactor `manual_filter` Move common functions to `manual_utils.rs`, better arm matching, use clippy utils `contains_unsafe_block` --- clippy_lints/src/matches/manual_filter.rs | 76 ++---- clippy_lints/src/matches/manual_map.rs | 287 +--------------------- clippy_lints/src/matches/manual_utils.rs | 278 +++++++++++++++++++++ clippy_lints/src/matches/mod.rs | 1 + clippy_utils/src/sugg.rs | 2 +- 5 files changed, 311 insertions(+), 333 deletions(-) create mode 100644 clippy_lints/src/matches/manual_utils.rs diff --git a/clippy_lints/src/matches/manual_filter.rs b/clippy_lints/src/matches/manual_filter.rs index 9931f1268ab3..66ba1f6f9c55 100644 --- a/clippy_lints/src/matches/manual_filter.rs +++ b/clippy_lints/src/matches/manual_filter.rs @@ -1,39 +1,20 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::visitors::contains_unsafe_block; use clippy_utils::{is_res_lang_ctor, path_res, path_to_local_id}; -use rustc_hir::intravisit::{walk_expr, Visitor}; + use rustc_hir::LangItem::OptionSome; -use rustc_hir::{Arm, Block, BlockCheckMode, Expr, ExprKind, HirId, Pat, PatKind, UnsafeSource}; +use rustc_hir::{Arm, Expr, ExprKind, HirId, Pat, PatKind}; use rustc_lint::LateContext; use rustc_span::{sym, SyntaxContext}; -use super::manual_map::{check_with, SomeExpr}; +use super::manual_utils::{check_with, SomeExpr}; use super::MANUAL_FILTER; -#[derive(Default)] -struct NeedsUnsafeBlock(pub bool); - -impl<'tcx> Visitor<'tcx> for NeedsUnsafeBlock { - fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { - match expr.kind { - ExprKind::Block( - Block { - rules: BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided), - .. - }, - _, - ) => { - self.0 = true; - }, - _ => walk_expr(self, expr), - } - } -} - -// Function called on the `expr` of `[&+]Some((ref | ref mut) x) => ` -// Need to check if it's of the `if {} else {}` +// Function called on the of `[&+]Some((ref | ref mut) x) => ` +// Need to check if it's of the form `=if {} else {}` // AND that only one `then/else_expr` resolves to `Some(x)` while the other resolves to `None` -// return `cond` if +// return the `cond` expression if so. fn get_cond_expr<'tcx>( cx: &LateContext<'tcx>, pat: &Pat<'_>, @@ -45,15 +26,13 @@ fn get_cond_expr<'tcx>( if let ExprKind::If(cond, then_expr, Some(else_expr)) = block_expr.kind; if let PatKind::Binding(_,target, ..) = pat.kind; if let (then_visitor, else_visitor) - = (handle_if_or_else_expr(cx, target, ctxt, then_expr), - handle_if_or_else_expr(cx, target, ctxt, else_expr)); + = (is_some_expr(cx, target, ctxt, then_expr), + is_some_expr(cx, target, ctxt, else_expr)); if then_visitor != else_visitor; // check that one expr resolves to `Some(x)`, the other to `None` then { - let mut needs_unsafe_block = NeedsUnsafeBlock::default(); - needs_unsafe_block.visit_expr(expr); return Some(SomeExpr { expr: peels_blocks_incl_unsafe(cond.peel_drop_temps()), - needs_unsafe_block: needs_unsafe_block.0, + needs_unsafe_block: contains_unsafe_block(cx, expr), needs_negated: !then_visitor // if the `then_expr` resolves to `None`, need to negate the cond }) } @@ -63,7 +42,7 @@ fn get_cond_expr<'tcx>( fn peels_blocks_incl_unsafe_opt<'a>(expr: &'a Expr<'a>) -> Option<&'a Expr<'a>> { // we don't want to use `peel_blocks` here because we don't care if the block is unsafe, it's - // checked by `NeedsUnsafeBlock` + // checked by `contains_unsafe_block` if let ExprKind::Block(block, None) = expr.kind { if block.stmts.is_empty() { return block.expr; @@ -76,23 +55,18 @@ fn peels_blocks_incl_unsafe<'a>(expr: &'a Expr<'a>) -> &'a Expr<'a> { peels_blocks_incl_unsafe_opt(expr).unwrap_or(expr) } -// function called for each expression: +// function called for each expression: // Some(x) => if { -// +// // } else { -// +// // } -// Returns true if resolves to `Some(x)`, `false` otherwise -fn handle_if_or_else_expr<'tcx>( - cx: &LateContext<'_>, - target: HirId, - ctxt: SyntaxContext, - if_or_else_expr: &'tcx Expr<'_>, -) -> bool { - if let Some(inner_expr) = peels_blocks_incl_unsafe_opt(if_or_else_expr) { +// Returns true if resolves to `Some(x)`, `false` otherwise +fn is_some_expr<'tcx>(cx: &LateContext<'_>, target: HirId, ctxt: SyntaxContext, expr: &'tcx Expr<'_>) -> bool { + if let Some(inner_expr) = peels_blocks_incl_unsafe_opt(expr) { // there can be not statements in the block as they would be removed when switching to `.filter` if let ExprKind::Call(callee, [arg]) = inner_expr.kind { - return ctxt == if_or_else_expr.span.ctxt() + return ctxt == expr.span.ctxt() && is_res_lang_ctor(cx, path_res(cx, callee), OptionSome) && path_to_local_id(arg, target); } @@ -119,15 +93,13 @@ pub(super) fn check_match<'tcx>( expr: &'tcx Expr<'_>, ) { let ty = cx.typeck_results().expr_ty(expr); - if_chain! { - if is_type_diagnostic_item(cx, ty, sym::Option); - if arms.len() == 2; - if arms[0].guard.is_none(); - if arms[1].guard.is_none(); - then { - check(cx, expr, scrutinee, arms[0].pat, arms[0].body, Some(arms[1].pat), arms[1].body) + if is_type_diagnostic_item(cx, ty, sym::Option) + && let [first_arm, second_arm] = arms + && first_arm.guard.is_none() + && second_arm.guard.is_none() + { + check(cx, expr, scrutinee, first_arm.pat, first_arm.body, Some(second_arm.pat), second_arm.body); } - } } pub(super) fn check_if_let<'tcx>( diff --git a/clippy_lints/src/matches/manual_map.rs b/clippy_lints/src/matches/manual_map.rs index 2b6a07c5d744..aaba239677ff 100644 --- a/clippy_lints/src/matches/manual_map.rs +++ b/clippy_lints/src/matches/manual_map.rs @@ -1,21 +1,13 @@ +use super::manual_utils::{check_with, SomeExpr}; use super::MANUAL_MAP; -use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF}; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; -use clippy_utils::ty::{is_copy, is_type_diagnostic_item, peel_mid_ty_refs_is_mutable, type_is_unsafe_function}; -use clippy_utils::{ - can_move_expr_to_closure, 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, sugg::Sugg, CaptureKind, -}; -use rustc_ast::util::parser::PREC_POSTFIX; -use rustc_errors::Applicability; -use rustc_hir::LangItem::{OptionNone, OptionSome}; -use rustc_hir::{ - def::Res, Arm, BindingAnnotation, Block, BlockCheckMode, Expr, ExprKind, HirId, Mutability, Pat, PatKind, Path, - QPath, UnsafeSource, -}; + +use clippy_utils::{is_res_lang_ctor, path_res}; + +use rustc_hir::LangItem::OptionSome; +use rustc_hir::{Arm, Block, BlockCheckMode, Expr, ExprKind, Pat, UnsafeSource}; use rustc_lint::LateContext; -use rustc_span::{sym, SyntaxContext}; +use rustc_span::SyntaxContext; pub(super) fn check_match<'tcx>( cx: &LateContext<'tcx>, @@ -83,266 +75,6 @@ fn check<'tcx>( } } -#[expect(clippy::too_many_arguments)] -#[expect(clippy::too_many_lines)] -pub(super) fn check_with<'tcx, F>( - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'_>, - scrutinee: &'tcx Expr<'_>, - then_pat: &'tcx Pat<'_>, - then_body: &'tcx Expr<'_>, - else_pat: Option<&'tcx Pat<'_>>, - else_body: &'tcx Expr<'_>, - get_some_expr_fn: F, -) -> Option> -where - F: Fn(&LateContext<'tcx>, &'tcx Pat<'_>, &'tcx Expr<'_>, SyntaxContext) -> Option>, -{ - let (scrutinee_ty, ty_ref_count, ty_mutability) = - peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(scrutinee)); - if !(is_type_diagnostic_item(cx, scrutinee_ty, sym::Option) - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Option)) - { - return None; - } - - let expr_ctxt = expr.span.ctxt(); - let (some_expr, some_pat, pat_ref_count, is_wild_none) = match ( - try_parse_pattern(cx, then_pat, expr_ctxt), - else_pat.map_or(Some(OptionPat::Wild), |p| try_parse_pattern(cx, p, expr_ctxt)), - ) { - (Some(OptionPat::Wild), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_body) => { - (else_body, pattern, ref_count, true) - }, - (Some(OptionPat::None), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_body) => { - (else_body, pattern, ref_count, false) - }, - (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::Wild)) if is_none_expr(cx, else_body) => { - (then_body, pattern, ref_count, true) - }, - (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::None)) if is_none_expr(cx, else_body) => { - (then_body, pattern, ref_count, false) - }, - _ => return None, - }; - - // Top level or patterns aren't allowed in closures. - if matches!(some_pat.kind, PatKind::Or(_)) { - return None; - } - - let some_expr = match get_some_expr_fn(cx, some_pat, some_expr, expr_ctxt) { - Some(expr) => expr, - None => return None, - }; - - // These two lints will go back and forth with each other. - if cx.typeck_results().expr_ty(some_expr.expr) == cx.tcx.types.unit - && !is_lint_allowed(cx, OPTION_MAP_UNIT_FN, expr.hir_id) - { - return None; - } - - // `map` won't perform any adjustments. - if !cx.typeck_results().expr_adjustments(some_expr.expr).is_empty() { - return None; - } - - // Determine which binding mode to use. - let explicit_ref = some_pat.contains_explicit_ref_binding(); - let binding_ref = explicit_ref.or_else(|| (ty_ref_count != pat_ref_count).then_some(ty_mutability)); - - let as_ref_str = match binding_ref { - Some(Mutability::Mut) => ".as_mut()", - Some(Mutability::Not) => ".as_ref()", - None => "", - }; - - match can_move_expr_to_closure(cx, some_expr.expr) { - Some(captures) => { - // Check if captures the closure will need conflict with borrows made in the scrutinee. - // TODO: check all the references made in the scrutinee expression. This will require interacting - // with the borrow checker. Currently only `[.]*` is checked for. - if let Some(binding_ref_mutability) = binding_ref { - let e = peel_hir_expr_while(scrutinee, |e| match e.kind { - ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) => Some(e), - _ => None, - }); - if let ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(l), .. })) = e.kind { - match captures.get(l) { - Some(CaptureKind::Value | CaptureKind::Ref(Mutability::Mut)) => return None, - Some(CaptureKind::Ref(Mutability::Not)) if binding_ref_mutability == Mutability::Mut => { - return None; - }, - Some(CaptureKind::Ref(Mutability::Not)) | None => (), - } - } - } - }, - None => return None, - }; - - let mut app = Applicability::MachineApplicable; - - // Remove address-of expressions from the scrutinee. Either `as_ref` will be called, or - // it's being passed by value. - let scrutinee = peel_hir_expr_refs(scrutinee).0; - let (scrutinee_str, _) = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app); - let scrutinee_str = if scrutinee.span.ctxt() == expr.span.ctxt() && scrutinee.precedence().order() < PREC_POSTFIX { - format!("({scrutinee_str})") - } else { - scrutinee_str.into() - }; - - let closure_expr_snip = some_expr.to_snippet_with_context(cx, expr_ctxt, &mut app); - let body_str = if let PatKind::Binding(annotation, id, some_binding, None) = some_pat.kind { - if_chain! { - if !some_expr.needs_unsafe_block; - if let Some(func) = can_pass_as_func(cx, id, some_expr.expr); - if func.span.ctxt() == some_expr.expr.span.ctxt(); - then { - snippet_with_applicability(cx, func.span, "..", &mut app).into_owned() - } else { - if path_to_local_id(some_expr.expr, id) - && !is_lint_allowed(cx, MATCH_AS_REF, expr.hir_id) - && binding_ref.is_some() - { - return None; - } - - // `ref` and `ref mut` annotations were handled earlier. - let annotation = if matches!(annotation, BindingAnnotation::MUT) { - "mut " - } else { - "" - }; - - if some_expr.needs_unsafe_block { - format!("|{annotation}{some_binding}| unsafe {{ {closure_expr_snip} }}") - } else { - format!("|{annotation}{some_binding}| {closure_expr_snip}") - } - } - } - } else if !is_wild_none && explicit_ref.is_none() { - // TODO: handle explicit reference annotations. - let pat_snip = snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app).0; - if some_expr.needs_unsafe_block { - format!("|{pat_snip}| unsafe {{ {closure_expr_snip} }}") - } else { - format!("|{pat_snip}| {closure_expr_snip}") - } - } else { - // Refutable bindings and mixed reference annotations can't be handled by `map`. - return None; - }; - - // relies on the fact that Option: Copy where T: copy - let scrutinee_impl_copy = is_copy(cx, scrutinee_ty); - - Some(SuggInfo { - needs_brackets: else_pat.is_none() && is_else_clause(cx.tcx, expr), - scrutinee_impl_copy, - scrutinee_str, - as_ref_str, - body_str, - app, - }) -} - -pub struct SuggInfo<'a> { - pub needs_brackets: bool, - pub scrutinee_impl_copy: bool, - pub scrutinee_str: String, - pub as_ref_str: &'a str, - pub body_str: String, - pub app: Applicability, -} - -// Checks whether the expression could be passed as a function, or whether a closure is needed. -// Returns the function to be passed to `map` if it exists. -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) - && cx.typeck_results().expr_adjustments(arg).is_empty() - && !type_is_unsafe_function(cx, cx.typeck_results().expr_ty(func).peel_refs()) => - { - Some(func) - }, - _ => None, - } -} - -#[derive(Debug)] -pub(super) enum OptionPat<'a> { - Wild, - None, - Some { - // The pattern contained in the `Some` tuple. - pattern: &'a Pat<'a>, - // The number of references before the `Some` tuple. - // e.g. `&&Some(_)` has a ref count of 2. - ref_count: usize, - }, -} - -pub(super) struct SomeExpr<'tcx> { - pub expr: &'tcx Expr<'tcx>, - pub needs_unsafe_block: bool, - pub needs_negated: bool, // for `manual_filter` lint -} - -impl<'tcx> SomeExpr<'tcx> { - pub fn new_no_negated(expr: &'tcx Expr<'tcx>, needs_unsafe_block: bool) -> Self { - Self { - expr, - needs_unsafe_block, - needs_negated: false, - } - } - - pub fn to_snippet_with_context( - &self, - cx: &LateContext<'tcx>, - ctxt: SyntaxContext, - app: &mut Applicability, - ) -> Sugg<'tcx> { - let sugg = Sugg::hir_with_context(cx, self.expr, ctxt, "..", app); - if self.needs_negated { !sugg } else { sugg } - } -} - -// Try to parse into a recognized `Option` pattern. -// i.e. `_`, `None`, `Some(..)`, or a reference to any of those. -pub(super) fn try_parse_pattern<'tcx>( - cx: &LateContext<'tcx>, - pat: &'tcx Pat<'_>, - ctxt: SyntaxContext, -) -> Option> { - fn f<'tcx>( - cx: &LateContext<'tcx>, - pat: &'tcx Pat<'_>, - ref_count: usize, - ctxt: SyntaxContext, - ) -> Option> { - match pat.kind { - PatKind::Wild => Some(OptionPat::Wild), - PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt), - PatKind::Path(ref qpath) if is_res_lang_ctor(cx, cx.qpath_res(qpath, pat.hir_id), 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 => - { - Some(OptionPat::Some { pattern, ref_count }) - }, - _ => None, - } - } - f(cx, pat, 0, ctxt) -} - // Checks for an expression wrapped by the `Some` constructor. Returns the contained expression. fn get_some_expr<'tcx>( cx: &LateContext<'tcx>, @@ -382,8 +114,3 @@ fn get_some_expr<'tcx>( } get_some_expr_internal(cx, expr, false, ctxt) } - -// 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) -} diff --git a/clippy_lints/src/matches/manual_utils.rs b/clippy_lints/src/matches/manual_utils.rs new file mode 100644 index 000000000000..792908aa7dfc --- /dev/null +++ b/clippy_lints/src/matches/manual_utils.rs @@ -0,0 +1,278 @@ +use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF}; +use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; +use clippy_utils::ty::{is_copy, is_type_diagnostic_item, peel_mid_ty_refs_is_mutable, type_is_unsafe_function}; +use clippy_utils::{ + can_move_expr_to_closure, 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, sugg::Sugg, CaptureKind, +}; +use rustc_ast::util::parser::PREC_POSTFIX; +use rustc_errors::Applicability; +use rustc_hir::LangItem::{OptionNone, OptionSome}; +use rustc_hir::{def::Res, BindingAnnotation, Expr, ExprKind, HirId, Mutability, Pat, PatKind, Path, QPath}; +use rustc_lint::LateContext; +use rustc_span::{sym, SyntaxContext}; + +#[expect(clippy::too_many_arguments)] +#[expect(clippy::too_many_lines)] +pub(super) fn check_with<'tcx, F>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + scrutinee: &'tcx Expr<'_>, + then_pat: &'tcx Pat<'_>, + then_body: &'tcx Expr<'_>, + else_pat: Option<&'tcx Pat<'_>>, + else_body: &'tcx Expr<'_>, + get_some_expr_fn: F, +) -> Option> +where + F: Fn(&LateContext<'tcx>, &'tcx Pat<'_>, &'tcx Expr<'_>, SyntaxContext) -> Option>, +{ + let (scrutinee_ty, ty_ref_count, ty_mutability) = + peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(scrutinee)); + if !(is_type_diagnostic_item(cx, scrutinee_ty, sym::Option) + && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Option)) + { + return None; + } + + let expr_ctxt = expr.span.ctxt(); + let (some_expr, some_pat, pat_ref_count, is_wild_none) = match ( + try_parse_pattern(cx, then_pat, expr_ctxt), + else_pat.map_or(Some(OptionPat::Wild), |p| try_parse_pattern(cx, p, expr_ctxt)), + ) { + (Some(OptionPat::Wild), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_body) => { + (else_body, pattern, ref_count, true) + }, + (Some(OptionPat::None), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_body) => { + (else_body, pattern, ref_count, false) + }, + (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::Wild)) if is_none_expr(cx, else_body) => { + (then_body, pattern, ref_count, true) + }, + (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::None)) if is_none_expr(cx, else_body) => { + (then_body, pattern, ref_count, false) + }, + _ => return None, + }; + + // Top level or patterns aren't allowed in closures. + if matches!(some_pat.kind, PatKind::Or(_)) { + return None; + } + + let some_expr = match get_some_expr_fn(cx, some_pat, some_expr, expr_ctxt) { + Some(expr) => expr, + None => return None, + }; + + // These two lints will go back and forth with each other. + if cx.typeck_results().expr_ty(some_expr.expr) == cx.tcx.types.unit + && !is_lint_allowed(cx, OPTION_MAP_UNIT_FN, expr.hir_id) + { + return None; + } + + // `map` won't perform any adjustments. + if !cx.typeck_results().expr_adjustments(some_expr.expr).is_empty() { + return None; + } + + // Determine which binding mode to use. + let explicit_ref = some_pat.contains_explicit_ref_binding(); + let binding_ref = explicit_ref.or_else(|| (ty_ref_count != pat_ref_count).then_some(ty_mutability)); + + let as_ref_str = match binding_ref { + Some(Mutability::Mut) => ".as_mut()", + Some(Mutability::Not) => ".as_ref()", + None => "", + }; + + match can_move_expr_to_closure(cx, some_expr.expr) { + Some(captures) => { + // Check if captures the closure will need conflict with borrows made in the scrutinee. + // TODO: check all the references made in the scrutinee expression. This will require interacting + // with the borrow checker. Currently only `[.]*` is checked for. + if let Some(binding_ref_mutability) = binding_ref { + let e = peel_hir_expr_while(scrutinee, |e| match e.kind { + ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) => Some(e), + _ => None, + }); + if let ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(l), .. })) = e.kind { + match captures.get(l) { + Some(CaptureKind::Value | CaptureKind::Ref(Mutability::Mut)) => return None, + Some(CaptureKind::Ref(Mutability::Not)) if binding_ref_mutability == Mutability::Mut => { + return None; + }, + Some(CaptureKind::Ref(Mutability::Not)) | None => (), + } + } + } + }, + None => return None, + }; + + let mut app = Applicability::MachineApplicable; + + // Remove address-of expressions from the scrutinee. Either `as_ref` will be called, or + // it's being passed by value. + let scrutinee = peel_hir_expr_refs(scrutinee).0; + let (scrutinee_str, _) = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app); + let scrutinee_str = if scrutinee.span.ctxt() == expr.span.ctxt() && scrutinee.precedence().order() < PREC_POSTFIX { + format!("({scrutinee_str})") + } else { + scrutinee_str.into() + }; + + let closure_expr_snip = some_expr.to_snippet_with_context(cx, expr_ctxt, &mut app); + let body_str = if let PatKind::Binding(annotation, id, some_binding, None) = some_pat.kind { + if_chain! { + if !some_expr.needs_unsafe_block; + if let Some(func) = can_pass_as_func(cx, id, some_expr.expr); + if func.span.ctxt() == some_expr.expr.span.ctxt(); + then { + snippet_with_applicability(cx, func.span, "..", &mut app).into_owned() + } else { + if path_to_local_id(some_expr.expr, id) + && !is_lint_allowed(cx, MATCH_AS_REF, expr.hir_id) + && binding_ref.is_some() + { + return None; + } + + // `ref` and `ref mut` annotations were handled earlier. + let annotation = if matches!(annotation, BindingAnnotation::MUT) { + "mut " + } else { + "" + }; + + if some_expr.needs_unsafe_block { + format!("|{annotation}{some_binding}| unsafe {{ {closure_expr_snip} }}") + } else { + format!("|{annotation}{some_binding}| {closure_expr_snip}") + } + } + } + } else if !is_wild_none && explicit_ref.is_none() { + // TODO: handle explicit reference annotations. + let pat_snip = snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app).0; + if some_expr.needs_unsafe_block { + format!("|{pat_snip}| unsafe {{ {closure_expr_snip} }}") + } else { + format!("|{pat_snip}| {closure_expr_snip}") + } + } else { + // Refutable bindings and mixed reference annotations can't be handled by `map`. + return None; + }; + + // relies on the fact that Option: Copy where T: copy + let scrutinee_impl_copy = is_copy(cx, scrutinee_ty); + + Some(SuggInfo { + needs_brackets: else_pat.is_none() && is_else_clause(cx.tcx, expr), + scrutinee_impl_copy, + scrutinee_str, + as_ref_str, + body_str, + app, + }) +} + +pub struct SuggInfo<'a> { + pub needs_brackets: bool, + pub scrutinee_impl_copy: bool, + pub scrutinee_str: String, + pub as_ref_str: &'a str, + pub body_str: String, + pub app: Applicability, +} + +// Checks whether the expression could be passed as a function, or whether a closure is needed. +// Returns the function to be passed to `map` if it exists. +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) + && cx.typeck_results().expr_adjustments(arg).is_empty() + && !type_is_unsafe_function(cx, cx.typeck_results().expr_ty(func).peel_refs()) => + { + Some(func) + }, + _ => None, + } +} + +#[derive(Debug)] +pub(super) enum OptionPat<'a> { + Wild, + None, + Some { + // The pattern contained in the `Some` tuple. + pattern: &'a Pat<'a>, + // The number of references before the `Some` tuple. + // e.g. `&&Some(_)` has a ref count of 2. + ref_count: usize, + }, +} + +pub(super) struct SomeExpr<'tcx> { + pub expr: &'tcx Expr<'tcx>, + pub needs_unsafe_block: bool, + pub needs_negated: bool, // for `manual_filter` lint +} + +impl<'tcx> SomeExpr<'tcx> { + pub fn new_no_negated(expr: &'tcx Expr<'tcx>, needs_unsafe_block: bool) -> Self { + Self { + expr, + needs_unsafe_block, + needs_negated: false, + } + } + + pub fn to_snippet_with_context( + &self, + cx: &LateContext<'tcx>, + ctxt: SyntaxContext, + app: &mut Applicability, + ) -> Sugg<'tcx> { + let sugg = Sugg::hir_with_context(cx, self.expr, ctxt, "..", app); + if self.needs_negated { !sugg } else { sugg } + } +} + +// Try to parse into a recognized `Option` pattern. +// i.e. `_`, `None`, `Some(..)`, or a reference to any of those. +pub(super) fn try_parse_pattern<'tcx>( + cx: &LateContext<'tcx>, + pat: &'tcx Pat<'_>, + ctxt: SyntaxContext, +) -> Option> { + fn f<'tcx>( + cx: &LateContext<'tcx>, + pat: &'tcx Pat<'_>, + ref_count: usize, + ctxt: SyntaxContext, + ) -> Option> { + match pat.kind { + PatKind::Wild => Some(OptionPat::Wild), + PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt), + PatKind::Path(ref qpath) if is_res_lang_ctor(cx, cx.qpath_res(qpath, pat.hir_id), 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 => + { + Some(OptionPat::Some { pattern, ref_count }) + }, + _ => None, + } + } + f(cx, pat, 0, ctxt) +} + +// 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) +} diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index cf1bd7a12ade..c472d6280985 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -3,6 +3,7 @@ mod infallible_destructuring_match; mod manual_filter; mod manual_map; mod manual_unwrap_or; +mod manual_utils; mod match_as_ref; mod match_bool; mod match_like_matches; diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index e88542b77a67..f25bced0c2b3 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -112,7 +112,7 @@ impl<'a> Sugg<'a> { if expr.span.ctxt() == ctxt { Self::hir_from_snippet(expr, |span| snippet(cx, span, default)) } else { - let snip = snippet_with_context(cx, expr.span, ctxt, default, applicability).0; + let (snip, _) = snippet_with_context(cx, expr.span, ctxt, default, applicability); Sugg::NonParen(snip) } } From 830fdf2b56d2a2f0f8e8135e05ec30b08e54ad3a Mon Sep 17 00:00:00 2001 From: kraktus Date: Sun, 2 Oct 2022 17:45:44 +0200 Subject: [PATCH 096/309] update rust version introduction --- clippy_lints/src/matches/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index c472d6280985..7d8171ead89e 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -922,7 +922,7 @@ declare_clippy_lint! { /// ```rust /// Some(0).filter(|&x| x % 2 == 0); /// ``` - #[clippy::version = "1.65.0"] + #[clippy::version = "1.66.0"] pub MANUAL_FILTER, complexity, "reimplentation of `filter`" From 093b075d327d1c7c7e0730e46f1d5c2d4aa47f5a Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Sun, 2 Oct 2022 17:31:43 +0200 Subject: [PATCH 097/309] rustc: Use `unix_sigpipe` instead of `rustc_driver::set_sigpipe_handler` This is the first (known) step towards starting to use `unix_sigpipe` in the wild. Eventually, `rustc_driver::set_sigpipe_handler` can be removed and all clients can use `unix_sigpipe` instead. For now we just start using `unix_sigpipe` in once place: `rustc` itself. It is easy to manually verify this change. If you remove `#[unix_sigpipe = "sig_dfl"]` and run `./x.py build` you will get an ICE when you do `./build/x86_64-unknown-linux-gnu/stage1/bin/rustc --help | false`. Add back `#[unix_sigpipe = "sig_dfl"]` and the ICE disappears again. --- compiler/rustc/src/main.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/rustc/src/main.rs b/compiler/rustc/src/main.rs index 0de1a7819135..e21c9b660444 100644 --- a/compiler/rustc/src/main.rs +++ b/compiler/rustc/src/main.rs @@ -1,3 +1,5 @@ +#![feature(unix_sigpipe)] + // A note about jemalloc: rustc uses jemalloc when built for CI and // distribution. The obvious way to do this is with the `#[global_allocator]` // mechanism. However, for complicated reasons (see @@ -23,6 +25,7 @@ // libraries. So we must reference jemalloc symbols one way or another, because // this file is the only object code in the rustc executable. +#[unix_sigpipe = "sig_dfl"] fn main() { // See the comment at the top of this file for an explanation of this. #[cfg(feature = "jemalloc-sys")] @@ -58,6 +61,5 @@ fn main() { } } - rustc_driver::set_sigpipe_handler(); rustc_driver::main() } From 64e7fd952243b6724dcd4e14e177c3e972b342bc Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Sun, 2 Oct 2022 22:08:54 +0000 Subject: [PATCH 098/309] Slightly tweak comments wrt `lint_overflowing_range_endpoint` --- compiler/rustc_lint/src/types.rs | 74 ++++++++++++++++---------------- 1 file changed, 38 insertions(+), 36 deletions(-) diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index b6009bd800a5..3cdca2178657 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -116,8 +116,8 @@ impl TypeLimits { } } -/// Attempts to special-case the overflowing literal lint when it occurs as a range endpoint. -/// Returns `true` iff the lint was overridden. +/// Attempts to special-case the overflowing literal lint when it occurs as a range endpoint (`expr..MAX+1`). +/// Returns `true` iff the lint was emitted. fn lint_overflowing_range_endpoint<'tcx>( cx: &LateContext<'tcx>, lit: &hir::Lit, @@ -140,44 +140,46 @@ fn lint_overflowing_range_endpoint<'tcx>( return false; } - let mut overwritten = false; // We can suggest using an inclusive range // (`..=`) instead only if it is the `end` that is // overflowing and only by 1. - if eps[1].expr.hir_id == expr.hir_id && lit_val - 1 == max - && let Ok(start) = cx.sess().source_map().span_to_snippet(eps[0].span) - { - cx.struct_span_lint( - OVERFLOWING_LITERALS, - struct_expr.span, - fluent::lint::range_endpoint_out_of_range, - |lint| { - use ast::{LitIntType, LitKind}; + if !(eps[1].expr.hir_id == expr.hir_id && lit_val - 1 == max) { + return false; + }; + let Ok(start) = cx.sess().source_map().span_to_snippet(eps[0].span) else { return false }; - lint.set_arg("ty", ty); + cx.struct_span_lint( + OVERFLOWING_LITERALS, + struct_expr.span, + fluent::lint::range_endpoint_out_of_range, + |lint| { + use ast::{LitIntType, LitKind}; - // We need to preserve the literal's suffix, - // as it may determine typing information. - let suffix = match lit.node { - LitKind::Int(_, LitIntType::Signed(s)) => s.name_str(), - LitKind::Int(_, LitIntType::Unsigned(s)) => s.name_str(), - LitKind::Int(_, LitIntType::Unsuffixed) => "", - _ => bug!(), - }; - let suggestion = format!("{}..={}{}", start, lit_val - 1, suffix); - lint.span_suggestion( - struct_expr.span, - fluent::lint::suggestion, - suggestion, - Applicability::MachineApplicable, - ); - overwritten = true; + lint.set_arg("ty", ty); - lint - }, - ); - } - overwritten + // We need to preserve the literal's suffix, + // as it may determine typing information. + let suffix = match lit.node { + LitKind::Int(_, LitIntType::Signed(s)) => s.name_str(), + LitKind::Int(_, LitIntType::Unsigned(s)) => s.name_str(), + LitKind::Int(_, LitIntType::Unsuffixed) => "", + _ => bug!(), + }; + let suggestion = format!("{}..={}{}", start, lit_val - 1, suffix); + lint.span_suggestion( + struct_expr.span, + fluent::lint::suggestion, + suggestion, + Applicability::MachineApplicable, + ); + + lint + }, + ); + + // We've just emitted a lint, special cased for `(...)..MAX+1` ranges, + // return `true` so the callers don't also emit a lint + true } // For `isize` & `usize`, be conservative with the warnings, so that the @@ -358,7 +360,7 @@ fn lint_int_literal<'tcx>( } if lint_overflowing_range_endpoint(cx, lit, v, max, e, t.name_str()) { - // The overflowing literal lint was overridden. + // The overflowing literal lint was emited by `lint_overflowing_range_endpoint`. return; } @@ -427,7 +429,7 @@ fn lint_uint_literal<'tcx>( } } if lint_overflowing_range_endpoint(cx, lit, lit_val, max, e, t.name_str()) { - // The overflowing literal lint was overridden. + // The overflowing literal lint was emited by `lint_overflowing_range_endpoint`. return; } if let Some(repr_str) = get_bin_hex_repr(cx, lit) { From 1d1827413f165d4b081253aa9b225a52a24d3c1d Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 5 Oct 2022 13:26:50 +0200 Subject: [PATCH 099/309] Rustup to rustc 1.66.0-nightly (01af5040f 2022-10-04) --- build_sysroot/Cargo.lock | 8 ++++---- example/mini_core.rs | 6 ++++++ rust-toolchain | 2 +- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/build_sysroot/Cargo.lock b/build_sysroot/Cargo.lock index a8077feaaaba..683ec4940f9e 100644 --- a/build_sysroot/Cargo.lock +++ b/build_sysroot/Cargo.lock @@ -66,9 +66,9 @@ dependencies = [ [[package]] name = "compiler_builtins" -version = "0.1.80" +version = "0.1.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3feb824e4dde07fb32acc53c8cc1f9e537bb4d7e0a331198000840979d410d3" +checksum = "7927b44b9659f9a9af77eb0e6d98d8172ee5aec6924ed9360ae527cdd8551245" dependencies = [ "rustc-std-workspace-core", ] @@ -145,9 +145,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.132" +version = "0.2.134" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5" +checksum = "329c933548736bc49fd575ee68c89e8be4d260064184389a5b77517cddd99ffb" dependencies = [ "rustc-std-workspace-core", ] diff --git a/example/mini_core.rs b/example/mini_core.rs index 42f8aa50ba1a..7f85b52f083a 100644 --- a/example/mini_core.rs +++ b/example/mini_core.rs @@ -559,16 +559,22 @@ pub union MaybeUninit { pub mod intrinsics { extern "rust-intrinsic" { + #[rustc_safe_intrinsic] pub fn abort() -> !; + #[rustc_safe_intrinsic] pub fn size_of() -> usize; pub fn size_of_val(val: *const T) -> usize; + #[rustc_safe_intrinsic] pub fn min_align_of() -> usize; pub fn min_align_of_val(val: *const T) -> usize; pub fn copy(src: *const T, dst: *mut T, count: usize); pub fn transmute(e: T) -> U; pub fn ctlz_nonzero(x: T) -> T; + #[rustc_safe_intrinsic] pub fn needs_drop() -> bool; + #[rustc_safe_intrinsic] pub fn bitreverse(x: T) -> T; + #[rustc_safe_intrinsic] pub fn bswap(x: T) -> T; pub fn write_bytes(dst: *mut T, val: u8, count: usize); } diff --git a/rust-toolchain b/rust-toolchain index 06e9106e685a..6799b9790840 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2022-09-25" +channel = "nightly-2022-10-05" components = ["rust-src", "rustc-dev", "llvm-tools-preview"] From dae6a30d0b888a02e8b3a450510b8683f920cb16 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 5 Oct 2022 13:55:13 +0200 Subject: [PATCH 100/309] Remove workaround for rustbuild bug --- scripts/setup_rust_fork.sh | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/scripts/setup_rust_fork.sh b/scripts/setup_rust_fork.sh index 25103a2fd503..d6a37789599f 100644 --- a/scripts/setup_rust_fork.sh +++ b/scripts/setup_rust_fork.sh @@ -27,21 +27,6 @@ index d95b5b7f17f..00b6f0e3635 100644 [dev-dependencies] rand = "0.7" rand_xorshift = "0.2" -diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs -index a6333976f2a..c9a872e876c 100644 ---- a/src/bootstrap/config.rs -+++ b/src/bootstrap/config.rs -@@ -917,6 +917,10 @@ pub fn parse(args: &[String]) -> Config { - config.initial_cargo = config.out.join(config.build.triple).join("stage0/bin/cargo"); - } - -+ // Workaround for rustbuild bug -+ config.initial_rustc = PathBuf::from("$(pwd)/../build/rustc-clif"); -+ config.initial_cargo = PathBuf::from("$(rustup which cargo)"); -+ - // NOTE: it's important this comes *after* we set \`initial_rustc\` just above. - if config.dry_run { - let dir = config.out.join("tmp-dry-run"); diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 8431aa7b818..a3ff7e68ce5 100644 --- a/src/tools/compiletest/src/runtest.rs From 9e70a0ff12547f0764041c9cff8adf617d7762c5 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Wed, 5 Oct 2022 17:17:57 +0000 Subject: [PATCH 101/309] Replace if_chain with let chains in `clippy::author` output --- clippy_lints/src/utils/author.rs | 117 +++++++++-------- tests/ui/author.stdout | 24 ++-- tests/ui/author/blocks.stdout | 116 ++++++++--------- tests/ui/author/call.stdout | 28 ++--- tests/ui/author/if.stdout | 92 +++++++------- tests/ui/author/issue_3849.stdout | 24 ++-- tests/ui/author/loop.stdout | 202 ++++++++++++++---------------- tests/ui/author/matches.stdout | 72 ++++++----- tests/ui/author/repeat.stdout | 20 ++- tests/ui/author/struct.stdout | 112 ++++++++--------- 10 files changed, 387 insertions(+), 420 deletions(-) diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index e069de8cb5c7..0c052d86eda4 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -12,6 +12,7 @@ use rustc_hir::{ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::{Ident, Symbol}; +use std::cell::Cell; use std::fmt::{Display, Formatter, Write as _}; declare_clippy_lint! { @@ -37,15 +38,13 @@ declare_clippy_lint! { /// /// ```rust,ignore /// // ./tests/ui/new_lint.stdout - /// if_chain! { - /// if let ExprKind::If(ref cond, ref then, None) = item.kind, - /// if let ExprKind::Binary(BinOp::Eq, ref left, ref right) = cond.kind, - /// if let ExprKind::Path(ref path) = left.kind, - /// if let ExprKind::Lit(ref lit) = right.kind, - /// if let LitKind::Int(42, _) = lit.node, - /// then { - /// // report your lint here - /// } + /// if ExprKind::If(ref cond, ref then, None) = item.kind + /// && let ExprKind::Binary(BinOp::Eq, ref left, ref right) = cond.kind + /// && let ExprKind::Path(ref path) = left.kind + /// && let ExprKind::Lit(ref lit) = right.kind + /// && let LitKind::Int(42, _) = lit.node + /// { + /// // report your lint here /// } /// ``` pub LINT_AUTHOR, @@ -91,15 +90,16 @@ macro_rules! field { }; } -fn prelude() { - println!("if_chain! {{"); -} - -fn done() { - println!(" then {{"); - println!(" // report your lint here"); - println!(" }}"); - println!("}}"); +/// Print a condition of a let chain, `chain!(self, "let Some(x) = y")` will print +/// `if let Some(x) = y` on the first call and ` && let Some(x) = y` thereafter +macro_rules! chain { + ($self:ident, $($t:tt)*) => { + if $self.first.take() { + println!("if {}", format_args!($($t)*)); + } else { + println!(" && {}", format_args!($($t)*)); + } + } } impl<'tcx> LateLintPass<'tcx> for Author { @@ -149,9 +149,10 @@ fn check_item(cx: &LateContext<'_>, hir_id: HirId) { fn check_node(cx: &LateContext<'_>, hir_id: HirId, f: impl Fn(&PrintVisitor<'_, '_>)) { if has_attr(cx, hir_id) { - prelude(); f(&PrintVisitor::new(cx)); - done(); + println!("{{"); + println!(" // report your lint here"); + println!("}}"); } } @@ -195,7 +196,9 @@ struct PrintVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, /// Fields are the current index that needs to be appended to pattern /// binding names - ids: std::cell::Cell>, + ids: Cell>, + /// Currently at the first condition in the if chain + first: Cell, } #[allow(clippy::unused_self)] @@ -203,7 +206,8 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { fn new(cx: &'a LateContext<'tcx>) -> Self { Self { cx, - ids: std::cell::Cell::default(), + ids: Cell::default(), + first: Cell::new(true), } } @@ -226,10 +230,10 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { fn option(&self, option: &Binding>, name: &'static str, f: impl Fn(&Binding)) { match option.value { - None => out!("if {option}.is_none();"), + None => chain!(self, "{option}.is_none()"), Some(value) => { let value = &self.bind(name, value); - out!("if let Some({value}) = {option};"); + chain!(self, "let Some({value}) = {option}"); f(value); }, } @@ -237,9 +241,9 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { fn slice(&self, slice: &Binding<&[T]>, f: impl Fn(&Binding<&T>)) { if slice.value.is_empty() { - out!("if {slice}.is_empty();"); + chain!(self, "{slice}.is_empty()"); } else { - out!("if {slice}.len() == {};", slice.value.len()); + chain!(self, "{slice}.len() == {}", slice.value.len()); for (i, value) in slice.value.iter().enumerate() { let name = format!("{slice}[{i}]"); f(&Binding { name, value }); @@ -254,23 +258,23 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { } fn ident(&self, ident: &Binding) { - out!("if {ident}.as_str() == {:?};", ident.value.as_str()); + chain!(self, "{ident}.as_str() == {:?}", ident.value.as_str()); } fn symbol(&self, symbol: &Binding) { - out!("if {symbol}.as_str() == {:?};", symbol.value.as_str()); + chain!(self, "{symbol}.as_str() == {:?}", symbol.value.as_str()); } fn qpath(&self, qpath: &Binding<&QPath<'_>>) { if let QPath::LangItem(lang_item, ..) = *qpath.value { - out!("if matches!({qpath}, QPath::LangItem(LangItem::{lang_item:?}, _));"); + chain!(self, "matches!({qpath}, QPath::LangItem(LangItem::{lang_item:?}, _))"); } else { - out!("if match_qpath({qpath}, &[{}]);", path_to_string(qpath.value)); + chain!(self, "match_qpath({qpath}, &[{}])", path_to_string(qpath.value)); } } fn lit(&self, lit: &Binding<&Lit>) { - let kind = |kind| out!("if let LitKind::{kind} = {lit}.node;"); + let kind = |kind| chain!(self, "let LitKind::{kind} = {lit}.node"); macro_rules! kind { ($($t:tt)*) => (kind(format_args!($($t)*))); } @@ -298,7 +302,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { LitKind::ByteStr(ref vec) => { bind!(self, vec); kind!("ByteStr(ref {vec})"); - out!("if let [{:?}] = **{vec};", vec.value); + chain!(self, "let [{:?}] = **{vec}", vec.value); }, LitKind::Str(s, _) => { bind!(self, s); @@ -311,15 +315,15 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { fn arm(&self, arm: &Binding<&hir::Arm<'_>>) { self.pat(field!(arm.pat)); match arm.value.guard { - None => out!("if {arm}.guard.is_none();"), + None => chain!(self, "{arm}.guard.is_none()"), Some(hir::Guard::If(expr)) => { bind!(self, expr); - out!("if let Some(Guard::If({expr})) = {arm}.guard;"); + chain!(self, "let Some(Guard::If({expr})) = {arm}.guard"); self.expr(expr); }, Some(hir::Guard::IfLet(let_expr)) => { bind!(self, let_expr); - out!("if let Some(Guard::IfLet({let_expr}) = {arm}.guard;"); + chain!(self, "let Some(Guard::IfLet({let_expr}) = {arm}.guard"); self.pat(field!(let_expr.pat)); self.expr(field!(let_expr.init)); }, @@ -331,9 +335,10 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { fn expr(&self, expr: &Binding<&hir::Expr<'_>>) { if let Some(higher::While { condition, body }) = higher::While::hir(expr.value) { bind!(self, condition, body); - out!( - "if let Some(higher::While {{ condition: {condition}, body: {body} }}) \ - = higher::While::hir({expr});" + chain!( + self, + "let Some(higher::While {{ condition: {condition}, body: {body} }}) \ + = higher::While::hir({expr})" ); self.expr(condition); self.expr(body); @@ -347,9 +352,10 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { }) = higher::WhileLet::hir(expr.value) { bind!(self, let_pat, let_expr, if_then); - out!( - "if let Some(higher::WhileLet {{ let_pat: {let_pat}, let_expr: {let_expr}, if_then: {if_then} }}) \ - = higher::WhileLet::hir({expr});" + chain!( + self, + "let Some(higher::WhileLet {{ let_pat: {let_pat}, let_expr: {let_expr}, if_then: {if_then} }}) \ + = higher::WhileLet::hir({expr})" ); self.pat(let_pat); self.expr(let_expr); @@ -359,9 +365,10 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { if let Some(higher::ForLoop { pat, arg, body, .. }) = higher::ForLoop::hir(expr.value) { bind!(self, pat, arg, body); - out!( - "if let Some(higher::ForLoop {{ pat: {pat}, arg: {arg}, body: {body}, .. }}) \ - = higher::ForLoop::hir({expr});" + chain!( + self, + "let Some(higher::ForLoop {{ pat: {pat}, arg: {arg}, body: {body}, .. }}) \ + = higher::ForLoop::hir({expr})" ); self.pat(pat); self.expr(arg); @@ -369,7 +376,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { return; } - let kind = |kind| out!("if let ExprKind::{kind} = {expr}.kind;"); + let kind = |kind| chain!(self, "let ExprKind::{kind} = {expr}.kind"); macro_rules! kind { ($($t:tt)*) => (kind(format_args!($($t)*))); } @@ -383,7 +390,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { // if it's a path if let Some(TyKind::Path(ref qpath)) = let_expr.value.ty.as_ref().map(|ty| &ty.kind) { bind!(self, qpath); - out!("if let TyKind::Path(ref {qpath}) = {let_expr}.ty.kind;"); + chain!(self, "let TyKind::Path(ref {qpath}) = {let_expr}.ty.kind"); self.qpath(qpath); } self.expr(field!(let_expr.init)); @@ -419,7 +426,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { ExprKind::Binary(op, left, right) => { bind!(self, op, left, right); kind!("Binary({op}, {left}, {right})"); - out!("if BinOpKind::{:?} == {op}.node;", op.value.node); + chain!(self, "BinOpKind::{:?} == {op}.node", op.value.node); self.expr(left); self.expr(right); }, @@ -438,7 +445,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { kind!("Cast({expr}, {cast_ty})"); if let TyKind::Path(ref qpath) = cast_ty.value.kind { bind!(self, qpath); - out!("if let TyKind::Path(ref {qpath}) = {cast_ty}.kind;"); + chain!(self, "let TyKind::Path(ref {qpath}) = {cast_ty}.kind"); self.qpath(qpath); } self.expr(expr); @@ -485,7 +492,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { bind!(self, fn_decl, body_id); kind!("Closure(CaptureBy::{capture_clause:?}, {fn_decl}, {body_id}, _, {movability})"); - out!("if let {ret_ty} = {fn_decl}.output;"); + chain!(self, "let {ret_ty} = {fn_decl}.output"); self.body(body_id); }, ExprKind::Yield(sub, source) => { @@ -509,7 +516,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { ExprKind::AssignOp(op, target, value) => { bind!(self, op, target, value); kind!("AssignOp({op}, {target}, {value})"); - out!("if BinOpKind::{:?} == {op}.node;", op.value.node); + chain!(self, "BinOpKind::{:?} == {op}.node", op.value.node); self.expr(target); self.expr(value); }, @@ -573,10 +580,10 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { kind!("Repeat({value}, {length})"); self.expr(value); match length.value { - ArrayLen::Infer(..) => out!("if let ArrayLen::Infer(..) = length;"), + ArrayLen::Infer(..) => chain!(self, "let ArrayLen::Infer(..) = length"), ArrayLen::Body(anon_const) => { bind!(self, anon_const); - out!("if let ArrayLen::Body({anon_const}) = {length};"); + chain!(self, "let ArrayLen::Body({anon_const}) = {length}"); self.body(field!(anon_const.body)); }, } @@ -600,12 +607,12 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { fn body(&self, body_id: &Binding) { let expr = self.cx.tcx.hir().body(body_id.value).value; bind!(self, expr); - out!("let {expr} = &cx.tcx.hir().body({body_id}).value;"); + chain!(self, "{expr} = &cx.tcx.hir().body({body_id}).value"); self.expr(expr); } fn pat(&self, pat: &Binding<&hir::Pat<'_>>) { - let kind = |kind| out!("if let PatKind::{kind} = {pat}.kind;"); + let kind = |kind| chain!(self, "let PatKind::{kind} = {pat}.kind"); macro_rules! kind { ($($t:tt)*) => (kind(format_args!($($t)*))); } @@ -688,7 +695,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { } fn stmt(&self, stmt: &Binding<&hir::Stmt<'_>>) { - let kind = |kind| out!("if let StmtKind::{kind} = {stmt}.kind;"); + let kind = |kind| chain!(self, "let StmtKind::{kind} = {stmt}.kind"); macro_rules! kind { ($($t:tt)*) => (kind(format_args!($($t)*))); } diff --git a/tests/ui/author.stdout b/tests/ui/author.stdout index 597318a556b8..27ad538f24d8 100644 --- a/tests/ui/author.stdout +++ b/tests/ui/author.stdout @@ -1,14 +1,12 @@ -if_chain! { - if let StmtKind::Local(local) = stmt.kind; - if let Some(init) = local.init; - if let ExprKind::Cast(expr, cast_ty) = init.kind; - if let TyKind::Path(ref qpath) = cast_ty.kind; - if match_qpath(qpath, &["char"]); - if let ExprKind::Lit(ref lit) = expr.kind; - if let LitKind::Int(69, LitIntType::Unsuffixed) = lit.node; - if let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = local.pat.kind; - if name.as_str() == "x"; - then { - // report your lint here - } +if let StmtKind::Local(local) = stmt.kind + && let Some(init) = local.init + && let ExprKind::Cast(expr, cast_ty) = init.kind + && let TyKind::Path(ref qpath) = cast_ty.kind + && match_qpath(qpath, &["char"]) + && let ExprKind::Lit(ref lit) = expr.kind + && let LitKind::Int(69, LitIntType::Unsuffixed) = lit.node + && let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = local.pat.kind + && name.as_str() == "x" +{ + // report your lint here } diff --git a/tests/ui/author/blocks.stdout b/tests/ui/author/blocks.stdout index a529981e2e68..9de0550d81d0 100644 --- a/tests/ui/author/blocks.stdout +++ b/tests/ui/author/blocks.stdout @@ -1,64 +1,58 @@ -if_chain! { - if let ExprKind::Block(block, None) = expr.kind; - if block.stmts.len() == 3; - if let StmtKind::Local(local) = block.stmts[0].kind; - if let Some(init) = local.init; - if let ExprKind::Lit(ref lit) = init.kind; - if let LitKind::Int(42, LitIntType::Signed(IntTy::I32)) = lit.node; - if let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = local.pat.kind; - if name.as_str() == "x"; - if let StmtKind::Local(local1) = block.stmts[1].kind; - if let Some(init1) = local1.init; - if let ExprKind::Lit(ref lit1) = init1.kind; - if let LitKind::Float(_, LitFloatType::Suffixed(FloatTy::F32)) = lit1.node; - if let PatKind::Binding(BindingAnnotation::NONE, _, name1, None) = local1.pat.kind; - if name1.as_str() == "_t"; - if let StmtKind::Semi(e) = block.stmts[2].kind; - if let ExprKind::Unary(UnOp::Neg, inner) = e.kind; - if let ExprKind::Path(ref qpath) = inner.kind; - if match_qpath(qpath, &["x"]); - if block.expr.is_none(); - then { - // report your lint here - } +if let ExprKind::Block(block, None) = expr.kind + && block.stmts.len() == 3 + && let StmtKind::Local(local) = block.stmts[0].kind + && let Some(init) = local.init + && let ExprKind::Lit(ref lit) = init.kind + && let LitKind::Int(42, LitIntType::Signed(IntTy::I32)) = lit.node + && let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = local.pat.kind + && name.as_str() == "x" + && let StmtKind::Local(local1) = block.stmts[1].kind + && let Some(init1) = local1.init + && let ExprKind::Lit(ref lit1) = init1.kind + && let LitKind::Float(_, LitFloatType::Suffixed(FloatTy::F32)) = lit1.node + && let PatKind::Binding(BindingAnnotation::NONE, _, name1, None) = local1.pat.kind + && name1.as_str() == "_t" + && let StmtKind::Semi(e) = block.stmts[2].kind + && let ExprKind::Unary(UnOp::Neg, inner) = e.kind + && let ExprKind::Path(ref qpath) = inner.kind + && match_qpath(qpath, &["x"]) + && block.expr.is_none() +{ + // report your lint here } -if_chain! { - if let ExprKind::Block(block, None) = expr.kind; - if block.stmts.len() == 1; - if let StmtKind::Local(local) = block.stmts[0].kind; - if let Some(init) = local.init; - if let ExprKind::Call(func, args) = init.kind; - if let ExprKind::Path(ref qpath) = func.kind; - if match_qpath(qpath, &["String", "new"]); - if args.is_empty(); - if let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = local.pat.kind; - if name.as_str() == "expr"; - if let Some(trailing_expr) = block.expr; - if let ExprKind::Call(func1, args1) = trailing_expr.kind; - if let ExprKind::Path(ref qpath1) = func1.kind; - if match_qpath(qpath1, &["drop"]); - if args1.len() == 1; - if let ExprKind::Path(ref qpath2) = args1[0].kind; - if match_qpath(qpath2, &["expr"]); - then { - // report your lint here - } +if let ExprKind::Block(block, None) = expr.kind + && block.stmts.len() == 1 + && let StmtKind::Local(local) = block.stmts[0].kind + && let Some(init) = local.init + && let ExprKind::Call(func, args) = init.kind + && let ExprKind::Path(ref qpath) = func.kind + && match_qpath(qpath, &["String", "new"]) + && args.is_empty() + && let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = local.pat.kind + && name.as_str() == "expr" + && let Some(trailing_expr) = block.expr + && let ExprKind::Call(func1, args1) = trailing_expr.kind + && let ExprKind::Path(ref qpath1) = func1.kind + && match_qpath(qpath1, &["drop"]) + && args1.len() == 1 + && let ExprKind::Path(ref qpath2) = args1[0].kind + && match_qpath(qpath2, &["expr"]) +{ + // report your lint here } -if_chain! { - if let ExprKind::Closure(CaptureBy::Value, fn_decl, body_id, _, None) = expr.kind; - if let FnRetTy::DefaultReturn(_) = fn_decl.output; - let expr1 = &cx.tcx.hir().body(body_id).value; - if let ExprKind::Call(func, args) = expr1.kind; - if let ExprKind::Path(ref qpath) = func.kind; - if matches!(qpath, QPath::LangItem(LangItem::FromGenerator, _)); - if args.len() == 1; - if let ExprKind::Closure(CaptureBy::Value, fn_decl1, body_id1, _, Some(Movability::Static)) = args[0].kind; - if let FnRetTy::DefaultReturn(_) = fn_decl1.output; - let expr2 = &cx.tcx.hir().body(body_id1).value; - if let ExprKind::Block(block, None) = expr2.kind; - if block.stmts.is_empty(); - if block.expr.is_none(); - then { - // report your lint here - } +if let ExprKind::Closure(CaptureBy::Value, fn_decl, body_id, _, None) = expr.kind + && let FnRetTy::DefaultReturn(_) = fn_decl.output + && expr1 = &cx.tcx.hir().body(body_id).value + && let ExprKind::Call(func, args) = expr1.kind + && let ExprKind::Path(ref qpath) = func.kind + && matches!(qpath, QPath::LangItem(LangItem::FromGenerator, _)) + && args.len() == 1 + && let ExprKind::Closure(CaptureBy::Value, fn_decl1, body_id1, _, Some(Movability::Static)) = args[0].kind + && let FnRetTy::DefaultReturn(_) = fn_decl1.output + && expr2 = &cx.tcx.hir().body(body_id1).value + && let ExprKind::Block(block, None) = expr2.kind + && block.stmts.is_empty() + && block.expr.is_none() +{ + // report your lint here } diff --git a/tests/ui/author/call.stdout b/tests/ui/author/call.stdout index 266312d63e50..f040f6330a64 100644 --- a/tests/ui/author/call.stdout +++ b/tests/ui/author/call.stdout @@ -1,16 +1,14 @@ -if_chain! { - if let StmtKind::Local(local) = stmt.kind; - if let Some(init) = local.init; - if let ExprKind::Call(func, args) = init.kind; - if let ExprKind::Path(ref qpath) = func.kind; - if match_qpath(qpath, &["{{root}}", "std", "cmp", "min"]); - if args.len() == 2; - if let ExprKind::Lit(ref lit) = args[0].kind; - if let LitKind::Int(3, LitIntType::Unsuffixed) = lit.node; - if let ExprKind::Lit(ref lit1) = args[1].kind; - if let LitKind::Int(4, LitIntType::Unsuffixed) = lit1.node; - if let PatKind::Wild = local.pat.kind; - then { - // report your lint here - } +if let StmtKind::Local(local) = stmt.kind + && let Some(init) = local.init + && let ExprKind::Call(func, args) = init.kind + && let ExprKind::Path(ref qpath) = func.kind + && match_qpath(qpath, &["{{root}}", "std", "cmp", "min"]) + && args.len() == 2 + && let ExprKind::Lit(ref lit) = args[0].kind + && let LitKind::Int(3, LitIntType::Unsuffixed) = lit.node + && let ExprKind::Lit(ref lit1) = args[1].kind + && let LitKind::Int(4, LitIntType::Unsuffixed) = lit1.node + && let PatKind::Wild = local.pat.kind +{ + // report your lint here } diff --git a/tests/ui/author/if.stdout b/tests/ui/author/if.stdout index 8d92849b3668..5d79618820d8 100644 --- a/tests/ui/author/if.stdout +++ b/tests/ui/author/if.stdout @@ -1,50 +1,46 @@ -if_chain! { - if let StmtKind::Local(local) = stmt.kind; - if let Some(init) = local.init; - if let ExprKind::If(cond, then, Some(else_expr)) = init.kind; - if let ExprKind::DropTemps(expr) = cond.kind; - if let ExprKind::Lit(ref lit) = expr.kind; - if let LitKind::Bool(true) = lit.node; - if let ExprKind::Block(block, None) = then.kind; - if block.stmts.len() == 1; - if let StmtKind::Semi(e) = block.stmts[0].kind; - if let ExprKind::Binary(op, left, right) = e.kind; - if BinOpKind::Eq == op.node; - if let ExprKind::Lit(ref lit1) = left.kind; - if let LitKind::Int(1, LitIntType::Unsuffixed) = lit1.node; - if let ExprKind::Lit(ref lit2) = right.kind; - if let LitKind::Int(1, LitIntType::Unsuffixed) = lit2.node; - if block.expr.is_none(); - if let ExprKind::Block(block1, None) = else_expr.kind; - if block1.stmts.len() == 1; - if let StmtKind::Semi(e1) = block1.stmts[0].kind; - if let ExprKind::Binary(op1, left1, right1) = e1.kind; - if BinOpKind::Eq == op1.node; - if let ExprKind::Lit(ref lit3) = left1.kind; - if let LitKind::Int(2, LitIntType::Unsuffixed) = lit3.node; - if let ExprKind::Lit(ref lit4) = right1.kind; - if let LitKind::Int(2, LitIntType::Unsuffixed) = lit4.node; - if block1.expr.is_none(); - if let PatKind::Wild = local.pat.kind; - then { - // report your lint here - } +if let StmtKind::Local(local) = stmt.kind + && let Some(init) = local.init + && let ExprKind::If(cond, then, Some(else_expr)) = init.kind + && let ExprKind::DropTemps(expr) = cond.kind + && let ExprKind::Lit(ref lit) = expr.kind + && let LitKind::Bool(true) = lit.node + && let ExprKind::Block(block, None) = then.kind + && block.stmts.len() == 1 + && let StmtKind::Semi(e) = block.stmts[0].kind + && let ExprKind::Binary(op, left, right) = e.kind + && BinOpKind::Eq == op.node + && let ExprKind::Lit(ref lit1) = left.kind + && let LitKind::Int(1, LitIntType::Unsuffixed) = lit1.node + && let ExprKind::Lit(ref lit2) = right.kind + && let LitKind::Int(1, LitIntType::Unsuffixed) = lit2.node + && block.expr.is_none() + && let ExprKind::Block(block1, None) = else_expr.kind + && block1.stmts.len() == 1 + && let StmtKind::Semi(e1) = block1.stmts[0].kind + && let ExprKind::Binary(op1, left1, right1) = e1.kind + && BinOpKind::Eq == op1.node + && let ExprKind::Lit(ref lit3) = left1.kind + && let LitKind::Int(2, LitIntType::Unsuffixed) = lit3.node + && let ExprKind::Lit(ref lit4) = right1.kind + && let LitKind::Int(2, LitIntType::Unsuffixed) = lit4.node + && block1.expr.is_none() + && let PatKind::Wild = local.pat.kind +{ + // report your lint here } -if_chain! { - if let ExprKind::If(cond, then, Some(else_expr)) = expr.kind; - if let ExprKind::Let(let_expr) = cond.kind; - if let PatKind::Lit(lit_expr) = let_expr.pat.kind; - if let ExprKind::Lit(ref lit) = lit_expr.kind; - if let LitKind::Bool(true) = lit.node; - if let ExprKind::Path(ref qpath) = let_expr.init.kind; - if match_qpath(qpath, &["a"]); - if let ExprKind::Block(block, None) = then.kind; - if block.stmts.is_empty(); - if block.expr.is_none(); - if let ExprKind::Block(block1, None) = else_expr.kind; - if block1.stmts.is_empty(); - if block1.expr.is_none(); - then { - // report your lint here - } +if let ExprKind::If(cond, then, Some(else_expr)) = expr.kind + && let ExprKind::Let(let_expr) = cond.kind + && let PatKind::Lit(lit_expr) = let_expr.pat.kind + && let ExprKind::Lit(ref lit) = lit_expr.kind + && let LitKind::Bool(true) = lit.node + && let ExprKind::Path(ref qpath) = let_expr.init.kind + && match_qpath(qpath, &["a"]) + && let ExprKind::Block(block, None) = then.kind + && block.stmts.is_empty() + && block.expr.is_none() + && let ExprKind::Block(block1, None) = else_expr.kind + && block1.stmts.is_empty() + && block1.expr.is_none() +{ + // report your lint here } diff --git a/tests/ui/author/issue_3849.stdout b/tests/ui/author/issue_3849.stdout index bce4bc702733..32a3127b85a3 100644 --- a/tests/ui/author/issue_3849.stdout +++ b/tests/ui/author/issue_3849.stdout @@ -1,14 +1,12 @@ -if_chain! { - if let StmtKind::Local(local) = stmt.kind; - if let Some(init) = local.init; - if let ExprKind::Call(func, args) = init.kind; - if let ExprKind::Path(ref qpath) = func.kind; - if match_qpath(qpath, &["std", "mem", "transmute"]); - if args.len() == 1; - if let ExprKind::Path(ref qpath1) = args[0].kind; - if match_qpath(qpath1, &["ZPTR"]); - if let PatKind::Wild = local.pat.kind; - then { - // report your lint here - } +if let StmtKind::Local(local) = stmt.kind + && let Some(init) = local.init + && let ExprKind::Call(func, args) = init.kind + && let ExprKind::Path(ref qpath) = func.kind + && match_qpath(qpath, &["std", "mem", "transmute"]) + && args.len() == 1 + && let ExprKind::Path(ref qpath1) = args[0].kind + && match_qpath(qpath1, &["ZPTR"]) + && let PatKind::Wild = local.pat.kind +{ + // report your lint here } diff --git a/tests/ui/author/loop.stdout b/tests/ui/author/loop.stdout index ceb53fcd4963..94a6436ed547 100644 --- a/tests/ui/author/loop.stdout +++ b/tests/ui/author/loop.stdout @@ -1,113 +1,101 @@ -if_chain! { - if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr); - if let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = pat.kind; - if name.as_str() == "y"; - if let ExprKind::Struct(qpath, fields, None) = arg.kind; - if matches!(qpath, QPath::LangItem(LangItem::Range, _)); - if fields.len() == 2; - if fields[0].ident.as_str() == "start"; - if let ExprKind::Lit(ref lit) = fields[0].expr.kind; - if let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node; - if fields[1].ident.as_str() == "end"; - if let ExprKind::Lit(ref lit1) = fields[1].expr.kind; - if let LitKind::Int(10, LitIntType::Unsuffixed) = lit1.node; - if let ExprKind::Block(block, None) = body.kind; - if block.stmts.len() == 1; - if let StmtKind::Local(local) = block.stmts[0].kind; - if let Some(init) = local.init; - if let ExprKind::Path(ref qpath1) = init.kind; - if match_qpath(qpath1, &["y"]); - if let PatKind::Binding(BindingAnnotation::NONE, _, name1, None) = local.pat.kind; - if name1.as_str() == "z"; - if block.expr.is_none(); - then { - // report your lint here - } +if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr) + && let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = pat.kind + && name.as_str() == "y" + && let ExprKind::Struct(qpath, fields, None) = arg.kind + && matches!(qpath, QPath::LangItem(LangItem::Range, _)) + && fields.len() == 2 + && fields[0].ident.as_str() == "start" + && let ExprKind::Lit(ref lit) = fields[0].expr.kind + && let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node + && fields[1].ident.as_str() == "end" + && let ExprKind::Lit(ref lit1) = fields[1].expr.kind + && let LitKind::Int(10, LitIntType::Unsuffixed) = lit1.node + && let ExprKind::Block(block, None) = body.kind + && block.stmts.len() == 1 + && let StmtKind::Local(local) = block.stmts[0].kind + && let Some(init) = local.init + && let ExprKind::Path(ref qpath1) = init.kind + && match_qpath(qpath1, &["y"]) + && let PatKind::Binding(BindingAnnotation::NONE, _, name1, None) = local.pat.kind + && name1.as_str() == "z" + && block.expr.is_none() +{ + // report your lint here } -if_chain! { - if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr); - if let PatKind::Wild = pat.kind; - if let ExprKind::Struct(qpath, fields, None) = arg.kind; - if matches!(qpath, QPath::LangItem(LangItem::Range, _)); - if fields.len() == 2; - if fields[0].ident.as_str() == "start"; - if let ExprKind::Lit(ref lit) = fields[0].expr.kind; - if let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node; - if fields[1].ident.as_str() == "end"; - if let ExprKind::Lit(ref lit1) = fields[1].expr.kind; - if let LitKind::Int(10, LitIntType::Unsuffixed) = lit1.node; - if let ExprKind::Block(block, None) = body.kind; - if block.stmts.len() == 1; - if let StmtKind::Semi(e) = block.stmts[0].kind; - if let ExprKind::Break(destination, None) = e.kind; - if destination.label.is_none(); - if block.expr.is_none(); - then { - // report your lint here - } +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, _)) + && fields.len() == 2 + && fields[0].ident.as_str() == "start" + && let ExprKind::Lit(ref lit) = fields[0].expr.kind + && let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node + && fields[1].ident.as_str() == "end" + && let ExprKind::Lit(ref lit1) = fields[1].expr.kind + && let LitKind::Int(10, LitIntType::Unsuffixed) = lit1.node + && let ExprKind::Block(block, None) = body.kind + && block.stmts.len() == 1 + && let StmtKind::Semi(e) = block.stmts[0].kind + && let ExprKind::Break(destination, None) = e.kind + && destination.label.is_none() + && block.expr.is_none() +{ + // report your lint here } -if_chain! { - if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr); - if let PatKind::Wild = pat.kind; - if let ExprKind::Struct(qpath, fields, None) = arg.kind; - if matches!(qpath, QPath::LangItem(LangItem::Range, _)); - if fields.len() == 2; - if fields[0].ident.as_str() == "start"; - if let ExprKind::Lit(ref lit) = fields[0].expr.kind; - if let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node; - if fields[1].ident.as_str() == "end"; - if let ExprKind::Lit(ref lit1) = fields[1].expr.kind; - if let LitKind::Int(10, LitIntType::Unsuffixed) = lit1.node; - if let ExprKind::Block(block, None) = body.kind; - if block.stmts.len() == 1; - if let StmtKind::Semi(e) = block.stmts[0].kind; - if let ExprKind::Break(destination, None) = e.kind; - if let Some(label) = destination.label; - if label.ident.as_str() == "'label"; - if block.expr.is_none(); - then { - // report your lint here - } +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, _)) + && fields.len() == 2 + && fields[0].ident.as_str() == "start" + && let ExprKind::Lit(ref lit) = fields[0].expr.kind + && let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node + && fields[1].ident.as_str() == "end" + && let ExprKind::Lit(ref lit1) = fields[1].expr.kind + && let LitKind::Int(10, LitIntType::Unsuffixed) = lit1.node + && let ExprKind::Block(block, None) = body.kind + && block.stmts.len() == 1 + && let StmtKind::Semi(e) = block.stmts[0].kind + && let ExprKind::Break(destination, None) = e.kind + && let Some(label) = destination.label + && label.ident.as_str() == "'label" + && block.expr.is_none() +{ + // report your lint here } -if_chain! { - if let Some(higher::While { condition: condition, body: body }) = higher::While::hir(expr); - if let ExprKind::Path(ref qpath) = condition.kind; - if match_qpath(qpath, &["a"]); - if let ExprKind::Block(block, None) = body.kind; - if block.stmts.len() == 1; - if let StmtKind::Semi(e) = block.stmts[0].kind; - if let ExprKind::Break(destination, None) = e.kind; - if destination.label.is_none(); - if block.expr.is_none(); - then { - // report your lint here - } +if let Some(higher::While { condition: condition, body: body }) = higher::While::hir(expr) + && let ExprKind::Path(ref qpath) = condition.kind + && match_qpath(qpath, &["a"]) + && let ExprKind::Block(block, None) = body.kind + && block.stmts.len() == 1 + && let StmtKind::Semi(e) = block.stmts[0].kind + && let ExprKind::Break(destination, None) = e.kind + && destination.label.is_none() + && block.expr.is_none() +{ + // report your lint here } -if_chain! { - if let Some(higher::WhileLet { let_pat: let_pat, let_expr: let_expr, if_then: if_then }) = higher::WhileLet::hir(expr); - if let PatKind::Lit(lit_expr) = let_pat.kind; - if let ExprKind::Lit(ref lit) = lit_expr.kind; - if let LitKind::Bool(true) = lit.node; - if let ExprKind::Path(ref qpath) = let_expr.kind; - if match_qpath(qpath, &["a"]); - if let ExprKind::Block(block, None) = if_then.kind; - if block.stmts.len() == 1; - if let StmtKind::Semi(e) = block.stmts[0].kind; - if let ExprKind::Break(destination, None) = e.kind; - if destination.label.is_none(); - if block.expr.is_none(); - then { - // report your lint here - } +if let Some(higher::WhileLet { let_pat: let_pat, let_expr: let_expr, if_then: if_then }) = higher::WhileLet::hir(expr) + && let PatKind::Lit(lit_expr) = let_pat.kind + && let ExprKind::Lit(ref lit) = lit_expr.kind + && let LitKind::Bool(true) = lit.node + && let ExprKind::Path(ref qpath) = let_expr.kind + && match_qpath(qpath, &["a"]) + && let ExprKind::Block(block, None) = if_then.kind + && block.stmts.len() == 1 + && let StmtKind::Semi(e) = block.stmts[0].kind + && let ExprKind::Break(destination, None) = e.kind + && destination.label.is_none() + && block.expr.is_none() +{ + // report your lint here } -if_chain! { - if let ExprKind::Loop(body, None, LoopSource::Loop, _) = expr.kind; - if body.stmts.len() == 1; - if let StmtKind::Semi(e) = body.stmts[0].kind; - if let ExprKind::Break(destination, None) = e.kind; - if destination.label.is_none(); - if body.expr.is_none(); - then { - // report your lint here - } +if let ExprKind::Loop(body, None, LoopSource::Loop, _) = expr.kind + && body.stmts.len() == 1 + && let StmtKind::Semi(e) = body.stmts[0].kind + && let ExprKind::Break(destination, None) = e.kind + && destination.label.is_none() + && body.expr.is_none() +{ + // report your lint here } diff --git a/tests/ui/author/matches.stdout b/tests/ui/author/matches.stdout index 2cf69a035b4c..88e2ca656a4f 100644 --- a/tests/ui/author/matches.stdout +++ b/tests/ui/author/matches.stdout @@ -1,38 +1,36 @@ -if_chain! { - if let StmtKind::Local(local) = stmt.kind; - if let Some(init) = local.init; - if let ExprKind::Match(scrutinee, arms, MatchSource::Normal) = init.kind; - if let ExprKind::Lit(ref lit) = scrutinee.kind; - if let LitKind::Int(42, LitIntType::Unsuffixed) = lit.node; - if arms.len() == 3; - if let PatKind::Lit(lit_expr) = arms[0].pat.kind; - if let ExprKind::Lit(ref lit1) = lit_expr.kind; - if let LitKind::Int(16, LitIntType::Unsuffixed) = lit1.node; - if arms[0].guard.is_none(); - if let ExprKind::Lit(ref lit2) = arms[0].body.kind; - if let LitKind::Int(5, LitIntType::Unsuffixed) = lit2.node; - if let PatKind::Lit(lit_expr1) = arms[1].pat.kind; - if let ExprKind::Lit(ref lit3) = lit_expr1.kind; - if let LitKind::Int(17, LitIntType::Unsuffixed) = lit3.node; - if arms[1].guard.is_none(); - if let ExprKind::Block(block, None) = arms[1].body.kind; - if block.stmts.len() == 1; - if let StmtKind::Local(local1) = block.stmts[0].kind; - if let Some(init1) = local1.init; - if let ExprKind::Lit(ref lit4) = init1.kind; - if let LitKind::Int(3, LitIntType::Unsuffixed) = lit4.node; - if let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = local1.pat.kind; - if name.as_str() == "x"; - if let Some(trailing_expr) = block.expr; - if let ExprKind::Path(ref qpath) = trailing_expr.kind; - if match_qpath(qpath, &["x"]); - if let PatKind::Wild = arms[2].pat.kind; - if arms[2].guard.is_none(); - if let ExprKind::Lit(ref lit5) = arms[2].body.kind; - if let LitKind::Int(1, LitIntType::Unsuffixed) = lit5.node; - if let PatKind::Binding(BindingAnnotation::NONE, _, name1, None) = local.pat.kind; - if name1.as_str() == "a"; - then { - // report your lint here - } +if let StmtKind::Local(local) = stmt.kind + && let Some(init) = local.init + && let ExprKind::Match(scrutinee, arms, MatchSource::Normal) = init.kind + && let ExprKind::Lit(ref lit) = scrutinee.kind + && let LitKind::Int(42, LitIntType::Unsuffixed) = lit.node + && arms.len() == 3 + && let PatKind::Lit(lit_expr) = arms[0].pat.kind + && let ExprKind::Lit(ref lit1) = lit_expr.kind + && let LitKind::Int(16, LitIntType::Unsuffixed) = lit1.node + && arms[0].guard.is_none() + && let ExprKind::Lit(ref lit2) = arms[0].body.kind + && let LitKind::Int(5, LitIntType::Unsuffixed) = lit2.node + && let PatKind::Lit(lit_expr1) = arms[1].pat.kind + && let ExprKind::Lit(ref lit3) = lit_expr1.kind + && let LitKind::Int(17, LitIntType::Unsuffixed) = lit3.node + && arms[1].guard.is_none() + && let ExprKind::Block(block, None) = arms[1].body.kind + && block.stmts.len() == 1 + && let StmtKind::Local(local1) = block.stmts[0].kind + && let Some(init1) = local1.init + && let ExprKind::Lit(ref lit4) = init1.kind + && let LitKind::Int(3, LitIntType::Unsuffixed) = lit4.node + && let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = local1.pat.kind + && name.as_str() == "x" + && let Some(trailing_expr) = block.expr + && let ExprKind::Path(ref qpath) = trailing_expr.kind + && match_qpath(qpath, &["x"]) + && let PatKind::Wild = arms[2].pat.kind + && arms[2].guard.is_none() + && let ExprKind::Lit(ref lit5) = arms[2].body.kind + && let LitKind::Int(1, LitIntType::Unsuffixed) = lit5.node + && let PatKind::Binding(BindingAnnotation::NONE, _, name1, None) = local.pat.kind + && name1.as_str() == "a" +{ + // report your lint here } diff --git a/tests/ui/author/repeat.stdout b/tests/ui/author/repeat.stdout index 471bbce4f418..c2a369610cc1 100644 --- a/tests/ui/author/repeat.stdout +++ b/tests/ui/author/repeat.stdout @@ -1,12 +1,10 @@ -if_chain! { - if let ExprKind::Repeat(value, length) = expr.kind; - if let ExprKind::Lit(ref lit) = value.kind; - if let LitKind::Int(1, LitIntType::Unsigned(UintTy::U8)) = lit.node; - if let ArrayLen::Body(anon_const) = length; - let expr1 = &cx.tcx.hir().body(anon_const.body).value; - if let ExprKind::Lit(ref lit1) = expr1.kind; - if let LitKind::Int(5, LitIntType::Unsuffixed) = lit1.node; - then { - // report your lint here - } +if let ExprKind::Repeat(value, length) = expr.kind + && let ExprKind::Lit(ref lit) = value.kind + && let LitKind::Int(1, LitIntType::Unsigned(UintTy::U8)) = lit.node + && let ArrayLen::Body(anon_const) = length + && expr1 = &cx.tcx.hir().body(anon_const.body).value + && let ExprKind::Lit(ref lit1) = expr1.kind + && let LitKind::Int(5, LitIntType::Unsuffixed) = lit1.node +{ + // report your lint here } diff --git a/tests/ui/author/struct.stdout b/tests/ui/author/struct.stdout index b5bbc9e213c6..0b332d5e7d0e 100644 --- a/tests/ui/author/struct.stdout +++ b/tests/ui/author/struct.stdout @@ -1,64 +1,56 @@ -if_chain! { - if let ExprKind::Struct(qpath, fields, None) = expr.kind; - if match_qpath(qpath, &["Test"]); - if fields.len() == 1; - if fields[0].ident.as_str() == "field"; - if let ExprKind::If(cond, then, Some(else_expr)) = fields[0].expr.kind; - if let ExprKind::DropTemps(expr1) = cond.kind; - if let ExprKind::Lit(ref lit) = expr1.kind; - if let LitKind::Bool(true) = lit.node; - if let ExprKind::Block(block, None) = then.kind; - if block.stmts.is_empty(); - if let Some(trailing_expr) = block.expr; - if let ExprKind::Lit(ref lit1) = trailing_expr.kind; - if let LitKind::Int(1, LitIntType::Unsuffixed) = lit1.node; - if let ExprKind::Block(block1, None) = else_expr.kind; - if block1.stmts.is_empty(); - if let Some(trailing_expr1) = block1.expr; - if let ExprKind::Lit(ref lit2) = trailing_expr1.kind; - if let LitKind::Int(0, LitIntType::Unsuffixed) = lit2.node; - then { - // report your lint here - } +if let ExprKind::Struct(qpath, fields, None) = expr.kind + && match_qpath(qpath, &["Test"]) + && fields.len() == 1 + && fields[0].ident.as_str() == "field" + && let ExprKind::If(cond, then, Some(else_expr)) = fields[0].expr.kind + && let ExprKind::DropTemps(expr1) = cond.kind + && let ExprKind::Lit(ref lit) = expr1.kind + && let LitKind::Bool(true) = lit.node + && let ExprKind::Block(block, None) = then.kind + && block.stmts.is_empty() + && let Some(trailing_expr) = block.expr + && let ExprKind::Lit(ref lit1) = trailing_expr.kind + && let LitKind::Int(1, LitIntType::Unsuffixed) = lit1.node + && let ExprKind::Block(block1, None) = else_expr.kind + && block1.stmts.is_empty() + && let Some(trailing_expr1) = block1.expr + && let ExprKind::Lit(ref lit2) = trailing_expr1.kind + && let LitKind::Int(0, LitIntType::Unsuffixed) = lit2.node +{ + // report your lint here } -if_chain! { - if let PatKind::Struct(ref qpath, fields, false) = arm.pat.kind; - if match_qpath(qpath, &["Test"]); - if fields.len() == 1; - if fields[0].ident.as_str() == "field"; - if let PatKind::Lit(lit_expr) = fields[0].pat.kind; - if let ExprKind::Lit(ref lit) = lit_expr.kind; - if let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node; - if arm.guard.is_none(); - if let ExprKind::Block(block, None) = arm.body.kind; - if block.stmts.is_empty(); - if block.expr.is_none(); - then { - // report your lint here - } +if let PatKind::Struct(ref qpath, fields, false) = arm.pat.kind + && match_qpath(qpath, &["Test"]) + && fields.len() == 1 + && fields[0].ident.as_str() == "field" + && let PatKind::Lit(lit_expr) = fields[0].pat.kind + && let ExprKind::Lit(ref lit) = lit_expr.kind + && let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node + && arm.guard.is_none() + && let ExprKind::Block(block, None) = arm.body.kind + && block.stmts.is_empty() + && block.expr.is_none() +{ + // report your lint here } -if_chain! { - if let PatKind::TupleStruct(ref qpath, fields, None) = arm.pat.kind; - if match_qpath(qpath, &["TestTuple"]); - if fields.len() == 1; - if let PatKind::Lit(lit_expr) = fields[0].kind; - if let ExprKind::Lit(ref lit) = lit_expr.kind; - if let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node; - if arm.guard.is_none(); - if let ExprKind::Block(block, None) = arm.body.kind; - if block.stmts.is_empty(); - if block.expr.is_none(); - then { - // report your lint here - } +if let PatKind::TupleStruct(ref qpath, fields, None) = arm.pat.kind + && match_qpath(qpath, &["TestTuple"]) + && fields.len() == 1 + && let PatKind::Lit(lit_expr) = fields[0].kind + && let ExprKind::Lit(ref lit) = lit_expr.kind + && let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node + && arm.guard.is_none() + && let ExprKind::Block(block, None) = arm.body.kind + && block.stmts.is_empty() + && block.expr.is_none() +{ + // report your lint here } -if_chain! { - if let ExprKind::MethodCall(method_name, receiver, args, _) = expr.kind; - if method_name.ident.as_str() == "test"; - if let ExprKind::Path(ref qpath) = receiver.kind; - if match_qpath(qpath, &["test_method_call"]); - if args.is_empty(); - then { - // report your lint here - } +if let ExprKind::MethodCall(method_name, receiver, args, _) = expr.kind + && method_name.ident.as_str() == "test" + && let ExprKind::Path(ref qpath) = receiver.kind + && match_qpath(qpath, &["test_method_call"]) + && args.is_empty() +{ + // report your lint here } From d75b25faabdcf0a22fe37928917c4ab1761fa265 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 6 Oct 2022 09:44:38 +0200 Subject: [PATCH 102/309] Merge commit 'ac0e10aa68325235069a842f47499852b2dee79e' into clippyup --- CHANGELOG.md | 159 +++- Cargo.toml | 8 +- README.md | 46 +- clippy_dev/src/fmt.rs | 6 +- clippy_dev/src/main.rs | 2 +- clippy_dev/src/new_lint.rs | 161 ++-- clippy_dev/src/serve.rs | 4 +- clippy_dev/src/setup/git_hook.rs | 7 +- clippy_dev/src/setup/intellij.rs | 25 +- clippy_dev/src/setup/vscode.rs | 19 +- clippy_dev/src/update_lints.rs | 103 +- clippy_lints/Cargo.toml | 2 +- clippy_lints/src/approx_const.rs | 4 +- clippy_lints/src/asm_syntax.rs | 6 +- clippy_lints/src/assertions_on_constants.rs | 4 +- .../src/assertions_on_result_states.rs | 10 +- clippy_lints/src/attrs.rs | 7 +- clippy_lints/src/await_holding_invalid.rs | 42 +- clippy_lints/src/blocks_in_if_conditions.rs | 68 +- clippy_lints/src/bool_assert_comparison.rs | 4 +- clippy_lints/src/bool_to_int_with_if.rs | 21 +- clippy_lints/src/booleans.rs | 5 +- clippy_lints/src/box_default.rs | 61 ++ clippy_lints/src/cargo/common_metadata.rs | 2 +- clippy_lints/src/cargo/feature_name.rs | 4 +- clippy_lints/src/cargo/mod.rs | 4 +- .../src/cargo/multiple_crate_versions.rs | 2 +- clippy_lints/src/casts/borrow_as_ptr.rs | 2 +- clippy_lints/src/casts/cast_lossless.rs | 12 +- .../src/casts/cast_possible_truncation.rs | 16 +- clippy_lints/src/casts/cast_possible_wrap.rs | 5 +- clippy_lints/src/casts/cast_ptr_alignment.rs | 4 +- clippy_lints/src/casts/cast_sign_loss.rs | 5 +- .../src/casts/cast_slice_different_sizes.rs | 4 +- clippy_lints/src/casts/char_lit_as_u8.rs | 2 +- clippy_lints/src/casts/fn_to_numeric_cast.rs | 4 +- .../src/casts/fn_to_numeric_cast_any.rs | 4 +- .../fn_to_numeric_cast_with_truncation.rs | 7 +- clippy_lints/src/casts/ptr_as_ptr.rs | 4 +- clippy_lints/src/casts/unnecessary_cast.rs | 81 +- clippy_lints/src/checked_conversions.rs | 16 +- clippy_lints/src/cognitive_complexity.rs | 64 +- clippy_lints/src/default.rs | 17 +- .../src/default_instead_of_iter_empty.rs | 2 +- clippy_lints/src/default_numeric_fallback.rs | 4 +- .../src/default_union_representation.rs | 2 +- clippy_lints/src/dereference.rs | 53 +- clippy_lints/src/derive.rs | 2 +- clippy_lints/src/disallowed_macros.rs | 151 +++ clippy_lints/src/disallowed_methods.rs | 17 +- clippy_lints/src/disallowed_script_idents.rs | 3 +- clippy_lints/src/disallowed_types.rs | 24 +- clippy_lints/src/doc.rs | 92 +- clippy_lints/src/doc_link_with_quotes.rs | 60 -- clippy_lints/src/drop_forget_ref.rs | 30 +- clippy_lints/src/entry.rs | 28 +- clippy_lints/src/enum_variants.rs | 7 +- clippy_lints/src/equatable_if_let.rs | 7 +- clippy_lints/src/escape.rs | 10 +- clippy_lints/src/eta_reduction.rs | 19 +- clippy_lints/src/exhaustive_items.rs | 2 +- clippy_lints/src/explicit_write.rs | 8 +- clippy_lints/src/float_literal.rs | 6 +- clippy_lints/src/floating_point_arithmetic.rs | 55 +- clippy_lints/src/format.rs | 2 +- clippy_lints/src/format_args.rs | 154 ++- clippy_lints/src/format_impl.rs | 6 +- clippy_lints/src/formatting.rs | 25 +- clippy_lints/src/from_str_radix_10.rs | 6 +- clippy_lints/src/functions/must_use.rs | 122 ++- .../src/functions/not_unsafe_ptr_arg_deref.rs | 91 +- .../src/functions/too_many_arguments.rs | 5 +- clippy_lints/src/functions/too_many_lines.rs | 5 +- clippy_lints/src/if_then_some_else_none.rs | 19 +- clippy_lints/src/implicit_hasher.rs | 9 +- clippy_lints/src/implicit_return.rs | 16 +- clippy_lints/src/implicit_saturating_add.rs | 114 +++ clippy_lints/src/implicit_saturating_sub.rs | 20 +- .../src/inconsistent_struct_constructor.rs | 6 +- clippy_lints/src/index_refutable_slice.rs | 4 +- clippy_lints/src/infinite_iter.rs | 15 +- clippy_lints/src/inherent_to_string.rs | 19 +- clippy_lints/src/inline_fn_without_body.rs | 2 +- clippy_lints/src/int_plus_one.rs | 4 +- .../src/iter_not_returning_iterator.rs | 5 +- clippy_lints/src/large_const_arrays.rs | 2 +- clippy_lints/src/len_zero.rs | 38 +- clippy_lints/src/let_if_seq.rs | 3 +- clippy_lints/src/lib.register_all.rs | 5 +- clippy_lints/src/lib.register_complexity.rs | 1 + clippy_lints/src/lib.register_internal.rs | 2 +- clippy_lints/src/lib.register_lints.rs | 11 +- clippy_lints/src/lib.register_nursery.rs | 1 + clippy_lints/src/lib.register_pedantic.rs | 3 +- clippy_lints/src/lib.register_perf.rs | 1 + clippy_lints/src/lib.register_style.rs | 3 +- clippy_lints/src/lib.rs | 56 +- clippy_lints/src/lifetimes.rs | 6 +- clippy_lints/src/literal_representation.rs | 2 +- .../src/loops/explicit_counter_loop.rs | 14 +- clippy_lints/src/loops/explicit_iter_loop.rs | 2 +- clippy_lints/src/loops/for_kv_map.rs | 4 +- clippy_lints/src/loops/manual_find.rs | 9 +- clippy_lints/src/loops/manual_flatten.rs | 14 +- clippy_lints/src/loops/manual_memcpy.rs | 13 +- clippy_lints/src/loops/mod.rs | 2 +- clippy_lints/src/loops/mut_range_bound.rs | 10 +- clippy_lints/src/loops/needless_collect.rs | 6 +- clippy_lints/src/loops/needless_range_loop.rs | 10 +- clippy_lints/src/loops/never_loop.rs | 37 +- clippy_lints/src/loops/same_item_push.rs | 5 +- clippy_lints/src/loops/utils.rs | 5 +- .../src/loops/while_let_on_iterator.rs | 12 +- clippy_lints/src/macro_use.rs | 6 +- clippy_lints/src/manual_assert.rs | 29 +- clippy_lints/src/manual_async_fn.rs | 6 +- clippy_lints/src/manual_clamp.rs | 713 ++++++++++++++ clippy_lints/src/manual_non_exhaustive.rs | 4 +- clippy_lints/src/manual_rem_euclid.rs | 2 +- clippy_lints/src/manual_retain.rs | 18 +- clippy_lints/src/manual_strip.rs | 9 +- clippy_lints/src/map_unit_fn.rs | 9 +- clippy_lints/src/match_result_ok.rs | 6 +- clippy_lints/src/matches/collapsible_match.rs | 6 +- clippy_lints/src/matches/manual_map.rs | 44 +- clippy_lints/src/matches/manual_unwrap_or.rs | 22 +- clippy_lints/src/matches/match_as_ref.rs | 18 +- .../src/matches/match_like_matches.rs | 5 +- clippy_lints/src/matches/match_same_arms.rs | 2 +- .../src/matches/match_single_binding.rs | 28 +- .../src/matches/match_str_case_mismatch.rs | 4 +- .../src/matches/match_wild_err_arm.rs | 2 +- clippy_lints/src/matches/needless_match.rs | 9 +- .../src/matches/redundant_pattern_match.rs | 65 +- .../matches/significant_drop_in_scrutinee.rs | 6 +- clippy_lints/src/matches/single_match.rs | 6 +- clippy_lints/src/matches/try_err.rs | 9 +- clippy_lints/src/mem_replace.rs | 72 +- .../src/methods/bind_instead_of_map.rs | 2 +- clippy_lints/src/methods/bytes_nth.rs | 2 +- clippy_lints/src/methods/chars_cmp.rs | 5 +- .../src/methods/chars_cmp_with_unwrap.rs | 5 +- clippy_lints/src/methods/clone_on_copy.rs | 13 +- clippy_lints/src/methods/clone_on_ref_ptr.rs | 2 +- clippy_lints/src/methods/expect_fun_call.rs | 11 +- clippy_lints/src/methods/filetype_is_file.rs | 11 +- clippy_lints/src/methods/filter_map_next.rs | 2 +- clippy_lints/src/methods/filter_next.rs | 2 +- .../methods/from_iter_instead_of_collect.rs | 6 +- clippy_lints/src/methods/get_first.rs | 4 +- clippy_lints/src/methods/get_last_with_len.rs | 6 +- clippy_lints/src/methods/get_unwrap.rs | 11 +- clippy_lints/src/methods/implicit_clone.rs | 6 +- .../src/methods/inefficient_to_string.rs | 9 +- clippy_lints/src/methods/into_iter_on_ref.rs | 3 +- .../src/methods/is_digit_ascii_radix.rs | 7 +- .../src/methods/iter_cloned_collect.rs | 4 +- clippy_lints/src/methods/iter_count.rs | 2 +- clippy_lints/src/methods/iter_kv_map.rs | 8 +- clippy_lints/src/methods/iter_next_slice.rs | 2 +- clippy_lints/src/methods/iter_nth.rs | 4 +- .../iter_on_single_or_empty_collections.rs | 27 +- clippy_lints/src/methods/iter_with_drain.rs | 2 +- clippy_lints/src/methods/manual_ok_or.rs | 38 +- .../methods/manual_saturating_arithmetic.rs | 5 +- clippy_lints/src/methods/manual_str_repeat.rs | 10 +- clippy_lints/src/methods/map_clone.rs | 5 +- clippy_lints/src/methods/map_flatten.rs | 9 +- clippy_lints/src/methods/map_identity.rs | 2 +- clippy_lints/src/methods/map_unwrap_or.rs | 2 +- clippy_lints/src/methods/mod.rs | 87 +- .../src/methods/option_as_ref_deref.rs | 9 +- .../src/methods/option_map_or_none.rs | 22 +- .../src/methods/option_map_unwrap_or.rs | 9 +- clippy_lints/src/methods/or_fun_call.rs | 10 +- clippy_lints/src/methods/or_then_unwrap.rs | 5 +- clippy_lints/src/methods/search_is_some.rs | 14 +- .../src/methods/single_char_insert_string.rs | 2 +- .../src/methods/single_char_push_string.rs | 2 +- .../src/methods/stable_sort_primitive.rs | 4 +- clippy_lints/src/methods/str_splitn.rs | 20 +- .../src/methods/string_extend_chars.rs | 3 +- clippy_lints/src/methods/suspicious_splitn.rs | 4 +- .../src/methods/suspicious_to_owned.rs | 4 +- .../src/methods/unnecessary_filter_map.rs | 73 +- clippy_lints/src/methods/unnecessary_fold.rs | 7 +- .../src/methods/unnecessary_iter_cloned.rs | 2 +- .../src/methods/unnecessary_lazy_eval.rs | 10 +- .../src/methods/unnecessary_to_owned.rs | 23 +- clippy_lints/src/methods/useless_asref.rs | 7 +- .../src/methods/wrong_self_convention.rs | 15 +- clippy_lints/src/minmax.rs | 4 +- clippy_lints/src/misc.rs | 24 +- clippy_lints/src/misc_early/literal_suffix.rs | 8 +- clippy_lints/src/misc_early/mod.rs | 5 +- .../src/misc_early/unneeded_field_pattern.rs | 4 +- .../src/mismatching_type_param_order.rs | 7 +- clippy_lints/src/missing_const_for_fn.rs | 2 +- clippy_lints/src/missing_doc.rs | 2 +- .../src/missing_enforced_import_rename.rs | 7 +- clippy_lints/src/missing_inline.rs | 2 +- clippy_lints/src/module_style.rs | 8 +- clippy_lints/src/mut_reference.rs | 2 +- clippy_lints/src/mutable_debug_assertion.rs | 5 +- clippy_lints/src/mutex_atomic.rs | 5 +- clippy_lints/src/needless_borrowed_ref.rs | 123 ++- clippy_lints/src/needless_continue.rs | 14 +- clippy_lints/src/needless_late_init.rs | 44 +- clippy_lints/src/needless_pass_by_value.rs | 17 +- clippy_lints/src/needless_question_mark.rs | 16 +- clippy_lints/src/neg_multiply.rs | 4 +- clippy_lints/src/new_without_default.rs | 7 +- clippy_lints/src/non_copy_const.rs | 5 +- clippy_lints/src/non_expressive_names.rs | 5 +- .../src/non_octal_unix_permissions.rs | 5 +- clippy_lints/src/nonstandard_macro_braces.rs | 79 +- clippy_lints/src/octal_escapes.rs | 2 +- .../operators/absurd_extreme_comparisons.rs | 5 +- .../src/operators/arithmetic_side_effects.rs | 120 ++- .../src/operators/assign_op_pattern.rs | 43 +- clippy_lints/src/operators/bit_mask.rs | 40 +- clippy_lints/src/operators/cmp_owned.rs | 10 +- clippy_lints/src/operators/duration_subsec.rs | 7 +- clippy_lints/src/operators/eq_op.rs | 2 +- .../src/operators/misrefactored_assign_op.rs | 12 +- clippy_lints/src/operators/mod.rs | 2 +- .../src/operators/needless_bitwise_bool.rs | 2 +- .../src/operators/numeric_arithmetic.rs | 9 +- clippy_lints/src/operators/ptr_eq.rs | 2 +- clippy_lints/src/operators/self_assignment.rs | 2 +- .../src/operators/verbose_bit_mask.rs | 2 +- clippy_lints/src/option_if_let_else.rs | 30 +- clippy_lints/src/panic_in_result_fn.rs | 19 +- clippy_lints/src/partialeq_to_none.rs | 5 +- clippy_lints/src/pass_by_ref_or_value.rs | 4 +- clippy_lints/src/ptr.rs | 4 +- clippy_lints/src/ptr_offset_with_cast.rs | 4 +- clippy_lints/src/question_mark.rs | 39 +- clippy_lints/src/ranges.rs | 16 +- clippy_lints/src/read_zero_byte_vec.rs | 45 +- clippy_lints/src/redundant_pub_crate.rs | 2 +- clippy_lints/src/redundant_slicing.rs | 8 +- .../src/redundant_static_lifetimes.rs | 2 +- clippy_lints/src/regex.rs | 4 +- clippy_lints/src/returns.rs | 212 ++--- clippy_lints/src/same_name_method.rs | 4 +- .../src/semicolon_if_nothing_returned.rs | 2 +- .../src/slow_vector_initialization.rs | 16 +- clippy_lints/src/std_instead_of_core.rs | 22 + clippy_lints/src/strings.rs | 6 +- clippy_lints/src/strlen_on_c_strings.rs | 2 +- .../src/suspicious_operation_groupings.rs | 9 +- clippy_lints/src/suspicious_trait_impl.rs | 33 +- clippy_lints/src/swap.rs | 19 +- clippy_lints/src/swap_ptr_to_ref.rs | 2 +- clippy_lints/src/to_digit_is_some.rs | 4 +- clippy_lints/src/trait_bounds.rs | 3 +- .../src/transmute/crosspointer_transmute.rs | 10 +- .../src/transmute/transmute_float_to_int.rs | 4 +- .../src/transmute/transmute_int_to_bool.rs | 2 +- .../src/transmute/transmute_int_to_char.rs | 4 +- .../src/transmute/transmute_int_to_float.rs | 4 +- .../src/transmute/transmute_num_to_bytes.rs | 4 +- .../src/transmute/transmute_ptr_to_ref.rs | 18 +- .../src/transmute/transmute_ref_to_ref.rs | 2 +- .../src/transmute/transmute_undefined_repr.rs | 23 +- .../transmutes_expressible_as_ptr_casts.rs | 5 +- .../src/transmute/transmuting_null.rs | 41 +- .../transmute/unsound_collection_transmute.rs | 5 +- .../src/transmute/useless_transmute.rs | 2 +- clippy_lints/src/transmute/utils.rs | 5 +- clippy_lints/src/transmute/wrong_transmute.rs | 2 +- clippy_lints/src/types/borrowed_box.rs | 6 +- clippy_lints/src/types/box_collection.rs | 2 +- clippy_lints/src/types/mod.rs | 8 +- clippy_lints/src/types/rc_buffer.rs | 4 +- .../src/types/redundant_allocation.rs | 31 +- clippy_lints/src/types/vec_box.rs | 2 +- clippy_lints/src/uninit_vec.rs | 9 +- clippy_lints/src/unit_return_expecting_ord.rs | 6 +- clippy_lints/src/unit_types/unit_arg.rs | 7 +- clippy_lints/src/unit_types/unit_cmp.rs | 7 +- .../src/unnecessary_owned_empty_strings.rs | 4 +- clippy_lints/src/unnecessary_self_imports.rs | 2 +- clippy_lints/src/unnecessary_wraps.rs | 13 +- clippy_lints/src/unsafe_removed_from_name.rs | 5 +- clippy_lints/src/unused_io_amount.rs | 7 +- clippy_lints/src/unused_rounding.rs | 4 +- clippy_lints/src/unwrap.rs | 7 +- clippy_lints/src/unwrap_in_result.rs | 74 +- clippy_lints/src/upper_case_acronyms.rs | 3 +- clippy_lints/src/use_self.rs | 2 +- clippy_lints/src/useless_conversion.rs | 14 +- clippy_lints/src/utils/author.rs | 2 +- clippy_lints/src/utils/conf.rs | 41 +- clippy_lints/src/utils/internal_lints.rs | 317 +++++-- .../internal_lints/metadata_collector.rs | 97 +- clippy_lints/src/wildcard_imports.rs | 2 +- clippy_lints/src/write.rs | 17 +- clippy_lints/src/zero_div_zero.rs | 3 +- clippy_lints/src/zero_sized_map_values.rs | 7 +- clippy_utils/Cargo.toml | 2 +- clippy_utils/src/attrs.rs | 4 +- clippy_utils/src/diagnostics.rs | 5 +- clippy_utils/src/eager_or_lazy.rs | 12 +- clippy_utils/src/hir_utils.rs | 4 +- clippy_utils/src/lib.rs | 228 +++-- clippy_utils/src/macros.rs | 261 +++-- clippy_utils/src/msrvs.rs | 3 +- clippy_utils/src/paths.rs | 3 - clippy_utils/src/ptr.rs | 37 +- clippy_utils/src/qualify_min_const_fn.rs | 21 +- clippy_utils/src/source.rs | 18 +- clippy_utils/src/sugg.rs | 62 +- clippy_utils/src/ty.rs | 2 +- clippy_utils/src/usage.rs | 59 +- clippy_utils/src/visitors.rs | 164 ++-- lintcheck/Cargo.toml | 2 + lintcheck/README.md | 20 +- lintcheck/lintcheck_crates.toml | 8 + lintcheck/src/config.rs | 10 +- lintcheck/src/driver.rs | 67 ++ lintcheck/src/main.rs | 164 ++-- lintcheck/src/recursive.rs | 123 +++ rust-toolchain | 2 +- rustc_tools_util/Cargo.toml | 2 +- rustc_tools_util/README.md | 8 +- rustc_tools_util/src/lib.rs | 12 +- src/docs.rs | 5 + src/docs/arithmetic_side_effects.txt | 2 +- src/docs/box_default.txt | 23 + src/docs/disallowed_macros.txt | 36 + src/docs/implicit_saturating_add.txt | 20 + src/docs/manual_clamp.txt | 46 + src/docs/needless_borrowed_reference.txt | 18 +- src/docs/similar_names.txt | 4 + src/docs/uninlined_format_args.txt | 36 + src/driver.rs | 6 +- src/main.rs | 6 +- tests/compile-test.rs | 23 +- tests/integration.rs | 23 +- tests/lint_message_convention.rs | 2 +- tests/missing-test-files.rs | 2 +- .../duplicate_mod/fail/src/main.stderr | 2 +- .../feature_name/fail/src/main.stderr | 4 +- .../module_style/fail_mod/src/main.stderr | 2 +- .../fail_mod_remap/src/main.stderr | 2 +- .../module_style/fail_no_mod/src/main.stderr | 2 +- tests/ui-internal/auxiliary/paths.rs | 2 + .../check_clippy_version_attribute.stderr | 4 +- tests/ui-internal/if_chain_style.stderr | 2 +- tests/ui-internal/match_type_on_diag_item.rs | 39 - .../match_type_on_diag_item.stderr | 27 - tests/ui-internal/unnecessary_def_path.fixed | 62 ++ tests/ui-internal/unnecessary_def_path.rs | 62 ++ tests/ui-internal/unnecessary_def_path.stderr | 101 ++ .../conf_deprecated_key.rs | 2 + .../conf_deprecated_key.stderr | 2 +- .../disallowed_macros/auxiliary/macros.rs | 32 + tests/ui-toml/disallowed_macros/clippy.toml | 11 + .../disallowed_macros/disallowed_macros.rs | 39 + .../disallowed_macros.stderr | 84 ++ .../conf_nonstandard_macro_braces.fixed | 62 ++ .../conf_nonstandard_macro_braces.rs | 1 + .../conf_nonstandard_macro_braces.stderr | 81 +- .../toml_disallowed_methods/clippy.toml | 1 + .../conf_disallowed_methods.rs | 6 + .../conf_disallowed_methods.stderr | 24 +- .../toml_unknown_key/conf_unknown_key.stderr | 1 + tests/ui/arithmetic_side_effects.rs | 95 +- tests/ui/arithmetic_side_effects.stderr | 298 +++++- tests/ui/assign_ops2.rs | 2 + tests/ui/assign_ops2.stderr | 20 +- tests/ui/auxiliary/proc_macro_attr.rs | 2 +- tests/ui/bind_instead_of_map.fixed | 1 + tests/ui/bind_instead_of_map.rs | 1 + tests/ui/bind_instead_of_map.stderr | 6 +- tests/ui/borrow_box.rs | 5 +- tests/ui/borrow_box.stderr | 20 +- tests/ui/box_collection.rs | 4 +- tests/ui/box_default.rs | 31 + tests/ui/box_default.stderr | 59 ++ .../branches_sharing_code/shared_at_bottom.rs | 3 +- .../shared_at_bottom.stderr | 20 +- .../ui/branches_sharing_code/shared_at_top.rs | 5 +- .../shared_at_top.stderr | 28 +- .../shared_at_top_and_bottom.rs | 3 +- .../shared_at_top_and_bottom.stderr | 26 +- .../branches_sharing_code/valid_if_blocks.rs | 5 +- .../valid_if_blocks.stderr | 26 +- tests/ui/cast_abs_to_unsigned.fixed | 1 + tests/ui/cast_abs_to_unsigned.rs | 1 + tests/ui/cast_abs_to_unsigned.stderr | 34 +- tests/ui/collapsible_match.rs | 3 +- tests/ui/collapsible_match.stderr | 40 +- tests/ui/crashes/ice-4775.rs | 2 + tests/ui/crashes/ice-9445.rs | 3 + tests/ui/crashes/ice-9459.rs | 5 + tests/ui/crashes/regressions.rs | 2 +- tests/ui/default_trait_access.fixed | 4 +- tests/ui/default_trait_access.rs | 4 +- tests/ui/default_trait_access.stderr | 2 +- tests/ui/doc_link_with_quotes.rs | 7 +- tests/ui/doc_link_with_quotes.stderr | 6 +- tests/ui/drop_forget_copy.rs | 20 + tests/ui/drop_forget_copy.stderr | 38 +- tests/ui/eta.fixed | 31 +- tests/ui/eta.rs | 31 +- tests/ui/eta.stderr | 20 +- tests/ui/expect_fun_call.fixed | 9 +- tests/ui/expect_fun_call.rs | 9 +- tests/ui/expect_fun_call.stderr | 40 +- tests/ui/explicit_counter_loop.rs | 1 + tests/ui/explicit_counter_loop.stderr | 18 +- tests/ui/explicit_deref_methods.fixed | 16 +- tests/ui/explicit_deref_methods.rs | 16 +- tests/ui/explicit_write.fixed | 3 +- tests/ui/explicit_write.rs | 3 +- tests/ui/explicit_write.stderr | 26 +- tests/ui/fallible_impl_from.rs | 1 + tests/ui/fallible_impl_from.stderr | 16 +- tests/ui/floating_point_exp.fixed | 1 + tests/ui/floating_point_exp.rs | 1 + tests/ui/floating_point_exp.stderr | 10 +- tests/ui/floating_point_log.fixed | 2 +- tests/ui/floating_point_log.rs | 2 +- tests/ui/floating_point_logbase.fixed | 1 + tests/ui/floating_point_logbase.rs | 1 + tests/ui/floating_point_logbase.stderr | 10 +- tests/ui/floating_point_mul_add.fixed | 2 + tests/ui/floating_point_mul_add.rs | 2 + tests/ui/floating_point_mul_add.stderr | 30 +- tests/ui/floating_point_powf.fixed | 1 + tests/ui/floating_point_powf.rs | 1 + tests/ui/floating_point_powf.stderr | 62 +- tests/ui/floating_point_powi.fixed | 3 + tests/ui/floating_point_powi.rs | 3 + tests/ui/floating_point_powi.stderr | 24 +- tests/ui/for_loop_fixable.fixed | 2 +- tests/ui/for_loop_fixable.rs | 2 +- tests/ui/for_loops_over_fallibles.rs | 1 + tests/ui/for_loops_over_fallibles.stderr | 22 +- tests/ui/format.fixed | 6 +- tests/ui/format.rs | 6 +- tests/ui/format_args.fixed | 12 +- tests/ui/format_args.rs | 12 +- tests/ui/format_args.stderr | 46 +- tests/ui/format_args_unfixable.rs | 6 +- tests/ui/format_args_unfixable.stderr | 36 +- tests/ui/functions.rs | 4 +- tests/ui/identity_op.fixed | 4 +- tests/ui/identity_op.rs | 4 +- tests/ui/implicit_saturating_add.fixed | 106 +++ tests/ui/implicit_saturating_add.rs | 154 +++ tests/ui/implicit_saturating_add.stderr | 197 ++++ .../if_let_slice_binding.rs | 1 + .../if_let_slice_binding.stderr | 20 +- tests/ui/infinite_iter.rs | 2 + tests/ui/infinite_iter.stderr | 32 +- tests/ui/issue_2356.fixed | 1 + tests/ui/issue_2356.rs | 1 + tests/ui/issue_2356.stderr | 2 +- tests/ui/issue_4266.rs | 1 + tests/ui/issue_4266.stderr | 6 +- tests/ui/item_after_statement.rs | 1 + tests/ui/item_after_statement.stderr | 6 +- tests/ui/manual_assert.edition2018.fixed | 14 +- tests/ui/manual_assert.edition2018.stderr | 90 +- tests/ui/manual_assert.edition2021.fixed | 14 +- tests/ui/manual_assert.edition2021.stderr | 90 +- tests/ui/manual_assert.fixed | 45 - tests/ui/manual_assert.rs | 15 +- tests/ui/manual_bits.fixed | 3 +- tests/ui/manual_bits.rs | 3 +- tests/ui/manual_bits.stderr | 58 +- tests/ui/manual_clamp.rs | 304 ++++++ tests/ui/manual_clamp.stderr | 375 ++++++++ tests/ui/manual_find_fixable.fixed | 4 +- tests/ui/manual_find_fixable.rs | 4 +- tests/ui/manual_flatten.rs | 2 +- tests/ui/map_unwrap_or.rs | 2 +- tests/ui/match_ref_pats.fixed | 3 +- tests/ui/match_ref_pats.rs | 3 +- tests/ui/match_ref_pats.stderr | 10 +- tests/ui/match_result_ok.fixed | 3 +- tests/ui/match_result_ok.rs | 3 +- tests/ui/match_result_ok.stderr | 6 +- tests/ui/match_same_arms2.rs | 6 +- tests/ui/match_same_arms2.stderr | 46 +- tests/ui/match_single_binding.fixed | 4 +- tests/ui/match_single_binding.rs | 4 +- tests/ui/match_single_binding2.fixed | 2 +- tests/ui/match_single_binding2.rs | 2 +- tests/ui/min_max.rs | 1 + tests/ui/min_max.stderr | 26 +- tests/ui/min_rust_version_attr.rs | 12 + tests/ui/min_rust_version_attr.stderr | 36 +- tests/ui/mut_mut.rs | 4 +- tests/ui/needless_borrow.fixed | 33 +- tests/ui/needless_borrow.rs | 33 +- tests/ui/needless_borrowed_ref.fixed | 41 +- tests/ui/needless_borrowed_ref.rs | 41 +- tests/ui/needless_borrowed_ref.stderr | 121 ++- tests/ui/needless_collect_indirect.rs | 2 + tests/ui/needless_collect_indirect.stderr | 32 +- tests/ui/needless_continue.rs | 1 + tests/ui/needless_continue.stderr | 16 +- tests/ui/needless_for_each_fixable.fixed | 7 +- tests/ui/needless_for_each_fixable.rs | 7 +- tests/ui/needless_for_each_fixable.stderr | 16 +- tests/ui/needless_for_each_unfixable.rs | 2 +- tests/ui/needless_late_init.fixed | 5 +- tests/ui/needless_late_init.rs | 5 +- tests/ui/needless_late_init.stderr | 32 +- tests/ui/needless_pass_by_value.rs | 9 +- tests/ui/needless_pass_by_value.stderr | 52 +- tests/ui/needless_range_loop.rs | 1 + tests/ui/needless_range_loop.stderr | 28 +- tests/ui/needless_return.fixed | 37 + tests/ui/needless_return.rs | 37 + tests/ui/needless_return.stderr | 205 +++- tests/ui/never_loop.rs | 26 + tests/ui/never_loop.stderr | 15 +- tests/ui/no_effect.rs | 6 +- tests/ui/no_effect.stderr | 60 +- tests/ui/option_map_unit_fn_fixable.fixed | 3 +- tests/ui/option_map_unit_fn_fixable.rs | 3 +- tests/ui/option_map_unit_fn_fixable.stderr | 38 +- tests/ui/option_take_on_temporary.fixed | 15 - tests/ui/or_fun_call.fixed | 3 +- tests/ui/or_fun_call.rs | 3 +- tests/ui/or_fun_call.stderr | 52 +- tests/ui/panic_in_result_fn_assertions.rs | 2 +- .../ui/panic_in_result_fn_debug_assertions.rs | 2 +- tests/ui/patterns.fixed | 3 +- tests/ui/patterns.rs | 3 +- tests/ui/patterns.stderr | 6 +- tests/ui/print_literal.rs | 1 + tests/ui/print_literal.stderr | 24 +- tests/ui/ptr_offset_with_cast.fixed | 1 + tests/ui/ptr_offset_with_cast.rs | 1 + tests/ui/ptr_offset_with_cast.stderr | 4 +- tests/ui/question_mark.fixed | 9 + tests/ui/question_mark.rs | 9 + tests/ui/recursive_format_impl.rs | 5 +- tests/ui/recursive_format_impl.stderr | 20 +- tests/ui/redundant_clone.fixed | 4 +- tests/ui/redundant_clone.rs | 4 +- .../redundant_pattern_matching_ipaddr.fixed | 11 +- tests/ui/redundant_pattern_matching_ipaddr.rs | 11 +- .../redundant_pattern_matching_ipaddr.stderr | 36 +- .../redundant_pattern_matching_result.fixed | 11 +- tests/ui/redundant_pattern_matching_result.rs | 11 +- .../redundant_pattern_matching_result.stderr | 44 +- tests/ui/result_map_unit_fn_fixable.fixed | 2 +- tests/ui/result_map_unit_fn_fixable.rs | 2 +- tests/ui/reversed_empty_ranges_fixable.fixed | 1 + tests/ui/reversed_empty_ranges_fixable.rs | 1 + tests/ui/reversed_empty_ranges_fixable.stderr | 8 +- .../reversed_empty_ranges_loops_fixable.fixed | 1 + .../ui/reversed_empty_ranges_loops_fixable.rs | 1 + ...reversed_empty_ranges_loops_fixable.stderr | 12 +- .../reversed_empty_ranges_loops_unfixable.rs | 1 + ...versed_empty_ranges_loops_unfixable.stderr | 4 +- tests/ui/same_functions_in_if_condition.rs | 12 +- .../ui/same_functions_in_if_condition.stderr | 24 +- tests/ui/semicolon_if_nothing_returned.rs | 2 +- .../ui/should_impl_trait/method_list_1.stderr | 12 +- tests/ui/significant_drop_in_scrutinee.rs | 7 +- tests/ui/significant_drop_in_scrutinee.stderr | 52 +- tests/ui/single_match.rs | 1 + tests/ui/single_match.stderr | 32 +- tests/ui/single_match_else.rs | 4 +- tests/ui/single_match_else.stderr | 10 +- tests/ui/std_instead_of_core.rs | 6 + tests/ui/std_instead_of_core.stderr | 16 +- tests/ui/toplevel_ref_arg.fixed | 2 +- tests/ui/toplevel_ref_arg.rs | 2 +- tests/ui/trivially_copy_pass_by_ref.rs | 7 +- tests/ui/trivially_copy_pass_by_ref.stderr | 38 +- tests/ui/uninit_vec.rs | 6 + tests/ui/uninlined_format_args.fixed | 164 ++++ tests/ui/uninlined_format_args.rs | 169 ++++ tests/ui/uninlined_format_args.stderr | 894 ++++++++++++++++++ tests/ui/unit_arg.rs | 19 +- tests/ui/unit_arg.stderr | 20 +- tests/ui/unit_arg_empty_blocks.fixed | 3 +- tests/ui/unit_arg_empty_blocks.rs | 3 +- tests/ui/unit_arg_empty_blocks.stderr | 8 +- tests/ui/unnecessary_cast.fixed | 14 + tests/ui/unnecessary_cast.rs | 14 + tests/ui/unnecessary_cast.stderr | 20 +- tests/ui/unnecessary_clone.rs | 4 +- tests/ui/unnecessary_join.fixed | 2 +- tests/ui/unnecessary_join.rs | 2 +- tests/ui/unnecessary_lazy_eval.fixed | 21 + tests/ui/unnecessary_lazy_eval.rs | 21 + tests/ui/unnecessary_lazy_eval.stderr | 68 +- tests/ui/upper_case_acronyms.rs | 9 + tests/ui/upper_case_acronyms.stderr | 14 +- tests/ui/used_underscore_binding.rs | 3 +- tests/ui/used_underscore_binding.stderr | 12 +- tests/ui/useless_asref.fixed | 3 +- tests/ui/useless_asref.rs | 3 +- tests/ui/useless_asref.stderr | 24 +- tests/ui/vec.fixed | 2 +- tests/ui/vec.rs | 2 +- tests/ui/while_let_loop.rs | 1 + tests/ui/while_let_loop.stderr | 10 +- tests/ui/while_let_on_iterator.fixed | 10 +- tests/ui/while_let_on_iterator.rs | 10 +- tests/ui/while_let_on_iterator.stderr | 52 +- tests/ui/wildcard_enum_match_arm.fixed | 10 +- tests/ui/wildcard_enum_match_arm.rs | 10 +- tests/ui/wildcard_enum_match_arm.stderr | 14 +- tests/ui/write_literal.rs | 2 +- tests/versioncheck.rs | 6 +- 617 files changed, 10259 insertions(+), 4400 deletions(-) create mode 100644 clippy_lints/src/box_default.rs create mode 100644 clippy_lints/src/disallowed_macros.rs delete mode 100644 clippy_lints/src/doc_link_with_quotes.rs create mode 100644 clippy_lints/src/implicit_saturating_add.rs create mode 100644 clippy_lints/src/manual_clamp.rs create mode 100644 lintcheck/src/driver.rs create mode 100644 lintcheck/src/recursive.rs create mode 100644 src/docs/box_default.txt create mode 100644 src/docs/disallowed_macros.txt create mode 100644 src/docs/implicit_saturating_add.txt create mode 100644 src/docs/manual_clamp.txt create mode 100644 src/docs/uninlined_format_args.txt create mode 100644 tests/ui-internal/auxiliary/paths.rs delete mode 100644 tests/ui-internal/match_type_on_diag_item.rs delete mode 100644 tests/ui-internal/match_type_on_diag_item.stderr create mode 100644 tests/ui-internal/unnecessary_def_path.fixed create mode 100644 tests/ui-internal/unnecessary_def_path.rs create mode 100644 tests/ui-internal/unnecessary_def_path.stderr create mode 100644 tests/ui-toml/disallowed_macros/auxiliary/macros.rs create mode 100644 tests/ui-toml/disallowed_macros/clippy.toml create mode 100644 tests/ui-toml/disallowed_macros/disallowed_macros.rs create mode 100644 tests/ui-toml/disallowed_macros/disallowed_macros.stderr create mode 100644 tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.fixed create mode 100644 tests/ui/box_default.rs create mode 100644 tests/ui/box_default.stderr create mode 100644 tests/ui/crashes/ice-9445.rs create mode 100644 tests/ui/crashes/ice-9459.rs create mode 100644 tests/ui/implicit_saturating_add.fixed create mode 100644 tests/ui/implicit_saturating_add.rs create mode 100644 tests/ui/implicit_saturating_add.stderr delete mode 100644 tests/ui/manual_assert.fixed create mode 100644 tests/ui/manual_clamp.rs create mode 100644 tests/ui/manual_clamp.stderr delete mode 100644 tests/ui/option_take_on_temporary.fixed create mode 100644 tests/ui/uninlined_format_args.fixed create mode 100644 tests/ui/uninlined_format_args.rs create mode 100644 tests/ui/uninlined_format_args.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 044cbff4b78e..42615179f705 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,11 +6,161 @@ document. ## Unreleased / In Rust Nightly -[d7b5cbf0...master](https://github.com/rust-lang/rust-clippy/compare/d7b5cbf0...master) +[3c7e7dbc...master](https://github.com/rust-lang/rust-clippy/compare/3c7e7dbc...master) + +## Rust 1.64 + +Current stable, released 2022-09-22 + +[d7b5cbf0...3c7e7dbc](https://github.com/rust-lang/rust-clippy/compare/d7b5cbf0...3c7e7dbc) + +### New Lints + +* [`arithmetic_side_effects`] + [#9130](https://github.com/rust-lang/rust-clippy/pull/9130) +* [`invalid_utf8_in_unchecked`] + [#9105](https://github.com/rust-lang/rust-clippy/pull/9105) +* [`assertions_on_result_states`] + [#9225](https://github.com/rust-lang/rust-clippy/pull/9225) +* [`manual_find`] + [#8649](https://github.com/rust-lang/rust-clippy/pull/8649) +* [`manual_retain`] + [#8972](https://github.com/rust-lang/rust-clippy/pull/8972) +* [`default_instead_of_iter_empty`] + [#8989](https://github.com/rust-lang/rust-clippy/pull/8989) +* [`manual_rem_euclid`] + [#9031](https://github.com/rust-lang/rust-clippy/pull/9031) +* [`obfuscated_if_else`] + [#9148](https://github.com/rust-lang/rust-clippy/pull/9148) +* [`std_instead_of_core`] + [#9103](https://github.com/rust-lang/rust-clippy/pull/9103) +* [`std_instead_of_alloc`] + [#9103](https://github.com/rust-lang/rust-clippy/pull/9103) +* [`alloc_instead_of_core`] + [#9103](https://github.com/rust-lang/rust-clippy/pull/9103) +* [`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) + [#9161](https://github.com/rust-lang/rust-clippy/pull/9161) + +### Enhancements + +* [`significant_drop_in_scrutinee`]: Now gives more context in the lint message + [#8981](https://github.com/rust-lang/rust-clippy/pull/8981) +* [`single_match`], [`single_match_else`]: Now catches more `Option` cases + [#8985](https://github.com/rust-lang/rust-clippy/pull/8985) +* [`unused_async`]: Now works for async methods + [#9025](https://github.com/rust-lang/rust-clippy/pull/9025) +* [`manual_filter_map`], [`manual_find_map`]: Now lint more expressions + [#8958](https://github.com/rust-lang/rust-clippy/pull/8958) +* [`question_mark`]: Now works for simple `if let` expressions + [#8356](https://github.com/rust-lang/rust-clippy/pull/8356) +* [`undocumented_unsafe_blocks`]: Now finds comments before the start of closures + [#9117](https://github.com/rust-lang/rust-clippy/pull/9117) +* [`trait_duplication_in_bounds`]: Now catches duplicate bounds in where clauses + [#8703](https://github.com/rust-lang/rust-clippy/pull/8703) +* [`shadow_reuse`], [`shadow_same`], [`shadow_unrelated`]: Now lint in const blocks + [#9124](https://github.com/rust-lang/rust-clippy/pull/9124) +* [`slow_vector_initialization`]: Now detects cases with `vec.capacity()` + [#8953](https://github.com/rust-lang/rust-clippy/pull/8953) +* [`unused_self`]: Now respects the `avoid-breaking-exported-api` config option + [#9199](https://github.com/rust-lang/rust-clippy/pull/9199) +* [`box_collection`]: Now supports all std collections + [#9170](https://github.com/rust-lang/rust-clippy/pull/9170) + +### False Positive Fixes + +* [`significant_drop_in_scrutinee`]: Now ignores calls to `IntoIterator::into_iter` + [#9140](https://github.com/rust-lang/rust-clippy/pull/9140) +* [`while_let_loop`]: Now ignores cases when the significant drop order would change + [#8981](https://github.com/rust-lang/rust-clippy/pull/8981) +* [`branches_sharing_code`]: Now ignores cases where moved variables have a significant + drop or variable modifications can affect the conditions + [#9138](https://github.com/rust-lang/rust-clippy/pull/9138) +* [`let_underscore_lock`]: Now ignores bindings that aren't locked + [#8990](https://github.com/rust-lang/rust-clippy/pull/8990) +* [`trivially_copy_pass_by_ref`]: Now tracks lifetimes and ignores cases where unsafe + pointers are used + [#8639](https://github.com/rust-lang/rust-clippy/pull/8639) +* [`let_unit_value`]: No longer ignores `#[allow]` attributes on the value + [#9082](https://github.com/rust-lang/rust-clippy/pull/9082) +* [`declare_interior_mutable_const`]: Now ignores the `thread_local!` macro + [#9015](https://github.com/rust-lang/rust-clippy/pull/9015) +* [`if_same_then_else`]: Now ignores branches with `todo!` and `unimplemented!` + [#9006](https://github.com/rust-lang/rust-clippy/pull/9006) +* [`enum_variant_names`]: Now ignores names with `_` prefixes + [#9032](https://github.com/rust-lang/rust-clippy/pull/9032) +* [`let_unit_value`]: Now ignores cases, where the unit type is manually specified + [#9056](https://github.com/rust-lang/rust-clippy/pull/9056) +* [`match_same_arms`]: Now ignores branches with `todo!` + [#9207](https://github.com/rust-lang/rust-clippy/pull/9207) +* [`assign_op_pattern`]: Ignores cases that break borrowing rules + [#9214](https://github.com/rust-lang/rust-clippy/pull/9214) +* [`extra_unused_lifetimes`]: No longer triggers in derive macros + [#9037](https://github.com/rust-lang/rust-clippy/pull/9037) +* [`mismatching_type_param_order`]: Now ignores complicated generic parameters + [#9146](https://github.com/rust-lang/rust-clippy/pull/9146) +* [`equatable_if_let`]: No longer lints in macros + [#9074](https://github.com/rust-lang/rust-clippy/pull/9074) +* [`new_without_default`]: Now ignores generics and lifetime parameters on `fn new` + [#9115](https://github.com/rust-lang/rust-clippy/pull/9115) +* [`needless_borrow`]: Now ignores cases that result in the execution of different traits + [#9096](https://github.com/rust-lang/rust-clippy/pull/9096) +* [`declare_interior_mutable_const`]: No longer triggers in thread-local initializers + [#9246](https://github.com/rust-lang/rust-clippy/pull/9246) + +### Suggestion Fixes/Improvements + +* [`type_repetition_in_bounds`]: The suggestion now works with maybe bounds + [#9132](https://github.com/rust-lang/rust-clippy/pull/9132) +* [`transmute_ptr_to_ref`]: Now suggests `pointer::cast` when possible + [#8939](https://github.com/rust-lang/rust-clippy/pull/8939) +* [`useless_format`]: Now suggests the correct variable name + [#9237](https://github.com/rust-lang/rust-clippy/pull/9237) +* [`or_fun_call`]: The lint emission will now only span over the `unwrap_or` call + [#9144](https://github.com/rust-lang/rust-clippy/pull/9144) +* [`neg_multiply`]: Now suggests adding parentheses around suggestion if needed + [#9026](https://github.com/rust-lang/rust-clippy/pull/9026) +* [`unnecessary_lazy_evaluations`]: Now suggest for `bool::then_some` for lazy evaluation + [#9099](https://github.com/rust-lang/rust-clippy/pull/9099) +* [`manual_flatten`]: Improved message for long code snippets + [#9156](https://github.com/rust-lang/rust-clippy/pull/9156) +* [`explicit_counter_loop`]: The suggestion is now machine applicable + [#9149](https://github.com/rust-lang/rust-clippy/pull/9149) +* [`needless_borrow`]: Now keeps parentheses around fields, when needed + [#9210](https://github.com/rust-lang/rust-clippy/pull/9210) +* [`while_let_on_iterator`]: The suggestion now works in `FnOnce` closures + [#9134](https://github.com/rust-lang/rust-clippy/pull/9134) + +### ICE Fixes + +* Fix ICEs related to `#![feature(generic_const_exprs)]` usage + [#9241](https://github.com/rust-lang/rust-clippy/pull/9241) +* Fix ICEs related to reference lints + [#9093](https://github.com/rust-lang/rust-clippy/pull/9093) +* [`question_mark`]: Fix ICE on zero field tuple structs + [#9244](https://github.com/rust-lang/rust-clippy/pull/9244) + +### Documentation Improvements + +* [`needless_option_take`]: Now includes a "What it does" and "Why is this bad?" section. + [#9022](https://github.com/rust-lang/rust-clippy/pull/9022) + +### Others + +* Using `--cap-lints=allow` and only `--force-warn`ing some will now work with Clippy's driver + [#9036](https://github.com/rust-lang/rust-clippy/pull/9036) +* Clippy now tries to read the `rust-version` from `Cargo.toml` to identify the + minimum supported rust version + [#8774](https://github.com/rust-lang/rust-clippy/pull/8774) ## Rust 1.63 -Current stable, released 2022-08-11 +Released 2022-08-11 [7c21f91b...d7b5cbf0](https://github.com/rust-lang/rust-clippy/compare/7c21f91b...d7b5cbf0) @@ -3609,6 +3759,7 @@ Released 2018-09-13 [`borrow_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_interior_mutable_const [`borrowed_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrowed_box [`box_collection`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_collection +[`box_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_default [`box_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_vec [`boxed_local`]: https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local [`branches_sharing_code`]: https://rust-lang.github.io/rust-clippy/master/index.html#branches_sharing_code @@ -3669,6 +3820,7 @@ Released 2018-09-13 [`derive_hash_xor_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq [`derive_ord_xor_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_ord_xor_partial_ord [`derive_partial_eq_without_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_partial_eq_without_eq +[`disallowed_macros`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_macros [`disallowed_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_method [`disallowed_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_methods [`disallowed_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_names @@ -3766,6 +3918,7 @@ Released 2018-09-13 [`implicit_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_clone [`implicit_hasher`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_hasher [`implicit_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_return +[`implicit_saturating_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_saturating_add [`implicit_saturating_sub`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_saturating_sub [`imprecise_flops`]: https://rust-lang.github.io/rust-clippy/master/index.html#imprecise_flops [`inconsistent_digit_grouping`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_digit_grouping @@ -3834,6 +3987,7 @@ Released 2018-09-13 [`manual_assert`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_assert [`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn [`manual_bits`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits +[`manual_clamp`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_clamp [`manual_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map [`manual_find`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find [`manual_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find_map @@ -4124,6 +4278,7 @@ Released 2018-09-13 [`unimplemented`]: https://rust-lang.github.io/rust-clippy/master/index.html#unimplemented [`uninit_assumed_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninit_assumed_init [`uninit_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninit_vec +[`uninlined_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args [`unit_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_arg [`unit_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_cmp [`unit_hash`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_hash diff --git a/Cargo.toml b/Cargo.toml index b7e136ce9b29..60200a88b858 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.1.65" +version = "0.1.66" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" @@ -23,12 +23,12 @@ path = "src/driver.rs" [dependencies] clippy_lints = { path = "clippy_lints" } semver = "1.0" -rustc_tools_util = { path = "rustc_tools_util" } +rustc_tools_util = "0.2.1" tempfile = { version = "3.2", optional = true } termize = "0.1" [dev-dependencies] -compiletest_rs = { version = "0.8", features = ["tmp"] } +compiletest_rs = { version = "0.9", features = ["tmp"] } tester = "0.9" regex = "1.5" toml = "0.5" @@ -55,7 +55,7 @@ tokio = { version = "1", features = ["io-util"] } rustc-semver = "1.1" [build-dependencies] -rustc_tools_util = { version = "0.2", path = "rustc_tools_util" } +rustc_tools_util = "0.2.1" [features] deny-warnings = ["clippy_lints/deny-warnings"] diff --git a/README.md b/README.md index 1193771ff736..a8a6b86d2a15 100644 --- a/README.md +++ b/README.md @@ -139,25 +139,6 @@ line. (You can swap `clippy::all` with the specific lint category you are target ## Configuration -Some lints can be configured in a TOML file named `clippy.toml` or `.clippy.toml`. It contains a basic `variable = -value` mapping e.g. - -```toml -avoid-breaking-exported-api = false -disallowed-names = ["toto", "tata", "titi"] -cognitive-complexity-threshold = 30 -``` - -See the [list of lints](https://rust-lang.github.io/rust-clippy/master/index.html) for more information about which -lints can be configured and the meaning of the variables. - -Note that configuration changes will not apply for code that has already been compiled and cached under `./target/`; -for example, adding a new string to `doc-valid-idents` may still result in Clippy flagging that string. To be sure that -any configuration changes are applied, you may want to run `cargo clean` and re-compile your crate from scratch. - -To deactivate the “for further information visit *lint-link*” message you can -define the `CLIPPY_DISABLE_DOCS_LINKS` environment variable. - ### Allowing/denying lints You can add options to your code to `allow`/`warn`/`deny` Clippy lints: @@ -205,6 +186,33 @@ the lint(s) you are interested in: cargo clippy -- -A clippy::all -W clippy::useless_format -W clippy::... ``` +### Configure the behavior of some lints + +Some lints can be configured in a TOML file named `clippy.toml` or `.clippy.toml`. It contains a basic `variable = +value` mapping e.g. + +```toml +avoid-breaking-exported-api = false +disallowed-names = ["toto", "tata", "titi"] +cognitive-complexity-threshold = 30 +``` + +See the [list of lints](https://rust-lang.github.io/rust-clippy/master/index.html) for more information about which +lints can be configured and the meaning of the variables. + +> **Note** +> +> `clippy.toml` or `.clippy.toml` cannot be used to allow/deny lints. + +> **Note** +> +> Configuration changes will not apply for code that has already been compiled and cached under `./target/`; +> for example, adding a new string to `doc-valid-idents` may still result in Clippy flagging that string. To be sure +> that any configuration changes are applied, you may want to run `cargo clean` and re-compile your crate from scratch. + +To deactivate the “for further information visit *lint-link*” message you can +define the `CLIPPY_DISABLE_DOCS_LINKS` environment variable. + ### Specifying the minimum supported Rust version Projects that intend to support old versions of Rust can disable lints pertaining to newer features by diff --git a/clippy_dev/src/fmt.rs b/clippy_dev/src/fmt.rs index 357cf6fc43aa..256231441817 100644 --- a/clippy_dev/src/fmt.rs +++ b/clippy_dev/src/fmt.rs @@ -82,16 +82,16 @@ pub fn run(check: bool, verbose: bool) { fn output_err(err: CliError) { match err { CliError::CommandFailed(command, stderr) => { - eprintln!("error: A command failed! `{}`\nstderr: {}", command, stderr); + eprintln!("error: A command failed! `{command}`\nstderr: {stderr}"); }, CliError::IoError(err) => { - eprintln!("error: {}", err); + eprintln!("error: {err}"); }, CliError::RustfmtNotInstalled => { eprintln!("error: rustfmt nightly is not installed."); }, CliError::WalkDirError(err) => { - eprintln!("error: {}", err); + eprintln!("error: {err}"); }, CliError::IntellijSetupActive => { eprintln!( diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index a417d3dd8a4e..d3e036692040 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -41,7 +41,7 @@ fn main() { matches.contains_id("msrv"), ) { Ok(_) => update_lints::update(update_lints::UpdateMode::Change), - Err(e) => eprintln!("Unable to create lint: {}", e), + Err(e) => eprintln!("Unable to create lint: {e}"), } }, Some(("setup", sub_command)) => match sub_command.subcommand() { diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index 02cb13a1d8af..9e15f1504fa9 100644 --- a/clippy_dev/src/new_lint.rs +++ b/clippy_dev/src/new_lint.rs @@ -1,5 +1,5 @@ use crate::clippy_project_root; -use indoc::{indoc, writedoc}; +use indoc::{formatdoc, writedoc}; use std::fmt::Write as _; use std::fs::{self, OpenOptions}; use std::io::prelude::*; @@ -23,7 +23,7 @@ impl Context for io::Result { match self { Ok(t) => Ok(t), Err(e) => { - let message = format!("{}: {}", text.as_ref(), e); + let message = format!("{}: {e}", text.as_ref()); Err(io::Error::new(ErrorKind::Other, message)) }, } @@ -72,7 +72,7 @@ fn create_lint(lint: &LintData<'_>, enable_msrv: bool) -> io::Result<()> { let lint_contents = get_lint_file_contents(lint, enable_msrv); let lint_path = format!("clippy_lints/src/{}.rs", lint.name); write_file(lint.project_root.join(&lint_path), lint_contents.as_bytes())?; - println!("Generated lint file: `{}`", lint_path); + println!("Generated lint file: `{lint_path}`"); Ok(()) } @@ -86,7 +86,7 @@ fn create_test(lint: &LintData<'_>) -> io::Result<()> { path.push("src"); fs::create_dir(&path)?; - let header = format!("// compile-flags: --crate-name={}", lint_name); + let header = format!("// compile-flags: --crate-name={lint_name}"); write_file(path.join("main.rs"), get_test_file_contents(lint_name, Some(&header)))?; Ok(()) @@ -106,7 +106,7 @@ fn create_test(lint: &LintData<'_>) -> io::Result<()> { let test_contents = get_test_file_contents(lint.name, None); write_file(lint.project_root.join(&test_path), test_contents)?; - println!("Generated test file: `{}`", test_path); + println!("Generated test file: `{test_path}`"); } Ok(()) @@ -186,38 +186,36 @@ pub(crate) fn get_stabilization_version() -> String { } fn get_test_file_contents(lint_name: &str, header_commands: Option<&str>) -> String { - let mut contents = format!( - indoc! {" - #![allow(unused)] - #![warn(clippy::{})] + let mut contents = formatdoc!( + r#" + #![allow(unused)] + #![warn(clippy::{lint_name})] - fn main() {{ - // test code goes here - }} - "}, - lint_name + fn main() {{ + // test code goes here + }} + "# ); if let Some(header) = header_commands { - contents = format!("{}\n{}", header, contents); + contents = format!("{header}\n{contents}"); } contents } fn get_manifest_contents(lint_name: &str, hint: &str) -> String { - format!( - indoc! {r#" - # {} + formatdoc!( + r#" + # {hint} - [package] - name = "{}" - version = "0.1.0" - publish = false + [package] + name = "{lint_name}" + version = "0.1.0" + publish = false - [workspace] - "#}, - hint, lint_name + [workspace] + "# ) } @@ -238,76 +236,61 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { let name_upper = lint_name.to_uppercase(); result.push_str(&if enable_msrv { - format!( - indoc! {" - use clippy_utils::msrvs; - {pass_import} - use rustc_lint::{{{context_import}, {pass_type}, LintContext}}; - use rustc_semver::RustcVersion; - use rustc_session::{{declare_tool_lint, impl_lint_pass}}; + formatdoc!( + r#" + use clippy_utils::msrvs; + {pass_import} + use rustc_lint::{{{context_import}, {pass_type}, LintContext}}; + use rustc_semver::RustcVersion; + use rustc_session::{{declare_tool_lint, impl_lint_pass}}; - "}, - pass_type = pass_type, - pass_import = pass_import, - context_import = context_import, + "# ) } else { - format!( - indoc! {" - {pass_import} - use rustc_lint::{{{context_import}, {pass_type}}}; - use rustc_session::{{declare_lint_pass, declare_tool_lint}}; + formatdoc!( + r#" + {pass_import} + use rustc_lint::{{{context_import}, {pass_type}}}; + use rustc_session::{{declare_lint_pass, declare_tool_lint}}; - "}, - pass_import = pass_import, - pass_type = pass_type, - context_import = context_import + "# ) }); let _ = write!(result, "{}", get_lint_declaration(&name_upper, category)); result.push_str(&if enable_msrv { - format!( - indoc! {" - pub struct {name_camel} {{ - msrv: Option, + formatdoc!( + r#" + pub struct {name_camel} {{ + msrv: Option, + }} + + impl {name_camel} {{ + #[must_use] + pub fn new(msrv: Option) -> Self {{ + Self {{ msrv }} }} + }} - impl {name_camel} {{ - #[must_use] - pub fn new(msrv: Option) -> Self {{ - Self {{ msrv }} - }} - }} + impl_lint_pass!({name_camel} => [{name_upper}]); - impl_lint_pass!({name_camel} => [{name_upper}]); + impl {pass_type}{pass_lifetimes} for {name_camel} {{ + extract_msrv_attr!({context_import}); + }} - impl {pass_type}{pass_lifetimes} for {name_camel} {{ - extract_msrv_attr!({context_import}); - }} - - // TODO: Add MSRV level to `clippy_utils/src/msrvs.rs` if needed. - // TODO: Add MSRV test to `tests/ui/min_rust_version_attr.rs`. - // TODO: Update msrv config comment in `clippy_lints/src/utils/conf.rs` - "}, - pass_type = pass_type, - pass_lifetimes = pass_lifetimes, - name_upper = name_upper, - name_camel = name_camel, - context_import = context_import, + // TODO: Add MSRV level to `clippy_utils/src/msrvs.rs` if needed. + // TODO: Add MSRV test to `tests/ui/min_rust_version_attr.rs`. + // TODO: Update msrv config comment in `clippy_lints/src/utils/conf.rs` + "# ) } else { - format!( - indoc! {" - declare_lint_pass!({name_camel} => [{name_upper}]); + formatdoc!( + r#" + declare_lint_pass!({name_camel} => [{name_upper}]); - impl {pass_type}{pass_lifetimes} for {name_camel} {{}} - "}, - pass_type = pass_type, - pass_lifetimes = pass_lifetimes, - name_upper = name_upper, - name_camel = name_camel, + impl {pass_type}{pass_lifetimes} for {name_camel} {{}} + "# ) }); @@ -315,8 +298,8 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { } fn get_lint_declaration(name_upper: &str, category: &str) -> String { - format!( - indoc! {r#" + formatdoc!( + r#" declare_clippy_lint! {{ /// ### What it does /// @@ -330,15 +313,13 @@ fn get_lint_declaration(name_upper: &str, category: &str) -> String { /// ```rust /// // example code which does not raise clippy warning /// ``` - #[clippy::version = "{version}"] + #[clippy::version = "{}"] pub {name_upper}, {category}, "default lint description" }} - "#}, - version = get_stabilization_version(), - name_upper = name_upper, - category = category, + "#, + get_stabilization_version(), ) } @@ -352,7 +333,7 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R _ => {}, } - let ty_dir = lint.project_root.join(format!("clippy_lints/src/{}", ty)); + let ty_dir = lint.project_root.join(format!("clippy_lints/src/{ty}")); assert!( ty_dir.exists() && ty_dir.is_dir(), "Directory `{}` does not exist!", @@ -412,10 +393,10 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R } write_file(lint_file_path.as_path(), lint_file_contents)?; - println!("Generated lint file: `clippy_lints/src/{}/{}.rs`", ty, lint.name); + println!("Generated lint file: `clippy_lints/src/{ty}/{}.rs`", lint.name); println!( - "Be sure to add a call to `{}::check` in `clippy_lints/src/{}/mod.rs`!", - lint.name, ty + "Be sure to add a call to `{}::check` in `clippy_lints/src/{ty}/mod.rs`!", + lint.name ); Ok(()) @@ -542,7 +523,7 @@ fn setup_mod_file(path: &Path, lint: &LintData<'_>) -> io::Result<&'static str> .chain(std::iter::once(&*lint_name_upper)) .filter(|s| !s.is_empty()) { - let _ = write!(new_arr_content, "\n {},", ident); + let _ = write!(new_arr_content, "\n {ident},"); } new_arr_content.push('\n'); diff --git a/clippy_dev/src/serve.rs b/clippy_dev/src/serve.rs index f15f24da9467..2e0794f12fa1 100644 --- a/clippy_dev/src/serve.rs +++ b/clippy_dev/src/serve.rs @@ -10,8 +10,8 @@ use std::time::{Duration, SystemTime}; /// Panics if the python commands could not be spawned pub fn run(port: u16, lint: Option<&String>) -> ! { let mut url = Some(match lint { - None => format!("http://localhost:{}", port), - Some(lint) => format!("http://localhost:{}/#{}", port, lint), + None => format!("http://localhost:{port}"), + Some(lint) => format!("http://localhost:{port}/#{lint}"), }); loop { diff --git a/clippy_dev/src/setup/git_hook.rs b/clippy_dev/src/setup/git_hook.rs index 3fbb77d59235..1de5b1940bae 100644 --- a/clippy_dev/src/setup/git_hook.rs +++ b/clippy_dev/src/setup/git_hook.rs @@ -30,10 +30,7 @@ pub fn install_hook(force_override: bool) { println!("info: the hook can be removed with `cargo dev remove git-hook`"); println!("git hook successfully installed"); }, - Err(err) => eprintln!( - "error: unable to copy `{}` to `{}` ({})", - HOOK_SOURCE_FILE, HOOK_TARGET_FILE, err - ), + Err(err) => eprintln!("error: unable to copy `{HOOK_SOURCE_FILE}` to `{HOOK_TARGET_FILE}` ({err})"), } } @@ -77,7 +74,7 @@ pub fn remove_hook() { fn delete_git_hook_file(path: &Path) -> bool { if let Err(err) = fs::remove_file(path) { - eprintln!("error: unable to delete existing pre-commit git hook ({})", err); + eprintln!("error: unable to delete existing pre-commit git hook ({err})"); false } else { true diff --git a/clippy_dev/src/setup/intellij.rs b/clippy_dev/src/setup/intellij.rs index bf741e6d1217..b64e79733eb2 100644 --- a/clippy_dev/src/setup/intellij.rs +++ b/clippy_dev/src/setup/intellij.rs @@ -60,7 +60,7 @@ fn check_and_get_rustc_dir(rustc_path: &str) -> Result { path = absolute_path; }, Err(err) => { - eprintln!("error: unable to get the absolute path of rustc ({})", err); + eprintln!("error: unable to get the absolute path of rustc ({err})"); return Err(()); }, }; @@ -103,14 +103,14 @@ fn inject_deps_into_project(rustc_source_dir: &Path, project: &ClippyProjectInfo fn read_project_file(file_path: &str) -> Result { let path = Path::new(file_path); if !path.exists() { - eprintln!("error: unable to find the file `{}`", file_path); + eprintln!("error: unable to find the file `{file_path}`"); return Err(()); } match fs::read_to_string(path) { Ok(content) => Ok(content), Err(err) => { - eprintln!("error: the file `{}` could not be read ({})", file_path, err); + eprintln!("error: the file `{file_path}` could not be read ({err})"); Err(()) }, } @@ -124,10 +124,7 @@ fn inject_deps_into_manifest( ) -> std::io::Result<()> { // do not inject deps if we have already done so if cargo_toml.contains(RUSTC_PATH_SECTION) { - eprintln!( - "warn: dependencies are already setup inside {}, skipping file", - manifest_path - ); + eprintln!("warn: dependencies are already setup inside {manifest_path}, skipping file"); return Ok(()); } @@ -142,11 +139,7 @@ fn inject_deps_into_manifest( let new_deps = extern_crates.map(|dep| { // format the dependencies that are going to be put inside the Cargo.toml - format!( - "{dep} = {{ path = \"{source_path}/{dep}\" }}\n", - dep = dep, - source_path = rustc_source_dir.display() - ) + format!("{dep} = {{ path = \"{}/{dep}\" }}\n", rustc_source_dir.display()) }); // format a new [dependencies]-block with the new deps we need to inject @@ -163,11 +156,11 @@ fn inject_deps_into_manifest( // etc let new_manifest = cargo_toml.replacen("[dependencies]\n", &all_deps, 1); - // println!("{}", new_manifest); + // println!("{new_manifest}"); let mut file = File::create(manifest_path)?; file.write_all(new_manifest.as_bytes())?; - println!("info: successfully setup dependencies inside {}", manifest_path); + println!("info: successfully setup dependencies inside {manifest_path}"); Ok(()) } @@ -214,8 +207,8 @@ fn remove_rustc_src_from_project(project: &ClippyProjectInfo) -> bool { }, Err(err) => { eprintln!( - "error: unable to open file `{}` to remove rustc dependencies for {} ({})", - project.cargo_file, project.name, err + "error: unable to open file `{}` to remove rustc dependencies for {} ({err})", + project.cargo_file, project.name ); false }, diff --git a/clippy_dev/src/setup/vscode.rs b/clippy_dev/src/setup/vscode.rs index d59001b2c66a..dbcdc9b59e52 100644 --- a/clippy_dev/src/setup/vscode.rs +++ b/clippy_dev/src/setup/vscode.rs @@ -17,10 +17,7 @@ pub fn install_tasks(force_override: bool) { println!("info: the task file can be removed with `cargo dev remove vscode-tasks`"); println!("vscode tasks successfully installed"); }, - Err(err) => eprintln!( - "error: unable to copy `{}` to `{}` ({})", - TASK_SOURCE_FILE, TASK_TARGET_FILE, err - ), + Err(err) => eprintln!("error: unable to copy `{TASK_SOURCE_FILE}` to `{TASK_TARGET_FILE}` ({err})"), } } @@ -44,23 +41,17 @@ fn check_install_precondition(force_override: bool) -> bool { return delete_vs_task_file(path); } - eprintln!( - "error: there is already a `task.json` file inside the `{}` directory", - VSCODE_DIR - ); + eprintln!("error: there is already a `task.json` file inside the `{VSCODE_DIR}` directory"); println!("info: use the `--force-override` flag to override the existing `task.json` file"); return false; } } else { match fs::create_dir(vs_dir_path) { Ok(_) => { - println!("info: created `{}` directory for clippy", VSCODE_DIR); + println!("info: created `{VSCODE_DIR}` directory for clippy"); }, Err(err) => { - eprintln!( - "error: the task target directory `{}` could not be created ({})", - VSCODE_DIR, err - ); + eprintln!("error: the task target directory `{VSCODE_DIR}` could not be created ({err})"); }, } } @@ -82,7 +73,7 @@ pub fn remove_tasks() { fn delete_vs_task_file(path: &Path) -> bool { if let Err(err) = fs::remove_file(path) { - eprintln!("error: unable to delete the existing `tasks.json` file ({})", err); + eprintln!("error: unable to delete the existing `tasks.json` file ({err})"); return false; } diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index b95061bf81a2..0eb443167ecf 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -45,9 +45,8 @@ fn generate_lint_files( renamed_lints: &[RenamedLint], ) { let internal_lints = Lint::internal_lints(lints); - let usable_lints = Lint::usable_lints(lints); - let mut sorted_usable_lints = usable_lints.clone(); - sorted_usable_lints.sort_by_key(|lint| lint.name.clone()); + let mut usable_lints = Lint::usable_lints(lints); + usable_lints.sort_by_key(|lint| lint.name.clone()); replace_region_in_file( update_mode, @@ -86,7 +85,7 @@ fn generate_lint_files( ) .sorted() { - writeln!(res, "[`{}`]: {}#{}", lint, DOCS_LINK, lint).unwrap(); + writeln!(res, "[`{lint}`]: {DOCS_LINK}#{lint}").unwrap(); } }, ); @@ -99,7 +98,7 @@ fn generate_lint_files( "// end lints modules, do not remove this comment, it’s used in `update_lints`", |res| { for lint_mod in usable_lints.iter().map(|l| &l.module).unique().sorted() { - writeln!(res, "mod {};", lint_mod).unwrap(); + writeln!(res, "mod {lint_mod};").unwrap(); } }, ); @@ -129,7 +128,7 @@ fn generate_lint_files( for (lint_group, lints) in Lint::by_lint_group(usable_lints.into_iter().chain(internal_lints)) { let content = gen_lint_group_list(&lint_group, lints.iter()); process_file( - &format!("clippy_lints/src/lib.register_{}.rs", lint_group), + &format!("clippy_lints/src/lib.register_{lint_group}.rs"), update_mode, &content, ); @@ -190,9 +189,9 @@ fn print_lint_names(header: &str, lints: &BTreeSet) -> bool { if lints.is_empty() { return false; } - println!("{}", header); + println!("{header}"); for lint in lints.iter().sorted() { - println!(" {}", lint); + println!(" {lint}"); } println!(); true @@ -205,16 +204,16 @@ pub fn print_lints() { let grouped_by_lint_group = Lint::by_lint_group(usable_lints.into_iter()); for (lint_group, mut lints) in grouped_by_lint_group { - println!("\n## {}", lint_group); + println!("\n## {lint_group}"); lints.sort_by_key(|l| l.name.clone()); for lint in lints { - println!("* [{}]({}#{}) ({})", lint.name, DOCS_LINK, lint.name, lint.desc); + println!("* [{}]({DOCS_LINK}#{}) ({})", lint.name, lint.name, lint.desc); } } - println!("there are {} lints", usable_lint_count); + println!("there are {usable_lint_count} lints"); } /// Runs the `rename_lint` command. @@ -235,10 +234,10 @@ pub fn print_lints() { #[allow(clippy::too_many_lines)] pub fn rename(old_name: &str, new_name: &str, uplift: bool) { if let Some((prefix, _)) = old_name.split_once("::") { - panic!("`{}` should not contain the `{}` prefix", old_name, prefix); + panic!("`{old_name}` should not contain the `{prefix}` prefix"); } if let Some((prefix, _)) = new_name.split_once("::") { - panic!("`{}` should not contain the `{}` prefix", new_name, prefix); + panic!("`{new_name}` should not contain the `{prefix}` prefix"); } let (mut lints, deprecated_lints, mut renamed_lints) = gather_all(); @@ -251,14 +250,14 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) { found_new_name = true; } } - let old_lint_index = old_lint_index.unwrap_or_else(|| panic!("could not find lint `{}`", old_name)); + let old_lint_index = old_lint_index.unwrap_or_else(|| panic!("could not find lint `{old_name}`")); let lint = RenamedLint { - old_name: format!("clippy::{}", old_name), + old_name: format!("clippy::{old_name}"), new_name: if uplift { new_name.into() } else { - format!("clippy::{}", new_name) + format!("clippy::{new_name}") }, }; @@ -266,13 +265,11 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) { // case. assert!( !renamed_lints.iter().any(|l| lint.old_name == l.old_name), - "`{}` has already been renamed", - old_name + "`{old_name}` has already been renamed" ); assert!( !deprecated_lints.iter().any(|l| lint.old_name == l.name), - "`{}` has already been deprecated", - old_name + "`{old_name}` has already been deprecated" ); // Update all lint level attributes. (`clippy::lint_name`) @@ -309,14 +306,12 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) { if uplift { write_file(Path::new("tests/ui/rename.rs"), &gen_renamed_lints_test(&renamed_lints)); println!( - "`{}` has be uplifted. All the code inside `clippy_lints` related to it needs to be removed manually.", - old_name + "`{old_name}` has be uplifted. All the code inside `clippy_lints` related to it needs to be removed manually." ); } else if found_new_name { write_file(Path::new("tests/ui/rename.rs"), &gen_renamed_lints_test(&renamed_lints)); println!( - "`{}` is already defined. The old linting code inside `clippy_lints` needs to be updated/removed manually.", - new_name + "`{new_name}` is already defined. The old linting code inside `clippy_lints` needs to be updated/removed manually." ); } else { // Rename the lint struct and source files sharing a name with the lint. @@ -327,16 +322,16 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) { // Rename test files. only rename `.stderr` and `.fixed` files if the new test name doesn't exist. if try_rename_file( - Path::new(&format!("tests/ui/{}.rs", old_name)), - Path::new(&format!("tests/ui/{}.rs", new_name)), + Path::new(&format!("tests/ui/{old_name}.rs")), + Path::new(&format!("tests/ui/{new_name}.rs")), ) { try_rename_file( - Path::new(&format!("tests/ui/{}.stderr", old_name)), - Path::new(&format!("tests/ui/{}.stderr", new_name)), + Path::new(&format!("tests/ui/{old_name}.stderr")), + Path::new(&format!("tests/ui/{new_name}.stderr")), ); try_rename_file( - Path::new(&format!("tests/ui/{}.fixed", old_name)), - Path::new(&format!("tests/ui/{}.fixed", new_name)), + Path::new(&format!("tests/ui/{old_name}.fixed")), + Path::new(&format!("tests/ui/{new_name}.fixed")), ); } @@ -344,8 +339,8 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) { let replacements; let replacements = if lint.module == old_name && try_rename_file( - Path::new(&format!("clippy_lints/src/{}.rs", old_name)), - Path::new(&format!("clippy_lints/src/{}.rs", new_name)), + Path::new(&format!("clippy_lints/src/{old_name}.rs")), + Path::new(&format!("clippy_lints/src/{new_name}.rs")), ) { // Edit the module name in the lint list. Note there could be multiple lints. for lint in lints.iter_mut().filter(|l| l.module == old_name) { @@ -356,14 +351,14 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) { } else if !lint.module.contains("::") // Catch cases like `methods/lint_name.rs` where the lint is stored in `methods/mod.rs` && try_rename_file( - Path::new(&format!("clippy_lints/src/{}/{}.rs", lint.module, old_name)), - Path::new(&format!("clippy_lints/src/{}/{}.rs", lint.module, new_name)), + Path::new(&format!("clippy_lints/src/{}/{old_name}.rs", lint.module)), + Path::new(&format!("clippy_lints/src/{}/{new_name}.rs", lint.module)), ) { // Edit the module name in the lint list. Note there could be multiple lints, or none. - let renamed_mod = format!("{}::{}", lint.module, old_name); + let renamed_mod = format!("{}::{old_name}", lint.module); for lint in lints.iter_mut().filter(|l| l.module == renamed_mod) { - lint.module = format!("{}::{}", lint.module, new_name); + lint.module = format!("{}::{new_name}", lint.module); } replacements = [(&*old_name_upper, &*new_name_upper), (old_name, new_name)]; replacements.as_slice() @@ -379,7 +374,7 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) { } generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); - println!("{} has been successfully renamed", old_name); + println!("{old_name} has been successfully renamed"); } println!("note: `cargo uitest` still needs to be run to update the test results"); @@ -408,7 +403,7 @@ pub fn deprecate(name: &str, reason: Option<&String>) { }); generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); - println!("info: `{}` has successfully been deprecated", name); + println!("info: `{name}` has successfully been deprecated"); if reason == DEFAULT_DEPRECATION_REASON { println!("note: the deprecation reason must be updated in `clippy_lints/src/deprecated_lints.rs`"); @@ -421,7 +416,7 @@ pub fn deprecate(name: &str, reason: Option<&String>) { let name_upper = name.to_uppercase(); let (mut lints, deprecated_lints, renamed_lints) = gather_all(); - let Some(lint) = lints.iter().find(|l| l.name == name_lower) else { eprintln!("error: failed to find lint `{}`", name); return; }; + let Some(lint) = lints.iter().find(|l| l.name == name_lower) else { eprintln!("error: failed to find lint `{name}`"); return; }; let mod_path = { let mut mod_path = PathBuf::from(format!("clippy_lints/src/{}", lint.module)); @@ -450,7 +445,7 @@ fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec) -> io } fn remove_test_assets(name: &str) { - let test_file_stem = format!("tests/ui/{}", name); + let test_file_stem = format!("tests/ui/{name}"); let path = Path::new(&test_file_stem); // Some lints have their own directories, delete them @@ -512,8 +507,7 @@ fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec) -> io fs::read_to_string(path).unwrap_or_else(|_| panic!("failed to read `{}`", path.to_string_lossy())); eprintln!( - "warn: you will have to manually remove any code related to `{}` from `{}`", - name, + "warn: you will have to manually remove any code related to `{name}` from `{}`", path.display() ); @@ -528,7 +522,7 @@ fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec) -> io content.replace_range(lint.declaration_range.clone(), ""); // Remove the module declaration (mod xyz;) - let mod_decl = format!("\nmod {};", name); + let mod_decl = format!("\nmod {name};"); content = content.replacen(&mod_decl, "", 1); remove_impl_lint_pass(&lint.name.to_uppercase(), &mut content); @@ -621,13 +615,13 @@ fn round_to_fifty(count: usize) -> usize { fn process_file(path: impl AsRef, update_mode: UpdateMode, content: &str) { if update_mode == UpdateMode::Check { let old_content = - fs::read_to_string(&path).unwrap_or_else(|e| panic!("Cannot read from {}: {}", path.as_ref().display(), e)); + fs::read_to_string(&path).unwrap_or_else(|e| panic!("Cannot read from {}: {e}", path.as_ref().display())); if content != old_content { exit_with_failure(); } } else { fs::write(&path, content.as_bytes()) - .unwrap_or_else(|e| panic!("Cannot write to {}: {}", path.as_ref().display(), e)); + .unwrap_or_else(|e| panic!("Cannot write to {}: {e}", path.as_ref().display())); } } @@ -731,11 +725,10 @@ fn gen_lint_group_list<'a>(group_name: &str, lints: impl Iterator( if !is_public { output.push_str(" #[cfg(feature = \"internal\")]\n"); } - let _ = writeln!(output, " {}::{},", module_name, lint_name); + let _ = writeln!(output, " {module_name}::{lint_name},"); } output.push_str("])\n"); @@ -841,7 +834,7 @@ fn gather_all() -> (Vec, Vec, Vec) { for (rel_path, file) in clippy_lints_src_files() { let path = file.path(); let contents = - fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {}", path.display(), e)); + fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {e}", path.display())); let module = rel_path .components() .map(|c| c.as_os_str().to_str().unwrap()) @@ -1050,7 +1043,7 @@ fn remove_line_splices(s: &str) -> String { .trim_matches('#') .strip_prefix('"') .and_then(|s| s.strip_suffix('"')) - .unwrap_or_else(|| panic!("expected quoted string, found `{}`", s)); + .unwrap_or_else(|| panic!("expected quoted string, found `{s}`")); let mut res = String::with_capacity(s.len()); unescape::unescape_literal(s, unescape::Mode::Str, &mut |range, ch| { if ch.is_ok() { @@ -1076,10 +1069,10 @@ fn replace_region_in_file( end: &str, write_replacement: impl FnMut(&mut String), ) { - let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {}", path.display(), e)); + let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {e}", path.display())); let new_contents = match replace_region_in_text(&contents, start, end, write_replacement) { Ok(x) => x, - Err(delim) => panic!("Couldn't find `{}` in file `{}`", delim, path.display()), + Err(delim) => panic!("Couldn't find `{delim}` in file `{}`", path.display()), }; match update_mode { @@ -1087,7 +1080,7 @@ fn replace_region_in_file( UpdateMode::Check => (), UpdateMode::Change => { if let Err(e) = fs::write(path, new_contents.as_bytes()) { - panic!("Cannot write to `{}`: {}", path.display(), e); + panic!("Cannot write to `{}`: {e}", path.display()); } }, } @@ -1135,7 +1128,7 @@ fn try_rename_file(old_name: &Path, new_name: &Path) -> bool { #[allow(clippy::needless_pass_by_value)] fn panic_file(error: io::Error, name: &Path, action: &str) -> ! { - panic!("failed to {} file `{}`: {}", action, name.display(), error) + panic!("failed to {action} file `{}`: {error}", name.display()) } fn rewrite_file(path: &Path, f: impl FnOnce(&str) -> Option) { diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 738562ef8559..6fbd6401ef3e 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_lints" -version = "0.1.65" +version = "0.1.66" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/clippy_lints/src/approx_const.rs b/clippy_lints/src/approx_const.rs index 159f3b0cd014..724490fb4959 100644 --- a/clippy_lints/src/approx_const.rs +++ b/clippy_lints/src/approx_const.rs @@ -92,7 +92,7 @@ impl ApproxConstant { cx, APPROX_CONSTANT, e.span, - &format!("approximate value of `{}::consts::{}` found", module, &name), + &format!("approximate value of `{module}::consts::{}` found", &name), None, "consider using the constant directly", ); @@ -126,7 +126,7 @@ fn is_approx_const(constant: f64, value: &str, min_digits: usize) -> bool { // The value is a truncated constant true } else { - let round_const = format!("{:.*}", value.len() - 2, constant); + let round_const = format!("{constant:.*}", value.len() - 2); value == round_const } } diff --git a/clippy_lints/src/asm_syntax.rs b/clippy_lints/src/asm_syntax.rs index f419781dbc82..9717aa9e981f 100644 --- a/clippy_lints/src/asm_syntax.rs +++ b/clippy_lints/src/asm_syntax.rs @@ -44,7 +44,7 @@ fn check_expr_asm_syntax(lint: &'static Lint, cx: &EarlyContext<'_>, expr: &Expr cx, lint, expr.span, - &format!("{} x86 assembly syntax used", style), + &format!("{style} x86 assembly syntax used"), None, &format!("use {} x86 assembly syntax", !style), ); @@ -64,6 +64,7 @@ declare_clippy_lint! { /// /// ```rust,no_run /// # #![feature(asm)] + /// # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] /// # unsafe { let ptr = "".as_ptr(); /// # use std::arch::asm; /// asm!("lea {}, [{}]", lateout(reg) _, in(reg) ptr); @@ -72,6 +73,7 @@ declare_clippy_lint! { /// Use instead: /// ```rust,no_run /// # #![feature(asm)] + /// # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] /// # unsafe { let ptr = "".as_ptr(); /// # use std::arch::asm; /// asm!("lea ({}), {}", in(reg) ptr, lateout(reg) _, options(att_syntax)); @@ -103,6 +105,7 @@ declare_clippy_lint! { /// /// ```rust,no_run /// # #![feature(asm)] + /// # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] /// # unsafe { let ptr = "".as_ptr(); /// # use std::arch::asm; /// asm!("lea ({}), {}", in(reg) ptr, lateout(reg) _, options(att_syntax)); @@ -111,6 +114,7 @@ declare_clippy_lint! { /// Use instead: /// ```rust,no_run /// # #![feature(asm)] + /// # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] /// # unsafe { let ptr = "".as_ptr(); /// # use std::arch::asm; /// asm!("lea {}, [{}]", lateout(reg) _, in(reg) ptr); diff --git a/clippy_lints/src/assertions_on_constants.rs b/clippy_lints/src/assertions_on_constants.rs index 2705ffffdcbf..a36df55d0bda 100644 --- a/clippy_lints/src/assertions_on_constants.rs +++ b/clippy_lints/src/assertions_on_constants.rs @@ -60,9 +60,9 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnConstants { cx, ASSERTIONS_ON_CONSTANTS, macro_call.span, - &format!("`assert!(false{})` should probably be replaced", assert_arg), + &format!("`assert!(false{assert_arg})` should probably be replaced"), None, - &format!("use `panic!({})` or `unreachable!({0})`", panic_arg), + &format!("use `panic!({panic_arg})` or `unreachable!({panic_arg})`"), ); } } diff --git a/clippy_lints/src/assertions_on_result_states.rs b/clippy_lints/src/assertions_on_result_states.rs index 656dc5feeb57..f6d6c23bb6ed 100644 --- a/clippy_lints/src/assertions_on_result_states.rs +++ b/clippy_lints/src/assertions_on_result_states.rs @@ -69,9 +69,8 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates { "called `assert!` with `Result::is_ok`", "replace with", format!( - "{}.unwrap(){}", - snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0, - semicolon + "{}.unwrap(){semicolon}", + snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0 ), app, ); @@ -84,9 +83,8 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates { "called `assert!` with `Result::is_err`", "replace with", format!( - "{}.unwrap_err(){}", - snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0, - semicolon + "{}.unwrap_err(){semicolon}", + snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0 ), app, ); diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 732dc2b43309..5f45c69d7f98 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -541,10 +541,7 @@ fn check_attrs(cx: &LateContext<'_>, span: Span, name: Symbol, attrs: &[Attribut cx, INLINE_ALWAYS, attr.span, - &format!( - "you have declared `#[inline(always)]` on `{}`. This is usually a bad idea", - name - ), + &format!("you have declared `#[inline(always)]` on `{name}`. This is usually a bad idea"), ); } } @@ -720,7 +717,7 @@ fn check_mismatched_target_os(cx: &EarlyContext<'_>, attr: &Attribute) { let mut unix_suggested = false; for (os, span) in mismatched { - let sugg = format!("target_os = \"{}\"", os); + let sugg = format!("target_os = \"{os}\""); diag.span_suggestion(span, "try", sugg, Applicability::MaybeIncorrect); if !unix_suggested && is_unix(os) { diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index 1761360fb281..34717811866d 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -1,14 +1,15 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::{match_def_path, paths}; use rustc_data_structures::fx::FxHashMap; +use rustc_hir::def::{Namespace, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::{def::Res, AsyncGeneratorKind, Body, BodyId, GeneratorKind}; +use rustc_hir::{AsyncGeneratorKind, Body, BodyId, GeneratorKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::GeneratorInteriorTypeCause; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::Span; +use rustc_span::{sym, Span}; -use crate::utils::conf::DisallowedType; +use crate::utils::conf::DisallowedPath; declare_clippy_lint! { /// ### What it does @@ -171,12 +172,12 @@ impl_lint_pass!(AwaitHolding => [AWAIT_HOLDING_LOCK, AWAIT_HOLDING_REFCELL_REF, #[derive(Debug)] pub struct AwaitHolding { - conf_invalid_types: Vec, - def_ids: FxHashMap, + conf_invalid_types: Vec, + def_ids: FxHashMap, } impl AwaitHolding { - pub(crate) fn new(conf_invalid_types: Vec) -> Self { + pub(crate) fn new(conf_invalid_types: Vec) -> Self { Self { conf_invalid_types, def_ids: FxHashMap::default(), @@ -187,11 +188,8 @@ impl AwaitHolding { impl LateLintPass<'_> for AwaitHolding { fn check_crate(&mut self, cx: &LateContext<'_>) { for conf in &self.conf_invalid_types { - let path = match conf { - DisallowedType::Simple(path) | DisallowedType::WithReason { path, .. } => path, - }; - let segs: Vec<_> = path.split("::").collect(); - if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs) { + let segs: Vec<_> = conf.path().split("::").collect(); + if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs, Some(Namespace::TypeNS)) { self.def_ids.insert(id, conf.clone()); } } @@ -256,29 +254,27 @@ impl AwaitHolding { } } -fn emit_invalid_type(cx: &LateContext<'_>, span: Span, disallowed: &DisallowedType) { - let (type_name, reason) = match disallowed { - DisallowedType::Simple(path) => (path, &None), - DisallowedType::WithReason { path, reason } => (path, reason), - }; - +fn emit_invalid_type(cx: &LateContext<'_>, span: Span, disallowed: &DisallowedPath) { span_lint_and_then( cx, AWAIT_HOLDING_INVALID_TYPE, span, - &format!("`{type_name}` may not be held across an `await` point per `clippy.toml`",), + &format!( + "`{}` may not be held across an `await` point per `clippy.toml`", + disallowed.path() + ), |diag| { - if let Some(reason) = reason { - diag.note(reason.clone()); + if let Some(reason) = disallowed.reason() { + diag.note(reason); } }, ); } fn is_mutex_guard(cx: &LateContext<'_>, def_id: DefId) -> bool { - match_def_path(cx, def_id, &paths::MUTEX_GUARD) - || match_def_path(cx, def_id, &paths::RWLOCK_READ_GUARD) - || match_def_path(cx, def_id, &paths::RWLOCK_WRITE_GUARD) + cx.tcx.is_diagnostic_item(sym::MutexGuard, def_id) + || cx.tcx.is_diagnostic_item(sym::RwLockReadGuard, def_id) + || cx.tcx.is_diagnostic_item(sym::RwLockWriteGuard, def_id) || match_def_path(cx, def_id, &paths::PARKING_LOT_MUTEX_GUARD) || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_READ_GUARD) || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_WRITE_GUARD) diff --git a/clippy_lints/src/blocks_in_if_conditions.rs b/clippy_lints/src/blocks_in_if_conditions.rs index d9e2c9c8578f..9c0532474024 100644 --- a/clippy_lints/src/blocks_in_if_conditions.rs +++ b/clippy_lints/src/blocks_in_if_conditions.rs @@ -3,10 +3,11 @@ use clippy_utils::get_parent_expr; use clippy_utils::higher; use clippy_utils::source::snippet_block_with_applicability; use clippy_utils::ty::implements_trait; +use clippy_utils::visitors::{for_each_expr, Descend}; +use core::ops::ControlFlow; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::intravisit::{walk_expr, Visitor}; -use rustc_hir::{BlockCheckMode, Closure, Expr, ExprKind}; +use rustc_hir::{BlockCheckMode, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -44,39 +45,6 @@ declare_clippy_lint! { declare_lint_pass!(BlocksInIfConditions => [BLOCKS_IN_IF_CONDITIONS]); -struct ExVisitor<'a, 'tcx> { - found_block: Option<&'tcx Expr<'tcx>>, - cx: &'a LateContext<'tcx>, -} - -impl<'a, 'tcx> Visitor<'tcx> for ExVisitor<'a, 'tcx> { - fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { - if let ExprKind::Closure(&Closure { body, .. }) = expr.kind { - // do not lint if the closure is called using an iterator (see #1141) - if_chain! { - if let Some(parent) = get_parent_expr(self.cx, expr); - if let ExprKind::MethodCall(_, self_arg, ..) = &parent.kind; - let caller = self.cx.typeck_results().expr_ty(self_arg); - if let Some(iter_id) = self.cx.tcx.get_diagnostic_item(sym::Iterator); - if implements_trait(self.cx, caller, iter_id, &[]); - then { - return; - } - } - - let body = self.cx.tcx.hir().body(body); - let ex = &body.value; - if let ExprKind::Block(block, _) = ex.kind { - if !body.value.span.from_expansion() && !block.stmts.is_empty() { - self.found_block = Some(ex); - return; - } - } - } - walk_expr(self, expr); - } -} - const BRACED_EXPR_MESSAGE: &str = "omit braces around single expression condition"; const COMPLEX_BLOCK_MESSAGE: &str = "in an `if` condition, avoid complex blocks or closures with blocks; \ instead, move the block or closure higher and bind it with a `let`"; @@ -145,11 +113,31 @@ impl<'tcx> LateLintPass<'tcx> for BlocksInIfConditions { } } } else { - let mut visitor = ExVisitor { found_block: None, cx }; - walk_expr(&mut visitor, cond); - if let Some(block) = visitor.found_block { - span_lint(cx, BLOCKS_IN_IF_CONDITIONS, block.span, COMPLEX_BLOCK_MESSAGE); - } + let _: Option = for_each_expr(cond, |e| { + if let ExprKind::Closure(closure) = e.kind { + // do not lint if the closure is called using an iterator (see #1141) + if_chain! { + if let Some(parent) = get_parent_expr(cx, e); + if let ExprKind::MethodCall(_, self_arg, _, _) = &parent.kind; + let caller = cx.typeck_results().expr_ty(self_arg); + if let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator); + if implements_trait(cx, caller, iter_id, &[]); + then { + return ControlFlow::Continue(Descend::No); + } + } + + let body = cx.tcx.hir().body(closure.body); + let ex = &body.value; + if let ExprKind::Block(block, _) = ex.kind { + if !body.value.span.from_expansion() && !block.stmts.is_empty() { + span_lint(cx, BLOCKS_IN_IF_CONDITIONS, ex.span, COMPLEX_BLOCK_MESSAGE); + return ControlFlow::Continue(Descend::No); + } + } + } + ControlFlow::Continue(Descend::Yes) + }); } } } diff --git a/clippy_lints/src/bool_assert_comparison.rs b/clippy_lints/src/bool_assert_comparison.rs index 95abe8aa59fb..4bd55c1429c3 100644 --- a/clippy_lints/src/bool_assert_comparison.rs +++ b/clippy_lints/src/bool_assert_comparison.rs @@ -98,9 +98,9 @@ impl<'tcx> LateLintPass<'tcx> for BoolAssertComparison { cx, BOOL_ASSERT_COMPARISON, macro_call.span, - &format!("used `{}!` with a literal bool", macro_name), + &format!("used `{macro_name}!` with a literal bool"), "replace it with", - format!("{}!(..)", non_eq_mac), + format!("{non_eq_mac}!(..)"), Applicability::MaybeIncorrect, ); } diff --git a/clippy_lints/src/bool_to_int_with_if.rs b/clippy_lints/src/bool_to_int_with_if.rs index 51e98cda8451..001d74c26054 100644 --- a/clippy_lints/src/bool_to_int_with_if.rs +++ b/clippy_lints/src/bool_to_int_with_if.rs @@ -3,7 +3,7 @@ use rustc_hir::{Block, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use clippy_utils::{diagnostics::span_lint_and_then, is_else_clause, sugg::Sugg}; +use clippy_utils::{diagnostics::span_lint_and_then, is_else_clause, is_integer_literal, sugg::Sugg}; use rustc_errors::Applicability; declare_clippy_lint! { @@ -56,13 +56,9 @@ fn check_if_else<'tcx>(ctx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx && let Some(then_lit) = int_literal(then) && let Some(else_lit) = int_literal(else_) { - let inverted = if - check_int_literal_equals_val(then_lit, 1) - && check_int_literal_equals_val(else_lit, 0) { + let inverted = if is_integer_literal(then_lit, 1) && is_integer_literal(else_lit, 0) { false - } else if - check_int_literal_equals_val(then_lit, 0) - && check_int_literal_equals_val(else_lit, 1) { + } else if is_integer_literal(then_lit, 0) && is_integer_literal(else_lit, 1) { true } else { // Expression isn't boolean, exit @@ -123,14 +119,3 @@ fn int_literal<'tcx>(expr: &'tcx rustc_hir::Expr<'tcx>) -> Option<&'tcx rustc_hi None } } - -fn check_int_literal_equals_val<'tcx>(expr: &'tcx rustc_hir::Expr<'tcx>, expected_value: u128) -> bool { - if let ExprKind::Lit(lit) = &expr.kind - && let LitKind::Int(val, _) = lit.node - && val == expected_value - { - true - } else { - false - } -} diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index 03d262d5a59c..2a15cbc7a3c3 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -263,9 +263,8 @@ fn simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { } .and_then(|op| { Some(format!( - "{}{}{}", + "{}{op}{}", snippet_opt(cx, lhs.span)?, - op, snippet_opt(cx, rhs.span)? )) }) @@ -285,7 +284,7 @@ fn simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { let path: &str = path.ident.name.as_str(); a == path }) - .and_then(|(_, neg_method)| Some(format!("{}.{}()", snippet_opt(cx, receiver.span)?, neg_method))) + .and_then(|(_, neg_method)| Some(format!("{}.{neg_method}()", snippet_opt(cx, receiver.span)?))) }, _ => None, } diff --git a/clippy_lints/src/box_default.rs b/clippy_lints/src/box_default.rs new file mode 100644 index 000000000000..792183ac4081 --- /dev/null +++ b/clippy_lints/src/box_default.rs @@ -0,0 +1,61 @@ +use clippy_utils::{diagnostics::span_lint_and_help, is_default_equivalent, path_def_id}; +use rustc_hir::{Expr, ExprKind, QPath}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// checks for `Box::new(T::default())`, which is better written as + /// `Box::::default()`. + /// + /// ### Why is this bad? + /// First, it's more complex, involving two calls instead of one. + /// Second, `Box::default()` can be faster + /// [in certain cases](https://nnethercote.github.io/perf-book/standard-library-types.html#box). + /// + /// ### Known problems + /// The lint may miss some cases (e.g. Box::new(String::from(""))). + /// On the other hand, it will trigger on cases where the `default` + /// code comes from a macro that does something different based on + /// e.g. target operating system. + /// + /// ### Example + /// ```rust + /// let x: Box = Box::new(Default::default()); + /// ``` + /// Use instead: + /// ```rust + /// let x: Box = Box::default(); + /// ``` + #[clippy::version = "1.65.0"] + pub BOX_DEFAULT, + perf, + "Using Box::new(T::default()) instead of Box::default()" +} + +declare_lint_pass!(BoxDefault => [BOX_DEFAULT]); + +impl LateLintPass<'_> for BoxDefault { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + if let ExprKind::Call(box_new, [arg]) = expr.kind + && let ExprKind::Path(QPath::TypeRelative(ty, seg)) = box_new.kind + && let ExprKind::Call(..) = arg.kind + && !in_external_macro(cx.sess(), expr.span) + && expr.span.eq_ctxt(arg.span) + && seg.ident.name == sym::new + && path_def_id(cx, ty) == cx.tcx.lang_items().owned_box() + && is_default_equivalent(cx, arg) + { + span_lint_and_help( + cx, + BOX_DEFAULT, + expr.span, + "`Box::new(_)` of default value", + None, + "use `Box::default()` instead", + ); + } + } +} diff --git a/clippy_lints/src/cargo/common_metadata.rs b/clippy_lints/src/cargo/common_metadata.rs index e0442dda479d..805121bcced3 100644 --- a/clippy_lints/src/cargo/common_metadata.rs +++ b/clippy_lints/src/cargo/common_metadata.rs @@ -40,7 +40,7 @@ pub(super) fn check(cx: &LateContext<'_>, metadata: &Metadata, ignore_publish: b } fn missing_warning(cx: &LateContext<'_>, package: &cargo_metadata::Package, field: &str) { - let message = format!("package `{}` is missing `{}` metadata", package.name, field); + let message = format!("package `{}` is missing `{field}` metadata", package.name); span_lint(cx, CARGO_COMMON_METADATA, DUMMY_SP, &message); } diff --git a/clippy_lints/src/cargo/feature_name.rs b/clippy_lints/src/cargo/feature_name.rs index 79a469a4258b..37c169dbd95e 100644 --- a/clippy_lints/src/cargo/feature_name.rs +++ b/clippy_lints/src/cargo/feature_name.rs @@ -57,10 +57,8 @@ fn lint(cx: &LateContext<'_>, feature: &str, substring: &str, is_prefix: bool) { }, DUMMY_SP, &format!( - "the \"{}\" {} in the feature name \"{}\" is {}", - substring, + "the \"{substring}\" {} in the feature name \"{feature}\" is {}", if is_prefix { "prefix" } else { "suffix" }, - feature, if is_negative { "negative" } else { "redundant" } ), None, diff --git a/clippy_lints/src/cargo/mod.rs b/clippy_lints/src/cargo/mod.rs index 9f45db86a091..3a872e54c9a2 100644 --- a/clippy_lints/src/cargo/mod.rs +++ b/clippy_lints/src/cargo/mod.rs @@ -196,7 +196,7 @@ impl LateLintPass<'_> for Cargo { }, Err(e) => { for lint in NO_DEPS_LINTS { - span_lint(cx, lint, DUMMY_SP, &format!("could not read cargo metadata: {}", e)); + span_lint(cx, lint, DUMMY_SP, &format!("could not read cargo metadata: {e}")); } }, } @@ -212,7 +212,7 @@ impl LateLintPass<'_> for Cargo { }, Err(e) => { for lint in WITH_DEPS_LINTS { - span_lint(cx, lint, DUMMY_SP, &format!("could not read cargo metadata: {}", e)); + span_lint(cx, lint, DUMMY_SP, &format!("could not read cargo metadata: {e}")); } }, } diff --git a/clippy_lints/src/cargo/multiple_crate_versions.rs b/clippy_lints/src/cargo/multiple_crate_versions.rs index 76fd0819a39a..f9b17d45e9fb 100644 --- a/clippy_lints/src/cargo/multiple_crate_versions.rs +++ b/clippy_lints/src/cargo/multiple_crate_versions.rs @@ -37,7 +37,7 @@ pub(super) fn check(cx: &LateContext<'_>, metadata: &Metadata) { cx, MULTIPLE_CRATE_VERSIONS, DUMMY_SP, - &format!("multiple versions for dependency `{}`: {}", name, versions), + &format!("multiple versions for dependency `{name}`: {versions}"), ); } } diff --git a/clippy_lints/src/casts/borrow_as_ptr.rs b/clippy_lints/src/casts/borrow_as_ptr.rs index 6e1f8cd64f07..294d22d34de9 100644 --- a/clippy_lints/src/casts/borrow_as_ptr.rs +++ b/clippy_lints/src/casts/borrow_as_ptr.rs @@ -30,7 +30,7 @@ pub(super) fn check<'tcx>( expr.span, "borrow as raw pointer", "try", - format!("{}::ptr::{}!({})", core_or_std, macro_name, snip), + format!("{core_or_std}::ptr::{macro_name}!({snip})"), Applicability::MachineApplicable, ); } diff --git a/clippy_lints/src/casts/cast_lossless.rs b/clippy_lints/src/casts/cast_lossless.rs index 938458e30cad..13c403234dad 100644 --- a/clippy_lints/src/casts/cast_lossless.rs +++ b/clippy_lints/src/casts/cast_lossless.rs @@ -41,15 +41,9 @@ pub(super) fn check( ); let message = if cast_from.is_bool() { - format!( - "casting `{0:}` to `{1:}` is more cleanly stated with `{1:}::from(_)`", - cast_from, cast_to - ) + format!("casting `{cast_from:}` to `{cast_to:}` is more cleanly stated with `{cast_to:}::from(_)`") } else { - format!( - "casting `{}` to `{}` may become silently lossy if you later change the type", - cast_from, cast_to - ) + format!("casting `{cast_from}` to `{cast_to}` may become silently lossy if you later change the type") }; span_lint_and_sugg( @@ -58,7 +52,7 @@ pub(super) fn check( expr.span, &message, "try", - format!("{}::from({})", cast_to, sugg), + format!("{cast_to}::from({sugg})"), applicability, ); } diff --git a/clippy_lints/src/casts/cast_possible_truncation.rs b/clippy_lints/src/casts/cast_possible_truncation.rs index 406547a4454e..88deb4565eb2 100644 --- a/clippy_lints/src/casts/cast_possible_truncation.rs +++ b/clippy_lints/src/casts/cast_possible_truncation.rs @@ -103,10 +103,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, return; } - format!( - "casting `{}` to `{}` may truncate the value{}", - cast_from, cast_to, suffix, - ) + format!("casting `{cast_from}` to `{cast_to}` may truncate the value{suffix}",) }, (ty::Adt(def, _), true) if def.is_enum() => { @@ -142,20 +139,17 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, CAST_ENUM_TRUNCATION, expr.span, &format!( - "casting `{}::{}` to `{}` will truncate the value{}", - cast_from, variant.name, cast_to, suffix, + "casting `{cast_from}::{}` to `{cast_to}` will truncate the value{suffix}", + variant.name, ), ); return; } - format!( - "casting `{}` to `{}` may truncate the value{}", - cast_from, cast_to, suffix, - ) + format!("casting `{cast_from}` to `{cast_to}` may truncate the value{suffix}",) }, (ty::Float(_), true) => { - format!("casting `{}` to `{}` may truncate the value", cast_from, cast_to) + format!("casting `{cast_from}` to `{cast_to}` may truncate the value") }, (ty::Float(FloatTy::F64), false) if matches!(cast_to.kind(), &ty::Float(FloatTy::F32)) => { diff --git a/clippy_lints/src/casts/cast_possible_wrap.rs b/clippy_lints/src/casts/cast_possible_wrap.rs index 2c5c1d7cb465..28ecdea7ea06 100644 --- a/clippy_lints/src/casts/cast_possible_wrap.rs +++ b/clippy_lints/src/casts/cast_possible_wrap.rs @@ -35,10 +35,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, ca cx, CAST_POSSIBLE_WRAP, expr.span, - &format!( - "casting `{}` to `{}` may wrap around the value{}", - cast_from, cast_to, suffix, - ), + &format!("casting `{cast_from}` to `{cast_to}` may wrap around the value{suffix}",), ); } } diff --git a/clippy_lints/src/casts/cast_ptr_alignment.rs b/clippy_lints/src/casts/cast_ptr_alignment.rs index da7b12f67266..97054a0d1015 100644 --- a/clippy_lints/src/casts/cast_ptr_alignment.rs +++ b/clippy_lints/src/casts/cast_ptr_alignment.rs @@ -49,9 +49,7 @@ fn lint_cast_ptr_alignment<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, cast_f CAST_PTR_ALIGNMENT, expr.span, &format!( - "casting from `{}` to a more-strictly-aligned pointer (`{}`) ({} < {} bytes)", - cast_from, - cast_to, + "casting from `{cast_from}` to a more-strictly-aligned pointer (`{cast_to}`) ({} < {} bytes)", from_layout.align.abi.bytes(), to_layout.align.abi.bytes(), ), diff --git a/clippy_lints/src/casts/cast_sign_loss.rs b/clippy_lints/src/casts/cast_sign_loss.rs index 5b59350be042..a20a97d4e56d 100644 --- a/clippy_lints/src/casts/cast_sign_loss.rs +++ b/clippy_lints/src/casts/cast_sign_loss.rs @@ -14,10 +14,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_op: &Expr<'_>, c cx, CAST_SIGN_LOSS, expr.span, - &format!( - "casting `{}` to `{}` may lose the sign of the value", - cast_from, cast_to - ), + &format!("casting `{cast_from}` to `{cast_to}` may lose the sign of the value"), ); } } diff --git a/clippy_lints/src/casts/cast_slice_different_sizes.rs b/clippy_lints/src/casts/cast_slice_different_sizes.rs index 027c660ce3b2..d31d10d22b92 100644 --- a/clippy_lints/src/casts/cast_slice_different_sizes.rs +++ b/clippy_lints/src/casts/cast_slice_different_sizes.rs @@ -35,8 +35,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: Optio CAST_SLICE_DIFFERENT_SIZES, expr.span, &format!( - "casting between raw pointers to `[{}]` (element size {}) and `[{}]` (element size {}) does not adjust the count", - start_ty.ty, from_size, end_ty.ty, to_size, + "casting between raw pointers to `[{}]` (element size {from_size}) and `[{}]` (element size {to_size}) does not adjust the count", + start_ty.ty, end_ty.ty, ), |diag| { let ptr_snippet = source::snippet(cx, left_cast.span, ".."); diff --git a/clippy_lints/src/casts/char_lit_as_u8.rs b/clippy_lints/src/casts/char_lit_as_u8.rs index 7cc406018dbe..82e07c98a7e0 100644 --- a/clippy_lints/src/casts/char_lit_as_u8.rs +++ b/clippy_lints/src/casts/char_lit_as_u8.rs @@ -31,7 +31,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { diag.span_suggestion( expr.span, "use a byte literal instead", - format!("b{}", snippet), + format!("b{snippet}"), applicability, ); } diff --git a/clippy_lints/src/casts/fn_to_numeric_cast.rs b/clippy_lints/src/casts/fn_to_numeric_cast.rs index 35350d8a25b8..a26bfab4e7c1 100644 --- a/clippy_lints/src/casts/fn_to_numeric_cast.rs +++ b/clippy_lints/src/casts/fn_to_numeric_cast.rs @@ -25,9 +25,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cx, FN_TO_NUMERIC_CAST, expr.span, - &format!("casting function pointer `{}` to `{}`", from_snippet, cast_to), + &format!("casting function pointer `{from_snippet}` to `{cast_to}`"), "try", - format!("{} as usize", from_snippet), + format!("{from_snippet} as usize"), applicability, ); } diff --git a/clippy_lints/src/casts/fn_to_numeric_cast_any.rs b/clippy_lints/src/casts/fn_to_numeric_cast_any.rs index 03621887a34a..75654129408e 100644 --- a/clippy_lints/src/casts/fn_to_numeric_cast_any.rs +++ b/clippy_lints/src/casts/fn_to_numeric_cast_any.rs @@ -23,9 +23,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cx, FN_TO_NUMERIC_CAST_ANY, expr.span, - &format!("casting function pointer `{}` to `{}`", from_snippet, cast_to), + &format!("casting function pointer `{from_snippet}` to `{cast_to}`"), "did you mean to invoke the function?", - format!("{}() as {}", from_snippet, cast_to), + format!("{from_snippet}() as {cast_to}"), applicability, ); }, diff --git a/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs b/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs index 6287f479b5bf..556be1d15066 100644 --- a/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs +++ b/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs @@ -24,12 +24,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cx, FN_TO_NUMERIC_CAST_WITH_TRUNCATION, expr.span, - &format!( - "casting function pointer `{}` to `{}`, which truncates the value", - from_snippet, cast_to - ), + &format!("casting function pointer `{from_snippet}` to `{cast_to}`, which truncates the value"), "try", - format!("{} as usize", from_snippet), + format!("{from_snippet} as usize"), applicability, ); } diff --git a/clippy_lints/src/casts/ptr_as_ptr.rs b/clippy_lints/src/casts/ptr_as_ptr.rs index 46d45d09661a..c2b9253ec35d 100644 --- a/clippy_lints/src/casts/ptr_as_ptr.rs +++ b/clippy_lints/src/casts/ptr_as_ptr.rs @@ -33,7 +33,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: Option Cow::Borrowed(""), TyKind::Ptr(mut_ty) if matches!(mut_ty.ty.kind, TyKind::Infer) => Cow::Borrowed(""), - _ => Cow::Owned(format!("::<{}>", to_pointee_ty)), + _ => Cow::Owned(format!("::<{to_pointee_ty}>")), }; span_lint_and_sugg( cx, @@ -41,7 +41,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: Option( } } + let cast_str = snippet_opt(cx, cast_expr.span).unwrap_or_default(); + if let Some(lit) = get_numeric_literal(cast_expr) { - let literal_str = snippet_opt(cx, cast_expr.span).unwrap_or_default(); + let literal_str = &cast_str; if_chain! { if let LitKind::Int(n, _) = lit.node; @@ -49,12 +52,16 @@ pub(super) fn check<'tcx>( match lit.node { LitKind::Int(_, LitIntType::Unsuffixed) if cast_to.is_integral() => { - lint_unnecessary_cast(cx, expr, &literal_str, cast_from, cast_to); + lint_unnecessary_cast(cx, expr, literal_str, cast_from, cast_to); + return false; }, LitKind::Float(_, LitFloatType::Unsuffixed) if cast_to.is_floating_point() => { - lint_unnecessary_cast(cx, expr, &literal_str, cast_from, cast_to); + lint_unnecessary_cast(cx, expr, literal_str, cast_from, cast_to); + return false; + }, + LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed) => { + return false; }, - LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed) => {}, LitKind::Int(_, LitIntType::Signed(_) | LitIntType::Unsigned(_)) | LitKind::Float(_, LitFloatType::Suffixed(_)) if cast_from.kind() == cast_to.kind() => @@ -62,48 +69,62 @@ pub(super) fn check<'tcx>( if let Some(src) = snippet_opt(cx, cast_expr.span) { if let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node) { lint_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to); + return true; } } }, - _ => { - if cast_from.kind() == cast_to.kind() && !in_external_macro(cx.sess(), expr.span) { - span_lint_and_sugg( - cx, - UNNECESSARY_CAST, - expr.span, - &format!( - "casting to the same type is unnecessary (`{}` -> `{}`)", - cast_from, cast_to - ), - "try", - literal_str, - Applicability::MachineApplicable, - ); - return true; - } - }, + _ => {}, } } + if cast_from.kind() == cast_to.kind() && !in_external_macro(cx.sess(), expr.span) { + span_lint_and_sugg( + cx, + UNNECESSARY_CAST, + expr.span, + &format!("casting to the same type is unnecessary (`{cast_from}` -> `{cast_to}`)"), + "try", + cast_str, + Applicability::MachineApplicable, + ); + return true; + } + false } -fn lint_unnecessary_cast(cx: &LateContext<'_>, expr: &Expr<'_>, literal_str: &str, cast_from: Ty<'_>, cast_to: Ty<'_>) { +fn lint_unnecessary_cast( + cx: &LateContext<'_>, + expr: &Expr<'_>, + raw_literal_str: &str, + cast_from: Ty<'_>, + cast_to: Ty<'_>, +) { let literal_kind_name = if cast_from.is_integral() { "integer" } else { "float" }; - let replaced_literal; - let matchless = if literal_str.contains(['(', ')']) { - replaced_literal = literal_str.replace(['(', ')'], ""); - &replaced_literal - } else { - literal_str + // first we remove all matches so `-(1)` become `-1`, and remove trailing dots, so `1.` become `1` + let literal_str = raw_literal_str + .replace(['(', ')'], "") + .trim_end_matches('.') + .to_string(); + // we know need to check if the parent is a method call, to add parenthesis accordingly (eg: + // (-1).foo() instead of -1.foo()) + let sugg = if let Some(parent_expr) = get_parent_expr(cx, expr) + && let ExprKind::MethodCall(..) = parent_expr.kind + && literal_str.starts_with('-') + { + format!("({literal_str}_{cast_to})") + + } else { + format!("{literal_str}_{cast_to}") }; + span_lint_and_sugg( cx, UNNECESSARY_CAST, expr.span, - &format!("casting {} literal to `{}` is unnecessary", literal_kind_name, cast_to), + &format!("casting {literal_kind_name} literal to `{cast_to}` is unnecessary"), "try", - format!("{}_{}", matchless.trim_end_matches('.'), cast_to), + sugg, Applicability::MachineApplicable, ); } diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs index 37b2fdcff09f..78e9921f036f 100644 --- a/clippy_lints/src/checked_conversions.rs +++ b/clippy_lints/src/checked_conversions.rs @@ -2,9 +2,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{in_constant, meets_msrv, msrvs, SpanlessEq}; +use clippy_utils::{in_constant, is_integer_literal, meets_msrv, msrvs, SpanlessEq}; use if_chain::if_chain; -use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, QPath, TyKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -82,7 +81,7 @@ impl<'tcx> LateLintPass<'tcx> for CheckedConversions { item.span, "checked cast can be simplified", "try", - format!("{}::try_from({}).is_ok()", to_type, snippet), + format!("{to_type}::try_from({snippet}).is_ok()"), applicability, ); } @@ -223,16 +222,7 @@ fn check_lower_bound<'tcx>(expr: &'tcx Expr<'tcx>) -> Option> { /// Check for `expr >= 0` fn check_lower_bound_zero<'a>(candidate: &'a Expr<'_>, check: &'a Expr<'_>) -> Option> { - if_chain! { - if let ExprKind::Lit(ref lit) = &check.kind; - if let LitKind::Int(0, _) = &lit.node; - - then { - Some(Conversion::new_any(candidate)) - } else { - None - } - } + is_integer_literal(check, 0).then(|| Conversion::new_any(candidate)) } /// Check for `expr >= (to_type::MIN as from_type)` diff --git a/clippy_lints/src/cognitive_complexity.rs b/clippy_lints/src/cognitive_complexity.rs index 33c44f8b2dba..77af3b53d633 100644 --- a/clippy_lints/src/cognitive_complexity.rs +++ b/clippy_lints/src/cognitive_complexity.rs @@ -3,10 +3,12 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::snippet_opt; use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::visitors::for_each_expr; use clippy_utils::LimitStack; +use core::ops::ControlFlow; use rustc_ast::ast::Attribute; -use rustc_hir::intravisit::{walk_expr, FnKind, Visitor}; -use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId}; +use rustc_hir::intravisit::FnKind; +use rustc_hir::{Body, ExprKind, FnDecl, HirId}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; @@ -61,11 +63,27 @@ impl CognitiveComplexity { return; } - let expr = &body.value; + let expr = body.value; + + let mut cc = 1u64; + let mut returns = 0u64; + let _: Option = for_each_expr(expr, |e| { + match e.kind { + ExprKind::If(_, _, _) => { + cc += 1; + }, + ExprKind::Match(_, arms, _) => { + if arms.len() > 1 { + cc += 1; + } + cc += arms.iter().filter(|arm| arm.guard.is_some()).count() as u64; + }, + ExprKind::Ret(_) => returns += 1, + _ => {}, + } + ControlFlow::Continue(()) + }); - let mut helper = CcHelper { cc: 1, returns: 0 }; - helper.visit_expr(expr); - let CcHelper { cc, returns } = helper; let ret_ty = cx.typeck_results().node_type(expr.hir_id); let ret_adjust = if is_type_diagnostic_item(cx, ret_ty, sym::Result) { returns @@ -74,13 +92,12 @@ impl CognitiveComplexity { (returns / 2) }; - let mut rust_cc = cc; // prevent degenerate cases where unreachable code contains `return` statements - if rust_cc >= ret_adjust { - rust_cc -= ret_adjust; + if cc >= ret_adjust { + cc -= ret_adjust; } - if rust_cc > self.limit.limit() { + if cc > self.limit.limit() { let fn_span = match kind { FnKind::ItemFn(ident, _, _) | FnKind::Method(ident, _) => ident.span, FnKind::Closure => { @@ -107,8 +124,7 @@ impl CognitiveComplexity { COGNITIVE_COMPLEXITY, fn_span, &format!( - "the function has a cognitive complexity of ({}/{})", - rust_cc, + "the function has a cognitive complexity of ({cc}/{})", self.limit.limit() ), None, @@ -141,27 +157,3 @@ impl<'tcx> LateLintPass<'tcx> for CognitiveComplexity { self.limit.pop_attrs(cx.sess(), attrs, "cognitive_complexity"); } } - -struct CcHelper { - cc: u64, - returns: u64, -} - -impl<'tcx> Visitor<'tcx> for CcHelper { - fn visit_expr(&mut self, e: &'tcx Expr<'_>) { - walk_expr(self, e); - match e.kind { - ExprKind::If(_, _, _) => { - self.cc += 1; - }, - ExprKind::Match(_, arms, _) => { - if arms.len() > 1 { - self.cc += 1; - } - self.cc += arms.iter().filter(|arm| arm.guard.is_some()).count() as u64; - }, - ExprKind::Ret(_) => self.returns += 1, - _ => {}, - } - } -} diff --git a/clippy_lints/src/default.rs b/clippy_lints/src/default.rs index 4e68d6810e29..7f937de1dd31 100644 --- a/clippy_lints/src/default.rs +++ b/clippy_lints/src/default.rs @@ -105,7 +105,7 @@ impl<'tcx> LateLintPass<'tcx> for Default { cx, DEFAULT_TRAIT_ACCESS, expr.span, - &format!("calling `{}` is more clear than this expression", replacement), + &format!("calling `{replacement}` is more clear than this expression"), "try", replacement, Applicability::Unspecified, // First resolve the TODO above @@ -210,7 +210,7 @@ impl<'tcx> LateLintPass<'tcx> for Default { .map(|(field, rhs)| { // extract and store the assigned value for help message let value_snippet = snippet_with_macro_callsite(cx, rhs.span, ".."); - format!("{}: {}", field, value_snippet) + format!("{field}: {value_snippet}") }) .collect::>() .join(", "); @@ -227,7 +227,7 @@ impl<'tcx> LateLintPass<'tcx> for Default { .map(ToString::to_string) .collect::>() .join(", "); - format!("{}::<{}>", adt_def_ty_name, &tys_str) + format!("{adt_def_ty_name}::<{}>", &tys_str) } else { binding_type.to_string() } @@ -235,12 +235,12 @@ impl<'tcx> LateLintPass<'tcx> for Default { let sugg = if ext_with_default { if field_list.is_empty() { - format!("{}::default()", binding_type) + format!("{binding_type}::default()") } else { - format!("{} {{ {}, ..Default::default() }}", binding_type, field_list) + format!("{binding_type} {{ {field_list}, ..Default::default() }}") } } else { - format!("{} {{ {} }}", binding_type, field_list) + format!("{binding_type} {{ {field_list} }}") }; // span lint once per statement that binds default @@ -250,10 +250,7 @@ impl<'tcx> LateLintPass<'tcx> for Default { first_assign.unwrap().span, "field assignment outside of initializer for an instance created with Default::default()", Some(local.span), - &format!( - "consider initializing the variable with `{}` and removing relevant reassignments", - sugg - ), + &format!("consider initializing the variable with `{sugg}` and removing relevant reassignments"), ); self.reassigned_linted.insert(span); } diff --git a/clippy_lints/src/default_instead_of_iter_empty.rs b/clippy_lints/src/default_instead_of_iter_empty.rs index 3c996d3d2aee..1ad929864b2a 100644 --- a/clippy_lints/src/default_instead_of_iter_empty.rs +++ b/clippy_lints/src/default_instead_of_iter_empty.rs @@ -23,7 +23,7 @@ declare_clippy_lint! { /// let _ = std::iter::empty::(); /// let iter: std::iter::Empty = std::iter::empty(); /// ``` - #[clippy::version = "1.63.0"] + #[clippy::version = "1.64.0"] pub DEFAULT_INSTEAD_OF_ITER_EMPTY, style, "check `std::iter::Empty::default()` and replace with `std::iter::empty()`" diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs index be02f328e989..3ed9cd36a229 100644 --- a/clippy_lints/src/default_numeric_fallback.rs +++ b/clippy_lints/src/default_numeric_fallback.rs @@ -95,8 +95,8 @@ impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> { src } else { match lit.node { - LitKind::Int(src, _) => format!("{}", src), - LitKind::Float(src, _) => format!("{}", src), + LitKind::Int(src, _) => format!("{src}"), + LitKind::Float(src, _) => format!("{src}"), _ => return, } }; diff --git a/clippy_lints/src/default_union_representation.rs b/clippy_lints/src/default_union_representation.rs index 3905a6c2e211..741edc131960 100644 --- a/clippy_lints/src/default_union_representation.rs +++ b/clippy_lints/src/default_union_representation.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_help; use rustc_hir::{self as hir, HirId, Item, ItemKind}; +use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::layout::LayoutOf; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; -use rustc_hir_analysis::hir_ty_to_ty; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index f0d5ed6f594b..3cd8f236e7a5 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -135,7 +135,7 @@ declare_clippy_lint! { /// let x = String::new(); /// let y: &str = &x; /// ``` - #[clippy::version = "1.60.0"] + #[clippy::version = "1.64.0"] pub EXPLICIT_AUTO_DEREF, complexity, "dereferencing when the compiler would automatically dereference" @@ -184,6 +184,7 @@ impl Dereferencing { } } +#[derive(Debug)] struct StateData { /// Span of the top level expression span: Span, @@ -191,12 +192,14 @@ struct StateData { position: Position, } +#[derive(Debug)] struct DerefedBorrow { count: usize, msg: &'static str, snip_expr: Option, } +#[derive(Debug)] enum State { // Any number of deref method calls. DerefMethod { @@ -276,10 +279,12 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing { (None, kind) => { let expr_ty = typeck.expr_ty(expr); let (position, adjustments) = walk_parents(cx, expr, self.msrv); - match kind { RefOp::Deref => { - if let Position::FieldAccess(name) = position + if let Position::FieldAccess { + name, + of_union: false, + } = position && !ty_contains_field(typeck.expr_ty(sub_expr), name) { self.state = Some(( @@ -451,7 +456,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing { (Some((State::DerefedBorrow(state), data)), RefOp::Deref) => { let position = data.position; report(cx, expr, State::DerefedBorrow(state), data); - if let Position::FieldAccess(name) = position + if let Position::FieldAccess{name, ..} = position && !ty_contains_field(typeck.expr_ty(sub_expr), name) { self.state = Some(( @@ -616,14 +621,17 @@ fn deref_method_same_type<'tcx>(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool { } /// The position of an expression relative to it's parent. -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug)] enum Position { MethodReceiver, /// The method is defined on a reference type. e.g. `impl Foo for &T` MethodReceiverRefImpl, Callee, ImplArg(HirId), - FieldAccess(Symbol), + FieldAccess { + name: Symbol, + of_union: bool, + }, // union fields cannot be auto borrowed Postfix, Deref, /// Any other location which will trigger auto-deref to a specific time. @@ -645,7 +653,10 @@ impl Position { } fn can_auto_borrow(self) -> bool { - matches!(self, Self::MethodReceiver | Self::FieldAccess(_) | Self::Callee) + matches!( + self, + Self::MethodReceiver | Self::FieldAccess { of_union: false, .. } | Self::Callee + ) } fn lint_explicit_deref(self) -> bool { @@ -657,7 +668,7 @@ impl Position { Self::MethodReceiver | Self::MethodReceiverRefImpl | Self::Callee - | Self::FieldAccess(_) + | Self::FieldAccess { .. } | Self::Postfix => PREC_POSTFIX, Self::ImplArg(_) | Self::Deref => PREC_PREFIX, Self::DerefStable(p, _) | Self::ReborrowStable(p) | Self::Other(p) => p, @@ -844,7 +855,10 @@ fn walk_parents<'tcx>( } }) }, - ExprKind::Field(child, name) if child.hir_id == e.hir_id => Some(Position::FieldAccess(name.name)), + ExprKind::Field(child, name) if child.hir_id == e.hir_id => Some(Position::FieldAccess { + name: name.name, + of_union: is_union(cx.typeck_results(), child), + }), ExprKind::Unary(UnOp::Deref, child) if child.hir_id == e.hir_id => Some(Position::Deref), ExprKind::Match(child, _, MatchSource::TryDesugar | MatchSource::AwaitDesugar) | ExprKind::Index(child, _) @@ -865,6 +879,13 @@ fn walk_parents<'tcx>( (position, adjustments) } +fn is_union<'tcx>(typeck: &'tcx TypeckResults<'_>, path_expr: &'tcx Expr<'_>) -> bool { + typeck + .expr_ty_adjusted(path_expr) + .ty_adt_def() + .map_or(false, rustc_middle::ty::AdtDef::is_union) +} + fn closure_result_position<'tcx>( cx: &LateContext<'tcx>, closure: &'tcx Closure<'_>, @@ -1308,7 +1329,7 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data }; let expr_str = if !expr_is_macro_call && is_final_ufcs && expr.precedence().order() < PREC_PREFIX { - format!("({})", expr_str) + format!("({expr_str})") } else { expr_str.into_owned() }; @@ -1322,7 +1343,7 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data Mutability::Mut => "explicit `deref_mut` method call", }, "try this", - format!("{}{}{}", addr_of_str, deref_str, expr_str), + format!("{addr_of_str}{deref_str}{expr_str}"), app, ); }, @@ -1336,7 +1357,7 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data && !has_enclosing_paren(&snip) && (expr.precedence().order() < data.position.precedence() || calls_field) { - format!("({})", snip) + format!("({snip})") } else { snip.into() }; @@ -1379,9 +1400,9 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data let (snip, snip_is_macro) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app); let sugg = if !snip_is_macro && expr.precedence().order() < precedence && !has_enclosing_paren(&snip) { - format!("{}({})", prefix, snip) + format!("{prefix}({snip})") } else { - format!("{}{}", prefix, snip) + format!("{prefix}{snip}") }; diag.span_suggestion(data.span, "try this", sugg, app); }, @@ -1460,14 +1481,14 @@ impl Dereferencing { } else { pat.always_deref = false; let snip = snippet_with_context(cx, e.span, parent.span.ctxt(), "..", &mut pat.app).0; - pat.replacements.push((e.span, format!("&{}", snip))); + pat.replacements.push((e.span, format!("&{snip}"))); } }, _ if !e.span.from_expansion() => { // Double reference might be needed at this point. pat.always_deref = false; let snip = snippet_with_applicability(cx, e.span, "..", &mut pat.app); - pat.replacements.push((e.span, format!("&{}", snip))); + pat.replacements.push((e.span, format!("&{snip}"))); }, // Edge case for macros. The span of the identifier will usually match the context of the // binding, but not if the identifier was created in a macro. e.g. `concat_idents` and proc diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 751ca24d5f59..3fac93dcc90c 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -191,7 +191,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.63.0"] pub DERIVE_PARTIAL_EQ_WITHOUT_EQ, - style, + nursery, "deriving `PartialEq` on a type that can implement `Eq`, without implementing `Eq`" } diff --git a/clippy_lints/src/disallowed_macros.rs b/clippy_lints/src/disallowed_macros.rs new file mode 100644 index 000000000000..5ab7144e2909 --- /dev/null +++ b/clippy_lints/src/disallowed_macros.rs @@ -0,0 +1,151 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::macros::macro_backtrace; +use rustc_data_structures::fx::FxHashSet; +use rustc_hir::def::{Namespace, Res}; +use rustc_hir::def_id::DefIdMap; +use rustc_hir::{Expr, ForeignItem, HirId, ImplItem, Item, Pat, Path, Stmt, TraitItem, Ty}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::{ExpnId, Span}; + +use crate::utils::conf; + +declare_clippy_lint! { + /// ### What it does + /// Denies the configured macros in clippy.toml + /// + /// Note: Even though this lint is warn-by-default, it will only trigger if + /// macros are defined in the clippy.toml file. + /// + /// ### Why is this bad? + /// Some macros are undesirable in certain contexts, and it's beneficial to + /// lint for them as needed. + /// + /// ### Example + /// An example clippy.toml configuration: + /// ```toml + /// # clippy.toml + /// disallowed-macros = [ + /// # Can use a string as the path of the disallowed macro. + /// "std::print", + /// # Can also use an inline table with a `path` key. + /// { path = "std::println" }, + /// # When using an inline table, can add a `reason` for why the macro + /// # is disallowed. + /// { path = "serde::Serialize", reason = "no serializing" }, + /// ] + /// ``` + /// ``` + /// use serde::Serialize; + /// + /// // Example code where clippy issues a warning + /// println!("warns"); + /// + /// // The diagnostic will contain the message "no serializing" + /// #[derive(Serialize)] + /// struct Data { + /// name: String, + /// value: usize, + /// } + /// ``` + #[clippy::version = "1.65.0"] + pub DISALLOWED_MACROS, + style, + "use of a disallowed macro" +} + +pub struct DisallowedMacros { + conf_disallowed: Vec, + disallowed: DefIdMap, + seen: FxHashSet, +} + +impl DisallowedMacros { + pub fn new(conf_disallowed: Vec) -> Self { + Self { + conf_disallowed, + disallowed: DefIdMap::default(), + seen: FxHashSet::default(), + } + } + + fn check(&mut self, cx: &LateContext<'_>, span: Span) { + if self.conf_disallowed.is_empty() { + return; + } + + for mac in macro_backtrace(span) { + if !self.seen.insert(mac.expn) { + return; + } + + if let Some(&index) = self.disallowed.get(&mac.def_id) { + let conf = &self.conf_disallowed[index]; + + span_lint_and_then( + cx, + DISALLOWED_MACROS, + mac.span, + &format!("use of a disallowed macro `{}`", conf.path()), + |diag| { + if let Some(reason) = conf.reason() { + diag.note(&format!("{reason} (from clippy.toml)")); + } + }, + ); + } + } + } +} + +impl_lint_pass!(DisallowedMacros => [DISALLOWED_MACROS]); + +impl LateLintPass<'_> for DisallowedMacros { + fn check_crate(&mut self, cx: &LateContext<'_>) { + for (index, conf) in self.conf_disallowed.iter().enumerate() { + let segs: Vec<_> = conf.path().split("::").collect(); + if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs, Some(Namespace::MacroNS)) { + self.disallowed.insert(id, index); + } + } + } + + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + self.check(cx, expr.span); + } + + fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &Stmt<'_>) { + self.check(cx, stmt.span); + } + + fn check_ty(&mut self, cx: &LateContext<'_>, ty: &Ty<'_>) { + self.check(cx, ty.span); + } + + fn check_pat(&mut self, cx: &LateContext<'_>, pat: &Pat<'_>) { + self.check(cx, pat.span); + } + + fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + self.check(cx, item.span); + self.check(cx, item.vis_span); + } + + fn check_foreign_item(&mut self, cx: &LateContext<'_>, item: &ForeignItem<'_>) { + self.check(cx, item.span); + self.check(cx, item.vis_span); + } + + fn check_impl_item(&mut self, cx: &LateContext<'_>, item: &ImplItem<'_>) { + self.check(cx, item.span); + self.check(cx, item.vis_span); + } + + fn check_trait_item(&mut self, cx: &LateContext<'_>, item: &TraitItem<'_>) { + self.check(cx, item.span); + } + + fn check_path(&mut self, cx: &LateContext<'_>, path: &Path<'_>, _: HirId) { + self.check(cx, path.span); + } +} diff --git a/clippy_lints/src/disallowed_methods.rs b/clippy_lints/src/disallowed_methods.rs index 53973ab792a9..1a381f92c031 100644 --- a/clippy_lints/src/disallowed_methods.rs +++ b/clippy_lints/src/disallowed_methods.rs @@ -1,7 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::{fn_def_id, get_parent_expr, path_def_id}; -use rustc_hir::{def::Res, def_id::DefIdMap, Expr, ExprKind}; +use rustc_hir::def::{Namespace, Res}; +use rustc_hir::def_id::DefIdMap; +use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -58,12 +60,12 @@ declare_clippy_lint! { #[derive(Clone, Debug)] pub struct DisallowedMethods { - conf_disallowed: Vec, + conf_disallowed: Vec, disallowed: DefIdMap, } impl DisallowedMethods { - pub fn new(conf_disallowed: Vec) -> Self { + pub fn new(conf_disallowed: Vec) -> Self { Self { conf_disallowed, disallowed: DefIdMap::default(), @@ -77,7 +79,7 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethods { fn check_crate(&mut self, cx: &LateContext<'_>) { for (index, conf) in self.conf_disallowed.iter().enumerate() { let segs: Vec<_> = conf.path().split("::").collect(); - if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs) { + if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs, Some(Namespace::ValueNS)) { self.disallowed.insert(id, index); } } @@ -102,11 +104,8 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethods { }; let msg = format!("use of a disallowed method `{}`", conf.path()); span_lint_and_then(cx, DISALLOWED_METHODS, expr.span, &msg, |diag| { - if let conf::DisallowedMethod::WithReason { - reason: Some(reason), .. - } = conf - { - diag.note(&format!("{} (from clippy.toml)", reason)); + if let Some(reason) = conf.reason() { + diag.note(&format!("{reason} (from clippy.toml)")); } }); } diff --git a/clippy_lints/src/disallowed_script_idents.rs b/clippy_lints/src/disallowed_script_idents.rs index 0c27c3f9255f..084190f00132 100644 --- a/clippy_lints/src/disallowed_script_idents.rs +++ b/clippy_lints/src/disallowed_script_idents.rs @@ -99,8 +99,7 @@ impl EarlyLintPass for DisallowedScriptIdents { DISALLOWED_SCRIPT_IDENTS, span, &format!( - "identifier `{}` has a Unicode script that is not allowed by configuration: {}", - symbol_str, + "identifier `{symbol_str}` has a Unicode script that is not allowed by configuration: {}", script.full_name() ), ); diff --git a/clippy_lints/src/disallowed_types.rs b/clippy_lints/src/disallowed_types.rs index 28dbfbab2e19..c7131fc164d3 100644 --- a/clippy_lints/src/disallowed_types.rs +++ b/clippy_lints/src/disallowed_types.rs @@ -1,9 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_then; use rustc_data_structures::fx::FxHashMap; -use rustc_hir::{ - def::Res, def_id::DefId, Item, ItemKind, PolyTraitRef, PrimTy, Ty, TyKind, UseKind, -}; +use rustc_hir::def::{Namespace, Res}; +use rustc_hir::def_id::DefId; +use rustc_hir::{Item, ItemKind, PolyTraitRef, PrimTy, Ty, TyKind, UseKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Span; @@ -52,13 +52,13 @@ declare_clippy_lint! { } #[derive(Clone, Debug)] pub struct DisallowedTypes { - conf_disallowed: Vec, + conf_disallowed: Vec, def_ids: FxHashMap>, prim_tys: FxHashMap>, } impl DisallowedTypes { - pub fn new(conf_disallowed: Vec) -> Self { + pub fn new(conf_disallowed: Vec) -> Self { Self { conf_disallowed, def_ids: FxHashMap::default(), @@ -88,15 +88,9 @@ impl_lint_pass!(DisallowedTypes => [DISALLOWED_TYPES]); impl<'tcx> LateLintPass<'tcx> for DisallowedTypes { fn check_crate(&mut self, cx: &LateContext<'_>) { for conf in &self.conf_disallowed { - let (path, reason) = match conf { - conf::DisallowedType::Simple(path) => (path, None), - conf::DisallowedType::WithReason { path, reason } => ( - path, - reason.as_ref().map(|reason| format!("{} (from clippy.toml)", reason)), - ), - }; - let segs: Vec<_> = path.split("::").collect(); - match clippy_utils::def_path_res(cx, &segs) { + let segs: Vec<_> = conf.path().split("::").collect(); + let reason = conf.reason().map(|reason| format!("{reason} (from clippy.toml)")); + match clippy_utils::def_path_res(cx, &segs, Some(Namespace::TypeNS)) { Res::Def(_, id) => { self.def_ids.insert(id, reason); }, @@ -130,7 +124,7 @@ fn emit(cx: &LateContext<'_>, name: &str, span: Span, reason: Option<&str>) { cx, DISALLOWED_TYPES, span, - &format!("`{}` is not allowed according to config", name), + &format!("`{name}` is not allowed according to config"), |diag| { if let Some(reason) = reason { diag.note(reason); diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index f48ba526d51e..36dc7e3396b8 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -198,6 +198,29 @@ declare_clippy_lint! { "presence of `fn main() {` in code examples" } +declare_clippy_lint! { + /// ### What it does + /// Detects the syntax `['foo']` in documentation comments (notice quotes instead of backticks) + /// outside of code blocks + /// ### Why is this bad? + /// It is likely a typo when defining an intra-doc link + /// + /// ### Example + /// ```rust + /// /// See also: ['foo'] + /// fn bar() {} + /// ``` + /// Use instead: + /// ```rust + /// /// See also: [`foo`] + /// fn bar() {} + /// ``` + #[clippy::version = "1.63.0"] + pub DOC_LINK_WITH_QUOTES, + pedantic, + "possible typo for an intra-doc link" +} + #[expect(clippy::module_name_repetitions)] #[derive(Clone)] pub struct DocMarkdown { @@ -214,9 +237,14 @@ impl DocMarkdown { } } -impl_lint_pass!(DocMarkdown => - [DOC_MARKDOWN, MISSING_SAFETY_DOC, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, NEEDLESS_DOCTEST_MAIN] -); +impl_lint_pass!(DocMarkdown => [ + DOC_LINK_WITH_QUOTES, + DOC_MARKDOWN, + MISSING_SAFETY_DOC, + MISSING_ERRORS_DOC, + MISSING_PANICS_DOC, + NEEDLESS_DOCTEST_MAIN +]); impl<'tcx> LateLintPass<'tcx> for DocMarkdown { fn check_crate(&mut self, cx: &LateContext<'tcx>) { @@ -237,7 +265,15 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown { panic_span: None, }; fpu.visit_expr(body.value); - lint_for_missing_headers(cx, item.def_id.def_id, item.span, sig, headers, Some(body_id), fpu.panic_span); + lint_for_missing_headers( + cx, + item.def_id.def_id, + item.span, + sig, + headers, + Some(body_id), + fpu.panic_span, + ); } }, hir::ItemKind::Impl(impl_) => { @@ -287,7 +323,15 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown { panic_span: None, }; fpu.visit_expr(body.value); - lint_for_missing_headers(cx, item.def_id.def_id, item.span, sig, headers, Some(body_id), fpu.panic_span); + lint_for_missing_headers( + cx, + item.def_id.def_id, + item.span, + sig, + headers, + Some(body_id), + fpu.panic_span, + ); } } } @@ -416,7 +460,7 @@ pub fn strip_doc_comment_decoration(doc: &str, comment_kind: CommentKind, span: (no_stars, sizes) } -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Default)] struct DocHeaders { safety: bool, errors: bool, @@ -460,11 +504,7 @@ fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs } if doc.is_empty() { - return DocHeaders { - safety: false, - errors: false, - panics: false, - }; + return DocHeaders::default(); } let mut cb = fake_broken_link_callback; @@ -505,11 +545,7 @@ fn check_doc<'a, Events: Iterator, Range, Range, Range, + in_link: bool, + trimmed_text: &str, + span: Span, + range: &Range, + begin: usize, + text_len: usize, +) { + if in_link && trimmed_text.starts_with('\'') && trimmed_text.ends_with('\'') { + // fix the span to only point at the text within the link + let lo = span.lo() + BytePos::from_usize(range.start - begin); + span_lint( + cx, + DOC_LINK_WITH_QUOTES, + span.with_lo(lo).with_hi(lo + BytePos::from_usize(text_len)), + "possible intra-doc link using quotes instead of backticks", + ); + } +} + fn get_current_span(spans: &[(usize, Span)], idx: usize) -> (usize, Span) { let index = match spans.binary_search_by(|c| c.0.cmp(&idx)) { Ok(o) => o, @@ -790,7 +848,7 @@ fn check_word(cx: &LateContext<'_>, word: &str, span: Span) { diag.span_suggestion_with_style( span, "try", - format!("`{}`", snippet), + format!("`{snippet}`"), applicability, // always show the suggestion in a separate line, since the // inline presentation adds another pair of backticks diff --git a/clippy_lints/src/doc_link_with_quotes.rs b/clippy_lints/src/doc_link_with_quotes.rs deleted file mode 100644 index 0ff1d2755daf..000000000000 --- a/clippy_lints/src/doc_link_with_quotes.rs +++ /dev/null @@ -1,60 +0,0 @@ -use clippy_utils::diagnostics::span_lint; -use itertools::Itertools; -use rustc_ast::{AttrKind, Attribute}; -use rustc_lint::{EarlyContext, EarlyLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; - -declare_clippy_lint! { - /// ### What it does - /// Detects the syntax `['foo']` in documentation comments (notice quotes instead of backticks) - /// outside of code blocks - /// ### Why is this bad? - /// It is likely a typo when defining an intra-doc link - /// - /// ### Example - /// ```rust - /// /// See also: ['foo'] - /// fn bar() {} - /// ``` - /// Use instead: - /// ```rust - /// /// See also: [`foo`] - /// fn bar() {} - /// ``` - #[clippy::version = "1.63.0"] - pub DOC_LINK_WITH_QUOTES, - pedantic, - "possible typo for an intra-doc link" -} -declare_lint_pass!(DocLinkWithQuotes => [DOC_LINK_WITH_QUOTES]); - -impl EarlyLintPass for DocLinkWithQuotes { - fn check_attribute(&mut self, ctx: &EarlyContext<'_>, attr: &Attribute) { - if let AttrKind::DocComment(_, symbol) = attr.kind { - if contains_quote_link(symbol.as_str()) { - span_lint( - ctx, - DOC_LINK_WITH_QUOTES, - attr.span, - "possible intra-doc link using quotes instead of backticks", - ); - } - } - } -} - -fn contains_quote_link(s: &str) -> bool { - let mut in_backticks = false; - let mut found_opening = false; - - for c in s.chars().tuple_windows::<(char, char)>() { - match c { - ('`', _) => in_backticks = !in_backticks, - ('[', '\'') if !in_backticks => found_opening = true, - ('\'', ']') if !in_backticks && found_opening => return true, - _ => {}, - } - } - - false -} diff --git a/clippy_lints/src/drop_forget_ref.rs b/clippy_lints/src/drop_forget_ref.rs index b35f0b8ca52d..4721a7b37056 100644 --- a/clippy_lints/src/drop_forget_ref.rs +++ b/clippy_lints/src/drop_forget_ref.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note}; +use clippy_utils::get_parent_node; use clippy_utils::is_must_use_func_call; use clippy_utils::ty::{is_copy, is_must_use_ty, is_type_lang_item}; -use rustc_hir::{Expr, ExprKind, LangItem}; +use rustc_hir::{Arm, Expr, ExprKind, LangItem, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -202,11 +203,13 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef { && let Some(fn_name) = cx.tcx.get_diagnostic_name(def_id) { let arg_ty = cx.typeck_results().expr_ty(arg); + let is_copy = is_copy(cx, arg_ty); + let drop_is_single_call_in_arm = is_single_call_in_arm(cx, arg, expr); let (lint, msg) = match fn_name { sym::mem_drop if arg_ty.is_ref() => (DROP_REF, DROP_REF_SUMMARY), sym::mem_forget if arg_ty.is_ref() => (FORGET_REF, FORGET_REF_SUMMARY), - sym::mem_drop if is_copy(cx, arg_ty) => (DROP_COPY, DROP_COPY_SUMMARY), - sym::mem_forget if is_copy(cx, arg_ty) => (FORGET_COPY, FORGET_COPY_SUMMARY), + sym::mem_drop if is_copy && !drop_is_single_call_in_arm => (DROP_COPY, DROP_COPY_SUMMARY), + sym::mem_forget if is_copy => (FORGET_COPY, FORGET_COPY_SUMMARY), sym::mem_drop if is_type_lang_item(cx, arg_ty, LangItem::ManuallyDrop) => { span_lint_and_help( cx, @@ -221,7 +224,9 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef { sym::mem_drop if !(arg_ty.needs_drop(cx.tcx, cx.param_env) || is_must_use_func_call(cx, arg) - || is_must_use_ty(cx, arg_ty)) => + || is_must_use_ty(cx, arg_ty) + || drop_is_single_call_in_arm + ) => { (DROP_NON_DROP, DROP_NON_DROP_SUMMARY) }, @@ -236,8 +241,23 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef { expr.span, msg, Some(arg.span), - &format!("argument has type `{}`", arg_ty), + &format!("argument has type `{arg_ty}`"), ); } } } + +// dropping returned value of a function like in the following snippet is considered idiomatic, see +// #9482 for examples match { +// => drop(fn_with_side_effect_and_returning_some_value()), +// .. +// } +fn is_single_call_in_arm<'tcx>(cx: &LateContext<'tcx>, arg: &'tcx Expr<'_>, drop_expr: &'tcx Expr<'_>) -> bool { + if matches!(arg.kind, ExprKind::Call(..) | ExprKind::MethodCall(..)) { + let parent_node = get_parent_node(cx.tcx, drop_expr.hir_id); + if let Some(Node::Arm(Arm { body, .. })) = &parent_node { + return body.hir_id == drop_expr.hir_id; + } + } + false +} diff --git a/clippy_lints/src/entry.rs b/clippy_lints/src/entry.rs index e70df3f53c75..9c834cf01448 100644 --- a/clippy_lints/src/entry.rs +++ b/clippy_lints/src/entry.rs @@ -113,13 +113,8 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass { ), }; format!( - "if let {}::{} = {}.entry({}) {} else {}", + "if let {}::{entry_kind} = {map_str}.entry({key_str}) {then_str} else {else_str}", map_ty.entry_path(), - entry_kind, - map_str, - key_str, - then_str, - else_str, ) } else { // if .. { insert } else { insert } @@ -137,16 +132,11 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass { let indent_str = snippet_indent(cx, expr.span); let indent_str = indent_str.as_deref().unwrap_or(""); format!( - "match {}.entry({}) {{\n{indent} {entry}::{} => {}\n\ - {indent} {entry}::{} => {}\n{indent}}}", - map_str, - key_str, - then_entry, + "match {map_str}.entry({key_str}) {{\n{indent_str} {entry}::{then_entry} => {}\n\ + {indent_str} {entry}::{else_entry} => {}\n{indent_str}}}", reindent_multiline(then_str.into(), true, Some(4 + indent_str.len())), - else_entry, reindent_multiline(else_str.into(), true, Some(4 + indent_str.len())), entry = map_ty.entry_path(), - indent = indent_str, ) } } else { @@ -163,20 +153,16 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass { then_search.snippet_occupied(cx, then_expr.span, &mut app) }; format!( - "if let {}::{} = {}.entry({}) {}", + "if let {}::{entry_kind} = {map_str}.entry({key_str}) {body_str}", map_ty.entry_path(), - entry_kind, - map_str, - key_str, - body_str, ) } else if let Some(insertion) = then_search.as_single_insertion() { let value_str = snippet_with_context(cx, insertion.value.span, then_expr.span.ctxt(), "..", &mut app).0; if contains_expr.negated { if insertion.value.can_have_side_effects() { - format!("{}.entry({}).or_insert_with(|| {});", map_str, key_str, value_str) + format!("{map_str}.entry({key_str}).or_insert_with(|| {value_str});") } else { - format!("{}.entry({}).or_insert({});", map_str, key_str, value_str) + format!("{map_str}.entry({key_str}).or_insert({value_str});") } } else { // TODO: suggest using `if let Some(v) = map.get_mut(k) { .. }` here. @@ -186,7 +172,7 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass { } else { let block_str = then_search.snippet_closure(cx, then_expr.span, &mut app); if contains_expr.negated { - format!("{}.entry({}).or_insert_with(|| {});", map_str, key_str, block_str) + format!("{map_str}.entry({key_str}).or_insert_with(|| {block_str});") } else { // TODO: suggest using `if let Some(v) = map.get_mut(k) { .. }` here. // This would need to be a different lint. diff --git a/clippy_lints/src/enum_variants.rs b/clippy_lints/src/enum_variants.rs index c39a909b3ccb..b019d07d53d1 100644 --- a/clippy_lints/src/enum_variants.rs +++ b/clippy_lints/src/enum_variants.rs @@ -202,12 +202,11 @@ fn check_variant(cx: &LateContext<'_>, threshold: u64, def: &EnumDef<'_>, item_n cx, ENUM_VARIANT_NAMES, span, - &format!("all variants have the same {}fix: `{}`", what, value), + &format!("all variants have the same {what}fix: `{value}`"), None, &format!( - "remove the {}fixes and use full paths to \ - the variants instead of glob imports", - what + "remove the {what}fixes and use full paths to \ + the variants instead of glob imports" ), ); } diff --git a/clippy_lints/src/equatable_if_let.rs b/clippy_lints/src/equatable_if_let.rs index bce49165e5b1..b40cb7cddaf1 100644 --- a/clippy_lints/src/equatable_if_let.rs +++ b/clippy_lints/src/equatable_if_let.rs @@ -51,9 +51,7 @@ fn unary_pattern(pat: &Pat<'_>) -> bool { false }, PatKind::Struct(_, a, etc) => !etc && a.iter().all(|x| unary_pattern(x.pat)), - PatKind::Tuple(a, etc) | PatKind::TupleStruct(_, a, etc) => { - !etc.as_opt_usize().is_some() && array_rec(a) - } + PatKind::Tuple(a, etc) | PatKind::TupleStruct(_, a, etc) => etc.as_opt_usize().is_none() && array_rec(a), PatKind::Ref(x, _) | PatKind::Box(x) => unary_pattern(x), PatKind::Path(_) | PatKind::Lit(_) => true, } @@ -93,9 +91,8 @@ impl<'tcx> LateLintPass<'tcx> for PatternEquality { "this pattern matching can be expressed using equality", "try", format!( - "{} == {}", + "{} == {pat_str}", snippet_with_context(cx, let_expr.init.span, expr.span.ctxt(), "..", &mut applicability).0, - pat_str, ), applicability, ); diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index 8ccc969646ec..2e608fe527fd 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_hir; use rustc_hir::intravisit; use rustc_hir::{self, AssocItemKind, Body, FnDecl, HirId, HirIdSet, Impl, ItemKind, Node, Pat, PatKind}; +use rustc_hir_analysis::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::FakeReadCause; @@ -10,7 +11,6 @@ use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; use rustc_span::symbol::kw; use rustc_target::spec::abi::Abi; -use rustc_hir_analysis::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; #[derive(Copy, Clone)] pub struct BoxedLocal { @@ -177,7 +177,13 @@ impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> { } } - fn fake_read(&mut self, _: &rustc_hir_analysis::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} + fn fake_read( + &mut self, + _: &rustc_hir_analysis::expr_use_visitor::PlaceWithHirId<'tcx>, + _: FakeReadCause, + _: HirId, + ) { + } } impl<'a, 'tcx> EscapeDelegate<'a, 'tcx> { diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 598f8c31859e..3732410e71e5 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::higher::VecArgs; use clippy_utils::source::snippet_opt; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use clippy_utils::usage::local_used_after_expr; use clippy_utils::{higher, is_adjusted, path_to_local, path_to_local_id}; use if_chain::if_chain; @@ -11,7 +11,7 @@ use rustc_hir::{Closure, Expr, ExprKind, Param, PatKind, Unsafety}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; use rustc_middle::ty::binding::BindingMode; -use rustc_middle::ty::{self, ClosureKind, Ty, TypeVisitable}; +use rustc_middle::ty::{self, Ty, TypeVisitable}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::sym; @@ -122,15 +122,12 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { then { span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| { if let Some(mut snippet) = snippet_opt(cx, callee.span) { - if_chain! { - if let ty::Closure(_, substs) = callee_ty.peel_refs().kind(); - if substs.as_closure().kind() == ClosureKind::FnMut; - if path_to_local(callee).map_or(false, |l| local_used_after_expr(cx, l, expr)); - - then { + if let Some(fn_mut_id) = cx.tcx.lang_items().fn_mut_trait() + && implements_trait(cx, callee_ty.peel_refs(), fn_mut_id, &[]) + && path_to_local(callee).map_or(false, |l| local_used_after_expr(cx, l, expr)) + { // Mutable closure is used after current expr; we cannot consume it. - snippet = format!("&mut {}", snippet); - } + snippet = format!("&mut {snippet}"); } diag.span_suggestion( expr.span, @@ -157,7 +154,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { diag.span_suggestion( expr.span, "replace the closure with the method itself", - format!("{}::{}", name, path.ident.name), + format!("{name}::{}", path.ident.name), Applicability::MachineApplicable, ); }) diff --git a/clippy_lints/src/exhaustive_items.rs b/clippy_lints/src/exhaustive_items.rs index f3d9ebc5f12d..be6242bd20b8 100644 --- a/clippy_lints/src/exhaustive_items.rs +++ b/clippy_lints/src/exhaustive_items.rs @@ -97,7 +97,7 @@ impl LateLintPass<'_> for ExhaustiveItems { item.span, msg, |diag| { - let sugg = format!("#[non_exhaustive]\n{}", indent); + let sugg = format!("#[non_exhaustive]\n{indent}"); diag.span_suggestion(suggestion_span, "try adding #[non_exhaustive]", sugg, diff --git a/clippy_lints/src/explicit_write.rs b/clippy_lints/src/explicit_write.rs index b9ed4af02190..c0ea6f338a23 100644 --- a/clippy_lints/src/explicit_write.rs +++ b/clippy_lints/src/explicit_write.rs @@ -80,12 +80,12 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite { // used. let (used, sugg_mac) = if let Some(macro_name) = calling_macro { ( - format!("{}!({}(), ...)", macro_name, dest_name), + format!("{macro_name}!({dest_name}(), ...)"), macro_name.replace("write", "print"), ) } else { ( - format!("{}().write_fmt(...)", dest_name), + format!("{dest_name}().write_fmt(...)"), "print".into(), ) }; @@ -100,9 +100,9 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite { cx, EXPLICIT_WRITE, expr.span, - &format!("use of `{}.unwrap()`", used), + &format!("use of `{used}.unwrap()`"), "try this", - format!("{}{}!({})", prefix, sugg_mac, inputs_snippet), + format!("{prefix}{sugg_mac}!({inputs_snippet})"), applicability, ) } diff --git a/clippy_lints/src/float_literal.rs b/clippy_lints/src/float_literal.rs index f2e079809637..6fee7fb308ce 100644 --- a/clippy_lints/src/float_literal.rs +++ b/clippy_lints/src/float_literal.rs @@ -173,9 +173,9 @@ impl FloatFormat { T: fmt::UpperExp + fmt::LowerExp + fmt::Display, { match self { - Self::LowerExp => format!("{:e}", f), - Self::UpperExp => format!("{:E}", f), - Self::Normal => format!("{}", f), + Self::LowerExp => format!("{f:e}"), + Self::UpperExp => format!("{f:E}"), + Self::Normal => format!("{f}"), } } } diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index ba53a9678801..0ed301964758 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -142,8 +142,7 @@ fn prepare_receiver_sugg<'a>(cx: &LateContext<'_>, mut expr: &'a Expr<'a>) -> Su if let ast::LitKind::Float(sym, ast::LitFloatType::Unsuffixed) = lit.node; then { let op = format!( - "{}{}{}", - suggestion, + "{suggestion}{}{}", // Check for float literals without numbers following the decimal // separator such as `2.` and adds a trailing zero if sym.as_str().ends_with('.') { @@ -172,7 +171,7 @@ fn check_log_base(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, ar expr.span, "logarithm for bases 2, 10 and e can be computed more accurately", "consider using", - format!("{}.{}()", Sugg::hir(cx, receiver, "..").maybe_par(), method), + format!("{}.{method}()", Sugg::hir(cx, receiver, "..").maybe_par()), Applicability::MachineApplicable, ); } @@ -251,7 +250,7 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: expr.span, "exponent for bases 2 and e can be computed more accurately", "consider using", - format!("{}.{}()", prepare_receiver_sugg(cx, &args[0]), method), + format!("{}.{method}()", prepare_receiver_sugg(cx, &args[0])), Applicability::MachineApplicable, ); } @@ -312,7 +311,8 @@ fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: if let ExprKind::Binary( Spanned { - node: BinOpKind::Add, .. + node: op @ (BinOpKind::Add | BinOpKind::Sub), + .. }, lhs, rhs, @@ -320,6 +320,16 @@ fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: { let other_addend = if lhs.hir_id == expr.hir_id { rhs } else { lhs }; + // Negate expr if original code has subtraction and expr is on the right side + let maybe_neg_sugg = |expr, hir_id| { + let sugg = Sugg::hir(cx, expr, ".."); + if matches!(op, BinOpKind::Sub) && hir_id == rhs.hir_id { + format!("-{sugg}") + } else { + sugg.to_string() + } + }; + span_lint_and_sugg( cx, SUBOPTIMAL_FLOPS, @@ -329,8 +339,8 @@ fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: format!( "{}.mul_add({}, {})", Sugg::hir(cx, receiver, "..").maybe_par(), - Sugg::hir(cx, receiver, ".."), - Sugg::hir(cx, other_addend, ".."), + maybe_neg_sugg(receiver, expr.hir_id), + maybe_neg_sugg(other_addend, other_addend.hir_id), ), Applicability::MachineApplicable, ); @@ -444,7 +454,8 @@ fn is_float_mul_expr<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(&' fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) { if let ExprKind::Binary( Spanned { - node: BinOpKind::Add, .. + node: op @ (BinOpKind::Add | BinOpKind::Sub), + .. }, lhs, rhs, @@ -458,10 +469,27 @@ fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) { } } + let maybe_neg_sugg = |expr| { + let sugg = Sugg::hir(cx, expr, ".."); + if let BinOpKind::Sub = op { + format!("-{sugg}") + } else { + sugg.to_string() + } + }; + let (recv, arg1, arg2) = if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, lhs) { - (inner_lhs, inner_rhs, rhs) + ( + inner_lhs, + Sugg::hir(cx, inner_rhs, "..").to_string(), + maybe_neg_sugg(rhs), + ) } else if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, rhs) { - (inner_lhs, inner_rhs, lhs) + ( + inner_lhs, + maybe_neg_sugg(inner_rhs), + Sugg::hir(cx, lhs, "..").to_string(), + ) } else { return; }; @@ -472,12 +500,7 @@ fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) { expr.span, "multiply and add expressions can be calculated more efficiently and accurately", "consider using", - format!( - "{}.mul_add({}, {})", - prepare_receiver_sugg(cx, recv), - Sugg::hir(cx, arg1, ".."), - Sugg::hir(cx, arg2, ".."), - ), + format!("{}.mul_add({arg1}, {arg2})", prepare_receiver_sugg(cx, recv)), Applicability::MachineApplicable, ); } diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs index f10d82569536..bc0c68f535a9 100644 --- a/clippy_lints/src/format.rs +++ b/clippy_lints/src/format.rs @@ -62,7 +62,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat { [_] => { // Simulate macro expansion, converting {{ and }} to { and }. let s_expand = format_args.format_string.snippet.replace("{{", "{").replace("}}", "}"); - let sugg = format!("{}.to_string()", s_expand); + let sugg = format!("{s_expand}.to_string()"); span_useless_format(cx, call_site, sugg, applicability); }, [..] => {}, diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index 9e1eaf248b73..cefebc2a98a2 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -1,16 +1,18 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::is_diag_trait_item; -use clippy_utils::macros::{is_format_macro, FormatArgsExpn}; +use clippy_utils::macros::FormatParamKind::{Implicit, Named, Numbered, Starred}; +use clippy_utils::macros::{is_format_macro, FormatArgsExpn, FormatParam, FormatParamUsage}; use clippy_utils::source::snippet_opt; use clippy_utils::ty::implements_trait; +use clippy_utils::{is_diag_trait_item, meets_msrv, msrvs}; use if_chain::if_chain; use itertools::Itertools; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, HirId}; +use rustc_hir::{Expr, ExprKind, HirId, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::adjustment::{Adjust, Adjustment}; use rustc_middle::ty::Ty; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{sym, ExpnData, ExpnKind, Span, Symbol}; declare_clippy_lint! { @@ -64,7 +66,67 @@ declare_clippy_lint! { "`to_string` applied to a type that implements `Display` in format args" } -declare_lint_pass!(FormatArgs => [FORMAT_IN_FORMAT_ARGS, TO_STRING_IN_FORMAT_ARGS]); +declare_clippy_lint! { + /// ### What it does + /// Detect when a variable is not inlined in a format string, + /// and suggests to inline it. + /// + /// ### Why is this bad? + /// Non-inlined code is slightly more difficult to read and understand, + /// as it requires arguments to be matched against the format string. + /// The inlined syntax, where allowed, is simpler. + /// + /// ### Example + /// ```rust + /// # let var = 42; + /// # let width = 1; + /// # let prec = 2; + /// format!("{}", var); + /// format!("{v:?}", v = var); + /// format!("{0} {0}", var); + /// format!("{0:1$}", var, width); + /// format!("{:.*}", prec, var); + /// ``` + /// Use instead: + /// ```rust + /// # let var = 42; + /// # let width = 1; + /// # let prec = 2; + /// format!("{var}"); + /// format!("{var:?}"); + /// format!("{var} {var}"); + /// format!("{var:width$}"); + /// format!("{var:.prec$}"); + /// ``` + /// + /// ### Known Problems + /// + /// There may be a false positive if the format string is expanded from certain proc macros: + /// + /// ```ignore + /// println!(indoc!("{}"), var); + /// ``` + /// + /// If a format string contains a numbered argument that cannot be inlined + /// nothing will be suggested, e.g. `println!("{0}={1}", var, 1+2)`. + #[clippy::version = "1.65.0"] + pub UNINLINED_FORMAT_ARGS, + pedantic, + "using non-inlined variables in `format!` calls" +} + +impl_lint_pass!(FormatArgs => [FORMAT_IN_FORMAT_ARGS, UNINLINED_FORMAT_ARGS, TO_STRING_IN_FORMAT_ARGS]); + +pub struct FormatArgs { + msrv: Option, +} + +impl FormatArgs { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} impl<'tcx> LateLintPass<'tcx> for FormatArgs { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { @@ -86,9 +148,72 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs { check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg.param.value); check_to_string_in_format_args(cx, name, arg.param.value); } + if meets_msrv(self.msrv, msrvs::FORMAT_ARGS_CAPTURE) { + check_uninlined_args(cx, &format_args, outermost_expn_data.call_site); + } } } } + + extract_msrv_attr!(LateContext); +} + +fn check_uninlined_args(cx: &LateContext<'_>, args: &FormatArgsExpn<'_>, call_site: Span) { + if args.format_string.span.from_expansion() { + return; + } + + let mut fixes = Vec::new(); + // If any of the arguments are referenced by an index number, + // and that argument is not a simple variable and cannot be inlined, + // we cannot remove any other arguments in the format string, + // because the index numbers might be wrong after inlining. + // Example of an un-inlinable format: print!("{}{1}", foo, 2) + if !args.params().all(|p| check_one_arg(args, &p, &mut fixes)) || fixes.is_empty() { + return; + } + + // FIXME: Properly ignore a rare case where the format string is wrapped in a macro. + // Example: `format!(indoc!("{}"), foo);` + // If inlined, they will cause a compilation error: + // > to avoid ambiguity, `format_args!` cannot capture variables + // > when the format string is expanded from a macro + // @Alexendoo explanation: + // > indoc! is a proc macro that is producing a string literal with its span + // > set to its input it's not marked as from expansion, and since it's compatible + // > tokenization wise clippy_utils::is_from_proc_macro wouldn't catch it either + // This might be a relatively expensive test, so do it only we are ready to replace. + // See more examples in tests/ui/uninlined_format_args.rs + + span_lint_and_then( + cx, + UNINLINED_FORMAT_ARGS, + call_site, + "variables can be used directly in the `format!` string", + |diag| { + diag.multipart_suggestion("change this to", fixes, Applicability::MachineApplicable); + }, + ); +} + +fn check_one_arg(args: &FormatArgsExpn<'_>, param: &FormatParam<'_>, fixes: &mut Vec<(Span, String)>) -> bool { + if matches!(param.kind, Implicit | Starred | Named(_) | Numbered) + && let ExprKind::Path(QPath::Resolved(None, path)) = param.value.kind + && let [segment] = path.segments + && let Some(arg_span) = args.value_with_prev_comma_span(param.value.hir_id) + { + let replacement = match param.usage { + FormatParamUsage::Argument => segment.ident.name.to_string(), + FormatParamUsage::Width => format!("{}$", segment.ident.name), + FormatParamUsage::Precision => format!(".{}$", segment.ident.name), + }; + fixes.push((param.span, replacement)); + fixes.push((arg_span, String::new())); + true // successful inlining, continue checking + } else { + // if we can't inline a numbered argument, we can't continue + param.kind != Numbered + } } fn outermost_expn_data(expn_data: ExpnData) -> ExpnData { @@ -117,11 +242,10 @@ fn check_format_in_format_args( cx, FORMAT_IN_FORMAT_ARGS, call_site, - &format!("`format!` in `{}!` args", name), + &format!("`format!` in `{name}!` args"), |diag| { diag.help(&format!( - "combine the `format!(..)` arguments with the outer `{}!(..)` call", - name + "combine the `format!(..)` arguments with the outer `{name}!(..)` call" )); diag.help("or consider changing `format!` to `format_args!`"); }, @@ -149,8 +273,7 @@ fn check_to_string_in_format_args(cx: &LateContext<'_>, name: Symbol, value: &Ex TO_STRING_IN_FORMAT_ARGS, value.span.with_lo(receiver.span.hi()), &format!( - "`to_string` applied to a type that implements `Display` in `{}!` args", - name + "`to_string` applied to a type that implements `Display` in `{name}!` args" ), "remove this", String::new(), @@ -162,16 +285,13 @@ fn check_to_string_in_format_args(cx: &LateContext<'_>, name: Symbol, value: &Ex TO_STRING_IN_FORMAT_ARGS, value.span, &format!( - "`to_string` applied to a type that implements `Display` in `{}!` args", - name + "`to_string` applied to a type that implements `Display` in `{name}!` args" ), "use this", format!( - "{}{:*>width$}{}", + "{}{:*>n_needed_derefs$}{receiver_snippet}", if needs_ref { "&" } else { "" }, - "", - receiver_snippet, - width = n_needed_derefs + "" ), Applicability::MachineApplicable, ); @@ -180,7 +300,7 @@ fn check_to_string_in_format_args(cx: &LateContext<'_>, name: Symbol, value: &Ex } } -// Returns true if `hir_id` is referred to by multiple format params +/// Returns true if `hir_id` is referred to by multiple format params fn is_aliased(args: &FormatArgsExpn<'_>, hir_id: HirId) -> bool { args.params().filter(|param| param.value.hir_id == hir_id).at_most_one().is_err() } diff --git a/clippy_lints/src/format_impl.rs b/clippy_lints/src/format_impl.rs index b628fd9f7581..ed1342a54654 100644 --- a/clippy_lints/src/format_impl.rs +++ b/clippy_lints/src/format_impl.rs @@ -214,12 +214,12 @@ fn check_print_in_format_impl(cx: &LateContext<'_>, expr: &Expr<'_>, impl_trait: cx, PRINT_IN_FORMAT_IMPL, macro_call.span, - &format!("use of `{}!` in `{}` impl", name, impl_trait.name), + &format!("use of `{name}!` in `{}` impl", impl_trait.name), "replace with", if let Some(formatter_name) = impl_trait.formatter_name { - format!("{}!({}, ..)", replacement, formatter_name) + format!("{replacement}!({formatter_name}, ..)") } else { - format!("{}!(..)", replacement) + format!("{replacement}!(..)") }, Applicability::HasPlaceholders, ); diff --git a/clippy_lints/src/formatting.rs b/clippy_lints/src/formatting.rs index 01cefe4af853..a866a68987d0 100644 --- a/clippy_lints/src/formatting.rs +++ b/clippy_lints/src/formatting.rs @@ -154,11 +154,10 @@ fn check_assign(cx: &EarlyContext<'_>, expr: &Expr) { eqop_span, &format!( "this looks like you are trying to use `.. {op}= ..`, but you \ - really are doing `.. = ({op} ..)`", - op = op + really are doing `.. = ({op} ..)`" ), None, - &format!("to remove this lint, use either `{op}=` or `= {op}`", op = op), + &format!("to remove this lint, use either `{op}=` or `= {op}`"), ); } } @@ -191,16 +190,12 @@ fn check_unop(cx: &EarlyContext<'_>, expr: &Expr) { SUSPICIOUS_UNARY_OP_FORMATTING, eqop_span, &format!( - "by not having a space between `{binop}` and `{unop}` it looks like \ - `{binop}{unop}` is a single operator", - binop = binop_str, - unop = unop_str + "by not having a space between `{binop_str}` and `{unop_str}` it looks like \ + `{binop_str}{unop_str}` is a single operator" ), None, &format!( - "put a space between `{binop}` and `{unop}` and remove the space after `{unop}`", - binop = binop_str, - unop = unop_str + "put a space between `{binop_str}` and `{unop_str}` and remove the space after `{unop_str}`" ), ); } @@ -246,12 +241,11 @@ fn check_else(cx: &EarlyContext<'_>, expr: &Expr) { cx, SUSPICIOUS_ELSE_FORMATTING, else_span, - &format!("this is an `else {}` but the formatting might hide it", else_desc), + &format!("this is an `else {else_desc}` but the formatting might hide it"), None, &format!( "to remove this lint, remove the `else` or remove the new line between \ - `else` and `{}`", - else_desc, + `else` and `{else_desc}`", ), ); } @@ -320,11 +314,10 @@ fn check_missing_else(cx: &EarlyContext<'_>, first: &Expr, second: &Expr) { cx, SUSPICIOUS_ELSE_FORMATTING, else_span, - &format!("this looks like {} but the `else` is missing", looks_like), + &format!("this looks like {looks_like} but the `else` is missing"), None, &format!( - "to remove this lint, add the missing `else` or add a new line before {}", - next_thing, + "to remove this lint, add the missing `else` or add a new line before {next_thing}", ), ); } diff --git a/clippy_lints/src/from_str_radix_10.rs b/clippy_lints/src/from_str_radix_10.rs index 74941d817be3..cf8b7acd66d2 100644 --- a/clippy_lints/src/from_str_radix_10.rs +++ b/clippy_lints/src/from_str_radix_10.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_integer_literal; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; @@ -60,8 +61,7 @@ impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 { if pathseg.ident.name.as_str() == "from_str_radix"; // check if the second argument is a primitive `10` - if let ExprKind::Lit(lit) = &radix.kind; - if let rustc_ast::ast::LitKind::Int(10, _) = lit.node; + if is_integer_literal(radix, 10); then { let expr = if let ExprKind::AddrOf(_, _, expr) = &src.kind { @@ -88,7 +88,7 @@ impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 { exp.span, "this call to `from_str_radix` can be replaced with a call to `str::parse`", "try", - format!("{}.parse::<{}>()", sugg, prim_ty.name_str()), + format!("{sugg}.parse::<{}>()", prim_ty.name_str()), Applicability::MaybeIncorrect ); } diff --git a/clippy_lints/src/functions/must_use.rs b/clippy_lints/src/functions/must_use.rs index d6d33bda1738..d263804f32cf 100644 --- a/clippy_lints/src/functions/must_use.rs +++ b/clippy_lints/src/functions/must_use.rs @@ -1,7 +1,7 @@ use rustc_ast::ast::Attribute; use rustc_errors::Applicability; use rustc_hir::def_id::{DefIdSet, LocalDefId}; -use rustc_hir::{self as hir, def::Res, intravisit, QPath}; +use rustc_hir::{self as hir, def::Res, QPath}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::{ lint::in_external_macro, @@ -13,8 +13,11 @@ use clippy_utils::attrs::is_proc_macro; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; use clippy_utils::source::snippet_opt; use clippy_utils::ty::is_must_use_ty; +use clippy_utils::visitors::for_each_expr; use clippy_utils::{match_def_path, return_ty, trait_ref_of_method}; +use core::ops::ControlFlow; + use super::{DOUBLE_MUST_USE, MUST_USE_CANDIDATE, MUST_USE_UNIT}; pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { @@ -47,7 +50,8 @@ pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Imp let attr = cx.tcx.get_attr(item.def_id.to_def_id(), sym::must_use); if let Some(attr) = attr { check_needless_must_use(cx, sig.decl, item.hir_id(), item.span, fn_header_span, attr); - } else if is_public && !is_proc_macro(cx.sess(), attrs) && trait_ref_of_method(cx, item.def_id.def_id).is_none() { + } else if is_public && !is_proc_macro(cx.sess(), attrs) && trait_ref_of_method(cx, item.def_id.def_id).is_none() + { check_must_use_candidate( cx, sig.decl, @@ -143,7 +147,7 @@ fn check_must_use_candidate<'tcx>( diag.span_suggestion( fn_span, "add the attribute", - format!("#[must_use] {}", snippet), + format!("#[must_use] {snippet}"), Applicability::MachineApplicable, ); } @@ -199,63 +203,6 @@ fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span, tys: &m } } -struct StaticMutVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - mutates_static: bool, -} - -impl<'a, 'tcx> intravisit::Visitor<'tcx> for StaticMutVisitor<'a, 'tcx> { - fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { - use hir::ExprKind::{AddrOf, Assign, AssignOp, Call, MethodCall}; - - if self.mutates_static { - return; - } - match expr.kind { - Call(_, args) => { - let mut tys = DefIdSet::default(); - for arg in args { - if self.cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id()) - && is_mutable_ty( - self.cx, - self.cx.tcx.typeck(arg.hir_id.owner.def_id).expr_ty(arg), - arg.span, - &mut tys, - ) - && is_mutated_static(arg) - { - self.mutates_static = true; - return; - } - tys.clear(); - } - }, - MethodCall(_, receiver, args, _) => { - let mut tys = DefIdSet::default(); - for arg in std::iter::once(receiver).chain(args.iter()) { - if self.cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id()) - && is_mutable_ty( - self.cx, - self.cx.tcx.typeck(arg.hir_id.owner.def_id).expr_ty(arg), - arg.span, - &mut tys, - ) - && is_mutated_static(arg) - { - self.mutates_static = true; - return; - } - tys.clear(); - } - }, - Assign(target, ..) | AssignOp(_, target, _) | AddrOf(_, hir::Mutability::Mut, target) => { - self.mutates_static |= is_mutated_static(target); - }, - _ => {}, - } - } -} - fn is_mutated_static(e: &hir::Expr<'_>) -> bool { use hir::ExprKind::{Field, Index, Path}; @@ -268,10 +215,53 @@ fn is_mutated_static(e: &hir::Expr<'_>) -> bool { } fn mutates_static<'tcx>(cx: &LateContext<'tcx>, body: &'tcx hir::Body<'_>) -> bool { - let mut v = StaticMutVisitor { - cx, - mutates_static: false, - }; - intravisit::walk_expr(&mut v, body.value); - v.mutates_static + for_each_expr(body.value, |e| { + use hir::ExprKind::{AddrOf, Assign, AssignOp, Call, MethodCall}; + + match e.kind { + Call(_, args) => { + let mut tys = DefIdSet::default(); + for arg in args { + if cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id()) + && is_mutable_ty( + cx, + cx.tcx.typeck(arg.hir_id.owner.def_id).expr_ty(arg), + arg.span, + &mut tys, + ) + && is_mutated_static(arg) + { + return ControlFlow::Break(()); + } + tys.clear(); + } + ControlFlow::Continue(()) + }, + MethodCall(_, receiver, args, _) => { + let mut tys = DefIdSet::default(); + for arg in std::iter::once(receiver).chain(args.iter()) { + if cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id()) + && is_mutable_ty( + cx, + cx.tcx.typeck(arg.hir_id.owner.def_id).expr_ty(arg), + arg.span, + &mut tys, + ) + && is_mutated_static(arg) + { + return ControlFlow::Break(()); + } + tys.clear(); + } + ControlFlow::Continue(()) + }, + Assign(target, ..) | AssignOp(_, target, _) | AddrOf(_, hir::Mutability::Mut, target) + if is_mutated_static(target) => + { + ControlFlow::Break(()) + }, + _ => ControlFlow::Continue(()), + } + }) + .is_some() } 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 0b50431fbaab..b7595d101e0f 100644 --- a/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs +++ b/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs @@ -5,8 +5,11 @@ use rustc_span::def_id::LocalDefId; use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::type_is_unsafe_function; +use clippy_utils::visitors::for_each_expr_with_closures; use clippy_utils::{iter_input_pats, path_to_local}; +use core::ops::ControlFlow; + use super::NOT_UNSAFE_PTR_ARG_DEREF; pub(super) fn check_fn<'tcx>( @@ -39,21 +42,34 @@ fn check_raw_ptr<'tcx>( body: &'tcx hir::Body<'tcx>, def_id: LocalDefId, ) { - let expr = &body.value; if unsafety == hir::Unsafety::Normal && cx.access_levels.is_exported(def_id) { let raw_ptrs = iter_input_pats(decl, body) .filter_map(|arg| raw_ptr_arg(cx, arg)) .collect::(); if !raw_ptrs.is_empty() { - let typeck_results = cx.tcx.typeck_body(body.id()); - let mut v = DerefVisitor { - cx, - ptrs: raw_ptrs, - typeck_results, - }; - - intravisit::walk_expr(&mut v, expr); + let typeck = cx.tcx.typeck_body(body.id()); + let _: Option = for_each_expr_with_closures(cx, body.value, |e| { + match e.kind { + hir::ExprKind::Call(f, args) if type_is_unsafe_function(cx, typeck.expr_ty(f)) => { + for arg in args { + check_arg(cx, &raw_ptrs, arg); + } + }, + hir::ExprKind::MethodCall(_, recv, args, _) => { + let def_id = typeck.type_dependent_def_id(e.hir_id).unwrap(); + if cx.tcx.fn_sig(def_id).skip_binder().unsafety == hir::Unsafety::Unsafe { + check_arg(cx, &raw_ptrs, recv); + for arg in args { + check_arg(cx, &raw_ptrs, arg); + } + } + }, + hir::ExprKind::Unary(hir::UnOp::Deref, ptr) => check_arg(cx, &raw_ptrs, ptr), + _ => (), + } + ControlFlow::Continue(()) + }); } } } @@ -70,54 +86,13 @@ fn raw_ptr_arg(cx: &LateContext<'_>, arg: &hir::Param<'_>) -> Option } } -struct DerefVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - ptrs: HirIdSet, - typeck_results: &'a ty::TypeckResults<'tcx>, -} - -impl<'a, 'tcx> intravisit::Visitor<'tcx> for DerefVisitor<'a, 'tcx> { - fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { - match expr.kind { - hir::ExprKind::Call(f, args) => { - let ty = self.typeck_results.expr_ty(f); - - if type_is_unsafe_function(self.cx, ty) { - for arg in args { - self.check_arg(arg); - } - } - }, - hir::ExprKind::MethodCall(_, receiver, args, _) => { - let def_id = self.typeck_results.type_dependent_def_id(expr.hir_id).unwrap(); - let base_type = self.cx.tcx.type_of(def_id); - - if type_is_unsafe_function(self.cx, base_type) { - self.check_arg(receiver); - for arg in args { - self.check_arg(arg); - } - } - }, - hir::ExprKind::Unary(hir::UnOp::Deref, ptr) => self.check_arg(ptr), - _ => (), - } - - intravisit::walk_expr(self, expr); - } -} - -impl<'a, 'tcx> DerefVisitor<'a, 'tcx> { - fn check_arg(&self, ptr: &hir::Expr<'_>) { - if let Some(id) = path_to_local(ptr) { - if self.ptrs.contains(&id) { - span_lint( - self.cx, - NOT_UNSAFE_PTR_ARG_DEREF, - ptr.span, - "this public function might dereference a raw pointer but is not marked `unsafe`", - ); - } - } +fn check_arg(cx: &LateContext<'_>, raw_ptrs: &HirIdSet, arg: &hir::Expr<'_>) { + if path_to_local(arg).map_or(false, |id| raw_ptrs.contains(&id)) { + span_lint( + cx, + NOT_UNSAFE_PTR_ARG_DEREF, + arg.span, + "this public function might dereference a raw pointer but is not marked `unsafe`", + ); } } diff --git a/clippy_lints/src/functions/too_many_arguments.rs b/clippy_lints/src/functions/too_many_arguments.rs index 5c8d8b8e7552..1e08922a6166 100644 --- a/clippy_lints/src/functions/too_many_arguments.rs +++ b/clippy_lints/src/functions/too_many_arguments.rs @@ -59,10 +59,7 @@ fn check_arg_number(cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, fn_span: Span, cx, TOO_MANY_ARGUMENTS, fn_span, - &format!( - "this function has too many arguments ({}/{})", - args, too_many_arguments_threshold - ), + &format!("this function has too many arguments ({args}/{too_many_arguments_threshold})"), ); } } diff --git a/clippy_lints/src/functions/too_many_lines.rs b/clippy_lints/src/functions/too_many_lines.rs index 54bdea7ea25d..f83f8b40f94b 100644 --- a/clippy_lints/src/functions/too_many_lines.rs +++ b/clippy_lints/src/functions/too_many_lines.rs @@ -78,10 +78,7 @@ pub(super) fn check_fn( cx, TOO_MANY_LINES, span, - &format!( - "this function has too many lines ({}/{})", - line_count, too_many_lines_threshold - ), + &format!("this function has too many lines ({line_count}/{too_many_lines_threshold})"), ); } } diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs index 11c43247868c..0d6718c168a5 100644 --- a/clippy_lints/src/if_then_some_else_none.rs +++ b/clippy_lints/src/if_then_some_else_none.rs @@ -1,7 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::source::snippet_with_macro_callsite; -use clippy_utils::{contains_return, higher, is_else_clause, is_lang_ctor, meets_msrv, msrvs, peel_blocks}; +use clippy_utils::{ + contains_return, higher, is_else_clause, is_res_lang_ctor, meets_msrv, msrvs, path_res, peel_blocks, +}; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::{Expr, ExprKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -76,15 +78,13 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { && let ExprKind::Block(then_block, _) = then.kind && let Some(then_expr) = then_block.expr && let ExprKind::Call(then_call, [then_arg]) = then_expr.kind - && let ExprKind::Path(ref then_call_qpath) = then_call.kind - && is_lang_ctor(cx, then_call_qpath, OptionSome) - && let ExprKind::Path(ref qpath) = peel_blocks(els).kind - && is_lang_ctor(cx, qpath, OptionNone) + && is_res_lang_ctor(cx, path_res(cx, then_call), OptionSome) + && is_res_lang_ctor(cx, path_res(cx, peel_blocks(els)), OptionNone) && !stmts_contains_early_return(then_block.stmts) { let cond_snip = snippet_with_macro_callsite(cx, cond.span, "[condition]"); let cond_snip = if matches!(cond.kind, ExprKind::Unary(_, _) | ExprKind::Binary(_, _, _)) { - format!("({})", cond_snip) + format!("({cond_snip})") } else { cond_snip.into_owned() }; @@ -92,7 +92,7 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { let mut method_body = if then_block.stmts.is_empty() { arg_snip.into_owned() } else { - format!("{{ /* snippet */ {} }}", arg_snip) + format!("{{ /* snippet */ {arg_snip} }}") }; let method_name = if switch_to_eager_eval(cx, expr) && meets_msrv(self.msrv, msrvs::BOOL_THEN_SOME) { "then_some" @@ -102,14 +102,13 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { }; let help = format!( - "consider using `bool::{}` like: `{}.{}({})`", - method_name, cond_snip, method_name, method_body, + "consider using `bool::{method_name}` like: `{cond_snip}.{method_name}({method_body})`", ); span_lint_and_help( cx, IF_THEN_SOME_ELSE_NONE, expr.span, - &format!("this could be simplified with `bool::{}`", method_name), + &format!("this could be simplified with `bool::{method_name}`"), None, &help, ); diff --git a/clippy_lints/src/implicit_hasher.rs b/clippy_lints/src/implicit_hasher.rs index a920c3bba2ae..93efe957b1dc 100644 --- a/clippy_lints/src/implicit_hasher.rs +++ b/clippy_lints/src/implicit_hasher.rs @@ -5,6 +5,7 @@ use rustc_errors::Diagnostic; use rustc_hir as hir; use rustc_hir::intravisit::{walk_body, walk_expr, walk_inf, walk_ty, Visitor}; use rustc_hir::{Body, Expr, ExprKind, GenericArg, Item, ItemKind, QPath, TyKind}; +use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::nested_filter; use rustc_middle::lint::in_external_macro; @@ -12,7 +13,6 @@ use rustc_middle::ty::{Ty, TypeckResults}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::symbol::sym; -use rustc_hir_analysis::hir_ty_to_ty; use if_chain::if_chain; @@ -89,8 +89,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher { ( generics_suggestion_span, format!( - "<{}{}S: ::std::hash::BuildHasher{}>", - generics_snip, + "<{generics_snip}{}S: ::std::hash::BuildHasher{}>", if generics_snip.is_empty() { "" } else { ", " }, if vis.suggestions.is_empty() { "" @@ -263,8 +262,8 @@ impl<'tcx> ImplicitHasherType<'tcx> { fn type_arguments(&self) -> String { match *self { - ImplicitHasherType::HashMap(.., ref k, ref v) => format!("{}, {}", k, v), - ImplicitHasherType::HashSet(.., ref t) => format!("{}", t), + ImplicitHasherType::HashMap(.., ref k, ref v) => format!("{k}, {v}"), + ImplicitHasherType::HashSet(.., ref t) => format!("{t}"), } } diff --git a/clippy_lints/src/implicit_return.rs b/clippy_lints/src/implicit_return.rs index feec8ec2e23f..946d04eff6f9 100644 --- a/clippy_lints/src/implicit_return.rs +++ b/clippy_lints/src/implicit_return.rs @@ -2,10 +2,11 @@ use clippy_utils::{ diagnostics::span_lint_hir_and_then, get_async_fn_body, is_async_fn, source::{snippet_with_applicability, snippet_with_context, walk_span_to_context}, - visitors::expr_visitor_no_bodies, + visitors::for_each_expr, }; +use core::ops::ControlFlow; use rustc_errors::Applicability; -use rustc_hir::intravisit::{FnKind, Visitor}; +use rustc_hir::intravisit::FnKind; use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, FnRetTy, HirId}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -53,7 +54,7 @@ fn lint_return(cx: &LateContext<'_>, emission_place: HirId, span: Span) { span, "missing `return` statement", |diag| { - diag.span_suggestion(span, "add `return` as shown", format!("return {}", snip), app); + diag.span_suggestion(span, "add `return` as shown", format!("return {snip}"), app); }, ); } @@ -71,7 +72,7 @@ fn lint_break(cx: &LateContext<'_>, emission_place: HirId, break_span: Span, exp diag.span_suggestion( break_span, "change `break` to `return` as shown", - format!("return {}", snip), + format!("return {snip}"), app, ); }, @@ -152,7 +153,7 @@ fn lint_implicit_returns( ExprKind::Loop(block, ..) => { let mut add_return = false; - expr_visitor_no_bodies(|e| { + let _: Option = for_each_expr(block, |e| { if let ExprKind::Break(dest, sub_expr) = e.kind { if dest.target_id.ok() == Some(expr.hir_id) { if call_site_span.is_none() && e.span.ctxt() == ctxt { @@ -167,9 +168,8 @@ fn lint_implicit_returns( } } } - true - }) - .visit_block(block); + ControlFlow::Continue(()) + }); if add_return { #[expect(clippy::option_if_let_else)] if let Some(span) = call_site_span { diff --git a/clippy_lints/src/implicit_saturating_add.rs b/clippy_lints/src/implicit_saturating_add.rs new file mode 100644 index 000000000000..bf1351829c6a --- /dev/null +++ b/clippy_lints/src/implicit_saturating_add.rs @@ -0,0 +1,114 @@ +use clippy_utils::consts::{constant, Constant}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::get_parent_expr; +use clippy_utils::source::snippet_with_applicability; +use if_chain::if_chain; +use rustc_ast::ast::{LitIntType, LitKind}; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Block, Expr, ExprKind, Stmt, StmtKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{Int, IntTy, Ty, Uint, UintTy}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for implicit saturating addition. + /// + /// ### Why is this bad? + /// The built-in function is more readable and may be faster. + /// + /// ### Example + /// ```rust + ///let mut u:u32 = 7000; + /// + /// if u != u32::MAX { + /// u += 1; + /// } + /// ``` + /// Use instead: + /// ```rust + ///let mut u:u32 = 7000; + /// + /// u = u.saturating_add(1); + /// ``` + #[clippy::version = "1.65.0"] + pub IMPLICIT_SATURATING_ADD, + style, + "Perform saturating addition instead of implicitly checking max bound of data type" +} +declare_lint_pass!(ImplicitSaturatingAdd => [IMPLICIT_SATURATING_ADD]); + +impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingAdd { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if_chain! { + if let ExprKind::If(cond, then, None) = expr.kind; + if let ExprKind::DropTemps(expr1) = cond.kind; + if let Some((c, op_node, l)) = get_const(cx, expr1); + if let BinOpKind::Ne | BinOpKind::Lt = op_node; + if let ExprKind::Block(block, None) = then.kind; + if let Block { + stmts: + [Stmt + { kind: StmtKind::Expr(ex) | StmtKind::Semi(ex), .. }], + expr: None, ..} | + Block { stmts: [], expr: Some(ex), ..} = block; + if let ExprKind::AssignOp(op1, target, value) = ex.kind; + let ty = cx.typeck_results().expr_ty(target); + if Some(c) == get_int_max(ty); + if clippy_utils::SpanlessEq::new(cx).eq_expr(l, target); + if BinOpKind::Add == op1.node; + if let ExprKind::Lit(ref lit) = value.kind; + if let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node; + if block.expr.is_none(); + then { + let mut app = Applicability::MachineApplicable; + let code = snippet_with_applicability(cx, target.span, "_", &mut app); + let sugg = if let Some(parent) = get_parent_expr(cx, expr) && let ExprKind::If(_cond, _then, Some(else_)) = parent.kind && else_.hir_id == expr.hir_id {format!("{{{code} = {code}.saturating_add(1); }}")} else {format!("{code} = {code}.saturating_add(1);")}; + span_lint_and_sugg(cx, IMPLICIT_SATURATING_ADD, expr.span, "manual saturating add detected", "use instead", sugg, app); + } + } + } +} + +fn get_int_max(ty: Ty<'_>) -> Option { + match ty.peel_refs().kind() { + Int(IntTy::I8) => i8::max_value().try_into().ok(), + Int(IntTy::I16) => i16::max_value().try_into().ok(), + Int(IntTy::I32) => i32::max_value().try_into().ok(), + Int(IntTy::I64) => i64::max_value().try_into().ok(), + Int(IntTy::I128) => i128::max_value().try_into().ok(), + Int(IntTy::Isize) => isize::max_value().try_into().ok(), + Uint(UintTy::U8) => u8::max_value().try_into().ok(), + Uint(UintTy::U16) => u16::max_value().try_into().ok(), + Uint(UintTy::U32) => u32::max_value().try_into().ok(), + Uint(UintTy::U64) => u64::max_value().try_into().ok(), + Uint(UintTy::U128) => Some(u128::max_value()), + Uint(UintTy::Usize) => usize::max_value().try_into().ok(), + _ => None, + } +} + +fn get_const<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<(u128, BinOpKind, &'tcx Expr<'tcx>)> { + if let ExprKind::Binary(op, l, r) = expr.kind { + let tr = cx.typeck_results(); + if let Some((Constant::Int(c), _)) = constant(cx, tr, r) { + return Some((c, op.node, l)); + }; + if let Some((Constant::Int(c), _)) = constant(cx, tr, l) { + return Some((c, invert_op(op.node)?, r)); + } + } + None +} + +fn invert_op(op: BinOpKind) -> Option { + use rustc_hir::BinOpKind::{Ge, Gt, Le, Lt, Ne}; + match op { + Lt => Some(Gt), + Le => Some(Ge), + Ne => Some(Ne), + Ge => Some(Le), + Gt => Some(Lt), + _ => None, + } +} diff --git a/clippy_lints/src/implicit_saturating_sub.rs b/clippy_lints/src/implicit_saturating_sub.rs index 46654bc61e0f..48edbf6ae576 100644 --- a/clippy_lints/src/implicit_saturating_sub.rs +++ b/clippy_lints/src/implicit_saturating_sub.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::{higher, peel_blocks_with_stmt, SpanlessEq}; +use clippy_utils::{higher, is_integer_literal, peel_blocks_with_stmt, SpanlessEq}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -131,17 +131,8 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub { fn subtracts_one<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a Expr<'a>> { match peel_blocks_with_stmt(expr).kind { ExprKind::AssignOp(ref op1, target, value) => { - if_chain! { - if BinOpKind::Sub == op1.node; - // Check if literal being subtracted is one - if let ExprKind::Lit(ref lit1) = value.kind; - if let LitKind::Int(1, _) = lit1.node; - then { - Some(target) - } else { - None - } - } + // Check if literal being subtracted is one + (BinOpKind::Sub == op1.node && is_integer_literal(value, 1)).then_some(target) }, ExprKind::Assign(target, value, _) => { if_chain! { @@ -150,8 +141,7 @@ fn subtracts_one<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a Exp if SpanlessEq::new(cx).eq_expr(left1, target); - if let ExprKind::Lit(ref lit1) = right1.kind; - if let LitKind::Int(1, _) = lit1.node; + if is_integer_literal(right1, 1); then { Some(target) } else { @@ -170,7 +160,7 @@ fn print_lint_and_sugg(cx: &LateContext<'_>, var_name: &str, expr: &Expr<'_>) { expr.span, "implicitly performing saturating subtraction", "try", - format!("{} = {}.saturating_sub({});", var_name, var_name, '1'), + format!("{var_name} = {var_name}.saturating_sub({});", '1'), Applicability::MachineApplicable, ); } diff --git a/clippy_lints/src/inconsistent_struct_constructor.rs b/clippy_lints/src/inconsistent_struct_constructor.rs index 14b22d2b50d0..e2f2d3d42e69 100644 --- a/clippy_lints/src/inconsistent_struct_constructor.rs +++ b/clippy_lints/src/inconsistent_struct_constructor.rs @@ -90,7 +90,7 @@ impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor { let mut fields_snippet = String::new(); let (last_ident, idents) = ordered_fields.split_last().unwrap(); for ident in idents { - let _ = write!(fields_snippet, "{}, ", ident); + let _ = write!(fields_snippet, "{ident}, "); } fields_snippet.push_str(&last_ident.to_string()); @@ -100,10 +100,8 @@ impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor { String::new() }; - let sugg = format!("{} {{ {}{} }}", + let sugg = format!("{} {{ {fields_snippet}{base_snippet} }}", snippet(cx, qpath.span(), ".."), - fields_snippet, - base_snippet, ); span_lint_and_sugg( diff --git a/clippy_lints/src/index_refutable_slice.rs b/clippy_lints/src/index_refutable_slice.rs index 0dd7f5bf000d..c7b5badaae51 100644 --- a/clippy_lints/src/index_refutable_slice.rs +++ b/clippy_lints/src/index_refutable_slice.rs @@ -139,14 +139,14 @@ fn lint_slice(cx: &LateContext<'_>, slice: &SliceLintInformation) { .map(|(index, _)| *index) .collect::>(); - let value_name = |index| format!("{}_{}", slice.ident.name, index); + let value_name = |index| format!("{}_{index}", slice.ident.name); if let Some(max_index) = used_indices.iter().max() { let opt_ref = if slice.needs_ref { "ref " } else { "" }; let pat_sugg_idents = (0..=*max_index) .map(|index| { if used_indices.contains(&index) { - format!("{}{}", opt_ref, value_name(index)) + format!("{opt_ref}{}", value_name(index)) } else { "_".to_string() } diff --git a/clippy_lints/src/infinite_iter.rs b/clippy_lints/src/infinite_iter.rs index 8c2c96fa105a..d1d2db27c6fc 100644 --- a/clippy_lints/src/infinite_iter.rs +++ b/clippy_lints/src/infinite_iter.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint; +use clippy_utils::higher; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; -use clippy_utils::{higher, match_def_path, path_def_id, paths}; use rustc_hir::{BorrowKind, Closure, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -168,9 +168,16 @@ fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { }, ExprKind::Block(block, _) => block.expr.as_ref().map_or(Finite, |e| is_infinite(cx, e)), ExprKind::Box(e) | ExprKind::AddrOf(BorrowKind::Ref, _, e) => is_infinite(cx, e), - ExprKind::Call(path, _) => path_def_id(cx, path) - .map_or(false, |id| match_def_path(cx, id, &paths::ITER_REPEAT)) - .into(), + ExprKind::Call(path, _) => { + if let ExprKind::Path(ref qpath) = path.kind { + cx.qpath_res(qpath, path.hir_id) + .opt_def_id() + .map_or(false, |id| cx.tcx.is_diagnostic_item(sym::iter_repeat, id)) + .into() + } else { + Finite + } + }, ExprKind::Struct(..) => higher::Range::hir(expr).map_or(false, |r| r.end.is_none()).into(), _ => Finite, } diff --git a/clippy_lints/src/inherent_to_string.rs b/clippy_lints/src/inherent_to_string.rs index cb6c2ec0fb98..676136df572b 100644 --- a/clippy_lints/src/inherent_to_string.rs +++ b/clippy_lints/src/inherent_to_string.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; -use clippy_utils::{get_trait_def_id, paths, return_ty, trait_ref_of_method}; +use clippy_utils::{return_ty, trait_ref_of_method}; use if_chain::if_chain; use rustc_hir::{GenericParamKind, ImplItem, ImplItemKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -118,7 +118,10 @@ impl<'tcx> LateLintPass<'tcx> for InherentToString { } fn show_lint(cx: &LateContext<'_>, item: &ImplItem<'_>) { - let display_trait_id = get_trait_def_id(cx, &paths::DISPLAY_TRAIT).expect("Failed to get trait ID of `Display`!"); + let display_trait_id = cx + .tcx + .get_diagnostic_item(sym::Display) + .expect("Failed to get trait ID of `Display`!"); // Get the real type of 'self' let self_type = cx.tcx.fn_sig(item.def_id).input(0); @@ -131,23 +134,19 @@ fn show_lint(cx: &LateContext<'_>, item: &ImplItem<'_>) { INHERENT_TO_STRING_SHADOW_DISPLAY, item.span, &format!( - "type `{}` implements inherent method `to_string(&self) -> String` which shadows the implementation of `Display`", - self_type + "type `{self_type}` implements inherent method `to_string(&self) -> String` which shadows the implementation of `Display`" ), None, - &format!("remove the inherent method from type `{}`", self_type), + &format!("remove the inherent method from type `{self_type}`"), ); } else { span_lint_and_help( cx, INHERENT_TO_STRING, item.span, - &format!( - "implementation of inherent method `to_string(&self) -> String` for type `{}`", - self_type - ), + &format!("implementation of inherent method `to_string(&self) -> String` for type `{self_type}`"), None, - &format!("implement trait `Display` for type `{}` instead", self_type), + &format!("implement trait `Display` for type `{self_type}` instead"), ); } } diff --git a/clippy_lints/src/inline_fn_without_body.rs b/clippy_lints/src/inline_fn_without_body.rs index dd7177e0131c..d609a5ca4d46 100644 --- a/clippy_lints/src/inline_fn_without_body.rs +++ b/clippy_lints/src/inline_fn_without_body.rs @@ -51,7 +51,7 @@ fn check_attrs(cx: &LateContext<'_>, name: Symbol, attrs: &[Attribute]) { cx, INLINE_FN_WITHOUT_BODY, attr.span, - &format!("use of `#[inline]` on trait method `{}` which has no body", name), + &format!("use of `#[inline]` on trait method `{name}` which has no body"), |diag| { diag.suggest_remove_item(cx, attr.span, "remove", Applicability::MachineApplicable); }, diff --git a/clippy_lints/src/int_plus_one.rs b/clippy_lints/src/int_plus_one.rs index 9a944def3eb2..33491da3fc5a 100644 --- a/clippy_lints/src/int_plus_one.rs +++ b/clippy_lints/src/int_plus_one.rs @@ -138,8 +138,8 @@ impl IntPlusOne { if let Some(snippet) = snippet_opt(cx, node.span) { if let Some(other_side_snippet) = snippet_opt(cx, other_side.span) { let rec = match side { - Side::Lhs => Some(format!("{} {} {}", snippet, binop_string, other_side_snippet)), - Side::Rhs => Some(format!("{} {} {}", other_side_snippet, binop_string, snippet)), + Side::Lhs => Some(format!("{snippet} {binop_string} {other_side_snippet}")), + Side::Rhs => Some(format!("{other_side_snippet} {binop_string} {snippet}")), }; return rec; } diff --git a/clippy_lints/src/iter_not_returning_iterator.rs b/clippy_lints/src/iter_not_returning_iterator.rs index 2027c23d328c..ea9f046fb973 100644 --- a/clippy_lints/src/iter_not_returning_iterator.rs +++ b/clippy_lints/src/iter_not_returning_iterator.rs @@ -80,10 +80,7 @@ fn check_sig(cx: &LateContext<'_>, name: &str, sig: &FnSig<'_>, fn_id: LocalDefI cx, ITER_NOT_RETURNING_ITERATOR, sig.span, - &format!( - "this method is named `{}` but its return type does not implement `Iterator`", - name - ), + &format!("this method is named `{name}` but its return type does not implement `Iterator`"), ); } } diff --git a/clippy_lints/src/large_const_arrays.rs b/clippy_lints/src/large_const_arrays.rs index d6eb53ae29b5..76c83ab47d09 100644 --- a/clippy_lints/src/large_const_arrays.rs +++ b/clippy_lints/src/large_const_arrays.rs @@ -2,12 +2,12 @@ use clippy_utils::diagnostics::span_lint_and_then; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind}; +use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, ConstKind}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{BytePos, Pos, Span}; -use rustc_hir_analysis::hir_ty_to_ty; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 7d15dd4cb216..3a563736fb07 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -210,7 +210,8 @@ fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, trait_items } } - if cx.access_levels.is_exported(visited_trait.def_id.def_id) && trait_items.iter().any(|i| is_named_self(cx, i, sym::len)) + if cx.access_levels.is_exported(visited_trait.def_id.def_id) + && trait_items.iter().any(|i| is_named_self(cx, i, sym::len)) { let mut current_and_super_traits = DefIdSet::default(); fill_trait_set(visited_trait.def_id.to_def_id(), &mut current_and_super_traits, cx); @@ -278,15 +279,13 @@ impl<'tcx> LenOutput<'tcx> { _ => "", }; match self { - Self::Integral => format!("expected signature: `({}self) -> bool`", self_ref), - Self::Option(_) => format!( - "expected signature: `({}self) -> bool` or `({}self) -> Option", - self_ref, self_ref - ), - Self::Result(..) => format!( - "expected signature: `({}self) -> bool` or `({}self) -> Result", - self_ref, self_ref - ), + Self::Integral => format!("expected signature: `({self_ref}self) -> bool`"), + Self::Option(_) => { + format!("expected signature: `({self_ref}self) -> bool` or `({self_ref}self) -> Option") + }, + Self::Result(..) => { + format!("expected signature: `({self_ref}self) -> bool` or `({self_ref}self) -> Result") + }, } } } @@ -326,8 +325,7 @@ fn check_for_is_empty<'tcx>( let (msg, is_empty_span, self_kind) = match is_empty { None => ( format!( - "{} `{}` has a public `len` method, but no `is_empty` method", - item_kind, + "{item_kind} `{}` has a public `len` method, but no `is_empty` method", item_name.as_str(), ), None, @@ -335,8 +333,7 @@ fn check_for_is_empty<'tcx>( ), Some(is_empty) if !cx.access_levels.is_exported(is_empty.def_id.expect_local()) => ( format!( - "{} `{}` has a public `len` method, but a private `is_empty` method", - item_kind, + "{item_kind} `{}` has a public `len` method, but a private `is_empty` method", item_name.as_str(), ), Some(cx.tcx.def_span(is_empty.def_id)), @@ -348,8 +345,7 @@ fn check_for_is_empty<'tcx>( { ( format!( - "{} `{}` has a public `len` method, but the `is_empty` method has an unexpected signature", - item_kind, + "{item_kind} `{}` has a public `len` method, but the `is_empty` method has an unexpected signature", item_name.as_str(), ), Some(cx.tcx.def_span(is_empty.def_id)), @@ -419,10 +415,9 @@ fn check_len( LEN_ZERO, span, &format!("length comparison to {}", if compare_to == 0 { "zero" } else { "one" }), - &format!("using `{}is_empty` is clearer and more explicit", op), + &format!("using `{op}is_empty` is clearer and more explicit"), format!( - "{}{}.is_empty()", - op, + "{op}{}.is_empty()", snippet_with_applicability(cx, receiver.span, "_", &mut applicability) ), applicability, @@ -439,10 +434,9 @@ fn check_empty_expr(cx: &LateContext<'_>, span: Span, lit1: &Expr<'_>, lit2: &Ex COMPARISON_TO_EMPTY, span, "comparison to empty slice", - &format!("using `{}is_empty` is clearer and more explicit", op), + &format!("using `{op}is_empty` is clearer and more explicit"), format!( - "{}{}.is_empty()", - op, + "{op}{}.is_empty()", snippet_with_applicability(cx, lit1.span, "_", &mut applicability) ), applicability, diff --git a/clippy_lints/src/let_if_seq.rs b/clippy_lints/src/let_if_seq.rs index 10fc0f4018ef..13071d64441a 100644 --- a/clippy_lints/src/let_if_seq.rs +++ b/clippy_lints/src/let_if_seq.rs @@ -106,8 +106,7 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq { // use mutably after the `if` let sug = format!( - "let {mut}{name} = if {cond} {{{then} {value} }} else {{{else} {default} }};", - mut=mutability, + "let {mutability}{name} = if {cond} {{{then} {value} }} else {{{else} {default} }};", name=ident.name, cond=snippet(cx, cond.span, "_"), then=if then.stmts.len() > 1 { " ..;" } else { "" }, diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 8718d5fa1da0..5d26e4b33601 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -21,6 +21,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(booleans::NONMINIMAL_BOOL), LintId::of(booleans::OVERLY_COMPLEX_BOOL_EXPR), LintId::of(borrow_deref_ref::BORROW_DEREF_REF), + LintId::of(box_default::BOX_DEFAULT), LintId::of(casts::CAST_ABS_TO_UNSIGNED), LintId::of(casts::CAST_ENUM_CONSTRUCTOR), LintId::of(casts::CAST_ENUM_TRUNCATION), @@ -44,7 +45,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(derivable_impls::DERIVABLE_IMPLS), LintId::of(derive::DERIVE_HASH_XOR_EQ), LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD), - LintId::of(derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ), + LintId::of(disallowed_macros::DISALLOWED_MACROS), LintId::of(disallowed_methods::DISALLOWED_METHODS), LintId::of(disallowed_names::DISALLOWED_NAMES), LintId::of(disallowed_types::DISALLOWED_TYPES), @@ -85,6 +86,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(functions::RESULT_UNIT_ERR), LintId::of(functions::TOO_MANY_ARGUMENTS), LintId::of(if_let_mutex::IF_LET_MUTEX), + LintId::of(implicit_saturating_add::IMPLICIT_SATURATING_ADD), LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING), LintId::of(infinite_iter::INFINITE_ITER), LintId::of(inherent_to_string::INHERENT_TO_STRING), @@ -125,6 +127,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(main_recursion::MAIN_RECURSION), LintId::of(manual_async_fn::MANUAL_ASYNC_FN), LintId::of(manual_bits::MANUAL_BITS), + LintId::of(manual_clamp::MANUAL_CLAMP), LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), LintId::of(manual_rem_euclid::MANUAL_REM_EUCLID), LintId::of(manual_retain::MANUAL_RETAIN), diff --git a/clippy_lints/src/lib.register_complexity.rs b/clippy_lints/src/lib.register_complexity.rs index 185189a6af5b..a58d066fa6b6 100644 --- a/clippy_lints/src/lib.register_complexity.rs +++ b/clippy_lints/src/lib.register_complexity.rs @@ -22,6 +22,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(loops::MANUAL_FLATTEN), LintId::of(loops::SINGLE_ELEMENT_LOOP), LintId::of(loops::WHILE_LET_LOOP), + LintId::of(manual_clamp::MANUAL_CLAMP), LintId::of(manual_rem_euclid::MANUAL_REM_EUCLID), LintId::of(manual_strip::MANUAL_STRIP), LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN), diff --git a/clippy_lints/src/lib.register_internal.rs b/clippy_lints/src/lib.register_internal.rs index be63646a12f5..71dfdab369b9 100644 --- a/clippy_lints/src/lib.register_internal.rs +++ b/clippy_lints/src/lib.register_internal.rs @@ -13,10 +13,10 @@ store.register_group(true, "clippy::internal", Some("clippy_internal"), vec![ LintId::of(utils::internal_lints::INVALID_CLIPPY_VERSION_ATTRIBUTE), LintId::of(utils::internal_lints::INVALID_PATHS), LintId::of(utils::internal_lints::LINT_WITHOUT_LINT_PASS), - LintId::of(utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM), LintId::of(utils::internal_lints::MISSING_CLIPPY_VERSION_ATTRIBUTE), LintId::of(utils::internal_lints::MISSING_MSRV_ATTR_IMPL), LintId::of(utils::internal_lints::OUTER_EXPN_EXPN_DATA), LintId::of(utils::internal_lints::PRODUCE_ICE), + LintId::of(utils::internal_lints::UNNECESSARY_DEF_PATH), LintId::of(utils::internal_lints::UNNECESSARY_SYMBOL_STR), ]) diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 02fcc8de5072..05d927dbea79 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -24,8 +24,6 @@ store.register_lints(&[ #[cfg(feature = "internal")] utils::internal_lints::LINT_WITHOUT_LINT_PASS, #[cfg(feature = "internal")] - utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM, - #[cfg(feature = "internal")] utils::internal_lints::MISSING_CLIPPY_VERSION_ATTRIBUTE, #[cfg(feature = "internal")] utils::internal_lints::MISSING_MSRV_ATTR_IMPL, @@ -34,6 +32,8 @@ store.register_lints(&[ #[cfg(feature = "internal")] utils::internal_lints::PRODUCE_ICE, #[cfg(feature = "internal")] + utils::internal_lints::UNNECESSARY_DEF_PATH, + #[cfg(feature = "internal")] utils::internal_lints::UNNECESSARY_SYMBOL_STR, almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE, approx_const::APPROX_CONSTANT, @@ -60,6 +60,7 @@ store.register_lints(&[ booleans::NONMINIMAL_BOOL, booleans::OVERLY_COMPLEX_BOOL_EXPR, borrow_deref_ref::BORROW_DEREF_REF, + box_default::BOX_DEFAULT, cargo::CARGO_COMMON_METADATA, cargo::MULTIPLE_CRATE_VERSIONS, cargo::NEGATIVE_FEATURE_NAMES, @@ -113,16 +114,17 @@ store.register_lints(&[ derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ, derive::EXPL_IMPL_CLONE_ON_COPY, derive::UNSAFE_DERIVE_DESERIALIZE, + disallowed_macros::DISALLOWED_MACROS, disallowed_methods::DISALLOWED_METHODS, disallowed_names::DISALLOWED_NAMES, disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS, disallowed_types::DISALLOWED_TYPES, + doc::DOC_LINK_WITH_QUOTES, doc::DOC_MARKDOWN, doc::MISSING_ERRORS_DOC, doc::MISSING_PANICS_DOC, doc::MISSING_SAFETY_DOC, doc::NEEDLESS_DOCTEST_MAIN, - doc_link_with_quotes::DOC_LINK_WITH_QUOTES, double_parens::DOUBLE_PARENS, drop_forget_ref::DROP_COPY, drop_forget_ref::DROP_NON_DROP, @@ -159,6 +161,7 @@ store.register_lints(&[ format::USELESS_FORMAT, format_args::FORMAT_IN_FORMAT_ARGS, format_args::TO_STRING_IN_FORMAT_ARGS, + format_args::UNINLINED_FORMAT_ARGS, format_impl::PRINT_IN_FORMAT_IMPL, format_impl::RECURSIVE_FORMAT_IMPL, format_push_string::FORMAT_PUSH_STRING, @@ -182,6 +185,7 @@ store.register_lints(&[ if_then_some_else_none::IF_THEN_SOME_ELSE_NONE, implicit_hasher::IMPLICIT_HASHER, implicit_return::IMPLICIT_RETURN, + implicit_saturating_add::IMPLICIT_SATURATING_ADD, implicit_saturating_sub::IMPLICIT_SATURATING_SUB, inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR, index_refutable_slice::INDEX_REFUTABLE_SLICE, @@ -243,6 +247,7 @@ store.register_lints(&[ manual_assert::MANUAL_ASSERT, manual_async_fn::MANUAL_ASYNC_FN, manual_bits::MANUAL_BITS, + manual_clamp::MANUAL_CLAMP, manual_instant_elapsed::MANUAL_INSTANT_ELAPSED, manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE, manual_rem_euclid::MANUAL_REM_EUCLID, diff --git a/clippy_lints/src/lib.register_nursery.rs b/clippy_lints/src/lib.register_nursery.rs index f1783dd9ddef..e0b4639af53e 100644 --- a/clippy_lints/src/lib.register_nursery.rs +++ b/clippy_lints/src/lib.register_nursery.rs @@ -6,6 +6,7 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ LintId::of(attrs::EMPTY_LINE_AFTER_OUTER_ATTR), LintId::of(cognitive_complexity::COGNITIVE_COMPLEXITY), LintId::of(copies::BRANCHES_SHARING_CODE), + LintId::of(derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ), LintId::of(equatable_if_let::EQUATABLE_IF_LET), LintId::of(fallible_impl_from::FALLIBLE_IMPL_FROM), LintId::of(floating_point_arithmetic::IMPRECISE_FLOPS), diff --git a/clippy_lints/src/lib.register_pedantic.rs b/clippy_lints/src/lib.register_pedantic.rs index 584ccf55e511..bc2f0beb358a 100644 --- a/clippy_lints/src/lib.register_pedantic.rs +++ b/clippy_lints/src/lib.register_pedantic.rs @@ -20,15 +20,16 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(dereference::REF_BINDING_TO_REFERENCE), LintId::of(derive::EXPL_IMPL_CLONE_ON_COPY), LintId::of(derive::UNSAFE_DERIVE_DESERIALIZE), + LintId::of(doc::DOC_LINK_WITH_QUOTES), LintId::of(doc::DOC_MARKDOWN), LintId::of(doc::MISSING_ERRORS_DOC), LintId::of(doc::MISSING_PANICS_DOC), - LintId::of(doc_link_with_quotes::DOC_LINK_WITH_QUOTES), LintId::of(empty_enum::EMPTY_ENUM), LintId::of(enum_variants::MODULE_NAME_REPETITIONS), LintId::of(eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS), LintId::of(excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS), LintId::of(excessive_bools::STRUCT_EXCESSIVE_BOOLS), + LintId::of(format_args::UNINLINED_FORMAT_ARGS), LintId::of(functions::MUST_USE_CANDIDATE), LintId::of(functions::TOO_MANY_LINES), LintId::of(if_not_else::IF_NOT_ELSE), diff --git a/clippy_lints/src/lib.register_perf.rs b/clippy_lints/src/lib.register_perf.rs index 195ce41e31e9..8e927470e02f 100644 --- a/clippy_lints/src/lib.register_perf.rs +++ b/clippy_lints/src/lib.register_perf.rs @@ -3,6 +3,7 @@ // Manual edits will be overwritten. store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![ + LintId::of(box_default::BOX_DEFAULT), LintId::of(entry::MAP_ENTRY), LintId::of(escape::BOXED_LOCAL), LintId::of(format_args::FORMAT_IN_FORMAT_ARGS), diff --git a/clippy_lints/src/lib.register_style.rs b/clippy_lints/src/lib.register_style.rs index 05d2ec2e9e1e..8e1390167dc8 100644 --- a/clippy_lints/src/lib.register_style.rs +++ b/clippy_lints/src/lib.register_style.rs @@ -15,7 +15,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT), LintId::of(default_instead_of_iter_empty::DEFAULT_INSTEAD_OF_ITER_EMPTY), LintId::of(dereference::NEEDLESS_BORROW), - LintId::of(derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ), + LintId::of(disallowed_macros::DISALLOWED_MACROS), LintId::of(disallowed_methods::DISALLOWED_METHODS), LintId::of(disallowed_names::DISALLOWED_NAMES), LintId::of(disallowed_types::DISALLOWED_TYPES), @@ -30,6 +30,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(functions::DOUBLE_MUST_USE), LintId::of(functions::MUST_USE_UNIT), LintId::of(functions::RESULT_UNIT_ERR), + LintId::of(implicit_saturating_add::IMPLICIT_SATURATING_ADD), LintId::of(inherent_to_string::INHERENT_TO_STRING), LintId::of(init_numbered_fields::INIT_NUMBERED_FIELDS), LintId::of(len_zero::COMPARISON_TO_EMPTY), diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index c3db194c4ad8..2dcefd78763b 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -31,6 +31,7 @@ extern crate rustc_data_structures; extern crate rustc_driver; extern crate rustc_errors; extern crate rustc_hir; +extern crate rustc_hir_analysis; extern crate rustc_hir_pretty; extern crate rustc_index; extern crate rustc_infer; @@ -43,7 +44,6 @@ extern crate rustc_session; extern crate rustc_span; extern crate rustc_target; extern crate rustc_trait_selection; -extern crate rustc_hir_analysis; #[macro_use] extern crate clippy_utils; @@ -180,6 +180,7 @@ mod bool_assert_comparison; mod bool_to_int_with_if; mod booleans; mod borrow_deref_ref; +mod box_default; mod cargo; mod casts; mod checked_conversions; @@ -198,12 +199,12 @@ mod default_union_representation; mod dereference; mod derivable_impls; mod derive; +mod disallowed_macros; mod disallowed_methods; mod disallowed_names; mod disallowed_script_idents; mod disallowed_types; mod doc; -mod doc_link_with_quotes; mod double_parens; mod drop_forget_ref; mod duplicate_mod; @@ -238,6 +239,7 @@ mod if_not_else; mod if_then_some_else_none; mod implicit_hasher; mod implicit_return; +mod implicit_saturating_add; mod implicit_saturating_sub; mod inconsistent_struct_constructor; mod index_refutable_slice; @@ -267,6 +269,7 @@ mod main_recursion; mod manual_assert; mod manual_async_fn; mod manual_bits; +mod manual_clamp; mod manual_instant_elapsed; mod manual_non_exhaustive; mod manual_rem_euclid; @@ -416,8 +419,7 @@ pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore, sess: &Se let msrv = conf.msrv.as_ref().and_then(|s| { parse_msrv(s, None, None).or_else(|| { sess.err(&format!( - "error reading Clippy's configuration file. `{}` is not a valid Rust version", - s + "error reading Clippy's configuration file. `{s}` is not a valid Rust version" )); None }) @@ -433,8 +435,7 @@ fn read_msrv(conf: &Conf, sess: &Session) -> Option { let clippy_msrv = conf.msrv.as_ref().and_then(|s| { parse_msrv(s, None, None).or_else(|| { sess.err(&format!( - "error reading Clippy's configuration file. `{}` is not a valid Rust version", - s + "error reading Clippy's configuration file. `{s}` is not a valid Rust version" )); None }) @@ -445,8 +446,7 @@ fn read_msrv(conf: &Conf, sess: &Session) -> Option { // if both files have an msrv, let's compare them and emit a warning if they differ if clippy_msrv != cargo_msrv { sess.warn(&format!( - "the MSRV in `clippy.toml` and `Cargo.toml` differ; using `{}` from `clippy.toml`", - clippy_msrv + "the MSRV in `clippy.toml` and `Cargo.toml` differ; using `{clippy_msrv}` from `clippy.toml`" )); } @@ -465,7 +465,7 @@ pub fn read_conf(sess: &Session) -> Conf { Ok(Some(path)) => path, Ok(None) => return Conf::default(), Err(error) => { - sess.struct_err(&format!("error finding Clippy's configuration file: {}", error)) + sess.struct_err(&format!("error finding Clippy's configuration file: {error}")) .emit(); return Conf::default(); }, @@ -535,9 +535,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(utils::internal_lints::CompilerLintFunctions::new())); store.register_late_pass(|_| Box::new(utils::internal_lints::IfChainStyle)); store.register_late_pass(|_| Box::new(utils::internal_lints::InvalidPaths)); - store.register_late_pass(|_| Box::new(utils::internal_lints::InterningDefinedSymbol::default())); - store.register_late_pass(|_| Box::new(utils::internal_lints::LintWithoutLintPass::default())); - store.register_late_pass(|_| Box::new(utils::internal_lints::MatchTypeOnDiagItem)); + store.register_late_pass(|_| Box::::default()); + store.register_late_pass(|_| Box::::default()); + store.register_late_pass(|_| Box::new(utils::internal_lints::UnnecessaryDefPath)); store.register_late_pass(|_| Box::new(utils::internal_lints::OuterExpnDataPass)); store.register_late_pass(|_| Box::new(utils::internal_lints::MsrvAttrImpl)); } @@ -629,10 +629,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: msrv, )) }); - store.register_late_pass(|_| Box::new(shadow::Shadow::default())); + store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::new(unit_types::UnitTypes)); store.register_late_pass(|_| Box::new(loops::Loops)); - store.register_late_pass(|_| Box::new(main_recursion::MainRecursion::default())); + store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::new(lifetimes::Lifetimes)); store.register_late_pass(|_| Box::new(entry::HashMapPass)); store.register_late_pass(|_| Box::new(minmax::MinMaxPass)); @@ -666,7 +666,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(format::UselessFormat)); store.register_late_pass(|_| Box::new(swap::Swap)); store.register_late_pass(|_| Box::new(overflow_check_conditional::OverflowCheckConditional)); - store.register_late_pass(|_| Box::new(new_without_default::NewWithoutDefault::default())); + store.register_late_pass(|_| Box::::default()); let disallowed_names = conf.disallowed_names.iter().cloned().collect::>(); store.register_late_pass(move |_| Box::new(disallowed_names::DisallowedNames::new(disallowed_names.clone()))); let too_many_arguments_threshold = conf.too_many_arguments_threshold; @@ -705,7 +705,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(ref_option_ref::RefOptionRef)); store.register_late_pass(|_| Box::new(infinite_iter::InfiniteIter)); store.register_late_pass(|_| Box::new(inline_fn_without_body::InlineFnWithoutBody)); - store.register_late_pass(|_| Box::new(useless_conversion::UselessConversion::default())); + store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::new(implicit_hasher::ImplicitHasher)); store.register_late_pass(|_| Box::new(fallible_impl_from::FallibleImplFrom)); store.register_late_pass(|_| Box::new(question_mark::QuestionMark)); @@ -775,7 +775,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: upper_case_acronyms_aggressive, )) }); - store.register_late_pass(|_| Box::new(default::Default::default())); + store.register_late_pass(|_| Box::::default()); store.register_late_pass(move |_| Box::new(unused_self::UnusedSelf::new(avoid_breaking_exported_api))); store.register_late_pass(|_| Box::new(mutable_debug_assertion::DebugAssertWithMutCall)); store.register_late_pass(|_| Box::new(exit::Exit)); @@ -798,7 +798,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| Box::new(option_env_unwrap::OptionEnvUnwrap)); let warn_on_all_wildcard_imports = conf.warn_on_all_wildcard_imports; store.register_late_pass(move |_| Box::new(wildcard_imports::WildcardImports::new(warn_on_all_wildcard_imports))); - store.register_late_pass(|_| Box::new(redundant_pub_crate::RedundantPubCrate::default())); + store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::new(unnamed_address::UnnamedAddress)); store.register_late_pass(move |_| Box::new(dereference::Dereferencing::new(msrv))); store.register_late_pass(|_| Box::new(option_if_let_else::OptionIfLetElse)); @@ -816,11 +816,13 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: }); let macro_matcher = conf.standard_macro_braces.iter().cloned().collect::>(); store.register_early_pass(move || Box::new(nonstandard_macro_braces::MacroBraces::new(¯o_matcher))); - store.register_late_pass(|_| Box::new(macro_use::MacroUseImports::default())); + store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::new(pattern_type_mismatch::PatternTypeMismatch)); store.register_late_pass(|_| Box::new(unwrap_in_result::UnwrapInResult)); store.register_late_pass(|_| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned)); store.register_late_pass(|_| Box::new(async_yields_async::AsyncYieldsAsync)); + let disallowed_macros = conf.disallowed_macros.clone(); + store.register_late_pass(move |_| Box::new(disallowed_macros::DisallowedMacros::new(disallowed_macros.clone()))); let disallowed_methods = conf.disallowed_methods.clone(); store.register_late_pass(move |_| Box::new(disallowed_methods::DisallowedMethods::new(disallowed_methods.clone()))); store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86AttSyntax)); @@ -829,7 +831,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(strings::StrToString)); store.register_late_pass(|_| Box::new(strings::StringToString)); store.register_late_pass(|_| Box::new(zero_sized_map_values::ZeroSizedMapValues)); - store.register_late_pass(|_| Box::new(vec_init_then_push::VecInitThenPush::default())); + store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::new(redundant_slicing::RedundantSlicing)); store.register_late_pass(|_| Box::new(from_str_radix_10::FromStrRadix10)); store.register_late_pass(move |_| Box::new(if_then_some_else_none::IfThenSomeElseNone::new(msrv))); @@ -857,7 +859,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: )) }); store.register_late_pass(move |_| Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks)); - store.register_late_pass(move |_| Box::new(format_args::FormatArgs)); + store.register_late_pass(move |_| Box::new(format_args::FormatArgs::new(msrv))); store.register_late_pass(|_| Box::new(trailing_empty_array::TrailingEmptyArray)); store.register_early_pass(|| Box::new(octal_escapes::OctalEscapes)); store.register_late_pass(|_| Box::new(needless_late_init::NeedlessLateInit)); @@ -866,8 +868,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| Box::new(single_char_lifetime_names::SingleCharLifetimeNames)); store.register_late_pass(move |_| Box::new(manual_bits::ManualBits::new(msrv))); store.register_late_pass(|_| Box::new(default_union_representation::DefaultUnionRepresentation)); - store.register_early_pass(|| Box::new(doc_link_with_quotes::DocLinkWithQuotes)); - store.register_late_pass(|_| Box::new(only_used_in_recursion::OnlyUsedInRecursion::default())); + store.register_late_pass(|_| Box::::default()); let allow_dbg_in_tests = conf.allow_dbg_in_tests; store.register_late_pass(move |_| Box::new(dbg_macro::DbgMacro::new(allow_dbg_in_tests))); let cargo_ignore_publish = conf.cargo_ignore_publish; @@ -876,7 +877,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: ignore_publish: cargo_ignore_publish, }) }); - store.register_late_pass(|_| Box::new(write::Write::default())); + store.register_late_pass(|_| Box::::default()); store.register_early_pass(|| Box::new(crate_in_macro_def::CrateInMacroDef)); store.register_early_pass(|| Box::new(empty_structs_with_brackets::EmptyStructsWithBrackets)); store.register_late_pass(|_| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings)); @@ -886,7 +887,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move |_| Box::new(large_include_file::LargeIncludeFile::new(max_include_file_size))); store.register_late_pass(|_| Box::new(strings::TrimSplitWhitespace)); store.register_late_pass(|_| Box::new(rc_clone_in_vec_init::RcCloneInVecInit)); - store.register_early_pass(|| Box::new(duplicate_mod::DuplicateMod::default())); + store.register_early_pass(|| Box::::default()); store.register_early_pass(|| Box::new(unused_rounding::UnusedRounding)); store.register_early_pass(move || Box::new(almost_complete_letter_range::AlmostCompleteLetterRange::new(msrv))); store.register_late_pass(|_| Box::new(swap_ptr_to_ref::SwapPtrToRef)); @@ -898,13 +899,16 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: let verbose_bit_mask_threshold = conf.verbose_bit_mask_threshold; store.register_late_pass(move |_| Box::new(operators::Operators::new(verbose_bit_mask_threshold))); store.register_late_pass(|_| Box::new(invalid_utf8_in_unchecked::InvalidUtf8InUnchecked)); - store.register_late_pass(|_| Box::new(std_instead_of_core::StdReexports::default())); + store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::new(manual_instant_elapsed::ManualInstantElapsed)); store.register_late_pass(|_| Box::new(partialeq_to_none::PartialeqToNone)); + store.register_late_pass(move |_| Box::new(manual_clamp::ManualClamp::new(msrv))); store.register_late_pass(|_| Box::new(manual_string_new::ManualStringNew)); store.register_late_pass(|_| Box::new(unused_peekable::UnusedPeekable)); store.register_early_pass(|| Box::new(multi_assignments::MultiAssignments)); store.register_late_pass(|_| Box::new(bool_to_int_with_if::BoolToIntWithIf)); + store.register_late_pass(|_| Box::new(box_default::BoxDefault)); + store.register_late_pass(|_| Box::new(implicit_saturating_add::ImplicitSaturatingAdd)); // 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 399a03187d99..aef253303a8f 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -9,8 +9,8 @@ use rustc_hir::intravisit::{ use rustc_hir::FnRetTy::Return; use rustc_hir::{ BareFnTy, BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, Impl, ImplItem, - ImplItemKind, Item, ItemKind, LangItem, Lifetime, LifetimeName, ParamName, PolyTraitRef, PredicateOrigin, - TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WherePredicate, + ImplItemKind, Item, ItemKind, LangItem, Lifetime, LifetimeName, ParamName, PolyTraitRef, PredicateOrigin, TraitFn, + TraitItem, TraitItemKind, Ty, TyKind, WherePredicate, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter as middle_nested_filter; @@ -276,7 +276,7 @@ fn could_use_elision<'tcx>( let mut checker = BodyLifetimeChecker { lifetimes_used_in_body: false, }; - checker.visit_expr(&body.value); + checker.visit_expr(body.value); if checker.lifetimes_used_in_body { return false; } diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index fb2104861c87..25f19b9c6e6c 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -478,7 +478,7 @@ impl DecimalLiteralRepresentation { if num_lit.radix == Radix::Decimal; if val >= u128::from(self.threshold); then { - let hex = format!("{:#X}", val); + let hex = format!("{val:#X}"); let num_lit = NumericLiteral::new(&hex, num_lit.suffix, false); let _ = Self::do_lint(num_lit.integer).map_err(|warning_type| { warning_type.display(num_lit.format(), cx, lit.span); diff --git a/clippy_lints/src/loops/explicit_counter_loop.rs b/clippy_lints/src/loops/explicit_counter_loop.rs index 8e3ab26a947f..14f223481327 100644 --- a/clippy_lints/src/loops/explicit_counter_loop.rs +++ b/clippy_lints/src/loops/explicit_counter_loop.rs @@ -44,11 +44,10 @@ pub(super) fn check<'tcx>( cx, EXPLICIT_COUNTER_LOOP, span, - &format!("the variable `{}` is used as a loop counter", name), + &format!("the variable `{name}` is used as a loop counter"), "consider using", format!( - "for ({}, {}) in {}.enumerate()", - name, + "for ({name}, {}) in {}.enumerate()", snippet_with_applicability(cx, pat.span, "item", &mut applicability), make_iterator_snippet(cx, arg, &mut applicability), ), @@ -65,24 +64,21 @@ pub(super) fn check<'tcx>( cx, EXPLICIT_COUNTER_LOOP, span, - &format!("the variable `{}` is used as a loop counter", name), + &format!("the variable `{name}` is used as a loop counter"), |diag| { diag.span_suggestion( span, "consider using", format!( - "for ({}, {}) in (0_{}..).zip({})", - name, + "for ({name}, {}) in (0_{int_name}..).zip({})", snippet_with_applicability(cx, pat.span, "item", &mut applicability), - int_name, make_iterator_snippet(cx, arg, &mut applicability), ), applicability, ); diag.note(&format!( - "`{}` is of type `{}`, making it ineligible for `Iterator::enumerate`", - name, int_name + "`{name}` is of type `{int_name}`, making it ineligible for `Iterator::enumerate`" )); }, ); diff --git a/clippy_lints/src/loops/explicit_iter_loop.rs b/clippy_lints/src/loops/explicit_iter_loop.rs index 5f5beccd030c..b1f2941622ab 100644 --- a/clippy_lints/src/loops/explicit_iter_loop.rs +++ b/clippy_lints/src/loops/explicit_iter_loop.rs @@ -41,7 +41,7 @@ pub(super) fn check(cx: &LateContext<'_>, self_arg: &Expr<'_>, arg: &Expr<'_>, m "it is more concise to loop over references to containers instead of using explicit \ iteration methods", "to write this more concisely, try", - format!("&{}{}", muta, object), + format!("&{muta}{object}"), applicability, ); } diff --git a/clippy_lints/src/loops/for_kv_map.rs b/clippy_lints/src/loops/for_kv_map.rs index bee0e1d76831..ed620460dbe6 100644 --- a/clippy_lints/src/loops/for_kv_map.rs +++ b/clippy_lints/src/loops/for_kv_map.rs @@ -38,7 +38,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, arg: &'tcx cx, FOR_KV_MAP, arg_span, - &format!("you seem to want to iterate on a map's {}s", kind), + &format!("you seem to want to iterate on a map's {kind}s"), |diag| { let map = sugg::Sugg::hir(cx, arg, "map"); multispan_sugg( @@ -46,7 +46,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, arg: &'tcx "use the corresponding method", vec![ (pat_span, snippet(cx, new_pat_span, kind).into_owned()), - (arg_span, format!("{}.{}s{}()", map.maybe_par(), kind, mutbl)), + (arg_span, format!("{}.{kind}s{mutbl}()", map.maybe_par())), ], ); }, diff --git a/clippy_lints/src/loops/manual_find.rs b/clippy_lints/src/loops/manual_find.rs index 09b2376d5c04..4bb9936e9cde 100644 --- a/clippy_lints/src/loops/manual_find.rs +++ b/clippy_lints/src/loops/manual_find.rs @@ -1,7 +1,7 @@ use super::utils::make_iterator_snippet; use super::MANUAL_FIND; use clippy_utils::{ - diagnostics::span_lint_and_then, higher, is_lang_ctor, path_res, peel_blocks_with_stmt, + diagnostics::span_lint_and_then, higher, is_res_lang_ctor, path_res, peel_blocks_with_stmt, source::snippet_with_applicability, ty::implements_trait, }; use if_chain::if_chain; @@ -30,8 +30,8 @@ pub(super) fn check<'tcx>( if let [stmt] = block.stmts; if let StmtKind::Semi(semi) = stmt.kind; if let ExprKind::Ret(Some(ret_value)) = semi.kind; - if let ExprKind::Call(Expr { kind: ExprKind::Path(ctor), .. }, [inner_ret]) = ret_value.kind; - if is_lang_ctor(cx, ctor, LangItem::OptionSome); + if let ExprKind::Call(ctor, [inner_ret]) = ret_value.kind; + if is_res_lang_ctor(cx, path_res(cx, ctor), LangItem::OptionSome); if path_res(cx, inner_ret) == Res::Local(binding_id); if let Some((last_stmt, last_ret)) = last_stmt_and_ret(cx, expr); then { @@ -143,8 +143,7 @@ fn last_stmt_and_ret<'tcx>( if let Some((_, Node::Block(block))) = parent_iter.next(); if let Some((last_stmt, last_ret)) = extract(block); if last_stmt.hir_id == node_hir; - if let ExprKind::Path(path) = &last_ret.kind; - if is_lang_ctor(cx, path, LangItem::OptionNone); + if is_res_lang_ctor(cx, path_res(cx, last_ret), LangItem::OptionNone); if let Some((_, Node::Expr(_block))) = parent_iter.next(); // This includes the function header if 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 1d6ddf4b99f7..8c27c09404b1 100644 --- a/clippy_lints/src/loops/manual_flatten.rs +++ b/clippy_lints/src/loops/manual_flatten.rs @@ -3,13 +3,13 @@ use super::MANUAL_FLATTEN; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher; use clippy_utils::visitors::is_local_used; -use clippy_utils::{is_lang_ctor, path_to_local_id, peel_blocks_with_stmt}; +use clippy_utils::{path_to_local_id, peel_blocks_with_stmt}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::LangItem::{OptionSome, ResultOk}; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Expr, Pat, PatKind}; use rustc_lint::LateContext; -use rustc_middle::ty; +use rustc_middle::ty::{self, DefIdTree}; use rustc_span::source_map::Span; /// Check for unnecessary `if let` usage in a for loop where only the `Some` or `Ok` variant of the @@ -30,15 +30,17 @@ pub(super) fn check<'tcx>( if path_to_local_id(let_expr, pat_hir_id); // Ensure the `if let` statement is for the `Some` variant of `Option` or the `Ok` variant of `Result` if let PatKind::TupleStruct(ref qpath, _, _) = let_pat.kind; - let some_ctor = is_lang_ctor(cx, qpath, OptionSome); - let ok_ctor = is_lang_ctor(cx, qpath, ResultOk); + if let Res::Def(DefKind::Ctor(..), ctor_id) = cx.qpath_res(qpath, let_pat.hir_id); + if let Some(variant_id) = cx.tcx.opt_parent(ctor_id); + let some_ctor = cx.tcx.lang_items().option_some_variant() == Some(variant_id); + let ok_ctor = cx.tcx.lang_items().result_ok_variant() == Some(variant_id); if some_ctor || ok_ctor; // Ensure expr in `if let` is not used afterwards if !is_local_used(cx, if_then, pat_hir_id); then { let if_let_type = if some_ctor { "Some" } else { "Ok" }; // Prepare the error message - let msg = format!("unnecessary `if let` since only the `{}` variant of the iterator element is used", if_let_type); + let msg = format!("unnecessary `if let` since only the `{if_let_type}` variant of the iterator element is used"); // Prepare the help message let mut applicability = Applicability::MaybeIncorrect; diff --git a/clippy_lints/src/loops/manual_memcpy.rs b/clippy_lints/src/loops/manual_memcpy.rs index 3fc569af89ec..c87fc4f90e21 100644 --- a/clippy_lints/src/loops/manual_memcpy.rs +++ b/clippy_lints/src/loops/manual_memcpy.rs @@ -177,13 +177,7 @@ fn build_manual_memcpy_suggestion<'tcx>( let dst = if dst_offset == sugg::EMPTY && dst_limit == sugg::EMPTY { dst_base_str } else { - format!( - "{}[{}..{}]", - dst_base_str, - dst_offset.maybe_par(), - dst_limit.maybe_par() - ) - .into() + format!("{dst_base_str}[{}..{}]", dst_offset.maybe_par(), dst_limit.maybe_par()).into() }; let method_str = if is_copy(cx, elem_ty) { @@ -193,10 +187,7 @@ fn build_manual_memcpy_suggestion<'tcx>( }; format!( - "{}.{}(&{}[{}..{}]);", - dst, - method_str, - src_base_str, + "{dst}.{method_str}(&{src_base_str}[{}..{}]);", src_offset.maybe_par(), src_limit.maybe_par() ) diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index 74f3bda9f43e..c0a0444485e3 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -635,7 +635,7 @@ declare_clippy_lint! { /// arr.into_iter().find(|&el| el == 1) /// } /// ``` - #[clippy::version = "1.61.0"] + #[clippy::version = "1.64.0"] pub MANUAL_FIND, complexity, "manual implementation of `Iterator::find`" diff --git a/clippy_lints/src/loops/mut_range_bound.rs b/clippy_lints/src/loops/mut_range_bound.rs index 6d585c2e45de..0ee42b61c9a5 100644 --- a/clippy_lints/src/loops/mut_range_bound.rs +++ b/clippy_lints/src/loops/mut_range_bound.rs @@ -4,11 +4,11 @@ use clippy_utils::{get_enclosing_block, higher, path_to_local}; use if_chain::if_chain; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{BindingAnnotation, Expr, ExprKind, HirId, Node, PatKind}; +use rustc_hir_analysis::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::{mir::FakeReadCause, ty}; use rustc_span::source_map::Span; -use rustc_hir_analysis::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>, body: &Expr<'_>) { if_chain! { @@ -114,7 +114,13 @@ impl<'tcx> Delegate<'tcx> for MutatePairDelegate<'_, 'tcx> { } } - fn fake_read(&mut self, _: &rustc_hir_analysis::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} + fn fake_read( + &mut self, + _: &rustc_hir_analysis::expr_use_visitor::PlaceWithHirId<'tcx>, + _: FakeReadCause, + _: HirId, + ) { + } } impl MutatePairDelegate<'_, '_> { diff --git a/clippy_lints/src/loops/needless_collect.rs b/clippy_lints/src/loops/needless_collect.rs index 6e6faa79adc9..66f9e28596e8 100644 --- a/clippy_lints/src/loops/needless_collect.rs +++ b/clippy_lints/src/loops/needless_collect.rs @@ -45,7 +45,7 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont let (arg, pred) = contains_arg .strip_prefix('&') .map_or(("&x", &*contains_arg), |s| ("x", s)); - format!("any(|{}| x == {})", arg, pred) + format!("any(|{arg}| x == {pred})") } _ => return, } @@ -141,9 +141,9 @@ impl IterFunction { IterFunctionKind::Contains(span) => { let s = snippet(cx, *span, ".."); if let Some(stripped) = s.strip_prefix('&') { - format!(".any(|x| x == {})", stripped) + format!(".any(|x| x == {stripped})") } else { - format!(".any(|x| x == *{})", s) + format!(".any(|x| x == *{s})") } }, } diff --git a/clippy_lints/src/loops/needless_range_loop.rs b/clippy_lints/src/loops/needless_range_loop.rs index 8ab640051b63..00cfc6d49f19 100644 --- a/clippy_lints/src/loops/needless_range_loop.rs +++ b/clippy_lints/src/loops/needless_range_loop.rs @@ -145,7 +145,7 @@ pub(super) fn check<'tcx>( cx, NEEDLESS_RANGE_LOOP, arg.span, - &format!("the loop variable `{}` is used to index `{}`", ident.name, indexed), + &format!("the loop variable `{}` is used to index `{indexed}`", ident.name), |diag| { multispan_sugg( diag, @@ -154,7 +154,7 @@ pub(super) fn check<'tcx>( (pat.span, format!("({}, )", ident.name)), ( arg.span, - format!("{}.{}().enumerate(){}{}", indexed, method, method_1, method_2), + format!("{indexed}.{method}().enumerate(){method_1}{method_2}"), ), ], ); @@ -162,16 +162,16 @@ pub(super) fn check<'tcx>( ); } else { let repl = if starts_at_zero && take_is_empty { - format!("&{}{}", ref_mut, indexed) + format!("&{ref_mut}{indexed}") } else { - format!("{}.{}(){}{}", indexed, method, method_1, method_2) + format!("{indexed}.{method}(){method_1}{method_2}") }; span_lint_and_then( cx, NEEDLESS_RANGE_LOOP, arg.span, - &format!("the loop variable `{}` is only used to index `{}`", ident.name, indexed), + &format!("the loop variable `{}` is only used to index `{indexed}`", ident.name), |diag| { multispan_sugg( diag, diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index 116e589cad6f..16b00ad66378 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -42,6 +42,7 @@ pub(super) fn check( } } +#[derive(Copy, Clone)] enum NeverLoopResult { // A break/return always get triggered but not necessarily for the main loop. AlwaysBreak, @@ -51,8 +52,8 @@ enum NeverLoopResult { } #[must_use] -fn absorb_break(arg: &NeverLoopResult) -> NeverLoopResult { - match *arg { +fn absorb_break(arg: NeverLoopResult) -> NeverLoopResult { + match arg { NeverLoopResult::AlwaysBreak | NeverLoopResult::Otherwise => NeverLoopResult::Otherwise, NeverLoopResult::MayContinueMainLoop => NeverLoopResult::MayContinueMainLoop, } @@ -92,19 +93,29 @@ fn combine_branches(b1: NeverLoopResult, b2: NeverLoopResult) -> NeverLoopResult } fn never_loop_block(block: &Block<'_>, main_loop_id: HirId) -> NeverLoopResult { - let mut iter = block.stmts.iter().filter_map(stmt_to_expr).chain(block.expr); + let mut iter = block + .stmts + .iter() + .filter_map(stmt_to_expr) + .chain(block.expr.map(|expr| (expr, None))); never_loop_expr_seq(&mut iter, main_loop_id) } -fn never_loop_expr_seq<'a, T: Iterator>>(es: &mut T, main_loop_id: HirId) -> NeverLoopResult { - es.map(|e| never_loop_expr(e, main_loop_id)) - .fold(NeverLoopResult::Otherwise, combine_seq) +fn never_loop_expr_seq<'a, T: Iterator, Option<&'a Block<'a>>)>>( + es: &mut T, + main_loop_id: HirId, +) -> NeverLoopResult { + es.map(|(e, els)| { + let e = never_loop_expr(e, main_loop_id); + els.map_or(e, |els| combine_branches(e, never_loop_block(els, main_loop_id))) + }) + .fold(NeverLoopResult::Otherwise, combine_seq) } -fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<&'tcx Expr<'tcx>> { +fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<(&'tcx Expr<'tcx>, Option<&'tcx Block<'tcx>>)> { match stmt.kind { - StmtKind::Semi(e, ..) | StmtKind::Expr(e, ..) => Some(e), - StmtKind::Local(local) => local.init, + StmtKind::Semi(e, ..) | StmtKind::Expr(e, ..) => Some((e, None)), + StmtKind::Local(local) => local.init.map(|init| (init, local.els)), StmtKind::Item(..) => None, } } @@ -139,7 +150,7 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { | ExprKind::Index(e1, e2) => never_loop_expr_all(&mut [e1, e2].iter().copied(), main_loop_id), ExprKind::Loop(b, _, _, _) => { // Break can come from the inner loop so remove them. - absorb_break(&never_loop_block(b, main_loop_id)) + absorb_break(never_loop_block(b, main_loop_id)) }, ExprKind::If(e, e2, e3) => { let e1 = never_loop_expr(e, main_loop_id); @@ -211,9 +222,5 @@ fn for_to_if_let_sugg(cx: &LateContext<'_>, iterator: &Expr<'_>, pat: &Pat<'_>) let pat_snippet = snippet(cx, pat.span, "_"); let iter_snippet = make_iterator_snippet(cx, iterator, &mut Applicability::Unspecified); - format!( - "if let Some({pat}) = {iter}.next()", - pat = pat_snippet, - iter = iter_snippet - ) + format!("if let Some({pat_snippet}) = {iter_snippet}.next()") } diff --git a/clippy_lints/src/loops/same_item_push.rs b/clippy_lints/src/loops/same_item_push.rs index aeefe6e33fbe..07edee46fa65 100644 --- a/clippy_lints/src/loops/same_item_push.rs +++ b/clippy_lints/src/loops/same_item_push.rs @@ -30,10 +30,7 @@ pub(super) fn check<'tcx>( vec.span, "it looks like the same item is being pushed into this Vec", None, - &format!( - "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", - item_str, vec_str, item_str - ), + &format!("try using vec![{item_str};SIZE] or {vec_str}.resize(NEW_SIZE, {item_str})"), ); } diff --git a/clippy_lints/src/loops/utils.rs b/clippy_lints/src/loops/utils.rs index f1f58db80b30..b6f4cf7bbb37 100644 --- a/clippy_lints/src/loops/utils.rs +++ b/clippy_lints/src/loops/utils.rs @@ -5,12 +5,12 @@ use rustc_ast::ast::{LitIntType, LitKind}; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, walk_local, walk_pat, walk_stmt, Visitor}; use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, HirId, HirIdMap, Local, Mutability, Pat, PatKind, Stmt}; +use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, Ty}; use rustc_span::source_map::Spanned; use rustc_span::symbol::{sym, Symbol}; -use rustc_hir_analysis::hir_ty_to_ty; use std::iter::Iterator; #[derive(Debug, PartialEq, Eq)] @@ -344,9 +344,8 @@ pub(super) fn make_iterator_snippet(cx: &LateContext<'_>, arg: &Expr<'_>, applic _ => arg, }; format!( - "{}.{}()", + "{}.{method_name}()", sugg::Sugg::hir_with_applicability(cx, caller, "_", applic_ref).maybe_par(), - method_name, ) }, _ => format!( diff --git a/clippy_lints/src/loops/while_let_on_iterator.rs b/clippy_lints/src/loops/while_let_on_iterator.rs index deb21894f36a..153f97e4e66c 100644 --- a/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/clippy_lints/src/loops/while_let_on_iterator.rs @@ -3,13 +3,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::higher; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{ - get_enclosing_loop_or_multi_call_closure, is_refutable, is_trait_method, match_def_path, paths, - visitors::is_res_used, + get_enclosing_loop_or_multi_call_closure, is_refutable, is_res_lang_ctor, is_trait_method, visitors::is_res_used, }; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, Visitor}; -use rustc_hir::{def::Res, Closure, Expr, ExprKind, HirId, Local, Mutability, PatKind, QPath, UnOp}; +use rustc_hir::{def::Res, Closure, Expr, ExprKind, HirId, LangItem, Local, Mutability, PatKind, UnOp}; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter::OnlyBodies; use rustc_middle::ty::adjustment::Adjust; @@ -19,9 +18,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { let (scrutinee_expr, iter_expr_struct, iter_expr, some_pat, loop_expr) = if_chain! { if let Some(higher::WhileLet { if_then, let_pat, let_expr }) = higher::WhileLet::hir(expr); // check for `Some(..)` pattern - if let PatKind::TupleStruct(QPath::Resolved(None, pat_path), some_pat, _) = let_pat.kind; - if let Res::Def(_, pat_did) = pat_path.res; - if match_def_path(cx, pat_did, &paths::OPTION_SOME); + if let PatKind::TupleStruct(ref pat_path, some_pat, _) = let_pat.kind; + if is_res_lang_ctor(cx, cx.qpath_res(pat_path, let_pat.hir_id), LangItem::OptionSome); // check for call to `Iterator::next` if let ExprKind::MethodCall(method_name, iter_expr, [], _) = let_expr.kind; if method_name.ident.name == sym::next; @@ -67,7 +65,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { expr.span.with_hi(scrutinee_expr.span.hi()), "this loop could be written as a `for` loop", "try", - format!("for {} in {}{}", loop_var, iterator, by_ref), + format!("for {loop_var} in {iterator}{by_ref}"), applicability, ); } diff --git a/clippy_lints/src/macro_use.rs b/clippy_lints/src/macro_use.rs index d573a1b4fbb5..594f6af76b3d 100644 --- a/clippy_lints/src/macro_use.rs +++ b/clippy_lints/src/macro_use.rs @@ -189,9 +189,9 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports { let mut suggestions = vec![]; for ((root, span, hir_id), path) in used { if path.len() == 1 { - suggestions.push((span, format!("{}::{}", root, path[0]), hir_id)); + suggestions.push((span, format!("{root}::{}", path[0]), hir_id)); } else { - suggestions.push((span, format!("{}::{{{}}}", root, path.join(", ")), hir_id)); + suggestions.push((span, format!("{root}::{{{}}}", path.join(", ")), hir_id)); } } @@ -199,7 +199,7 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports { // such as `std::prelude::v1::foo` or some other macro that expands to an import. if self.mac_refs.is_empty() { for (span, import, hir_id) in suggestions { - let help = format!("use {};", import); + let help = format!("use {import};"); span_lint_hir_and_then( cx, MACRO_USE_IMPORTS, diff --git a/clippy_lints/src/manual_assert.rs b/clippy_lints/src/manual_assert.rs index 26b53ab5d683..825ec84b4a81 100644 --- a/clippy_lints/src/manual_assert.rs +++ b/clippy_lints/src/manual_assert.rs @@ -1,7 +1,8 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use crate::rustc_lint::LintContext; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::{root_macro_call, FormatArgsExpn}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{peel_blocks_with_stmt, sugg}; +use clippy_utils::{peel_blocks_with_stmt, span_extract_comment, sugg}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; @@ -50,20 +51,36 @@ impl<'tcx> LateLintPass<'tcx> for ManualAssert { let mut applicability = Applicability::MachineApplicable; let format_args_snip = snippet_with_applicability(cx, format_args.inputs_span(), "..", &mut applicability); let cond = cond.peel_drop_temps(); + let mut comments = span_extract_comment(cx.sess().source_map(), expr.span); + if !comments.is_empty() { + comments += "\n"; + } let (cond, not) = match cond.kind { ExprKind::Unary(UnOp::Not, e) => (e, ""), _ => (cond, "!"), }; let cond_sugg = sugg::Sugg::hir_with_applicability(cx, cond, "..", &mut applicability).maybe_par(); let sugg = format!("assert!({not}{cond_sugg}, {format_args_snip});"); - span_lint_and_sugg( + // we show to the user the suggestion without the comments, but when applicating the fix, include the comments in the block + span_lint_and_then( cx, MANUAL_ASSERT, expr.span, "only a `panic!` in `if`-then statement", - "try", - sugg, - Applicability::MachineApplicable, + |diag| { + // comments can be noisy, do not show them to the user + diag.tool_only_span_suggestion( + expr.span.shrink_to_lo(), + "add comments back", + comments, + applicability); + diag.span_suggestion( + expr.span, + "try instead", + sugg, + applicability); + } + ); } } diff --git a/clippy_lints/src/manual_async_fn.rs b/clippy_lints/src/manual_async_fn.rs index 754b0e78a148..9a0a26c0991b 100644 --- a/clippy_lints/src/manual_async_fn.rs +++ b/clippy_lints/src/manual_async_fn.rs @@ -74,11 +74,11 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn { if let Some(ret_pos) = position_before_rarrow(&header_snip); if let Some((ret_sugg, ret_snip)) = suggested_ret(cx, output); then { - let help = format!("make the function `async` and {}", ret_sugg); + let help = format!("make the function `async` and {ret_sugg}"); diag.span_suggestion( header_span, &help, - format!("async {}{}", &header_snip[..ret_pos], ret_snip), + format!("async {}{ret_snip}", &header_snip[..ret_pos]), Applicability::MachineApplicable ); @@ -196,7 +196,7 @@ fn suggested_ret(cx: &LateContext<'_>, output: &Ty<'_>) -> Option<(&'static str, }, _ => { let sugg = "return the output of the future directly"; - snippet_opt(cx, output.span).map(|snip| (sugg, format!(" -> {}", snip))) + snippet_opt(cx, output.span).map(|snip| (sugg, format!(" -> {snip}"))) }, } } diff --git a/clippy_lints/src/manual_clamp.rs b/clippy_lints/src/manual_clamp.rs new file mode 100644 index 000000000000..ece4df95505c --- /dev/null +++ b/clippy_lints/src/manual_clamp.rs @@ -0,0 +1,713 @@ +use itertools::Itertools; +use rustc_errors::Diagnostic; +use rustc_hir::{ + def::Res, Arm, BinOpKind, Block, Expr, ExprKind, Guard, HirId, PatKind, PathSegment, PrimTy, QPath, StmtKind, +}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::Ty; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::{symbol::sym, Span}; +use std::ops::Deref; + +use clippy_utils::{ + diagnostics::{span_lint_and_then, span_lint_hir_and_then}, + eq_expr_value, get_trait_def_id, + higher::If, + is_diag_trait_item, is_trait_method, meets_msrv, msrvs, path_res, path_to_local_id, paths, peel_blocks, + peel_blocks_with_stmt, + sugg::Sugg, + ty::implements_trait, + visitors::is_const_evaluatable, + MaybePath, +}; +use rustc_errors::Applicability; + +declare_clippy_lint! { + /// ### What it does + /// Identifies good opportunities for a clamp function from std or core, and suggests using it. + /// + /// ### Why is this bad? + /// clamp is much shorter, easier to read, and doesn't use any control flow. + /// + /// ### Known issue(s) + /// If the clamped variable is NaN this suggestion will cause the code to propagate NaN + /// rather than returning either `max` or `min`. + /// + /// `clamp` functions will panic if `max < min`, `max.is_nan()`, or `min.is_nan()`. + /// Some may consider panicking in these situations to be desirable, but it also may + /// introduce panicking where there wasn't any before. + /// + /// ### Examples + /// ```rust + /// # let (input, min, max) = (0, -2, 1); + /// if input > max { + /// max + /// } else if input < min { + /// min + /// } else { + /// input + /// } + /// # ; + /// ``` + /// + /// ```rust + /// # let (input, min, max) = (0, -2, 1); + /// input.max(min).min(max) + /// # ; + /// ``` + /// + /// ```rust + /// # let (input, min, max) = (0, -2, 1); + /// match input { + /// x if x > max => max, + /// x if x < min => min, + /// x => x, + /// } + /// # ; + /// ``` + /// + /// ```rust + /// # let (input, min, max) = (0, -2, 1); + /// let mut x = input; + /// if x < min { x = min; } + /// if x > max { x = max; } + /// ``` + /// Use instead: + /// ```rust + /// # let (input, min, max) = (0, -2, 1); + /// input.clamp(min, max) + /// # ; + /// ``` + #[clippy::version = "1.66.0"] + pub MANUAL_CLAMP, + complexity, + "using a clamp pattern instead of the clamp function" +} +impl_lint_pass!(ManualClamp => [MANUAL_CLAMP]); + +pub struct ManualClamp { + msrv: Option, +} + +impl ManualClamp { + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} + +#[derive(Debug)] +struct ClampSuggestion<'tcx> { + params: InputMinMax<'tcx>, + span: Span, + make_assignment: Option<&'tcx Expr<'tcx>>, + hir_with_ignore_attr: Option, +} + +#[derive(Debug)] +struct InputMinMax<'tcx> { + input: &'tcx Expr<'tcx>, + min: &'tcx Expr<'tcx>, + max: &'tcx Expr<'tcx>, + is_float: bool, +} + +impl<'tcx> LateLintPass<'tcx> for ManualClamp { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if !meets_msrv(self.msrv, msrvs::CLAMP) { + return; + } + if !expr.span.from_expansion() { + let suggestion = is_if_elseif_else_pattern(cx, expr) + .or_else(|| is_max_min_pattern(cx, expr)) + .or_else(|| is_call_max_min_pattern(cx, expr)) + .or_else(|| is_match_pattern(cx, expr)) + .or_else(|| is_if_elseif_pattern(cx, expr)); + if let Some(suggestion) = suggestion { + emit_suggestion(cx, &suggestion); + } + } + } + + fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { + if !meets_msrv(self.msrv, msrvs::CLAMP) { + return; + } + for suggestion in is_two_if_pattern(cx, block) { + emit_suggestion(cx, &suggestion); + } + } + extract_msrv_attr!(LateContext); +} + +fn emit_suggestion<'tcx>(cx: &LateContext<'tcx>, suggestion: &ClampSuggestion<'tcx>) { + let ClampSuggestion { + params: InputMinMax { + input, + min, + max, + is_float, + }, + span, + make_assignment, + hir_with_ignore_attr, + } = suggestion; + let input = Sugg::hir(cx, input, "..").maybe_par(); + let min = Sugg::hir(cx, min, ".."); + let max = Sugg::hir(cx, max, ".."); + let semicolon = if make_assignment.is_some() { ";" } else { "" }; + let assignment = if let Some(assignment) = make_assignment { + let assignment = Sugg::hir(cx, assignment, ".."); + format!("{assignment} = ") + } else { + String::new() + }; + let suggestion = format!("{assignment}{input}.clamp({min}, {max}){semicolon}"); + let msg = "clamp-like pattern without using clamp function"; + let lint_builder = |d: &mut Diagnostic| { + d.span_suggestion(*span, "replace with clamp", suggestion, Applicability::MaybeIncorrect); + if *is_float { + d.note("clamp will panic if max < min, min.is_nan(), or max.is_nan()") + .note("clamp returns NaN if the input is NaN"); + } else { + d.note("clamp will panic if max < min"); + } + }; + if let Some(hir_id) = hir_with_ignore_attr { + span_lint_hir_and_then(cx, MANUAL_CLAMP, *hir_id, *span, msg, lint_builder); + } else { + span_lint_and_then(cx, MANUAL_CLAMP, *span, msg, lint_builder); + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +enum TypeClampability { + Float, + Ord, +} + +impl TypeClampability { + fn is_clampable<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option { + if ty.is_floating_point() { + Some(TypeClampability::Float) + } else if get_trait_def_id(cx, &paths::ORD).map_or(false, |id| implements_trait(cx, ty, id, &[])) { + Some(TypeClampability::Ord) + } else { + None + } + } + + fn is_float(self) -> bool { + matches!(self, TypeClampability::Float) + } +} + +/// Targets patterns like +/// +/// ``` +/// # let (input, min, max) = (0, -3, 12); +/// +/// if input < min { +/// min +/// } else if input > max { +/// max +/// } else { +/// input +/// } +/// # ; +/// ``` +fn is_if_elseif_else_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option> { + if let Some(If { + cond, + then, + r#else: Some(else_if), + }) = If::hir(expr) + && let Some(If { + cond: else_if_cond, + then: else_if_then, + r#else: Some(else_body), + }) = If::hir(peel_blocks(else_if)) + { + let params = is_clamp_meta_pattern( + cx, + &BinaryOp::new(peel_blocks(cond))?, + &BinaryOp::new(peel_blocks(else_if_cond))?, + peel_blocks(then), + peel_blocks(else_if_then), + None, + )?; + // Contents of the else should be the resolved input. + if !eq_expr_value(cx, params.input, peel_blocks(else_body)) { + return None; + } + Some(ClampSuggestion { + params, + span: expr.span, + make_assignment: None, + hir_with_ignore_attr: None, + }) + } else { + None + } +} + +/// Targets patterns like +/// +/// ``` +/// # let (input, min_value, max_value) = (0, -3, 12); +/// +/// input.max(min_value).min(max_value) +/// # ; +/// ``` +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)) + && 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)) + { + let is_float = cx.typeck_results().expr_ty_adjusted(input).is_floating_point(); + let (min, max) = match (seg_first.ident.as_str(), seg_second.ident.as_str()) { + ("min", "max") => (arg_second, arg_first), + ("max", "min") => (arg_first, arg_second), + _ => return None, + }; + Some(ClampSuggestion { + params: InputMinMax { input, min, max, is_float }, + span: expr.span, + make_assignment: None, + hir_with_ignore_attr: None, + }) + } else { + None + } +} + +/// Targets patterns like +/// +/// ``` +/// # let (input, min_value, max_value) = (0, -3, 12); +/// # use std::cmp::{max, min}; +/// min(max(input, min_value), max_value) +/// # ; +/// ``` +fn is_call_max_min_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option> { + 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) { + Some(sym::cmp_min) => Some(FunctionType::CmpMin), + Some(sym::cmp_max) => Some(FunctionType::CmpMax), + _ if is_diag_trait_item(cx, id, 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)) + }, + _ => None, + } + } + + enum FunctionType<'tcx> { + CmpMin, + CmpMax, + OrdOrFloat(&'tcx PathSegment<'tcx>), + } + + fn check<'tcx>( + cx: &LateContext<'tcx>, + outer_fn: &'tcx Expr<'tcx>, + inner_call: &'tcx Expr<'tcx>, + outer_arg: &'tcx Expr<'tcx>, + span: Span, + ) -> Option> { + if let ExprKind::Call(inner_fn, [first, second]) = &inner_call.kind + && let Some(inner_seg) = segment(cx, inner_fn) + && let Some(outer_seg) = segment(cx, outer_fn) + { + let (input, inner_arg) = match (is_const_evaluatable(cx, first), is_const_evaluatable(cx, second)) { + (true, false) => (second, first), + (false, true) => (first, second), + _ => return None, + }; + let is_float = cx.typeck_results().expr_ty_adjusted(input).is_floating_point(); + let (min, max) = match (inner_seg, outer_seg) { + (FunctionType::CmpMin, FunctionType::CmpMax) => (outer_arg, inner_arg), + (FunctionType::CmpMax, FunctionType::CmpMin) => (inner_arg, outer_arg), + (FunctionType::OrdOrFloat(first_segment), FunctionType::OrdOrFloat(second_segment)) => { + match (first_segment.ident.as_str(), second_segment.ident.as_str()) { + ("min", "max") => (outer_arg, inner_arg), + ("max", "min") => (inner_arg, outer_arg), + _ => return None, + } + } + _ => return None, + }; + Some(ClampSuggestion { + params: InputMinMax { input, min, max, is_float }, + span, + make_assignment: None, + hir_with_ignore_attr: None, + }) + } else { + None + } + } + + if let ExprKind::Call(outer_fn, [first, second]) = &expr.kind { + check(cx, outer_fn, first, second, expr.span).or_else(|| check(cx, outer_fn, second, first, expr.span)) + } else { + None + } +} + +/// Targets patterns like +/// +/// ``` +/// # let (input, min, max) = (0, -3, 12); +/// +/// match input { +/// input if input > max => max, +/// input if input < min => min, +/// input => input, +/// } +/// # ; +/// ``` +fn is_match_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option> { + if let ExprKind::Match(value, [first_arm, second_arm, last_arm], rustc_hir::MatchSource::Normal) = &expr.kind { + // Find possible min/max branches + let minmax_values = |a: &'tcx Arm<'tcx>| { + if let PatKind::Binding(_, var_hir_id, _, None) = &a.pat.kind + && let Some(Guard::If(e)) = a.guard { + Some((e, var_hir_id, a.body)) + } else { + None + } + }; + let (first, first_hir_id, first_expr) = minmax_values(first_arm)?; + let (second, second_hir_id, second_expr) = minmax_values(second_arm)?; + 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) + && last_arm.guard.is_none() + { + // Proceed as normal + } else { + return None; + } + if let Some(params) = is_clamp_meta_pattern( + cx, + &first, + &second, + first_expr, + second_expr, + Some((*first_hir_id, *second_hir_id)), + ) { + return Some(ClampSuggestion { + params: InputMinMax { + input: value, + min: params.min, + max: params.max, + is_float: params.is_float, + }, + span: expr.span, + make_assignment: None, + hir_with_ignore_attr: None, + }); + } + } + None +} + +/// Targets patterns like +/// +/// ``` +/// # let (input, min, max) = (0, -3, 12); +/// +/// let mut x = input; +/// if x < min { x = min; } +/// if x > max { x = max; } +/// ``` +fn is_two_if_pattern<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) -> Vec> { + block_stmt_with_last(block) + .tuple_windows() + .filter_map(|(maybe_set_first, maybe_set_second)| { + if let StmtKind::Expr(first_expr) = *maybe_set_first + && let StmtKind::Expr(second_expr) = *maybe_set_second + && let Some(If { cond: first_cond, then: first_then, r#else: None }) = If::hir(first_expr) + && let Some(If { cond: second_cond, then: second_then, r#else: None }) = If::hir(second_expr) + && let ExprKind::Assign( + maybe_input_first_path, + maybe_min_max_first, + _ + ) = peel_blocks_with_stmt(first_then).kind + && let ExprKind::Assign( + maybe_input_second_path, + maybe_min_max_second, + _ + ) = peel_blocks_with_stmt(second_then).kind + && eq_expr_value(cx, maybe_input_first_path, maybe_input_second_path) + && let Some(first_bin) = BinaryOp::new(first_cond) + && let Some(second_bin) = BinaryOp::new(second_cond) + && let Some(input_min_max) = is_clamp_meta_pattern( + cx, + &first_bin, + &second_bin, + maybe_min_max_first, + maybe_min_max_second, + None + ) + { + Some(ClampSuggestion { + params: InputMinMax { + input: maybe_input_first_path, + min: input_min_max.min, + max: input_min_max.max, + is_float: input_min_max.is_float, + }, + span: first_expr.span.to(second_expr.span), + make_assignment: Some(maybe_input_first_path), + hir_with_ignore_attr: Some(first_expr.hir_id()), + }) + } else { + None + } + }) + .collect() +} + +/// Targets patterns like +/// +/// ``` +/// # let (mut input, min, max) = (0, -3, 12); +/// +/// if input < min { +/// input = min; +/// } else if input > max { +/// input = max; +/// } +/// ``` +fn is_if_elseif_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option> { + if let Some(If { + cond, + then, + r#else: Some(else_if), + }) = If::hir(expr) + && let Some(If { + cond: else_if_cond, + then: else_if_then, + r#else: None, + }) = If::hir(peel_blocks(else_if)) + && let ExprKind::Assign( + maybe_input_first_path, + maybe_min_max_first, + _ + ) = peel_blocks_with_stmt(then).kind + && let ExprKind::Assign( + maybe_input_second_path, + maybe_min_max_second, + _ + ) = peel_blocks_with_stmt(else_if_then).kind + { + let params = is_clamp_meta_pattern( + cx, + &BinaryOp::new(peel_blocks(cond))?, + &BinaryOp::new(peel_blocks(else_if_cond))?, + peel_blocks(maybe_min_max_first), + peel_blocks(maybe_min_max_second), + None, + )?; + if !eq_expr_value(cx, maybe_input_first_path, maybe_input_second_path) { + return None; + } + Some(ClampSuggestion { + params, + span: expr.span, + make_assignment: Some(maybe_input_first_path), + hir_with_ignore_attr: None, + }) + } else { + None + } +} + +/// `ExprKind::Binary` but more narrowly typed +#[derive(Debug, Clone, Copy)] +struct BinaryOp<'tcx> { + op: BinOpKind, + left: &'tcx Expr<'tcx>, + right: &'tcx Expr<'tcx>, +} + +impl<'tcx> BinaryOp<'tcx> { + fn new(e: &'tcx Expr<'tcx>) -> Option> { + match &e.kind { + ExprKind::Binary(op, left, right) => Some(BinaryOp { + op: op.node, + left, + right, + }), + _ => None, + } + } + + fn flip(&self) -> Self { + Self { + op: match self.op { + BinOpKind::Le => BinOpKind::Ge, + BinOpKind::Lt => BinOpKind::Gt, + BinOpKind::Ge => BinOpKind::Le, + BinOpKind::Gt => BinOpKind::Lt, + other => other, + }, + left: self.right, + right: self.left, + } + } +} + +/// The clamp meta pattern is a pattern shared between many (but not all) patterns. +/// In summary, this pattern consists of two if statements that meet many criteria, +/// - binary operators that are one of [`>`, `<`, `>=`, `<=`]. +/// - Both binary statements must have a shared argument +/// - Which can appear on the left or right side of either statement +/// - The binary operators must define a finite range for the shared argument. To put this in +/// the terms of Rust `std` library, the following ranges are acceptable +/// - `Range` +/// - `RangeInclusive` +/// And all other range types are not accepted. For the purposes of `clamp` it's irrelevant +/// whether the range is inclusive or not, the output is the same. +/// - The result of each if statement must be equal to the argument unique to that if statement. The +/// result can not be the shared argument in either case. +fn is_clamp_meta_pattern<'tcx>( + cx: &LateContext<'tcx>, + first_bin: &BinaryOp<'tcx>, + second_bin: &BinaryOp<'tcx>, + first_expr: &'tcx Expr<'tcx>, + second_expr: &'tcx Expr<'tcx>, + // This parameters is exclusively for the match pattern. + // It exists because the variable bindings used in that pattern + // refer to the variable bound in the match arm, not the variable + // bound outside of it. Fortunately due to context we know this has to + // be the input variable, not the min or max. + input_hir_ids: Option<(HirId, HirId)>, +) -> Option> { + fn check<'tcx>( + cx: &LateContext<'tcx>, + first_bin: &BinaryOp<'tcx>, + second_bin: &BinaryOp<'tcx>, + first_expr: &'tcx Expr<'tcx>, + second_expr: &'tcx Expr<'tcx>, + input_hir_ids: Option<(HirId, HirId)>, + is_float: bool, + ) -> Option> { + match (&first_bin.op, &second_bin.op) { + (BinOpKind::Ge | BinOpKind::Gt, BinOpKind::Le | BinOpKind::Lt) => { + 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) + }, + None => eq_expr_value(cx, first_bin.left, second_bin.left), + }; + (refers_to_input + && eq_expr_value(cx, first_bin.right, first_expr) + && eq_expr_value(cx, second_bin.right, second_expr)) + .then_some(InputMinMax { + input: first_bin.left, + min, + max, + is_float, + }) + }, + _ => None, + } + } + // First filter out any expressions with side effects + let exprs = [ + first_bin.left, + first_bin.right, + second_bin.left, + second_bin.right, + first_expr, + second_expr, + ]; + let clampability = TypeClampability::is_clampable(cx, cx.typeck_results().expr_ty(first_expr))?; + let is_float = clampability.is_float(); + if exprs.iter().any(|e| peel_blocks(e).can_have_side_effects()) { + return None; + } + if !(is_ord_op(first_bin.op) && is_ord_op(second_bin.op)) { + return None; + } + let cases = [ + (*first_bin, *second_bin), + (first_bin.flip(), second_bin.flip()), + (first_bin.flip(), *second_bin), + (*first_bin, second_bin.flip()), + ]; + + cases.into_iter().find_map(|(first, second)| { + check(cx, &first, &second, first_expr, second_expr, input_hir_ids, is_float).or_else(|| { + check( + cx, + &second, + &first, + second_expr, + first_expr, + input_hir_ids.map(|(l, r)| (r, l)), + is_float, + ) + }) + }) +} + +fn block_stmt_with_last<'tcx>(block: &'tcx Block<'tcx>) -> impl Iterator> { + block + .stmts + .iter() + .map(|s| MaybeBorrowedStmtKind::Borrowed(&s.kind)) + .chain( + block + .expr + .as_ref() + .map(|e| MaybeBorrowedStmtKind::Owned(StmtKind::Expr(e))), + ) +} + +fn is_ord_op(op: BinOpKind) -> bool { + matches!(op, BinOpKind::Ge | BinOpKind::Gt | BinOpKind::Le | BinOpKind::Lt) +} + +/// Really similar to Cow, but doesn't have a `Clone` requirement. +#[derive(Debug)] +enum MaybeBorrowedStmtKind<'a> { + Borrowed(&'a StmtKind<'a>), + Owned(StmtKind<'a>), +} + +impl<'a> Clone for MaybeBorrowedStmtKind<'a> { + fn clone(&self) -> Self { + match self { + Self::Borrowed(t) => Self::Borrowed(t), + Self::Owned(StmtKind::Expr(e)) => Self::Owned(StmtKind::Expr(e)), + Self::Owned(_) => unreachable!("Owned should only ever contain a StmtKind::Expr."), + } + } +} + +impl<'a> Deref for MaybeBorrowedStmtKind<'a> { + type Target = StmtKind<'a>; + + fn deref(&self) -> &Self::Target { + match self { + Self::Borrowed(t) => t, + Self::Owned(t) => t, + } + } +} diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs index 53e7565bd33f..6a42275322b4 100644 --- a/clippy_lints/src/manual_non_exhaustive.rs +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -133,7 +133,7 @@ impl EarlyLintPass for ManualNonExhaustiveStruct { diag.span_suggestion( header_span, "add the attribute", - format!("#[non_exhaustive] {}", snippet), + format!("#[non_exhaustive] {snippet}"), Applicability::Unspecified, ); } @@ -207,7 +207,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum { diag.span_suggestion( header_span, "add the attribute", - format!("#[non_exhaustive] {}", snippet), + format!("#[non_exhaustive] {snippet}"), Applicability::Unspecified, ); } diff --git a/clippy_lints/src/manual_rem_euclid.rs b/clippy_lints/src/manual_rem_euclid.rs index 95cc6bdbd8ba..6f25a2ed8e43 100644 --- a/clippy_lints/src/manual_rem_euclid.rs +++ b/clippy_lints/src/manual_rem_euclid.rs @@ -27,7 +27,7 @@ declare_clippy_lint! { /// let x: i32 = 24; /// let rem = x.rem_euclid(4); /// ``` - #[clippy::version = "1.63.0"] + #[clippy::version = "1.64.0"] pub MANUAL_REM_EUCLID, complexity, "manually reimplementing `rem_euclid`" diff --git a/clippy_lints/src/manual_retain.rs b/clippy_lints/src/manual_retain.rs index f28c37d3dca7..3181bc86d179 100644 --- a/clippy_lints/src/manual_retain.rs +++ b/clippy_lints/src/manual_retain.rs @@ -43,7 +43,7 @@ declare_clippy_lint! { /// let mut vec = vec![0, 1, 2]; /// vec.retain(|x| x % 2 == 0); /// ``` - #[clippy::version = "1.63.0"] + #[clippy::version = "1.64.0"] pub MANUAL_RETAIN, perf, "`retain()` is simpler and the same functionalitys" @@ -92,7 +92,7 @@ fn check_into_iter( && match_def_path(cx, filter_def_id, &paths::CORE_ITER_FILTER) && let hir::ExprKind::MethodCall(_, struct_expr, [], _) = &into_iter_expr.kind && let Some(into_iter_def_id) = cx.typeck_results().type_dependent_def_id(into_iter_expr.hir_id) - && match_def_path(cx, into_iter_def_id, &paths::CORE_ITER_INTO_ITER) + && cx.tcx.lang_items().require(hir::LangItem::IntoIterIntoIter).ok() == Some(into_iter_def_id) && match_acceptable_type(cx, left_expr, msrv) && SpanlessEq::new(cx).eq_expr(left_expr, struct_expr) { suggest(cx, parent_expr, left_expr, target_expr); @@ -153,7 +153,7 @@ fn suggest(cx: &LateContext<'_>, parent_expr: &hir::Expr<'_>, left_expr: &hir::E && let [filter_params] = filter_body.params && let Some(sugg) = match filter_params.pat.kind { hir::PatKind::Binding(_, _, filter_param_ident, None) => { - Some(format!("{}.retain(|{}| {})", snippet(cx, left_expr.span, ".."), filter_param_ident, snippet(cx, filter_body.value.span, ".."))) + Some(format!("{}.retain(|{filter_param_ident}| {})", snippet(cx, left_expr.span, ".."), snippet(cx, filter_body.value.span, ".."))) }, hir::PatKind::Tuple([key_pat, value_pat], _) => { make_sugg(cx, key_pat, value_pat, left_expr, filter_body) @@ -161,7 +161,7 @@ fn suggest(cx: &LateContext<'_>, parent_expr: &hir::Expr<'_>, left_expr: &hir::E hir::PatKind::Ref(pat, _) => { match pat.kind { hir::PatKind::Binding(_, _, filter_param_ident, None) => { - Some(format!("{}.retain(|{}| {})", snippet(cx, left_expr.span, ".."), filter_param_ident, snippet(cx, filter_body.value.span, ".."))) + Some(format!("{}.retain(|{filter_param_ident}| {})", snippet(cx, left_expr.span, ".."), snippet(cx, filter_body.value.span, ".."))) }, _ => None } @@ -190,23 +190,19 @@ fn make_sugg( match (&key_pat.kind, &value_pat.kind) { (hir::PatKind::Binding(_, _, key_param_ident, None), hir::PatKind::Binding(_, _, value_param_ident, None)) => { Some(format!( - "{}.retain(|{}, &mut {}| {})", + "{}.retain(|{key_param_ident}, &mut {value_param_ident}| {})", snippet(cx, left_expr.span, ".."), - key_param_ident, - value_param_ident, snippet(cx, filter_body.value.span, "..") )) }, (hir::PatKind::Binding(_, _, key_param_ident, None), hir::PatKind::Wild) => Some(format!( - "{}.retain(|{}, _| {})", + "{}.retain(|{key_param_ident}, _| {})", snippet(cx, left_expr.span, ".."), - key_param_ident, snippet(cx, filter_body.value.span, "..") )), (hir::PatKind::Wild, hir::PatKind::Binding(_, _, value_param_ident, None)) => Some(format!( - "{}.retain(|_, &mut {}| {})", + "{}.retain(|_, &mut {value_param_ident}| {})", snippet(cx, left_expr.span, ".."), - value_param_ident, snippet(cx, filter_body.value.span, "..") )), _ => None, diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index 7941c8c9c7e3..0976940afac3 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -108,15 +108,14 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip { }; let test_span = expr.span.until(then.span); - span_lint_and_then(cx, MANUAL_STRIP, strippings[0], &format!("stripping a {} manually", kind_word), |diag| { - diag.span_note(test_span, &format!("the {} was tested here", kind_word)); + span_lint_and_then(cx, MANUAL_STRIP, strippings[0], &format!("stripping a {kind_word} manually"), |diag| { + diag.span_note(test_span, &format!("the {kind_word} was tested here")); multispan_sugg( diag, - &format!("try using the `strip_{}` method", kind_word), + &format!("try using the `strip_{kind_word}` method"), vec![(test_span, - format!("if let Some() = {}.strip_{}({}) ", + format!("if let Some() = {}.strip_{kind_word}({}) ", snippet(cx, target_arg.span, ".."), - kind_word, snippet(cx, pattern.span, "..")))] .into_iter().chain(strippings.into_iter().map(|span| (span, "".into()))), ); diff --git a/clippy_lints/src/map_unit_fn.rs b/clippy_lints/src/map_unit_fn.rs index 33d744815299..32da37a862d8 100644 --- a/clippy_lints/src/map_unit_fn.rs +++ b/clippy_lints/src/map_unit_fn.rs @@ -131,12 +131,12 @@ fn reduce_unit_expression<'a>(cx: &LateContext<'_>, expr: &'a hir::Expr<'_>) -> }, hir::ExprKind::Block(block, _) => { match (block.stmts, block.expr.as_ref()) { - (&[], Some(inner_expr)) => { + ([], Some(inner_expr)) => { // If block only contains an expression, // reduce `{ X }` to `X` reduce_unit_expression(cx, inner_expr) }, - (&[ref inner_stmt], None) => { + ([inner_stmt], None) => { // If block only contains statements, // reduce `{ X; }` to `X` or `X;` match inner_stmt.kind { @@ -194,10 +194,7 @@ fn let_binding_name(cx: &LateContext<'_>, var_arg: &hir::Expr<'_>) -> String { #[must_use] fn suggestion_msg(function_type: &str, map_type: &str) -> String { - format!( - "called `map(f)` on an `{0}` value where `f` is a {1} that returns the unit type `()`", - map_type, function_type - ) + format!("called `map(f)` on an `{map_type}` value where `f` is a {function_type} that returns the unit type `()`") } fn lint_map_unit_fn( diff --git a/clippy_lints/src/match_result_ok.rs b/clippy_lints/src/match_result_ok.rs index 8588ab1ed8db..a020282d234f 100644 --- a/clippy_lints/src/match_result_ok.rs +++ b/clippy_lints/src/match_result_ok.rs @@ -70,9 +70,7 @@ impl<'tcx> LateLintPass<'tcx> for MatchResultOk { let some_expr_string = snippet_with_applicability(cx, y[0].span, "", &mut applicability); let trimmed_ok = snippet_with_applicability(cx, let_expr.span.until(ok_path.ident.span), "", &mut applicability); let sugg = format!( - "{} let Ok({}) = {}", - ifwhile, - some_expr_string, + "{ifwhile} let Ok({some_expr_string}) = {}", trimmed_ok.trim().trim_end_matches('.'), ); span_lint_and_sugg( @@ -80,7 +78,7 @@ impl<'tcx> LateLintPass<'tcx> for MatchResultOk { MATCH_RESULT_OK, expr.span.with_hi(let_expr.span.hi()), "matching on `Some` with `ok()` is redundant", - &format!("consider matching on `Ok({})` and removing the call to `ok` instead", some_expr_string), + &format!("consider matching on `Ok({some_expr_string})` and removing the call to `ok` instead"), sugg, applicability, ); diff --git a/clippy_lints/src/matches/collapsible_match.rs b/clippy_lints/src/matches/collapsible_match.rs index 07021f1bcad8..fd14d868df34 100644 --- a/clippy_lints/src/matches/collapsible_match.rs +++ b/clippy_lints/src/matches/collapsible_match.rs @@ -1,7 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::IfLetOrMatch; use clippy_utils::visitors::is_local_used; -use clippy_utils::{is_lang_ctor, is_unit_expr, path_to_local, peel_blocks_with_stmt, peel_ref_operators, SpanlessEq}; +use clippy_utils::{ + is_res_lang_ctor, is_unit_expr, path_to_local, peel_blocks_with_stmt, peel_ref_operators, SpanlessEq, +}; use if_chain::if_chain; use rustc_errors::MultiSpan; use rustc_hir::LangItem::OptionNone; @@ -110,7 +112,7 @@ fn arm_is_wild_like(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { } match arm.pat.kind { PatKind::Binding(..) | PatKind::Wild => true, - PatKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone), + PatKind::Path(ref qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), OptionNone), _ => false, } } diff --git a/clippy_lints/src/matches/manual_map.rs b/clippy_lints/src/matches/manual_map.rs index b0198e856d5b..76f5e1c941c7 100644 --- a/clippy_lints/src/matches/manual_map.rs +++ b/clippy_lints/src/matches/manual_map.rs @@ -3,8 +3,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable, type_is_unsafe_function}; use clippy_utils::{ - can_move_expr_to_closure, is_else_clause, is_lang_ctor, is_lint_allowed, path_to_local_id, peel_blocks, - peel_hir_expr_refs, peel_hir_expr_while, CaptureKind, + can_move_expr_to_closure, 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, }; use rustc_ast::util::parser::PREC_POSTFIX; use rustc_errors::Applicability; @@ -144,7 +144,7 @@ fn check<'tcx>( let scrutinee = peel_hir_expr_refs(scrutinee).0; let (scrutinee_str, _) = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app); let scrutinee_str = if scrutinee.span.ctxt() == expr.span.ctxt() && scrutinee.precedence().order() < PREC_POSTFIX { - format!("({})", scrutinee_str) + format!("({scrutinee_str})") } else { scrutinee_str.into() }; @@ -172,9 +172,9 @@ fn check<'tcx>( }; let expr_snip = snippet_with_context(cx, some_expr.expr.span, expr_ctxt, "..", &mut app).0; if some_expr.needs_unsafe_block { - format!("|{}{}| unsafe {{ {} }}", annotation, some_binding, expr_snip) + format!("|{annotation}{some_binding}| unsafe {{ {expr_snip} }}") } else { - format!("|{}{}| {}", annotation, some_binding, expr_snip) + format!("|{annotation}{some_binding}| {expr_snip}") } } } @@ -183,9 +183,9 @@ fn check<'tcx>( let pat_snip = snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app).0; let expr_snip = snippet_with_context(cx, some_expr.expr.span, expr_ctxt, "..", &mut app).0; if some_expr.needs_unsafe_block { - format!("|{}| unsafe {{ {} }}", pat_snip, expr_snip) + format!("|{pat_snip}| unsafe {{ {expr_snip} }}") } else { - format!("|{}| {}", pat_snip, expr_snip) + format!("|{pat_snip}| {expr_snip}") } } else { // Refutable bindings and mixed reference annotations can't be handled by `map`. @@ -199,9 +199,9 @@ fn check<'tcx>( "manual implementation of `Option::map`", "try this", if else_pat.is_none() && is_else_clause(cx.tcx, expr) { - format!("{{ {}{}.map({}) }}", scrutinee_str, as_ref_str, body_str) + format!("{{ {scrutinee_str}{as_ref_str}.map({body_str}) }}") } else { - format!("{}{}.map({})", scrutinee_str, as_ref_str, body_str) + format!("{scrutinee_str}{as_ref_str}.map({body_str})") }, app, ); @@ -251,9 +251,11 @@ fn try_parse_pattern<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: Syn match pat.kind { PatKind::Wild => Some(OptionPat::Wild), PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt), - PatKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone) => Some(OptionPat::None), + PatKind::Path(ref qpath) if is_res_lang_ctor(cx, cx.qpath_res(qpath, pat.hir_id), OptionNone) => { + Some(OptionPat::None) + }, PatKind::TupleStruct(ref qpath, [pattern], _) - if is_lang_ctor(cx, qpath, OptionSome) && pat.span.ctxt() == ctxt => + if is_res_lang_ctor(cx, cx.qpath_res(qpath, pat.hir_id), OptionSome) && pat.span.ctxt() == ctxt => { Some(OptionPat::Some { pattern, ref_count }) }, @@ -272,16 +274,14 @@ fn get_some_expr<'tcx>( ) -> Option> { // TODO: Allow more complex expressions. match expr.kind { - ExprKind::Call( - Expr { - kind: ExprKind::Path(ref qpath), - .. - }, - [arg], - ) if ctxt == expr.span.ctxt() && is_lang_ctor(cx, qpath, OptionSome) => Some(SomeExpr { - expr: arg, - needs_unsafe_block, - }), + ExprKind::Call(callee, [arg]) + if ctxt == expr.span.ctxt() && is_res_lang_ctor(cx, path_res(cx, callee), OptionSome) => + { + Some(SomeExpr { + expr: arg, + needs_unsafe_block, + }) + }, ExprKind::Block( Block { stmts: [], @@ -302,5 +302,5 @@ fn get_some_expr<'tcx>( // Checks for the `None` value. fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - matches!(peel_blocks(expr).kind, ExprKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone)) + is_res_lang_ctor(cx, path_res(cx, peel_blocks(expr)), OptionNone) } diff --git a/clippy_lints/src/matches/manual_unwrap_or.rs b/clippy_lints/src/matches/manual_unwrap_or.rs index e1111c80f2fe..587c926dc01c 100644 --- a/clippy_lints/src/matches/manual_unwrap_or.rs +++ b/clippy_lints/src/matches/manual_unwrap_or.rs @@ -3,12 +3,14 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt}; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::usage::contains_return_break_continue_macro; -use clippy_utils::{is_lang_ctor, path_to_local_id, sugg}; +use clippy_utils::{is_res_lang_ctor, path_to_local_id, sugg}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::LangItem::{OptionNone, ResultErr}; use rustc_hir::{Arm, Expr, PatKind}; use rustc_lint::LateContext; +use rustc_middle::ty::DefIdTree; use rustc_span::sym; use super::MANUAL_UNWRAP_OR; @@ -42,12 +44,10 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, scrutinee: span_lint_and_sugg( cx, MANUAL_UNWRAP_OR, expr.span, - &format!("this pattern reimplements `{}::unwrap_or`", ty_name), + &format!("this pattern reimplements `{ty_name}::unwrap_or`"), "replace with", format!( - "{}.unwrap_or({})", - suggestion, - reindented_or_body, + "{suggestion}.unwrap_or({reindented_or_body})", ), Applicability::MachineApplicable, ); @@ -61,15 +61,19 @@ fn applicable_or_arm<'a>(cx: &LateContext<'_>, arms: &'a [Arm<'a>]) -> Option<&' if arms.iter().all(|arm| arm.guard.is_none()); if let Some((idx, or_arm)) = arms.iter().enumerate().find(|(_, arm)| { match arm.pat.kind { - PatKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone), + PatKind::Path(ref qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), OptionNone), PatKind::TupleStruct(ref qpath, [pat], _) => - matches!(pat.kind, PatKind::Wild) && is_lang_ctor(cx, qpath, ResultErr), + matches!(pat.kind, PatKind::Wild) + && is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), ResultErr), _ => false, } }); let unwrap_arm = &arms[1 - idx]; if let PatKind::TupleStruct(ref qpath, [unwrap_pat], _) = unwrap_arm.pat.kind; - if is_lang_ctor(cx, qpath, OptionSome) || is_lang_ctor(cx, qpath, ResultOk); + if let Res::Def(DefKind::Ctor(..), ctor_id) = cx.qpath_res(qpath, unwrap_arm.pat.hir_id); + if let Some(variant_id) = cx.tcx.opt_parent(ctor_id); + if cx.tcx.lang_items().option_some_variant() == Some(variant_id) + || cx.tcx.lang_items().result_ok_variant() == Some(variant_id); if let PatKind::Binding(_, binding_hir_id, ..) = unwrap_pat.kind; if path_to_local_id(unwrap_arm.body, binding_hir_id); if cx.typeck_results().expr_adjustments(unwrap_arm.body).is_empty(); diff --git a/clippy_lints/src/matches/match_as_ref.rs b/clippy_lints/src/matches/match_as_ref.rs index 91d17f481e2d..2818f030b7a6 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_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{is_lang_ctor, peel_blocks}; +use clippy_utils::{is_res_lang_ctor, path_res, peel_blocks}; use rustc_errors::Applicability; use rustc_hir::{Arm, BindingAnnotation, ByRef, Expr, ExprKind, LangItem, Mutability, PatKind, QPath}; use rustc_lint::LateContext; @@ -45,13 +45,11 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: cx, MATCH_AS_REF, expr.span, - &format!("use `{}()` instead", suggestion), + &format!("use `{suggestion}()` instead"), "try this", format!( - "{}.{}(){}", + "{}.{suggestion}(){cast}", snippet_with_applicability(cx, ex.span, "_", &mut applicability), - suggestion, - cast, ), applicability, ); @@ -61,18 +59,20 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: // Checks if arm has the form `None => None` fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { - matches!(arm.pat.kind, PatKind::Path(ref qpath) if is_lang_ctor(cx, qpath, LangItem::OptionNone)) + matches!( + arm.pat.kind, + PatKind::Path(ref qpath) if is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), LangItem::OptionNone) + ) } // 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_chain! { if let PatKind::TupleStruct(ref qpath, [first_pat, ..], _) = arm.pat.kind; - if is_lang_ctor(cx, qpath, LangItem::OptionSome); + if is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), LangItem::OptionSome); if let PatKind::Binding(BindingAnnotation(ByRef::Yes, mutabl), .., ident, _) = first_pat.kind; if let ExprKind::Call(e, [arg]) = peel_blocks(arm.body).kind; - if let ExprKind::Path(ref some_path) = e.kind; - if is_lang_ctor(cx, some_path, LangItem::OptionSome); + if is_res_lang_ctor(cx, path_res(cx, e), LangItem::OptionSome); if let ExprKind::Path(QPath::Resolved(_, path2)) = arg.kind; if path2.segments.len() == 1 && ident.name == path2.segments[0].ident.name; then { diff --git a/clippy_lints/src/matches/match_like_matches.rs b/clippy_lints/src/matches/match_like_matches.rs index 34cc082687ec..107fad32393c 100644 --- a/clippy_lints/src/matches/match_like_matches.rs +++ b/clippy_lints/src/matches/match_like_matches.rs @@ -112,7 +112,7 @@ where .join(" | ") }; let pat_and_guard = if let Some(Guard::If(g)) = first_guard { - format!("{} if {}", pat, snippet_with_applicability(cx, g.span, "..", &mut applicability)) + format!("{pat} if {}", snippet_with_applicability(cx, g.span, "..", &mut applicability)) } else { pat }; @@ -131,10 +131,9 @@ where &format!("{} expression looks like `matches!` macro", if is_if_let { "if let .. else" } else { "match" }), "try this", format!( - "{}matches!({}, {})", + "{}matches!({}, {pat_and_guard})", if b0 { "" } else { "!" }, snippet_with_applicability(cx, ex_new.span, "..", &mut applicability), - pat_and_guard, ), applicability, ); diff --git a/clippy_lints/src/matches/match_same_arms.rs b/clippy_lints/src/matches/match_same_arms.rs index d37f44d4a17e..37049f835775 100644 --- a/clippy_lints/src/matches/match_same_arms.rs +++ b/clippy_lints/src/matches/match_same_arms.rs @@ -134,7 +134,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { diag.span_suggestion( keep_arm.pat.span, "try merging the arm patterns", - format!("{} | {}", keep_pat_snip, move_pat_snip), + format!("{keep_pat_snip} | {move_pat_snip}"), Applicability::MaybeIncorrect, ) .help("or try changing either arm body") diff --git a/clippy_lints/src/matches/match_single_binding.rs b/clippy_lints/src/matches/match_single_binding.rs index 5ae4a65acaf3..68682cedf1de 100644 --- a/clippy_lints/src/matches/match_single_binding.rs +++ b/clippy_lints/src/matches/match_single_binding.rs @@ -75,12 +75,11 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e Some(AssignmentExpr::Local { span, pat_span }) => ( span, format!( - "let {} = {};\n{}let {} = {};", + "let {} = {};\n{}let {} = {snippet_body};", snippet_with_applicability(cx, bind_names, "..", &mut applicability), snippet_with_applicability(cx, matched_vars, "..", &mut applicability), " ".repeat(indent_of(cx, expr.span).unwrap_or(0)), - snippet_with_applicability(cx, pat_span, "..", &mut applicability), - snippet_body + snippet_with_applicability(cx, pat_span, "..", &mut applicability) ), ), None => { @@ -110,10 +109,8 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e if ex.can_have_side_effects() { let indent = " ".repeat(indent_of(cx, expr.span).unwrap_or(0)); let sugg = format!( - "{};\n{}{}", - snippet_with_applicability(cx, ex.span, "..", &mut applicability), - indent, - snippet_body + "{};\n{indent}{snippet_body}", + snippet_with_applicability(cx, ex.span, "..", &mut applicability) ); span_lint_and_sugg( @@ -178,10 +175,10 @@ fn sugg_with_curlies<'a>( let (mut cbrace_start, mut cbrace_end) = (String::new(), String::new()); if let Some(parent_expr) = get_parent_expr(cx, match_expr) { if let ExprKind::Closure { .. } = parent_expr.kind { - cbrace_end = format!("\n{}}}", indent); + cbrace_end = format!("\n{indent}}}"); // Fix body indent due to the closure indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0)); - cbrace_start = format!("{{\n{}", indent); + cbrace_start = format!("{{\n{indent}"); } } @@ -190,10 +187,10 @@ fn sugg_with_curlies<'a>( let parent_node_id = cx.tcx.hir().get_parent_node(match_expr.hir_id); if let Node::Arm(arm) = &cx.tcx.hir().get(parent_node_id) { if let ExprKind::Match(..) = arm.body.kind { - cbrace_end = format!("\n{}}}", indent); + cbrace_end = format!("\n{indent}}}"); // Fix body indent due to the match indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0)); - cbrace_start = format!("{{\n{}", indent); + cbrace_start = format!("{{\n{indent}"); } } @@ -204,13 +201,8 @@ fn sugg_with_curlies<'a>( }); format!( - "{}let {} = {};\n{}{}{}{}", - cbrace_start, + "{cbrace_start}let {} = {};\n{indent}{assignment_str}{snippet_body}{cbrace_end}", snippet_with_applicability(cx, bind_names, "..", applicability), - snippet_with_applicability(cx, matched_vars, "..", applicability), - indent, - assignment_str, - snippet_body, - cbrace_end + snippet_with_applicability(cx, matched_vars, "..", applicability) ) } diff --git a/clippy_lints/src/matches/match_str_case_mismatch.rs b/clippy_lints/src/matches/match_str_case_mismatch.rs index 1e80b6cf2d83..6647322caa37 100644 --- a/clippy_lints/src/matches/match_str_case_mismatch.rs +++ b/clippy_lints/src/matches/match_str_case_mismatch.rs @@ -118,8 +118,8 @@ fn lint(cx: &LateContext<'_>, case_method: &CaseMethod, bad_case_span: Span, bad MATCH_STR_CASE_MISMATCH, bad_case_span, "this `match` arm has a differing case than its expression", - &format!("consider changing the case of this arm to respect `{}`", method_str), - format!("\"{}\"", suggestion), + &format!("consider changing the case of this arm to respect `{method_str}`"), + format!("\"{suggestion}\""), Applicability::MachineApplicable, ); } diff --git a/clippy_lints/src/matches/match_wild_err_arm.rs b/clippy_lints/src/matches/match_wild_err_arm.rs index a3aa2e4b389a..42f1e2629d41 100644 --- a/clippy_lints/src/matches/match_wild_err_arm.rs +++ b/clippy_lints/src/matches/match_wild_err_arm.rs @@ -38,7 +38,7 @@ pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm<' span_lint_and_note(cx, MATCH_WILD_ERR_ARM, arm.pat.span, - &format!("`Err({})` matches all errors", ident_bind_name), + &format!("`Err({ident_bind_name})` matches all errors"), None, "match each error separately or use the error output, or use `.expect(msg)` if the error case is unreachable", ); diff --git a/clippy_lints/src/matches/needless_match.rs b/clippy_lints/src/matches/needless_match.rs index 58ea43e69d9b..c4f6852aedc3 100644 --- a/clippy_lints/src/matches/needless_match.rs +++ b/clippy_lints/src/matches/needless_match.rs @@ -3,15 +3,15 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{is_type_diagnostic_item, same_type_and_consts}; use clippy_utils::{ - eq_expr_value, get_parent_expr_for_hir, get_parent_node, higher, is_else_clause, is_lang_ctor, over, + eq_expr_value, get_parent_expr_for_hir, get_parent_node, higher, is_else_clause, is_res_lang_ctor, over, path_res, peel_blocks_with_stmt, }; use rustc_errors::Applicability; use rustc_hir::LangItem::OptionNone; use rustc_hir::{Arm, BindingAnnotation, ByRef, Expr, ExprKind, FnRetTy, Guard, Node, Pat, PatKind, Path, QPath}; +use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::LateContext; use rustc_span::sym; -use rustc_hir_analysis::hir_ty_to_ty; pub(crate) fn check_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) { if arms.len() > 1 && expr_ty_matches_p_ty(cx, ex, expr) && check_all_arms(cx, ex, arms) { @@ -112,10 +112,7 @@ fn check_if_let_inner(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool let ret = strip_return(else_expr); let let_expr_ty = cx.typeck_results().expr_ty(if_let.let_expr); if is_type_diagnostic_item(cx, let_expr_ty, sym::Option) { - if let ExprKind::Path(ref qpath) = ret.kind { - return is_lang_ctor(cx, qpath, OptionNone) || eq_expr_value(cx, if_let.let_expr, ret); - } - return false; + return is_res_lang_ctor(cx, path_res(cx, ret), OptionNone) || eq_expr_value(cx, if_let.let_expr, ret); } return eq_expr_value(cx, if_let.let_expr, ret); } diff --git a/clippy_lints/src/matches/redundant_pattern_match.rs b/clippy_lints/src/matches/redundant_pattern_match.rs index c89784065b8b..81bebff34c82 100644 --- a/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/clippy_lints/src/matches/redundant_pattern_match.rs @@ -4,11 +4,12 @@ use clippy_utils::source::snippet; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{is_type_diagnostic_item, needs_ordered_drop}; use clippy_utils::visitors::any_temporaries_need_ordered_drop; -use clippy_utils::{higher, is_lang_ctor, is_trait_method}; +use clippy_utils::{higher, is_trait_method}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; -use rustc_hir::LangItem::{self, OptionSome, OptionNone, PollPending, PollReady, ResultOk, ResultErr}; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::LangItem::{self, OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk}; use rustc_hir::{Arm, Expr, ExprKind, Node, Pat, PatKind, QPath, UnOp}; use rustc_lint::LateContext; use rustc_middle::ty::{self, subst::GenericArgKind, DefIdTree, Ty}; @@ -87,15 +88,21 @@ fn find_sugg_for_if_let<'tcx>( } }, PatKind::Path(ref path) => { - let method = if is_lang_ctor(cx, path, OptionNone) { - "is_none()" - } else if is_lang_ctor(cx, path, PollPending) { - "is_pending()" + if let Res::Def(DefKind::Ctor(..), ctor_id) = cx.qpath_res(path, check_pat.hir_id) + && let Some(variant_id) = cx.tcx.opt_parent(ctor_id) + { + let method = if cx.tcx.lang_items().option_none_variant() == Some(variant_id) { + "is_none()" + } else if cx.tcx.lang_items().poll_pending_variant() == Some(variant_id) { + "is_pending()" + } else { + return; + }; + // `None` and `Pending` don't have an inner type. + (method, cx.tcx.types.unit) } else { return; - }; - // `None` and `Pending` don't have an inner type. - (method, cx.tcx.types.unit) + } }, _ => return, }; @@ -138,7 +145,7 @@ fn find_sugg_for_if_let<'tcx>( cx, REDUNDANT_PATTERN_MATCHING, let_pat.span, - &format!("redundant pattern matching, consider using `{}`", good_method), + &format!("redundant pattern matching, consider using `{good_method}`"), |diag| { // if/while let ... = ... { ... } // ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -162,7 +169,7 @@ fn find_sugg_for_if_let<'tcx>( .maybe_par() .to_string(); - diag.span_suggestion(span, "try this", format!("{} {}.{}", keyword, sugg, good_method), app); + diag.span_suggestion(span, "try this", format!("{keyword} {sugg}.{good_method}"), app); if needs_drop { diag.note("this will change drop order of the result, as well as all temporaries"); @@ -213,7 +220,6 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op if patterns.len() == 1 => { if let PatKind::Wild = patterns[0].kind { - find_good_method_for_match( cx, arms, @@ -253,12 +259,12 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op cx, REDUNDANT_PATTERN_MATCHING, expr.span, - &format!("redundant pattern matching, consider using `{}`", good_method), + &format!("redundant pattern matching, consider using `{good_method}`"), |diag| { diag.span_suggestion( span, "try this", - format!("{}.{}", snippet(cx, result_expr.span, "_"), good_method), + format!("{}.{good_method}", snippet(cx, result_expr.span, "_")), Applicability::MaybeIncorrect, // snippet ); }, @@ -269,8 +275,8 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op #[derive(Clone, Copy)] enum Item { - Lang(LangItem), - Diag(Symbol, Symbol), + Lang(LangItem), + Diag(Symbol, Symbol), } fn is_pat_variant(cx: &LateContext<'_>, pat: &Pat<'_>, path: &QPath<'_>, expected_item: Item) -> bool { @@ -285,15 +291,16 @@ fn is_pat_variant(cx: &LateContext<'_>, pat: &Pat<'_>, path: &QPath<'_>, expecte let ty = cx.typeck_results().pat_ty(pat); if is_type_diagnostic_item(cx, ty, expected_ty) { - let variant = ty.ty_adt_def() + let variant = ty + .ty_adt_def() .expect("struct pattern type is not an ADT") .variant_of_res(cx.qpath_res(path, pat.hir_id)); - return variant.name == expected_variant + return variant.name == expected_variant; } false - } + }, } } @@ -308,20 +315,16 @@ fn find_good_method_for_match<'a>( should_be_left: &'a str, should_be_right: &'a str, ) -> Option<&'a str> { - let pat_left = arms[0].pat; - let pat_right = arms[1].pat; + let first_pat = arms[0].pat; + let second_pat = arms[1].pat; - let body_node_pair = if ( - is_pat_variant(cx, pat_left, path_left, expected_item_left) - ) && ( - is_pat_variant(cx, pat_right, path_right, expected_item_right) - ) { + let body_node_pair = if (is_pat_variant(cx, first_pat, path_left, expected_item_left)) + && (is_pat_variant(cx, second_pat, path_right, expected_item_right)) + { (&arms[0].body.kind, &arms[1].body.kind) - } else if ( - is_pat_variant(cx, pat_left, path_left, expected_item_right) - ) && ( - is_pat_variant(cx, pat_right, path_right, expected_item_left) - ) { + } else if (is_pat_variant(cx, first_pat, path_left, expected_item_right)) + && (is_pat_variant(cx, second_pat, path_right, expected_item_left)) + { (&arms[1].body.kind, &arms[0].body.kind) } else { return None; diff --git a/clippy_lints/src/matches/significant_drop_in_scrutinee.rs b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs index 86a9df034979..85269e533a06 100644 --- a/clippy_lints/src/matches/significant_drop_in_scrutinee.rs +++ b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs @@ -50,13 +50,13 @@ fn set_diagnostic<'tcx>(diag: &mut Diagnostic, cx: &LateContext<'tcx>, expr: &'t let trailing_indent = " ".repeat(indent_of(cx, found.found_span).unwrap_or(0)); let replacement = if found.lint_suggestion == LintSuggestion::MoveAndDerefToCopy { - format!("let value = *{};\n{}", original, trailing_indent) + format!("let value = *{original};\n{trailing_indent}") } else if found.is_unit_return_val { // If the return value of the expression to be moved is unit, then we don't need to // capture the result in a temporary -- we can just replace it completely with `()`. - format!("{};\n{}", original, trailing_indent) + format!("{original};\n{trailing_indent}") } else { - format!("let value = {};\n{}", original, trailing_indent) + format!("let value = {original};\n{trailing_indent}") }; let suggestion_message = if found.lint_suggestion == LintSuggestion::MoveOnly { diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index 56bcdc01fe4d..d496107ffd6b 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -99,23 +99,21 @@ fn report_single_pattern( let msg = "you seem to be trying to use `match` for an equality check. Consider using `if`"; let sugg = format!( - "if {} == {}{} {}{}", + "if {} == {}{} {}{els_str}", snippet(cx, ex.span, ".."), // PartialEq for different reference counts may not exist. "&".repeat(ref_count_diff), snippet(cx, arms[0].pat.span, ".."), expr_block(cx, arms[0].body, None, "..", Some(expr.span)), - els_str, ); (msg, sugg) } else { let msg = "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`"; let sugg = format!( - "if let {} = {} {}{}", + "if let {} = {} {}{els_str}", snippet(cx, arms[0].pat.span, ".."), snippet(cx, ex.span, ".."), expr_block(cx, arms[0].body, None, "..", Some(expr.span)), - els_str, ); (msg, sugg) } diff --git a/clippy_lints/src/matches/try_err.rs b/clippy_lints/src/matches/try_err.rs index 663277d11365..c6cba81d8718 100644 --- a/clippy_lints/src/matches/try_err.rs +++ b/clippy_lints/src/matches/try_err.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{get_parent_expr, is_lang_ctor, match_def_path, paths}; +use clippy_utils::{get_parent_expr, is_res_lang_ctor, match_def_path, path_res, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::LangItem::ResultErr; @@ -27,8 +27,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, scrutine if let ExprKind::Path(ref match_fun_path) = match_fun.kind; if matches!(match_fun_path, QPath::LangItem(LangItem::TryTraitBranch, ..)); if let ExprKind::Call(err_fun, [err_arg, ..]) = try_arg.kind; - if let ExprKind::Path(ref err_fun_path) = err_fun.kind; - if is_lang_ctor(cx, err_fun_path, ResultErr); + if is_res_lang_ctor(cx, path_res(cx, err_fun), ResultErr); if let Some(return_ty) = find_return_type(cx, &expr.kind); then { let prefix; @@ -61,9 +60,9 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, scrutine "return " }; let suggestion = if err_ty == expr_err_ty { - format!("{}{}{}{}", ret_prefix, prefix, origin_snippet, suffix) + format!("{ret_prefix}{prefix}{origin_snippet}{suffix}") } else { - format!("{}{}{}.into(){}", ret_prefix, prefix, origin_snippet, suffix) + format!("{ret_prefix}{prefix}{origin_snippet}.into(){suffix}") }; span_lint_and_sugg( diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs index cad3ea2a176c..0c4d9f100f7a 100644 --- a/clippy_lints/src/mem_replace.rs +++ b/clippy_lints/src/mem_replace.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::ty::is_non_aggregate_primitive_type; -use clippy_utils::{is_default_equivalent, is_lang_ctor, meets_msrv, msrvs}; +use clippy_utils::{is_default_equivalent, is_res_lang_ctor, meets_msrv, msrvs, path_res}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::LangItem::OptionNone; @@ -102,40 +102,38 @@ impl_lint_pass!(MemReplace => [MEM_REPLACE_OPTION_WITH_NONE, MEM_REPLACE_WITH_UNINIT, MEM_REPLACE_WITH_DEFAULT]); fn check_replace_option_with_none(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) { - if let ExprKind::Path(ref replacement_qpath) = src.kind { - // Check that second argument is `Option::None` - if is_lang_ctor(cx, replacement_qpath, 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 - // argument's type. All that's left is to get - // replacee's path. - let replaced_path = match dest.kind { - ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, replaced) => { - if let ExprKind::Path(QPath::Resolved(None, replaced_path)) = replaced.kind { - replaced_path - } else { - return; - } - }, - ExprKind::Path(QPath::Resolved(None, replaced_path)) => replaced_path, - _ => return, - }; + // Check that second argument is `Option::None` + if is_res_lang_ctor(cx, path_res(cx, src), 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 + // argument's type. All that's left is to get + // replacee's path. + let replaced_path = match dest.kind { + ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, replaced) => { + if let ExprKind::Path(QPath::Resolved(None, replaced_path)) = replaced.kind { + replaced_path + } else { + return; + } + }, + ExprKind::Path(QPath::Resolved(None, replaced_path)) => replaced_path, + _ => return, + }; - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - MEM_REPLACE_OPTION_WITH_NONE, - expr_span, - "replacing an `Option` with `None`", - "consider `Option::take()` instead", - format!( - "{}.take()", - snippet_with_applicability(cx, replaced_path.span, "", &mut applicability) - ), - applicability, - ); - } + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + MEM_REPLACE_OPTION_WITH_NONE, + expr_span, + "replacing an `Option` with `None`", + "consider `Option::take()` instead", + format!( + "{}.take()", + snippet_with_applicability(cx, replaced_path.span, "", &mut applicability) + ), + applicability, + ); } } @@ -203,10 +201,8 @@ fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr< return; } // disable lint for Option since it is covered in another lint - if let ExprKind::Path(q) = &src.kind { - if is_lang_ctor(cx, q, OptionNone) { - return; - } + if is_res_lang_ctor(cx, path_res(cx, src), OptionNone) { + return; } if is_default_equivalent(cx, src) && !in_external_macro(cx.tcx.sess, expr_span) { span_lint_and_then( diff --git a/clippy_lints/src/methods/bind_instead_of_map.rs b/clippy_lints/src/methods/bind_instead_of_map.rs index 22f5635a5bcc..cc26b0f7fa82 100644 --- a/clippy_lints/src/methods/bind_instead_of_map.rs +++ b/clippy_lints/src/methods/bind_instead_of_map.rs @@ -85,7 +85,7 @@ pub(crate) trait BindInsteadOfMap { let closure_args_snip = snippet(cx, closure_args_span, ".."); let option_snip = snippet(cx, recv.span, ".."); - let note = format!("{}.{}({} {})", option_snip, Self::GOOD_METHOD_NAME, closure_args_snip, some_inner_snip); + let note = format!("{option_snip}.{}({closure_args_snip} {some_inner_snip})", Self::GOOD_METHOD_NAME); span_lint_and_sugg( cx, BIND_INSTEAD_OF_MAP, diff --git a/clippy_lints/src/methods/bytes_nth.rs b/clippy_lints/src/methods/bytes_nth.rs index 44857d61fef8..2e96346be977 100644 --- a/clippy_lints/src/methods/bytes_nth.rs +++ b/clippy_lints/src/methods/bytes_nth.rs @@ -22,7 +22,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx E cx, BYTES_NTH, expr.span, - &format!("called `.bytes().nth()` on a `{}`", caller_type), + &format!("called `.bytes().nth()` on a `{caller_type}`"), "try", format!( "{}.as_bytes().get({})", diff --git a/clippy_lints/src/methods/chars_cmp.rs b/clippy_lints/src/methods/chars_cmp.rs index b2bc1ad5c9ed..56b7fbb9d4bc 100644 --- a/clippy_lints/src/methods/chars_cmp.rs +++ b/clippy_lints/src/methods/chars_cmp.rs @@ -33,12 +33,11 @@ pub(super) fn check( cx, lint, info.expr.span, - &format!("you should use the `{}` method", suggest), + &format!("you should use the `{suggest}` method"), "like this", - format!("{}{}.{}({})", + format!("{}{}.{suggest}({})", if info.eq { "" } else { "!" }, snippet_with_applicability(cx, args[0].0.span, "..", &mut applicability), - suggest, snippet_with_applicability(cx, arg_char.span, "..", &mut applicability)), applicability, ); diff --git a/clippy_lints/src/methods/chars_cmp_with_unwrap.rs b/clippy_lints/src/methods/chars_cmp_with_unwrap.rs index b85bfec2b12b..7e808760663a 100644 --- a/clippy_lints/src/methods/chars_cmp_with_unwrap.rs +++ b/clippy_lints/src/methods/chars_cmp_with_unwrap.rs @@ -26,12 +26,11 @@ pub(super) fn check<'tcx>( cx, lint, info.expr.span, - &format!("you should use the `{}` method", suggest), + &format!("you should use the `{suggest}` method"), "like this", - format!("{}{}.{}('{}')", + format!("{}{}.{suggest}('{}')", if info.eq { "" } else { "!" }, snippet_with_applicability(cx, args[0].0.span, "..", &mut applicability), - suggest, c.escape_default()), applicability, ); diff --git a/clippy_lints/src/methods/clone_on_copy.rs b/clippy_lints/src/methods/clone_on_copy.rs index 7ab6b84c2074..7c7938dd2e8b 100644 --- a/clippy_lints/src/methods/clone_on_copy.rs +++ b/clippy_lints/src/methods/clone_on_copy.rs @@ -49,8 +49,7 @@ pub(super) fn check( expr.span, &format!( "using `clone` on a double-reference; \ - this will copy the reference of type `{}` instead of cloning the inner type", - ty + this will copy the reference of type `{ty}` instead of cloning the inner type" ), |diag| { if let Some(snip) = sugg::Sugg::hir_opt(cx, arg) { @@ -62,11 +61,11 @@ pub(super) fn check( } let refs = "&".repeat(n + 1); let derefs = "*".repeat(n); - let explicit = format!("<{}{}>::clone({})", refs, ty, snip); + let explicit = format!("<{refs}{ty}>::clone({snip})"); diag.span_suggestion( expr.span, "try dereferencing it", - format!("{}({}{}).clone()", refs, derefs, snip.deref()), + format!("{refs}({derefs}{}).clone()", snip.deref()), Applicability::MaybeIncorrect, ); diag.span_suggestion( @@ -121,16 +120,16 @@ pub(super) fn check( let (help, sugg) = if deref_count == 0 { ("try removing the `clone` call", snip.into()) } else if parent_is_suffix_expr { - ("try dereferencing it", format!("({}{})", "*".repeat(deref_count), snip)) + ("try dereferencing it", format!("({}{snip})", "*".repeat(deref_count))) } else { - ("try dereferencing it", format!("{}{}", "*".repeat(deref_count), snip)) + ("try dereferencing it", format!("{}{snip}", "*".repeat(deref_count))) }; span_lint_and_sugg( cx, CLONE_ON_COPY, expr.span, - &format!("using `clone` on type `{}` which implements the `Copy` trait", ty), + &format!("using `clone` on type `{ty}` which implements the `Copy` trait"), help, sugg, app, diff --git a/clippy_lints/src/methods/clone_on_ref_ptr.rs b/clippy_lints/src/methods/clone_on_ref_ptr.rs index f82ca8912006..355f53532e26 100644 --- a/clippy_lints/src/methods/clone_on_ref_ptr.rs +++ b/clippy_lints/src/methods/clone_on_ref_ptr.rs @@ -41,7 +41,7 @@ pub(super) fn check( expr.span, "using `.clone()` on a ref-counted pointer", "try this", - format!("{}::<{}>::clone(&{})", caller_type, subst.type_at(0), snippet), + format!("{caller_type}::<{}>::clone(&{snippet})", subst.type_at(0)), Applicability::Unspecified, // Sometimes unnecessary ::<_> after Rc/Arc/Weak ); } diff --git a/clippy_lints/src/methods/expect_fun_call.rs b/clippy_lints/src/methods/expect_fun_call.rs index bd846d71d466..d0cf411dfd34 100644 --- a/clippy_lints/src/methods/expect_fun_call.rs +++ b/clippy_lints/src/methods/expect_fun_call.rs @@ -143,9 +143,9 @@ pub(super) fn check<'tcx>( cx, EXPECT_FUN_CALL, span_replace_word, - &format!("use of `{}` followed by a function call", name), + &format!("use of `{name}` followed by a function call"), "try this", - format!("unwrap_or_else({} panic!({}))", closure_args, sugg), + format!("unwrap_or_else({closure_args} panic!({sugg}))"), applicability, ); return; @@ -160,12 +160,9 @@ pub(super) fn check<'tcx>( cx, EXPECT_FUN_CALL, span_replace_word, - &format!("use of `{}` followed by a function call", name), + &format!("use of `{name}` followed by a function call"), "try this", - format!( - "unwrap_or_else({} {{ panic!(\"{{}}\", {}) }})", - closure_args, arg_root_snippet - ), + format!("unwrap_or_else({closure_args} {{ panic!(\"{{}}\", {arg_root_snippet}) }})"), applicability, ); } diff --git a/clippy_lints/src/methods/filetype_is_file.rs b/clippy_lints/src/methods/filetype_is_file.rs index 7b2967feb0fe..3fef53739fbd 100644 --- a/clippy_lints/src/methods/filetype_is_file.rs +++ b/clippy_lints/src/methods/filetype_is_file.rs @@ -1,17 +1,18 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::ty::match_type; -use clippy_utils::{get_parent_expr, paths}; +use clippy_utils::get_parent_expr; +use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::source_map::Span; +use rustc_span::sym; 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 !match_type(cx, ty, &paths::FILE_TYPE) { + if !is_type_diagnostic_item(cx, ty, sym::FileType) { return; } @@ -35,7 +36,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr span = expr.span; } } - let lint_msg = format!("`{}FileType::is_file()` only {} regular files", lint_unary, verb); - let help_msg = format!("use `{}FileType::is_dir()` instead", help_unary); + let lint_msg = format!("`{lint_unary}FileType::is_file()` only {verb} regular files"); + let help_msg = format!("use `{help_unary}FileType::is_dir()` instead"); span_lint_and_help(cx, FILETYPE_IS_FILE, span, &lint_msg, None, &help_msg); } diff --git a/clippy_lints/src/methods/filter_map_next.rs b/clippy_lints/src/methods/filter_map_next.rs index 38ec4d8e3ab8..ddf8a1f09b87 100644 --- a/clippy_lints/src/methods/filter_map_next.rs +++ b/clippy_lints/src/methods/filter_map_next.rs @@ -32,7 +32,7 @@ pub(super) fn check<'tcx>( expr.span, msg, "try this", - format!("{}.find_map({})", iter_snippet, filter_snippet), + format!("{iter_snippet}.find_map({filter_snippet})"), Applicability::MachineApplicable, ); } else { diff --git a/clippy_lints/src/methods/filter_next.rs b/clippy_lints/src/methods/filter_next.rs index bcf8d93b602e..edcec0fc1015 100644 --- a/clippy_lints/src/methods/filter_next.rs +++ b/clippy_lints/src/methods/filter_next.rs @@ -32,7 +32,7 @@ pub(super) fn check<'tcx>( expr.span, msg, "try this", - format!("{}.find({})", iter_snippet, filter_snippet), + format!("{iter_snippet}.find({filter_snippet})"), Applicability::MachineApplicable, ); } else { 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 6436e28a63c5..66dfce3682b5 100644 --- a/clippy_lints/src/methods/from_iter_instead_of_collect.rs +++ b/clippy_lints/src/methods/from_iter_instead_of_collect.rs @@ -23,7 +23,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Exp // `expr` implements `FromIterator` trait let iter_expr = sugg::Sugg::hir(cx, &args[0], "..").maybe_par(); let turbofish = extract_turbofish(cx, expr, ty); - let sugg = format!("{}.collect::<{}>()", iter_expr, turbofish); + let sugg = format!("{iter_expr}.collect::<{turbofish}>()"); span_lint_and_sugg( cx, FROM_ITER_INSTEAD_OF_COLLECT, @@ -63,7 +63,7 @@ fn extract_turbofish(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ty: Ty<'_>) -> if e == type_specifier { None } else { Some((*e).to_string()) } }).collect::>(); // join and add the type specifier at the end (i.e.: `collections::BTreeSet`) - format!("{}{}", without_ts.join("::"), type_specifier) + format!("{}{type_specifier}", without_ts.join("::")) } else { // type is not explicitly specified so wildcards are needed // i.e.: 2 wildcards in `std::collections::BTreeMap<&i32, &char>` @@ -72,7 +72,7 @@ fn extract_turbofish(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ty: Ty<'_>) -> let end = ty_str.find('>').unwrap_or(ty_str.len()); let nb_wildcard = ty_str[start..end].split(',').count(); let wildcards = format!("_{}", ", _".repeat(nb_wildcard - 1)); - format!("{}<{}>", elements.join("::"), wildcards) + format!("{}<{wildcards}>", elements.join("::")) } } } diff --git a/clippy_lints/src/methods/get_first.rs b/clippy_lints/src/methods/get_first.rs index 4de77de74042..cb17af608a3f 100644 --- a/clippy_lints/src/methods/get_first.rs +++ b/clippy_lints/src/methods/get_first.rs @@ -29,9 +29,9 @@ pub(super) fn check<'tcx>( cx, GET_FIRST, expr.span, - &format!("accessing first element with `{0}.get(0)`", slice_name), + &format!("accessing first element with `{slice_name}.get(0)`"), "try", - format!("{}.first()", slice_name), + format!("{slice_name}.first()"), app, ); } diff --git a/clippy_lints/src/methods/get_last_with_len.rs b/clippy_lints/src/methods/get_last_with_len.rs index 02aada87202c..3bdc154df049 100644 --- a/clippy_lints/src/methods/get_last_with_len.rs +++ b/clippy_lints/src/methods/get_last_with_len.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::SpanlessEq; -use rustc_ast::LitKind; +use clippy_utils::{is_integer_literal, SpanlessEq}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; @@ -26,8 +25,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: && lhs_path.ident.name == sym::len // RHS of subtraction is 1 - && let ExprKind::Lit(rhs_lit) = &rhs.kind - && let LitKind::Int(1, ..) = rhs_lit.node + && is_integer_literal(rhs, 1) // check that recv == lhs_recv `recv.get(lhs_recv.len() - 1)` && SpanlessEq::new(cx).eq_expr(recv, lhs_recv) diff --git a/clippy_lints/src/methods/get_unwrap.rs b/clippy_lints/src/methods/get_unwrap.rs index 18e08d6ee232..ffc3a4d780e5 100644 --- a/clippy_lints/src/methods/get_unwrap.rs +++ b/clippy_lints/src/methods/get_unwrap.rs @@ -71,16 +71,11 @@ pub(super) fn check<'tcx>( cx, GET_UNWRAP, span, - &format!( - "called `.get{0}().unwrap()` on a {1}. Using `[]` is more clear and more concise", - mut_str, caller_type - ), + &format!("called `.get{mut_str}().unwrap()` on a {caller_type}. Using `[]` is more clear and more concise"), "try this", format!( - "{}{}[{}]", - borrow_str, - snippet_with_applicability(cx, recv.span, "..", &mut applicability), - get_args_str + "{borrow_str}{}[{get_args_str}]", + snippet_with_applicability(cx, recv.span, "..", &mut applicability) ), applicability, ); diff --git a/clippy_lints/src/methods/implicit_clone.rs b/clippy_lints/src/methods/implicit_clone.rs index 9651a52be4e7..429cdc1918d7 100644 --- a/clippy_lints/src/methods/implicit_clone.rs +++ b/clippy_lints/src/methods/implicit_clone.rs @@ -26,12 +26,12 @@ pub fn check(cx: &LateContext<'_>, method_name: &str, expr: &hir::Expr<'_>, recv cx, IMPLICIT_CLONE, expr.span, - &format!("implicitly cloning a `{}` by calling `{}` on its dereferenced type", ty_name, method_name), + &format!("implicitly cloning a `{ty_name}` by calling `{method_name}` on its dereferenced type"), "consider using", if ref_count > 1 { - format!("({}{}).clone()", "*".repeat(ref_count - 1), recv_snip) + format!("({}{recv_snip}).clone()", "*".repeat(ref_count - 1)) } else { - format!("{}.clone()", recv_snip) + format!("{recv_snip}.clone()") }, app, ); diff --git a/clippy_lints/src/methods/inefficient_to_string.rs b/clippy_lints/src/methods/inefficient_to_string.rs index e1c9b5248a8a..ede3b8bb74e9 100644 --- a/clippy_lints/src/methods/inefficient_to_string.rs +++ b/clippy_lints/src/methods/inefficient_to_string.rs @@ -34,18 +34,17 @@ pub fn check<'tcx>( cx, INEFFICIENT_TO_STRING, expr.span, - &format!("calling `to_string` on `{}`", arg_ty), + &format!("calling `to_string` on `{arg_ty}`"), |diag| { diag.help(&format!( - "`{}` implements `ToString` through a slower blanket impl, but `{}` has a fast specialization of `ToString`", - self_ty, deref_self_ty + "`{self_ty}` implements `ToString` through a slower blanket impl, but `{deref_self_ty}` has a fast specialization of `ToString`" )); let mut applicability = Applicability::MachineApplicable; let arg_snippet = snippet_with_applicability(cx, receiver.span, "..", &mut applicability); diag.span_suggestion( expr.span, "try dereferencing the receiver", - format!("({}{}).to_string()", "*".repeat(deref_count), arg_snippet), + format!("({}{arg_snippet}).to_string()", "*".repeat(deref_count)), applicability, ); }, @@ -66,7 +65,7 @@ fn specializes_tostring(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { } if let ty::Adt(adt, substs) = ty.kind() { - match_def_path(cx, adt.did(), &paths::COW) && substs.type_at(1).is_str() + cx.tcx.is_diagnostic_item(sym::Cow, adt.did()) && substs.type_at(1).is_str() } else { false } diff --git a/clippy_lints/src/methods/into_iter_on_ref.rs b/clippy_lints/src/methods/into_iter_on_ref.rs index 11e76841e9f0..be56b63506a4 100644 --- a/clippy_lints/src/methods/into_iter_on_ref.rs +++ b/clippy_lints/src/methods/into_iter_on_ref.rs @@ -30,8 +30,7 @@ pub(super) fn check( INTO_ITER_ON_REF, method_span, &format!( - "this `.into_iter()` call is equivalent to `.{}()` and will not consume the `{}`", - method_name, kind, + "this `.into_iter()` call is equivalent to `.{method_name}()` and will not consume the `{kind}`", ), "call directly", method_name.to_string(), diff --git a/clippy_lints/src/methods/is_digit_ascii_radix.rs b/clippy_lints/src/methods/is_digit_ascii_radix.rs index aa176dcc8b4a..304024e80666 100644 --- a/clippy_lints/src/methods/is_digit_ascii_radix.rs +++ b/clippy_lints/src/methods/is_digit_ascii_radix.rs @@ -37,12 +37,11 @@ pub(super) fn check<'tcx>( cx, IS_DIGIT_ASCII_RADIX, expr.span, - &format!("use of `char::is_digit` with literal radix of {}", num), + &format!("use of `char::is_digit` with literal radix of {num}"), "try", format!( - "{}.{}()", - snippet_with_applicability(cx, self_arg.span, "..", &mut applicability), - replacement + "{}.{replacement}()", + snippet_with_applicability(cx, self_arg.span, "..", &mut applicability) ), applicability, ); diff --git a/clippy_lints/src/methods/iter_cloned_collect.rs b/clippy_lints/src/methods/iter_cloned_collect.rs index 30d56113c6c1..bde6f92b076e 100644 --- a/clippy_lints/src/methods/iter_cloned_collect.rs +++ b/clippy_lints/src/methods/iter_cloned_collect.rs @@ -20,8 +20,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, method_name: &str, expr: &hir: cx, ITER_CLONED_COLLECT, to_replace, - &format!("called `iter().{}().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and \ - more readable", method_name), + &format!("called `iter().{method_name}().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and \ + more readable"), "try", ".to_vec()".to_string(), Applicability::MachineApplicable, diff --git a/clippy_lints/src/methods/iter_count.rs b/clippy_lints/src/methods/iter_count.rs index 052be3d8ee7c..bcddc7c786a5 100644 --- a/clippy_lints/src/methods/iter_count.rs +++ b/clippy_lints/src/methods/iter_count.rs @@ -37,7 +37,7 @@ pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx E cx, ITER_COUNT, expr.span, - &format!("called `.{}().count()` on a `{}`", iter_method, caller_type), + &format!("called `.{iter_method}().count()` on a `{caller_type}`"), "try", format!( "{}.len()", diff --git a/clippy_lints/src/methods/iter_kv_map.rs b/clippy_lints/src/methods/iter_kv_map.rs index a7eecabd6849..2244ebfb1292 100644 --- a/clippy_lints/src/methods/iter_kv_map.rs +++ b/clippy_lints/src/methods/iter_kv_map.rs @@ -54,9 +54,9 @@ pub(super) fn check<'tcx>( cx, ITER_KV_MAP, expr.span, - &format!("iterating on a map's {}s", replacement_kind), + &format!("iterating on a map's {replacement_kind}s"), "try", - format!("{}.{}{}s()", recv_snippet, into_prefix, replacement_kind), + format!("{recv_snippet}.{into_prefix}{replacement_kind}s()"), applicability, ); } else { @@ -64,9 +64,9 @@ pub(super) fn check<'tcx>( cx, ITER_KV_MAP, expr.span, - &format!("iterating on a map's {}s", replacement_kind), + &format!("iterating on a map's {replacement_kind}s"), "try", - format!("{}.{}{}s().map(|{}| {})", recv_snippet, into_prefix, replacement_kind, binded_ident, + format!("{recv_snippet}.{into_prefix}{replacement_kind}s().map(|{binded_ident}| {})", snippet_with_applicability(cx, body_expr.span, "/* body */", &mut applicability)), applicability, ); diff --git a/clippy_lints/src/methods/iter_next_slice.rs b/clippy_lints/src/methods/iter_next_slice.rs index b8d1dabe0076..83c1bf203467 100644 --- a/clippy_lints/src/methods/iter_next_slice.rs +++ b/clippy_lints/src/methods/iter_next_slice.rs @@ -37,7 +37,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, cal let suggest = if start_idx == 0 { format!("{}.first()", snippet_with_applicability(cx, caller_var.span, "..", &mut applicability)) } else { - format!("{}.get({})", snippet_with_applicability(cx, caller_var.span, "..", &mut applicability), start_idx) + format!("{}.get({start_idx})", snippet_with_applicability(cx, caller_var.span, "..", &mut applicability)) }; span_lint_and_sugg( cx, diff --git a/clippy_lints/src/methods/iter_nth.rs b/clippy_lints/src/methods/iter_nth.rs index 80ca4c94219f..ceee12784cbb 100644 --- a/clippy_lints/src/methods/iter_nth.rs +++ b/clippy_lints/src/methods/iter_nth.rs @@ -32,8 +32,8 @@ pub(super) fn check<'tcx>( cx, ITER_NTH, expr.span, - &format!("called `.iter{0}().nth()` on a {1}", mut_str, caller_type), + &format!("called `.iter{mut_str}().nth()` on a {caller_type}"), None, - &format!("calling `.get{}()` is both faster and more readable", mut_str), + &format!("calling `.get{mut_str}()` is both faster and more readable"), ); } 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 cea7b0d82ff3..4f73b3ec4224 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,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; -use clippy_utils::{get_expr_use_or_unification_node, is_lang_ctor, is_no_std_crate}; +use clippy_utils::{get_expr_use_or_unification_node, is_no_std_crate, is_res_lang_ctor, path_res}; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome}; @@ -26,26 +26,11 @@ impl IterType { } pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, method_name: &str, recv: &Expr<'_>) { - let item = match &recv.kind { - ExprKind::Array(v) if v.len() <= 1 => v.first(), - ExprKind::Path(p) => { - if is_lang_ctor(cx, p, OptionNone) { - None - } else { - return; - } - }, - ExprKind::Call(f, some_args) if some_args.len() == 1 => { - if let ExprKind::Path(p) = &f.kind { - if is_lang_ctor(cx, p, OptionSome) { - Some(&some_args[0]) - } else { - return; - } - } else { - return; - } - }, + 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), _ => return, }; let iter_type = match method_name { diff --git a/clippy_lints/src/methods/iter_with_drain.rs b/clippy_lints/src/methods/iter_with_drain.rs index a669cbbbcc60..3da230e12d7f 100644 --- a/clippy_lints/src/methods/iter_with_drain.rs +++ b/clippy_lints/src/methods/iter_with_drain.rs @@ -22,7 +22,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span cx, ITER_WITH_DRAIN, span.with_hi(expr.span.hi()), - &format!("`drain(..)` used on a `{}`", ty_name), + &format!("`drain(..)` used on a `{ty_name}`"), "try this", "into_iter()".to_string(), Applicability::MaybeIncorrect, diff --git a/clippy_lints/src/methods/manual_ok_or.rs b/clippy_lints/src/methods/manual_ok_or.rs index ffd2f4a38b8a..5b758f1e6547 100644 --- a/clippy_lints/src/methods/manual_ok_or.rs +++ b/clippy_lints/src/methods/manual_ok_or.rs @@ -1,11 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt}; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_lang_ctor, path_to_local_id}; +use clippy_utils::{is_res_lang_ctor, path_res, path_to_local_id}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::LangItem::{ResultErr, ResultOk}; -use rustc_hir::{Closure, Expr, ExprKind, PatKind}; +use rustc_hir::{Expr, ExprKind, PatKind}; use rustc_lint::LateContext; use rustc_span::symbol::sym; @@ -22,8 +22,8 @@ pub(super) fn check<'tcx>( if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); if let Some(impl_id) = cx.tcx.impl_of_method(method_id); if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id), sym::Option); - if let ExprKind::Call(Expr { kind: ExprKind::Path(err_path), .. }, [err_arg]) = or_expr.kind; - if is_lang_ctor(cx, err_path, ResultErr); + if let ExprKind::Call(err_path, [err_arg]) = or_expr.kind; + if is_res_lang_ctor(cx, path_res(cx, err_path), ResultErr); if is_ok_wrapping(cx, map_expr); if let Some(recv_snippet) = snippet_opt(cx, recv.span); if let Some(err_arg_snippet) = snippet_opt(cx, err_arg.span); @@ -37,9 +37,7 @@ pub(super) fn check<'tcx>( "this pattern reimplements `Option::ok_or`", "replace with", format!( - "{}.ok_or({})", - recv_snippet, - reindented_err_arg_snippet + "{recv_snippet}.ok_or({reindented_err_arg_snippet})" ), Applicability::MachineApplicable, ); @@ -48,17 +46,19 @@ pub(super) fn check<'tcx>( } fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool { - if let ExprKind::Path(ref qpath) = map_expr.kind { - if is_lang_ctor(cx, qpath, ResultOk) { - return true; - } - } - if_chain! { - if let ExprKind::Closure(&Closure { body, .. }) = map_expr.kind; - let body = cx.tcx.hir().body(body); - if let PatKind::Binding(_, param_id, ..) = body.params[0].pat.kind; - if let ExprKind::Call(Expr { kind: ExprKind::Path(ok_path), .. }, &[ref ok_arg]) = body.value.kind; - if is_lang_ctor(cx, ok_path, ResultOk); - then { path_to_local_id(ok_arg, param_id) } else { false } + 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::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) + { + path_to_local_id(ok_arg, param_id) + } else { + false + } + }, + _ => false, } } diff --git a/clippy_lints/src/methods/manual_saturating_arithmetic.rs b/clippy_lints/src/methods/manual_saturating_arithmetic.rs index 0fe510beaa07..ec694cf6882e 100644 --- a/clippy_lints/src/methods/manual_saturating_arithmetic.rs +++ b/clippy_lints/src/methods/manual_saturating_arithmetic.rs @@ -57,11 +57,10 @@ pub fn check( super::MANUAL_SATURATING_ARITHMETIC, expr.span, "manual saturating arithmetic", - &format!("try using `saturating_{}`", arith), + &format!("try using `saturating_{arith}`"), format!( - "{}.saturating_{}({})", + "{}.saturating_{arith}({})", snippet_with_applicability(cx, arith_lhs.span, "..", &mut applicability), - arith, snippet_with_applicability(cx, arith_rhs.span, "..", &mut applicability), ), applicability, diff --git a/clippy_lints/src/methods/manual_str_repeat.rs b/clippy_lints/src/methods/manual_str_repeat.rs index 46d2fc493f81..8b6b8f1bf16c 100644 --- a/clippy_lints/src/methods/manual_str_repeat.rs +++ b/clippy_lints/src/methods/manual_str_repeat.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_path_diagnostic_item; 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, match_type}; -use clippy_utils::{is_expr_path_def_path, paths}; +use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; use if_chain::if_chain; use rustc_ast::LitKind; use rustc_errors::Applicability; @@ -38,7 +38,7 @@ fn parse_repeat_arg(cx: &LateContext<'_>, e: &Expr<'_>) -> Option { let ty = cx.typeck_results().expr_ty(e); if is_type_diagnostic_item(cx, ty, sym::String) || (is_type_lang_item(cx, ty, LangItem::OwnedBox) && get_ty_param(ty).map_or(false, Ty::is_str)) - || (match_type(cx, ty, &paths::COW) && get_ty_param(ty).map_or(false, Ty::is_str)) + || (is_type_diagnostic_item(cx, ty, sym::Cow) && get_ty_param(ty).map_or(false, Ty::is_str)) { Some(RepeatKind::String) } else { @@ -57,7 +57,7 @@ pub(super) fn check( ) { if_chain! { if let ExprKind::Call(repeat_fn, [repeat_arg]) = take_self_arg.kind; - if is_expr_path_def_path(cx, repeat_fn, &paths::ITER_REPEAT); + if is_path_diagnostic_item(cx, repeat_fn, sym::iter_repeat); if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(collect_expr), sym::String); if let Some(collect_id) = cx.typeck_results().type_dependent_def_id(collect_expr.hir_id); if let Some(take_id) = cx.typeck_results().type_dependent_def_id(take_expr.hir_id); @@ -91,7 +91,7 @@ pub(super) fn check( collect_expr.span, "manual implementation of `str::repeat` using iterators", "try this", - format!("{}.repeat({})", val_str, count_snip), + format!("{val_str}.repeat({count_snip})"), app ) } diff --git a/clippy_lints/src/methods/map_clone.rs b/clippy_lints/src/methods/map_clone.rs index 8261ef5e1ce3..7ce14ec080b1 100644 --- a/clippy_lints/src/methods/map_clone.rs +++ b/clippy_lints/src/methods/map_clone.rs @@ -111,11 +111,10 @@ fn lint_explicit_closure(cx: &LateContext<'_>, replace: Span, root: Span, is_cop MAP_CLONE, replace, message, - &format!("consider calling the dedicated `{}` method", sugg_method), + &format!("consider calling the dedicated `{sugg_method}` method"), format!( - "{}.{}()", + "{}.{sugg_method}()", snippet_with_applicability(cx, root, "..", &mut applicability), - sugg_method, ), applicability, ); diff --git a/clippy_lints/src/methods/map_flatten.rs b/clippy_lints/src/methods/map_flatten.rs index 13853dec99de..361ffcb5ef3f 100644 --- a/clippy_lints/src/methods/map_flatten.rs +++ b/clippy_lints/src/methods/map_flatten.rs @@ -20,12 +20,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, map_ cx, MAP_FLATTEN, expr.span.with_lo(map_span.lo()), - &format!("called `map(..).flatten()` on `{}`", caller_ty_name), - &format!( - "try replacing `map` with `{}` and remove the `.flatten()`", - method_to_use - ), - format!("{}({})", method_to_use, closure_snippet), + &format!("called `map(..).flatten()` on `{caller_ty_name}`"), + &format!("try replacing `map` with `{method_to_use}` and remove the `.flatten()`"), + format!("{method_to_use}({closure_snippet})"), applicability, ); } diff --git a/clippy_lints/src/methods/map_identity.rs b/clippy_lints/src/methods/map_identity.rs index 862a9578e6ff..0f25ef82ed42 100644 --- a/clippy_lints/src/methods/map_identity.rs +++ b/clippy_lints/src/methods/map_identity.rs @@ -30,7 +30,7 @@ pub(super) fn check( MAP_IDENTITY, sugg_span, "unnecessary map of the identity function", - &format!("remove the call to `{}`", name), + &format!("remove the call to `{name}`"), String::new(), Applicability::MachineApplicable, ) diff --git a/clippy_lints/src/methods/map_unwrap_or.rs b/clippy_lints/src/methods/map_unwrap_or.rs index 4a8e7ce4ddbb..74fdead216b0 100644 --- a/clippy_lints/src/methods/map_unwrap_or.rs +++ b/clippy_lints/src/methods/map_unwrap_or.rs @@ -65,7 +65,7 @@ pub(super) fn check<'tcx>( expr.span, msg, "try this", - format!("{}.map_or_else({}, {})", var_snippet, unwrap_snippet, map_snippet), + format!("{var_snippet}.map_or_else({unwrap_snippet}, {map_snippet})"), Applicability::MachineApplicable, ); return true; diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 428a354ec6b1..cfcf9596c50d 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -109,13 +109,13 @@ use if_chain::if_chain; use rustc_hir as hir; use rustc_hir::def::Res; use rustc_hir::{Expr, ExprKind, PrimTy, QPath, TraitItem, TraitItemKind}; +use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, TraitRef, Ty}; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{sym, Span}; -use rustc_hir_analysis::hir_ty_to_ty; declare_clippy_lint! { /// ### What it does @@ -3255,65 +3255,59 @@ impl<'tcx> LateLintPass<'tcx> for Methods { let self_ty = cx.tcx.type_of(item.def_id); let implements_trait = matches!(item.kind, hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. })); - if_chain! { - if let hir::ImplItemKind::Fn(ref sig, id) = impl_item.kind; - if let Some(first_arg) = iter_input_pats(sig.decl, cx.tcx.hir().body(id)).next(); - - let method_sig = cx.tcx.fn_sig(impl_item.def_id.def_id); + if let hir::ImplItemKind::Fn(ref sig, id) = impl_item.kind { + let method_sig = cx.tcx.fn_sig(impl_item.def_id); let method_sig = cx.tcx.erase_late_bound_regions(method_sig); - - let first_arg_ty = method_sig.inputs().iter().next(); - - // check conventions w.r.t. conversion method names and predicates - if let Some(first_arg_ty) = first_arg_ty; - - then { - // if this impl block implements a trait, lint in trait definition instead - if !implements_trait && cx.access_levels.is_exported(impl_item.def_id.def_id) { - // check missing trait implementations - for method_config in &TRAIT_METHODS { - if name == method_config.method_name && - sig.decl.inputs.len() == method_config.param_count && - method_config.output_type.matches(&sig.decl.output) && - method_config.self_kind.matches(cx, self_ty, *first_arg_ty) && - fn_header_equals(method_config.fn_header, sig.header) && - method_config.lifetime_param_cond(impl_item) - { - span_lint_and_help( - cx, - SHOULD_IMPLEMENT_TRAIT, - impl_item.span, - &format!( - "method `{}` can be confused for the standard trait method `{}::{}`", - method_config.method_name, - method_config.trait_name, - method_config.method_name - ), - None, - &format!( - "consider implementing the trait `{}` or choosing a less ambiguous method name", - method_config.trait_name - ) - ); - } + let first_arg_ty_opt = method_sig.inputs().iter().next().copied(); + // if this impl block implements a trait, lint in trait definition instead + if !implements_trait && cx.access_levels.is_exported(impl_item.def_id.def_id) { + // check missing trait implementations + for method_config in &TRAIT_METHODS { + if name == method_config.method_name + && sig.decl.inputs.len() == method_config.param_count + && method_config.output_type.matches(&sig.decl.output) + // in case there is no first arg, since we already have checked the number of arguments + // it's should be always true + && first_arg_ty_opt.map_or(true, |first_arg_ty| method_config + .self_kind.matches(cx, self_ty, first_arg_ty) + ) + && fn_header_equals(method_config.fn_header, sig.header) + && method_config.lifetime_param_cond(impl_item) + { + span_lint_and_help( + cx, + SHOULD_IMPLEMENT_TRAIT, + impl_item.span, + &format!( + "method `{}` can be confused for the standard trait method `{}::{}`", + method_config.method_name, method_config.trait_name, method_config.method_name + ), + None, + &format!( + "consider implementing the trait `{}` or choosing a less ambiguous method name", + method_config.trait_name + ), + ); } } + } - if sig.decl.implicit_self.has_implicit_self() + if sig.decl.implicit_self.has_implicit_self() && !(self.avoid_breaking_exported_api - && cx.access_levels.is_exported(impl_item.def_id.def_id)) + && cx.access_levels.is_exported(impl_item.def_id.def_id)) + && let Some(first_arg) = iter_input_pats(sig.decl, cx.tcx.hir().body(id)).next() + && let Some(first_arg_ty) = first_arg_ty_opt { wrong_self_convention::check( cx, name, self_ty, - *first_arg_ty, + first_arg_ty, first_arg.pat.span, implements_trait, false ); } - } } // if this impl block implements a trait, lint in trait definition instead @@ -3799,7 +3793,6 @@ const TRAIT_METHODS: [ShouldImplTraitCase; 30] = [ ShouldImplTraitCase::new("std::borrow::BorrowMut", "borrow_mut", 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), ShouldImplTraitCase::new("std::clone::Clone", "clone", 1, FN_HEADER, SelfKind::Ref, OutType::Any, true), ShouldImplTraitCase::new("std::cmp::Ord", "cmp", 2, FN_HEADER, SelfKind::Ref, OutType::Any, true), - // FIXME: default doesn't work ShouldImplTraitCase::new("std::default::Default", "default", 0, FN_HEADER, SelfKind::No, OutType::Any, true), ShouldImplTraitCase::new("std::ops::Deref", "deref", 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true), ShouldImplTraitCase::new("std::ops::DerefMut", "deref_mut", 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), @@ -3827,7 +3820,7 @@ enum SelfKind { Value, Ref, RefMut, - No, + No, // When we want the first argument type to be different than `Self` } impl SelfKind { diff --git a/clippy_lints/src/methods/option_as_ref_deref.rs b/clippy_lints/src/methods/option_as_ref_deref.rs index c409268de769..6fb92d1c663c 100644 --- a/clippy_lints/src/methods/option_as_ref_deref.rs +++ b/clippy_lints/src/methods/option_as_ref_deref.rs @@ -98,13 +98,12 @@ pub(super) fn check<'tcx>( format!(".as_ref().map({})", snippet(cx, map_arg.span, "..")) }; let method_hint = if is_mut { "as_deref_mut" } else { "as_deref" }; - let hint = format!("{}.{}()", snippet(cx, as_ref_recv.span, ".."), method_hint); - let suggestion = format!("try using {} instead", method_hint); + let hint = format!("{}.{method_hint}()", snippet(cx, as_ref_recv.span, "..")); + let suggestion = format!("try using {method_hint} instead"); let msg = format!( - "called `{0}` on an Option value. This can be done more directly \ - by calling `{1}` instead", - current_method, hint + "called `{current_method}` on an Option value. This can be done more directly \ + by calling `{hint}` instead" ); span_lint_and_sugg( cx, diff --git a/clippy_lints/src/methods/option_map_or_none.rs b/clippy_lints/src/methods/option_map_or_none.rs index 6657cdccd010..3a23ecc50dc1 100644 --- a/clippy_lints/src/methods/option_map_or_none.rs +++ b/clippy_lints/src/methods/option_map_or_none.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_lang_ctor, path_def_id}; +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}; @@ -51,22 +51,12 @@ pub(super) fn check<'tcx>( return; } - let default_arg_is_none = if let hir::ExprKind::Path(ref qpath) = def_arg.kind { - is_lang_ctor(cx, qpath, OptionNone) - } else { - return; - }; - - if !default_arg_is_none { + if !is_res_lang_ctor(cx, path_res(cx, def_arg), OptionNone) { // nothing to lint! return; } - let f_arg_is_some = if let hir::ExprKind::Path(ref qpath) = map_arg.kind { - is_lang_ctor(cx, qpath, OptionSome) - } else { - false - }; + let f_arg_is_some = is_res_lang_ctor(cx, path_res(cx, map_arg), OptionSome); if is_option { let self_snippet = snippet(cx, recv.span, ".."); @@ -87,7 +77,7 @@ pub(super) fn check<'tcx>( expr.span, msg, "try using `map` instead", - format!("{0}.map({1} {2})", self_snippet, arg_snippet,func_snippet), + format!("{self_snippet}.map({arg_snippet} {func_snippet})"), Applicability::MachineApplicable, ); } @@ -102,7 +92,7 @@ pub(super) fn check<'tcx>( expr.span, msg, "try using `and_then` instead", - format!("{0}.and_then({1})", self_snippet, func_snippet), + format!("{self_snippet}.and_then({func_snippet})"), Applicability::MachineApplicable, ); } else if f_arg_is_some { @@ -115,7 +105,7 @@ pub(super) fn check<'tcx>( expr.span, msg, "try using `ok` instead", - format!("{0}.ok()", self_snippet), + format!("{self_snippet}.ok()"), Applicability::MachineApplicable, ); } diff --git a/clippy_lints/src/methods/option_map_unwrap_or.rs b/clippy_lints/src/methods/option_map_unwrap_or.rs index 3c4002a3aef9..30421a6dd5af 100644 --- a/clippy_lints/src/methods/option_map_unwrap_or.rs +++ b/clippy_lints/src/methods/option_map_unwrap_or.rs @@ -65,9 +65,8 @@ pub(super) fn check<'tcx>( "map_or(, )" }; let msg = &format!( - "called `map().unwrap_or({})` on an `Option` value. \ - This can be done more directly by calling `{}` instead", - arg, suggest + "called `map().unwrap_or({arg})` on an `Option` value. \ + This can be done more directly by calling `{suggest}` instead" ); span_lint_and_then(cx, MAP_UNWRAP_OR, expr.span, msg, |diag| { @@ -82,10 +81,10 @@ pub(super) fn check<'tcx>( ]; if !unwrap_snippet_none { - suggestion.push((map_arg_span.with_hi(map_arg_span.lo()), format!("{}, ", unwrap_snippet))); + suggestion.push((map_arg_span.with_hi(map_arg_span.lo()), format!("{unwrap_snippet}, "))); } - diag.multipart_suggestion(&format!("use `{}` instead", suggest), suggestion, applicability); + diag.multipart_suggestion(&format!("use `{suggest}` instead"), suggestion, applicability); }); } } diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index b43b9258c471..6a35024d0361 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -62,9 +62,9 @@ pub(super) fn check<'tcx>( cx, OR_FUN_CALL, method_span.with_hi(span.hi()), - &format!("use of `{}` followed by a call to `{}`", name, path), + &format!("use of `{name}` followed by a call to `{path}`"), "try this", - format!("{}()", sugg), + format!("{sugg}()"), Applicability::MachineApplicable, ); @@ -131,7 +131,7 @@ pub(super) fn check<'tcx>( if use_lambda { let l_arg = if fn_has_arguments { "_" } else { "" }; - format!("|{}| {}", l_arg, snippet).into() + format!("|{l_arg}| {snippet}").into() } else { snippet } @@ -141,9 +141,9 @@ pub(super) fn check<'tcx>( cx, OR_FUN_CALL, span_replace_word, - &format!("use of `{}` followed by a function call", name), + &format!("use of `{name}` followed by a function call"), "try this", - format!("{}_{}({})", name, suffix, sugg), + format!("{name}_{suffix}({sugg})"), Applicability::HasPlaceholders, ); } diff --git a/clippy_lints/src/methods/or_then_unwrap.rs b/clippy_lints/src/methods/or_then_unwrap.rs index be5768c35450..55ba6e262df7 100644 --- a/clippy_lints/src/methods/or_then_unwrap.rs +++ b/clippy_lints/src/methods/or_then_unwrap.rs @@ -1,6 +1,6 @@ use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{diagnostics::span_lint_and_sugg, is_lang_ctor}; +use clippy_utils::{diagnostics::span_lint_and_sugg, is_res_lang_ctor, path_res}; use rustc_errors::Applicability; use rustc_hir::{lang_items::LangItem, Expr, ExprKind}; use rustc_lint::LateContext; @@ -58,8 +58,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 - && let ExprKind::Path(qpath) = &some_expr.kind - && is_lang_ctor(cx, qpath, item) + && is_res_lang_ctor(cx, path_res(cx, some_expr), item) { Some(arg.span) } else { diff --git a/clippy_lints/src/methods/search_is_some.rs b/clippy_lints/src/methods/search_is_some.rs index 7572ba3fe9a9..324c9c17b5a9 100644 --- a/clippy_lints/src/methods/search_is_some.rs +++ b/clippy_lints/src/methods/search_is_some.rs @@ -30,10 +30,7 @@ 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) { - let msg = format!( - "called `{}()` after searching an `Iterator` with `{}`", - option_check_method, search_method - ); + 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()` @@ -86,8 +83,7 @@ pub(super) fn check<'tcx>( &msg, "use `!_.any()` instead", format!( - "!{}.any({})", - iter, + "!{iter}.any({})", any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str) ), applicability, @@ -119,7 +115,7 @@ pub(super) fn check<'tcx>( if is_string_or_str_slice(search_recv); if is_string_or_str_slice(search_arg); then { - let msg = format!("called `{}()` after calling `find()` on a string", option_check_method); + let msg = format!("called `{option_check_method}()` after calling `find()` on a string"); match option_check_method { "is_some" => { let mut applicability = Applicability::MachineApplicable; @@ -130,7 +126,7 @@ pub(super) fn check<'tcx>( method_span.with_hi(expr.span.hi()), &msg, "use `contains()` instead", - format!("contains({})", find_arg), + format!("contains({find_arg})"), applicability, ); }, @@ -144,7 +140,7 @@ pub(super) fn check<'tcx>( expr.span, &msg, "use `!_.contains()` instead", - format!("!{}.contains({})", string, find_arg), + format!("!{string}.contains({find_arg})"), applicability, ); }, diff --git a/clippy_lints/src/methods/single_char_insert_string.rs b/clippy_lints/src/methods/single_char_insert_string.rs index 18b6b5be175d..44a7ad394fa0 100644 --- a/clippy_lints/src/methods/single_char_insert_string.rs +++ b/clippy_lints/src/methods/single_char_insert_string.rs @@ -14,7 +14,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir:: let base_string_snippet = snippet_with_applicability(cx, receiver.span.source_callsite(), "_", &mut applicability); let pos_arg = snippet_with_applicability(cx, args[0].span, "..", &mut applicability); - let sugg = format!("{}.insert({}, {})", base_string_snippet, pos_arg, extension_string); + let sugg = format!("{base_string_snippet}.insert({pos_arg}, {extension_string})"); span_lint_and_sugg( cx, SINGLE_CHAR_ADD_STR, diff --git a/clippy_lints/src/methods/single_char_push_string.rs b/clippy_lints/src/methods/single_char_push_string.rs index 9ea6751956ab..0698bd6a0c52 100644 --- a/clippy_lints/src/methods/single_char_push_string.rs +++ b/clippy_lints/src/methods/single_char_push_string.rs @@ -13,7 +13,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir:: if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[0], &mut applicability) { let base_string_snippet = snippet_with_applicability(cx, receiver.span.source_callsite(), "..", &mut applicability); - let sugg = format!("{}.push({})", base_string_snippet, extension_string); + let sugg = format!("{base_string_snippet}.push({extension_string})"); span_lint_and_sugg( cx, SINGLE_CHAR_ADD_STR, diff --git a/clippy_lints/src/methods/stable_sort_primitive.rs b/clippy_lints/src/methods/stable_sort_primitive.rs index 91951c65bb30..09c8ca4cbe44 100644 --- a/clippy_lints/src/methods/stable_sort_primitive.rs +++ b/clippy_lints/src/methods/stable_sort_primitive.rs @@ -17,11 +17,11 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, recv: &'tcx cx, STABLE_SORT_PRIMITIVE, e.span, - &format!("used `sort` on primitive type `{}`", slice_type), + &format!("used `sort` on primitive type `{slice_type}`"), |diag| { let mut app = Applicability::MachineApplicable; let recv_snip = snippet_with_context(cx, recv.span, e.span.ctxt(), "..", &mut app).0; - diag.span_suggestion(e.span, "try", format!("{}.sort_unstable()", recv_snip), app); + diag.span_suggestion(e.span, "try", format!("{recv_snip}.sort_unstable()"), app); diag.note( "an unstable sort typically performs faster without any observable difference for this data type", ); diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs index 9ca4d65550d3..ae3594bd36c3 100644 --- a/clippy_lints/src/methods/str_splitn.rs +++ b/clippy_lints/src/methods/str_splitn.rs @@ -2,11 +2,11 @@ use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_with_context; use clippy_utils::usage::local_used_after_expr; -use clippy_utils::visitors::expr_visitor; +use clippy_utils::visitors::{for_each_expr_with_closures, Descend}; use clippy_utils::{is_diag_item_method, match_def_path, meets_msrv, msrvs, path_to_local_id, paths}; +use core::ops::ControlFlow; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::intravisit::Visitor; use rustc_hir::{ BindingAnnotation, Expr, ExprKind, HirId, LangItem, Local, MatchSource, Node, Pat, PatKind, QPath, Stmt, StmtKind, }; @@ -211,7 +211,7 @@ fn indirect_usage<'tcx>( binding: HirId, ctxt: SyntaxContext, ) -> Option> { - if let StmtKind::Local(Local { + if let StmtKind::Local(&Local { pat: Pat { kind: PatKind::Binding(BindingAnnotation::NONE, _, ident, None), .. @@ -222,14 +222,12 @@ fn indirect_usage<'tcx>( }) = stmt.kind { let mut path_to_binding = None; - expr_visitor(cx, |expr| { - if path_to_local_id(expr, binding) { - path_to_binding = Some(expr); + let _: Option = for_each_expr_with_closures(cx, init_expr, |e| { + if path_to_local_id(e, binding) { + path_to_binding = Some(e); } - - path_to_binding.is_none() - }) - .visit_expr(init_expr); + ControlFlow::Continue(Descend::from(path_to_binding.is_none())) + }); let mut parents = cx.tcx.hir().parent_iter(path_to_binding?.hir_id); let iter_usage = parse_iter_usage(cx, ctxt, &mut parents)?; @@ -250,7 +248,7 @@ fn indirect_usage<'tcx>( .. } = iter_usage { - if parent_id == *local_hir_id { + if parent_id == local_hir_id { return Some(IndirectUsage { name: ident.name, span: stmt.span, diff --git a/clippy_lints/src/methods/string_extend_chars.rs b/clippy_lints/src/methods/string_extend_chars.rs index 143dcee35052..6974260f70db 100644 --- a/clippy_lints/src/methods/string_extend_chars.rs +++ b/clippy_lints/src/methods/string_extend_chars.rs @@ -34,9 +34,8 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr "calling `.extend(_.chars())`", "try this", format!( - "{}.push_str({}{})", + "{}.push_str({ref_str}{})", snippet_with_applicability(cx, recv.span, "..", &mut applicability), - ref_str, snippet_with_applicability(cx, target.span, "..", &mut applicability) ), applicability, diff --git a/clippy_lints/src/methods/suspicious_splitn.rs b/clippy_lints/src/methods/suspicious_splitn.rs index 55567d8625e5..219a9edd6576 100644 --- a/clippy_lints/src/methods/suspicious_splitn.rs +++ b/clippy_lints/src/methods/suspicious_splitn.rs @@ -24,10 +24,10 @@ pub(super) fn check(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, se } let (msg, note_msg) = if count == 0 { - (format!("`{}` called with `0` splits", method_name), + (format!("`{method_name}` called with `0` splits"), "the resulting iterator will always return `None`") } else { - (format!("`{}` called with `1` split", method_name), + (format!("`{method_name}` called with `1` split"), if self_ty.is_slice() { "the resulting iterator will always return the entire slice followed by `None`" } else { diff --git a/clippy_lints/src/methods/suspicious_to_owned.rs b/clippy_lints/src/methods/suspicious_to_owned.rs index 6b306fbf0085..15c1c618c513 100644 --- a/clippy_lints/src/methods/suspicious_to_owned.rs +++ b/clippy_lints/src/methods/suspicious_to_owned.rs @@ -24,9 +24,9 @@ pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) - cx, SUSPICIOUS_TO_OWNED, expr.span, - &format!("this `to_owned` call clones the {0} itself and does not cause the {0} contents to become owned", input_type), + &format!("this `to_owned` call clones the {input_type} itself and does not cause the {input_type} contents to become owned"), "consider using, depending on intent", - format!("{0}.clone()` or `{0}.into_owned()", recv_snip), + format!("{recv_snip}.clone()` or `{recv_snip}.into_owned()"), app, ); return true; diff --git a/clippy_lints/src/methods/unnecessary_filter_map.rs b/clippy_lints/src/methods/unnecessary_filter_map.rs index 4e8c201f470b..1cef6226ad4f 100644 --- a/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -2,9 +2,10 @@ use super::utils::clone_or_copy_needed; use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::is_copy; use clippy_utils::usage::mutated_variables; -use clippy_utils::{is_lang_ctor, is_trait_method, path_to_local_id}; +use clippy_utils::visitors::{for_each_expr, Descend}; +use clippy_utils::{is_res_lang_ctor, is_trait_method, path_res, path_to_local_id}; +use core::ops::ControlFlow; use rustc_hir as hir; -use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_lint::LateContext; use rustc_middle::ty; @@ -13,7 +14,7 @@ use rustc_span::sym; use super::UNNECESSARY_FILTER_MAP; use super::UNNECESSARY_FIND_MAP; -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>, name: &str) { +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>, arg: &'tcx hir::Expr<'tcx>, name: &str) { if !is_trait_method(cx, expr, sym::Iterator) { return; } @@ -26,10 +27,16 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr< let (mut found_mapping, mut found_filtering) = check_expression(cx, arg_id, body.value); - let mut return_visitor = ReturnVisitor::new(cx, arg_id); - return_visitor.visit_expr(body.value); - found_mapping |= return_visitor.found_mapping; - found_filtering |= return_visitor.found_filtering; + let _: Option = for_each_expr(body.value, |e| { + if let hir::ExprKind::Ret(Some(e)) = &e.kind { + let (found_mapping_res, found_filtering_res) = check_expression(cx, arg_id, e); + found_mapping |= found_mapping_res; + found_filtering |= found_filtering_res; + ControlFlow::Continue(Descend::No) + } else { + ControlFlow::Continue(Descend::Yes) + } + }); let in_ty = cx.typeck_results().node_type(body.params[0].hir_id); let sugg = if !found_filtering { @@ -54,22 +61,20 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr< UNNECESSARY_FIND_MAP }, expr.span, - &format!("this `.{}` can be written more simply using `.{}`", name, sugg), + &format!("this `.{name}` can be written more simply using `.{sugg}`"), ); } } // 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 { + match expr.kind { hir::ExprKind::Call(func, args) => { - if let hir::ExprKind::Path(ref path) = func.kind { - if is_lang_ctor(cx, path, OptionSome) { - if path_to_local_id(&args[0], arg_id) { - return (false, false); - } - return (true, false); + if is_res_lang_ctor(cx, path_res(cx, func), OptionSome) { + if path_to_local_id(&args[0], arg_id) { + return (false, false); } + return (true, false); } (true, true) }, @@ -80,7 +85,7 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc hir::ExprKind::Match(_, arms, _) => { let mut found_mapping = false; let mut found_filtering = false; - for arm in *arms { + for arm in arms { let (m, f) = check_expression(cx, arg_id, arm.body); found_mapping |= m; found_filtering |= f; @@ -93,39 +98,9 @@ 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(path) if is_lang_ctor(cx, path, OptionNone) => (false, true), + hir::ExprKind::Path(ref path) if is_res_lang_ctor(cx, cx.qpath_res(path, expr.hir_id), OptionNone) => { + (false, true) + }, _ => (true, true), } } - -struct ReturnVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - arg_id: hir::HirId, - // Found a non-None return that isn't Some(input) - found_mapping: bool, - // Found a return that isn't Some - found_filtering: bool, -} - -impl<'a, 'tcx> ReturnVisitor<'a, 'tcx> { - fn new(cx: &'a LateContext<'tcx>, arg_id: hir::HirId) -> ReturnVisitor<'a, 'tcx> { - ReturnVisitor { - cx, - arg_id, - found_mapping: false, - found_filtering: false, - } - } -} - -impl<'a, 'tcx> Visitor<'tcx> for ReturnVisitor<'a, 'tcx> { - fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { - if let hir::ExprKind::Ret(Some(expr)) = &expr.kind { - let (found_mapping, found_filtering) = check_expression(self.cx, self.arg_id, expr); - self.found_mapping |= found_mapping; - self.found_filtering |= found_filtering; - } else { - walk_expr(self, expr); - } - } -} diff --git a/clippy_lints/src/methods/unnecessary_fold.rs b/clippy_lints/src/methods/unnecessary_fold.rs index c17ef6809f91..aa87dead38f0 100644 --- a/clippy_lints/src/methods/unnecessary_fold.rs +++ b/clippy_lints/src/methods/unnecessary_fold.rs @@ -49,15 +49,12 @@ pub(super) fn check( let mut applicability = Applicability::MachineApplicable; let sugg = if replacement_has_args { format!( - "{replacement}(|{s}| {r})", - replacement = replacement_method_name, - s = second_arg_ident, + "{replacement_method_name}(|{second_arg_ident}| {r})", r = snippet_with_applicability(cx, right_expr.span, "EXPR", &mut applicability), ) } else { format!( - "{replacement}()", - replacement = replacement_method_name, + "{replacement_method_name}()", ) }; diff --git a/clippy_lints/src/methods/unnecessary_iter_cloned.rs b/clippy_lints/src/methods/unnecessary_iter_cloned.rs index 95138c0e25b0..1966a85f7a73 100644 --- a/clippy_lints/src/methods/unnecessary_iter_cloned.rs +++ b/clippy_lints/src/methods/unnecessary_iter_cloned.rs @@ -68,7 +68,7 @@ pub fn check_for_loop_iter( cx, UNNECESSARY_TO_OWNED, expr.span, - &format!("unnecessary use of `{}`", method_name), + &format!("unnecessary use of `{method_name}`"), |diag| { // If `check_into_iter_call_arg` called `check_for_loop_iter` because a call to // a `to_owned`-like function was removed, then the next suggestion may be diff --git a/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/clippy_lints/src/methods/unnecessary_lazy_eval.rs index a187a8d6016f..0e73459ad65f 100644 --- a/clippy_lints/src/methods/unnecessary_lazy_eval.rs +++ b/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{eager_or_lazy, usage}; +use clippy_utils::{eager_or_lazy, is_from_proc_macro, usage}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -18,6 +18,10 @@ pub(super) fn check<'tcx>( arg: &'tcx hir::Expr<'_>, simplify_using: &str, ) { + if is_from_proc_macro(cx, expr) { + return; + } + 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_bool = cx.typeck_results().expr_ty(recv).is_bool(); @@ -58,8 +62,8 @@ pub(super) fn check<'tcx>( span_lint_and_then(cx, UNNECESSARY_LAZY_EVALUATIONS, expr.span, msg, |diag| { diag.span_suggestion( span, - &format!("use `{}(..)` instead", simplify_using), - format!("{}({})", simplify_using, snippet(cx, body_expr.span, "..")), + &format!("use `{simplify_using}(..)` instead"), + format!("{simplify_using}({})", snippet(cx, body_expr.span, "..")), applicability, ); }); diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index 559f32a563ed..9ab0d6141146 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -8,6 +8,7 @@ use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trai use clippy_utils::{meets_msrv, msrvs}; use rustc_errors::Applicability; use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind, ItemKind, LangItem, Node}; +use rustc_hir_analysis::check::{FnCtxt, Inherited}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::mir::Mutability; @@ -18,7 +19,6 @@ use rustc_middle::ty::{self, ParamTy, PredicateKind, ProjectionPredicate, TraitP use rustc_semver::RustcVersion; use rustc_span::{sym, Symbol}; use rustc_trait_selection::traits::{query::evaluate_obligation::InferCtxtExt as _, Obligation, ObligationCause}; -use rustc_hir_analysis::check::{FnCtxt, Inherited}; use std::cmp::max; use super::UNNECESSARY_TO_OWNED; @@ -132,12 +132,11 @@ fn check_addr_of_expr( cx, UNNECESSARY_TO_OWNED, parent.span, - &format!("unnecessary use of `{}`", method_name), + &format!("unnecessary use of `{method_name}`"), "use", format!( - "{:&>width$}{}", + "{:&>width$}{receiver_snippet}", "", - receiver_snippet, width = n_target_refs - n_receiver_refs ), Applicability::MachineApplicable, @@ -154,7 +153,7 @@ fn check_addr_of_expr( cx, UNNECESSARY_TO_OWNED, parent.span, - &format!("unnecessary use of `{}`", method_name), + &format!("unnecessary use of `{method_name}`"), "use", receiver_snippet, Applicability::MachineApplicable, @@ -164,7 +163,7 @@ fn check_addr_of_expr( cx, UNNECESSARY_TO_OWNED, expr.span.with_lo(receiver.span.hi()), - &format!("unnecessary use of `{}`", method_name), + &format!("unnecessary use of `{method_name}`"), "remove this", String::new(), Applicability::MachineApplicable, @@ -181,9 +180,9 @@ fn check_addr_of_expr( cx, UNNECESSARY_TO_OWNED, parent.span, - &format!("unnecessary use of `{}`", method_name), + &format!("unnecessary use of `{method_name}`"), "use", - format!("{}.as_ref()", receiver_snippet), + format!("{receiver_snippet}.as_ref()"), Applicability::MachineApplicable, ); return true; @@ -228,9 +227,9 @@ fn check_into_iter_call_arg( cx, UNNECESSARY_TO_OWNED, parent.span, - &format!("unnecessary use of `{}`", method_name), + &format!("unnecessary use of `{method_name}`"), "use", - format!("{}.iter().{}()", receiver_snippet, cloned_or_copied), + format!("{receiver_snippet}.iter().{cloned_or_copied}()"), Applicability::MaybeIncorrect, ); return true; @@ -275,9 +274,9 @@ fn check_other_call_arg<'tcx>( cx, UNNECESSARY_TO_OWNED, maybe_arg.span, - &format!("unnecessary use of `{}`", method_name), + &format!("unnecessary use of `{method_name}`"), "use", - format!("{:&>width$}{}", "", receiver_snippet, width = n_refs), + format!("{:&>n_refs$}{receiver_snippet}", ""), Applicability::MachineApplicable, ); return true; diff --git a/clippy_lints/src/methods/useless_asref.rs b/clippy_lints/src/methods/useless_asref.rs index ca5d33ee8b07..c1139d84e2f4 100644 --- a/clippy_lints/src/methods/useless_asref.rs +++ b/clippy_lints/src/methods/useless_asref.rs @@ -1,11 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::walk_ptrs_ty_depth; -use clippy_utils::{get_parent_expr, match_trait_method, paths}; +use clippy_utils::{get_parent_expr, is_trait_method}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; +use rustc_span::sym; use super::USELESS_ASREF; @@ -13,7 +14,7 @@ use super::USELESS_ASREF; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, 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 - if match_trait_method(cx, expr, &paths::ASREF_TRAIT) || match_trait_method(cx, expr, &paths::ASMUT_TRAIT) { + if is_trait_method(cx, expr, sym::AsRef) || is_trait_method(cx, expr, 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); @@ -35,7 +36,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, cx, USELESS_ASREF, expr.span, - &format!("this call to `{}` does nothing", call_name), + &format!("this call to `{call_name}` does nothing"), "try this", snippet_with_applicability(cx, recvr.span, "..", &mut applicability).to_string(), applicability, diff --git a/clippy_lints/src/methods/wrong_self_convention.rs b/clippy_lints/src/methods/wrong_self_convention.rs index 4b368d3ffae2..1fbf783b8860 100644 --- a/clippy_lints/src/methods/wrong_self_convention.rs +++ b/clippy_lints/src/methods/wrong_self_convention.rs @@ -61,20 +61,20 @@ impl Convention { impl fmt::Display for Convention { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { match *self { - Self::Eq(this) => format!("`{}`", this).fmt(f), - Self::StartsWith(this) => format!("`{}*`", this).fmt(f), - Self::EndsWith(this) => format!("`*{}`", this).fmt(f), - Self::NotEndsWith(this) => format!("`~{}`", this).fmt(f), + Self::Eq(this) => format!("`{this}`").fmt(f), + Self::StartsWith(this) => format!("`{this}*`").fmt(f), + Self::EndsWith(this) => format!("`*{this}`").fmt(f), + Self::NotEndsWith(this) => format!("`~{this}`").fmt(f), Self::IsSelfTypeCopy(is_true) => { format!("`self` type is{} `Copy`", if is_true { "" } else { " not" }).fmt(f) }, Self::ImplementsTrait(is_true) => { let (negation, s_suffix) = if is_true { ("", "s") } else { (" does not", "") }; - format!("method{} implement{} a trait", negation, s_suffix).fmt(f) + format!("method{negation} implement{s_suffix} a trait").fmt(f) }, Self::IsTraitItem(is_true) => { let suffix = if is_true { " is" } else { " is not" }; - format!("method{} a trait item", suffix).fmt(f) + format!("method{suffix} a trait item").fmt(f) }, } } @@ -138,8 +138,7 @@ pub(super) fn check<'tcx>( WRONG_SELF_CONVENTION, first_arg_span, &format!( - "{} usually take {}", - suggestion, + "{suggestion} usually take {}", &self_kinds .iter() .map(|k| k.description()) diff --git a/clippy_lints/src/minmax.rs b/clippy_lints/src/minmax.rs index 4d8579135fc0..4f967755bfa1 100644 --- a/clippy_lints/src/minmax.rs +++ b/clippy_lints/src/minmax.rs @@ -1,6 +1,6 @@ use clippy_utils::consts::{constant_simple, Constant}; use clippy_utils::diagnostics::span_lint; -use clippy_utils::{match_trait_method, paths}; +use clippy_utils::is_trait_method; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -83,7 +83,7 @@ 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() || match_trait_method(cx, expr, &paths::ORD) { + if cx.typeck_results().expr_ty(receiver).is_floating_point() || is_trait_method(cx, expr, sym::Ord) { if path.ident.name == sym!(max) { fetch_const(cx, Some(receiver), args, MinMax::Max) } else if path.ident.name == sym!(min) { diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index ea245edd7704..516dee20f8b1 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::source::{snippet, snippet_opt}; use if_chain::if_chain; -use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::{ @@ -15,7 +14,7 @@ use rustc_span::hygiene::DesugaringKind; use rustc_span::source_map::{ExpnKind, Span}; use clippy_utils::sugg::Sugg; -use clippy_utils::{get_parent_expr, in_constant, iter_input_pats, last_path_segment, SpanlessEq}; +use clippy_utils::{get_parent_expr, in_constant, is_integer_literal, iter_input_pats, last_path_segment, SpanlessEq}; declare_clippy_lint! { /// ### What it does @@ -178,7 +177,7 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { ("", sugg_init.addr()) }; let tyopt = if let Some(ty) = local.ty { - format!(": &{mutopt}{ty}", mutopt=mutopt, ty=snippet(cx, ty.span, "..")) + format!(": &{mutopt}{ty}", ty=snippet(cx, ty.span, "..")) } else { String::new() }; @@ -195,8 +194,6 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { format!( "let {name}{tyopt} = {initref};", name=snippet(cx, name.span, ".."), - tyopt=tyopt, - initref=initref, ), Applicability::MachineApplicable, ); @@ -222,8 +219,7 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { stmt.span, "replace it with", format!( - "if {} {{ {}; }}", - sugg, + "if {sugg} {{ {}; }}", &snippet(cx, b.span, ".."), ), Applicability::MachineApplicable, // snippet @@ -275,9 +271,8 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { USED_UNDERSCORE_BINDING, expr.span, &format!( - "used binding `{}` which is prefixed with an underscore. A leading \ - underscore signals that a binding will not be used", - binding + "used binding `{binding}` which is prefixed with an underscore. A leading \ + underscore signals that a binding will not be used" ), ); } @@ -318,8 +313,7 @@ fn non_macro_local(cx: &LateContext<'_>, res: def::Res) -> bool { fn check_cast(cx: &LateContext<'_>, span: Span, e: &Expr<'_>, ty: &hir::Ty<'_>) { if_chain! { if let TyKind::Ptr(ref mut_ty) = ty.kind; - if let ExprKind::Lit(ref lit) = e.kind; - if let LitKind::Int(0, _) = lit.node; + if is_integer_literal(e, 0); if !in_constant(cx, e.hir_id); then { let (msg, sugg_fn) = match mut_ty.mutbl { @@ -328,12 +322,12 @@ fn check_cast(cx: &LateContext<'_>, span: Span, e: &Expr<'_>, ty: &hir::Ty<'_>) }; let (sugg, appl) = if let TyKind::Infer = mut_ty.ty.kind { - (format!("{}()", sugg_fn), Applicability::MachineApplicable) + (format!("{sugg_fn}()"), Applicability::MachineApplicable) } else if let Some(mut_ty_snip) = snippet_opt(cx, mut_ty.ty.span) { - (format!("{}::<{}>()", sugg_fn, mut_ty_snip), Applicability::MachineApplicable) + (format!("{sugg_fn}::<{mut_ty_snip}>()"), Applicability::MachineApplicable) } else { // `MaybeIncorrect` as type inference may not work with the suggested code - (format!("{}()", sugg_fn), Applicability::MaybeIncorrect) + (format!("{sugg_fn}()"), Applicability::MaybeIncorrect) }; span_lint_and_sugg(cx, ZERO_PTR, span, msg, "try", sugg, appl); } diff --git a/clippy_lints/src/misc_early/literal_suffix.rs b/clippy_lints/src/misc_early/literal_suffix.rs index 1165c19a0cf0..62c6ca32d31a 100644 --- a/clippy_lints/src/misc_early/literal_suffix.rs +++ b/clippy_lints/src/misc_early/literal_suffix.rs @@ -18,9 +18,9 @@ pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: &str, suffix: &s cx, SEPARATED_LITERAL_SUFFIX, lit.span, - &format!("{} type suffix should not be separated by an underscore", sugg_type), + &format!("{sugg_type} type suffix should not be separated by an underscore"), "remove the underscore", - format!("{}{}", &lit_snip[..maybe_last_sep_idx], suffix), + format!("{}{suffix}", &lit_snip[..maybe_last_sep_idx]), Applicability::MachineApplicable, ); } else { @@ -28,9 +28,9 @@ pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: &str, suffix: &s cx, UNSEPARATED_LITERAL_SUFFIX, lit.span, - &format!("{} type suffix should be separated by an underscore", sugg_type), + &format!("{sugg_type} type suffix should be separated by an underscore"), "add an underscore", - format!("{}_{}", &lit_snip[..=maybe_last_sep_idx], suffix), + format!("{}_{suffix}", &lit_snip[..=maybe_last_sep_idx]), Applicability::MachineApplicable, ); } diff --git a/clippy_lints/src/misc_early/mod.rs b/clippy_lints/src/misc_early/mod.rs index 704918c0b979..c8227ca44505 100644 --- a/clippy_lints/src/misc_early/mod.rs +++ b/clippy_lints/src/misc_early/mod.rs @@ -357,9 +357,8 @@ impl EarlyLintPass for MiscEarlyLints { DUPLICATE_UNDERSCORE_ARGUMENT, *correspondence, &format!( - "`{}` already exists, having another argument having almost the same \ - name makes code comprehension and documentation more difficult", - arg_name + "`{arg_name}` already exists, having another argument having almost the same \ + name makes code comprehension and documentation more difficult" ), ); } diff --git a/clippy_lints/src/misc_early/unneeded_field_pattern.rs b/clippy_lints/src/misc_early/unneeded_field_pattern.rs index fff533167ede..676e5d40bb77 100644 --- a/clippy_lints/src/misc_early/unneeded_field_pattern.rs +++ b/clippy_lints/src/misc_early/unneeded_field_pattern.rs @@ -27,7 +27,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, pat: &Pat) { pat.span, "all the struct fields are matched to a wildcard pattern, consider using `..`", None, - &format!("try with `{} {{ .. }}` instead", type_name), + &format!("try with `{type_name} {{ .. }}` instead"), ); return; } @@ -63,7 +63,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, pat: &Pat) { "you matched a field with a wildcard pattern, consider using `..` \ instead", None, - &format!("try with `{} {{ {}, .. }}`", type_name, normal[..].join(", ")), + &format!("try with `{type_name} {{ {}, .. }}`", normal[..].join(", ")), ); } } diff --git a/clippy_lints/src/mismatching_type_param_order.rs b/clippy_lints/src/mismatching_type_param_order.rs index 020efeaebf02..6dd76a6531e4 100644 --- a/clippy_lints/src/mismatching_type_param_order.rs +++ b/clippy_lints/src/mismatching_type_param_order.rs @@ -91,10 +91,9 @@ impl<'tcx> LateLintPass<'tcx> for TypeParamMismatch { let type_name = segment.ident; for (i, (impl_param_name, impl_param_span)) in impl_params.iter().enumerate() { if mismatch_param_name(i, impl_param_name, &type_param_names_hashmap) { - let msg = format!("`{}` has a similarly named generic type parameter `{}` in its declaration, but in a different order", - type_name, impl_param_name); - let help = format!("try `{}`, or a name that does not conflict with `{}`'s generic params", - type_param_names[i], type_name); + let msg = format!("`{type_name}` has a similarly named generic type parameter `{impl_param_name}` in its declaration, but in a different order"); + let help = format!("try `{}`, or a name that does not conflict with `{type_name}`'s generic params", + type_param_names[i]); span_lint_and_help( cx, MISMATCHING_TYPE_PARAM_ORDER, diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs index 00376f0d7902..71cc0d0a81cd 100644 --- a/clippy_lints/src/missing_const_for_fn.rs +++ b/clippy_lints/src/missing_const_for_fn.rs @@ -8,12 +8,12 @@ use rustc_hir as hir; use rustc_hir::def_id::CRATE_DEF_ID; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, Constness, FnDecl, GenericParamKind, HirId}; +use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Span; -use rustc_hir_analysis::hir_ty_to_ty; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/missing_doc.rs b/clippy_lints/src/missing_doc.rs index 472195566768..b3f1553cfea9 100644 --- a/clippy_lints/src/missing_doc.rs +++ b/clippy_lints/src/missing_doc.rs @@ -103,7 +103,7 @@ impl MissingDoc { cx, MISSING_DOCS_IN_PRIVATE_ITEMS, sp, - &format!("missing documentation for {} {}", article, desc), + &format!("missing documentation for {article} {desc}"), ); } } diff --git a/clippy_lints/src/missing_enforced_import_rename.rs b/clippy_lints/src/missing_enforced_import_rename.rs index 3d0a23822838..872679f25ab5 100644 --- a/clippy_lints/src/missing_enforced_import_rename.rs +++ b/clippy_lints/src/missing_enforced_import_rename.rs @@ -58,7 +58,8 @@ impl_lint_pass!(ImportRename => [MISSING_ENFORCED_IMPORT_RENAMES]); impl LateLintPass<'_> for ImportRename { fn check_crate(&mut self, cx: &LateContext<'_>) { for Rename { path, rename } in &self.conf_renames { - if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &path.split("::").collect::>()) { + let segs = path.split("::").collect::>(); + if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs, None) { self.renames.insert(id, Symbol::intern(rename)); } } @@ -90,9 +91,7 @@ impl LateLintPass<'_> for ImportRename { "this import should be renamed", "try", format!( - "{} as {}", - import, - name, + "{import} as {name}", ), Applicability::MachineApplicable, ); diff --git a/clippy_lints/src/missing_inline.rs b/clippy_lints/src/missing_inline.rs index 9d5764ac0926..655df5419ac6 100644 --- a/clippy_lints/src/missing_inline.rs +++ b/clippy_lints/src/missing_inline.rs @@ -65,7 +65,7 @@ fn check_missing_inline_attrs(cx: &LateContext<'_>, attrs: &[ast::Attribute], sp cx, MISSING_INLINE_IN_PUBLIC_ITEMS, sp, - &format!("missing `#[inline]` for {}", desc), + &format!("missing `#[inline]` for {desc}"), ); } } diff --git a/clippy_lints/src/module_style.rs b/clippy_lints/src/module_style.rs index 102b9fbae83c..0742943dff2b 100644 --- a/clippy_lints/src/module_style.rs +++ b/clippy_lints/src/module_style.rs @@ -118,13 +118,7 @@ impl EarlyLintPass for ModStyle { SELF_NAMED_MODULE_FILES, Span::new(file.start_pos, file.start_pos, SyntaxContext::root(), None), format!("`mod.rs` files are required, found `{}`", path.display()), - |lint| { - lint.help(format!( - "move `{}` to `{}`", - path.display(), - correct.display(), - )) - }, + |lint| lint.help(format!("move `{}` to `{}`", path.display(), correct.display(),)), ); } } diff --git a/clippy_lints/src/mut_reference.rs b/clippy_lints/src/mut_reference.rs index 084c0d471dde..4547ed7eafc8 100644 --- a/clippy_lints/src/mut_reference.rs +++ b/clippy_lints/src/mut_reference.rs @@ -87,7 +87,7 @@ fn check_arguments<'tcx>( cx, UNNECESSARY_MUT_PASSED, argument.span, - &format!("the {} `{}` doesn't need a mutable reference", fn_kind, name), + &format!("the {fn_kind} `{name}` doesn't need a mutable reference"), ); } }, diff --git a/clippy_lints/src/mutable_debug_assertion.rs b/clippy_lints/src/mutable_debug_assertion.rs index 44fdf84c6df7..d8647a991058 100644 --- a/clippy_lints/src/mutable_debug_assertion.rs +++ b/clippy_lints/src/mutable_debug_assertion.rs @@ -56,10 +56,7 @@ impl<'tcx> LateLintPass<'tcx> for DebugAssertWithMutCall { cx, DEBUG_ASSERT_WITH_MUT_CALL, span, - &format!( - "do not call a function with mutable arguments inside of `{}!`", - macro_name - ), + &format!("do not call a function with mutable arguments inside of `{macro_name}!`"), ); } } diff --git a/clippy_lints/src/mutex_atomic.rs b/clippy_lints/src/mutex_atomic.rs index a98577093ed5..09cb53331763 100644 --- a/clippy_lints/src/mutex_atomic.rs +++ b/clippy_lints/src/mutex_atomic.rs @@ -84,9 +84,8 @@ impl<'tcx> LateLintPass<'tcx> for Mutex { let mutex_param = subst.type_at(0); if let Some(atomic_name) = get_atomic_name(mutex_param) { let msg = format!( - "consider using an `{}` instead of a `Mutex` here; if you just want the locking \ - behavior and not the internal type, consider using `Mutex<()>`", - atomic_name + "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 != ty::UintTy::Usize => span_lint(cx, MUTEX_INTEGER, expr.span, &msg), diff --git a/clippy_lints/src/needless_borrowed_ref.rs b/clippy_lints/src/needless_borrowed_ref.rs index b8855e5adbff..10c3ff026b6d 100644 --- a/clippy_lints/src/needless_borrowed_ref.rs +++ b/clippy_lints/src/needless_borrowed_ref.rs @@ -1,6 +1,4 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::snippet_with_applicability; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BindingAnnotation, Mutability, Node, Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -8,36 +6,26 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// ### What it does - /// Checks for bindings that destructure a reference and borrow the inner + /// Checks for bindings that needlessly destructure a reference and borrow the inner /// value with `&ref`. /// /// ### Why is this bad? /// This pattern has no effect in almost all cases. /// - /// ### Known problems - /// In some cases, `&ref` is needed to avoid a lifetime mismatch error. - /// Example: - /// ```rust - /// fn foo(a: &Option, b: &Option) { - /// match (a, b) { - /// (None, &ref c) | (&ref c, None) => (), - /// (&Some(ref c), _) => (), - /// }; - /// } - /// ``` - /// /// ### Example /// ```rust /// let mut v = Vec::::new(); - /// # #[allow(unused)] /// v.iter_mut().filter(|&ref a| a.is_empty()); + /// + /// if let &[ref first, ref second] = v.as_slice() {} /// ``` /// /// Use instead: /// ```rust /// let mut v = Vec::::new(); - /// # #[allow(unused)] /// v.iter_mut().filter(|a| a.is_empty()); + /// + /// if let [first, second] = v.as_slice() {} /// ``` #[clippy::version = "pre 1.29.0"] pub NEEDLESS_BORROWED_REFERENCE, @@ -54,34 +42,83 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowedRef { return; } - if_chain! { - // Only lint immutable refs, because `&mut ref T` may be useful. - if let PatKind::Ref(sub_pat, Mutability::Not) = pat.kind; + // Do not lint patterns that are part of an OR `|` pattern, the binding mode must match in all arms + for (_, node) in cx.tcx.hir().parent_iter(pat.hir_id) { + let Node::Pat(pat) = node else { break }; - // Check sub_pat got a `ref` keyword (excluding `ref mut`). - if let PatKind::Binding(BindingAnnotation::REF, .., spanned_name, _) = sub_pat.kind; - let parent_id = cx.tcx.hir().get_parent_node(pat.hir_id); - if let Some(parent_node) = cx.tcx.hir().find(parent_id); - then { - // do not recurse within patterns, as they may have other references - // XXXManishearth we can relax this constraint if we only check patterns - // with a single ref pattern inside them - if let Node::Pat(_) = parent_node { - return; - } - let mut applicability = Applicability::MachineApplicable; - span_lint_and_then(cx, NEEDLESS_BORROWED_REFERENCE, pat.span, - "this pattern takes a reference on something that is being de-referenced", - |diag| { - let hint = snippet_with_applicability(cx, spanned_name.span, "..", &mut applicability).into_owned(); - diag.span_suggestion( - pat.span, - "try removing the `&ref` part and just keep", - hint, - applicability, - ); - }); + if matches!(pat.kind, PatKind::Or(_)) { + return; } } + + // Only lint immutable refs, because `&mut ref T` may be useful. + let PatKind::Ref(sub_pat, Mutability::Not) = pat.kind else { return }; + + match sub_pat.kind { + // Check sub_pat got a `ref` keyword (excluding `ref mut`). + PatKind::Binding(BindingAnnotation::REF, _, ident, None) => { + span_lint_and_then( + cx, + NEEDLESS_BORROWED_REFERENCE, + pat.span, + "this pattern takes a reference on something that is being dereferenced", + |diag| { + // `&ref ident` + // ^^^^^ + let span = pat.span.until(ident.span); + diag.span_suggestion_verbose( + span, + "try removing the `&ref` part", + String::new(), + Applicability::MachineApplicable, + ); + }, + ); + }, + // Slices where each element is `ref`: `&[ref a, ref b, ..., ref z]` + PatKind::Slice( + before, + None + | Some(Pat { + kind: PatKind::Wild, .. + }), + after, + ) => { + let mut suggestions = Vec::new(); + + for element_pat in itertools::chain(before, after) { + if let PatKind::Binding(BindingAnnotation::REF, _, ident, None) = element_pat.kind { + // `&[..., ref ident, ...]` + // ^^^^ + let span = element_pat.span.until(ident.span); + suggestions.push((span, String::new())); + } else { + return; + } + } + + if !suggestions.is_empty() { + span_lint_and_then( + cx, + NEEDLESS_BORROWED_REFERENCE, + pat.span, + "dereferencing a slice pattern where every element takes a reference", + |diag| { + // `&[...]` + // ^ + let span = pat.span.until(sub_pat.span); + suggestions.push((span, String::new())); + + diag.multipart_suggestion( + "try removing the `&` and `ref` parts", + suggestions, + Applicability::MachineApplicable, + ); + }, + ); + } + }, + _ => {}, + } } } diff --git a/clippy_lints/src/needless_continue.rs b/clippy_lints/src/needless_continue.rs index 98a3bce1ff38..6f0e755466e5 100644 --- a/clippy_lints/src/needless_continue.rs +++ b/clippy_lints/src/needless_continue.rs @@ -309,7 +309,7 @@ fn emit_warning<'a>(cx: &EarlyContext<'_>, data: &'a LintData<'_>, header: &str, expr.span, message, None, - &format!("{}\n{}", header, snip), + &format!("{header}\n{snip}"), ); } @@ -322,10 +322,7 @@ fn suggestion_snippet_for_continue_inside_if<'a>(cx: &EarlyContext<'_>, data: &' let indent_if = indent_of(cx, data.if_expr.span).unwrap_or(0); format!( - "{indent}if {} {}\n{indent}{}", - cond_code, - continue_code, - else_code, + "{indent}if {cond_code} {continue_code}\n{indent}{else_code}", indent = " ".repeat(indent_if), ) } @@ -349,7 +346,7 @@ fn suggestion_snippet_for_continue_inside_else<'a>(cx: &EarlyContext<'_>, data: let span = cx.sess().source_map().stmt_span(stmt.span, data.loop_block.span); let snip = snippet_block(cx, span, "..", None).into_owned(); snip.lines() - .map(|line| format!("{}{}", " ".repeat(indent), line)) + .map(|line| format!("{}{line}", " ".repeat(indent))) .collect::>() .join("\n") }) @@ -358,10 +355,7 @@ fn suggestion_snippet_for_continue_inside_else<'a>(cx: &EarlyContext<'_>, data: let indent_if = indent_of(cx, data.if_expr.span).unwrap_or(0); format!( - "{indent_if}if {} {}\n{indent}// merged code follows:\n{}\n{indent_if}}}", - cond_code, - block_code, - to_annex, + "{indent_if}if {cond_code} {block_code}\n{indent}// merged code follows:\n{to_annex}\n{indent_if}}}", indent = " ".repeat(indent), indent_if = " ".repeat(indent_if), ) diff --git a/clippy_lints/src/needless_late_init.rs b/clippy_lints/src/needless_late_init.rs index de99f1d7078e..9d26e5900866 100644 --- a/clippy_lints/src/needless_late_init.rs +++ b/clippy_lints/src/needless_late_init.rs @@ -2,9 +2,9 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::path_to_local; use clippy_utils::source::snippet_opt; use clippy_utils::ty::needs_ordered_drop; -use clippy_utils::visitors::{expr_visitor, expr_visitor_no_bodies, is_local_used}; +use clippy_utils::visitors::{for_each_expr, for_each_expr_with_closures, is_local_used}; +use core::ops::ControlFlow; use rustc_errors::{Applicability, MultiSpan}; -use rustc_hir::intravisit::Visitor; use rustc_hir::{ BindingAnnotation, Block, Expr, ExprKind, HirId, Local, LocalSource, MatchSource, Node, Pat, PatKind, Stmt, StmtKind, @@ -64,31 +64,25 @@ declare_clippy_lint! { declare_lint_pass!(NeedlessLateInit => [NEEDLESS_LATE_INIT]); fn contains_assign_expr<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) -> bool { - let mut seen = false; - expr_visitor(cx, |expr| { - if let ExprKind::Assign(..) = expr.kind { - seen = true; + for_each_expr_with_closures(cx, stmt, |e| { + if matches!(e.kind, ExprKind::Assign(..)) { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) } - - !seen }) - .visit_stmt(stmt); - - seen + .is_some() } fn contains_let(cond: &Expr<'_>) -> bool { - let mut seen = false; - expr_visitor_no_bodies(|expr| { - if let ExprKind::Let(_) = expr.kind { - seen = true; + for_each_expr(cond, |e| { + if matches!(e.kind, ExprKind::Let(_)) { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) } - - !seen }) - .visit_expr(cond); - - seen + .is_some() } fn stmt_needs_ordered_drop(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool { @@ -287,7 +281,7 @@ fn check<'tcx>( diag.span_suggestion( assign.lhs_span, - &format!("declare `{}` here", binding_name), + &format!("declare `{binding_name}` here"), let_snippet, Applicability::MachineApplicable, ); @@ -307,8 +301,8 @@ fn check<'tcx>( diag.span_suggestion_verbose( usage.stmt.span.shrink_to_lo(), - &format!("declare `{}` here", binding_name), - format!("{} = ", let_snippet), + &format!("declare `{binding_name}` here"), + format!("{let_snippet} = "), applicability, ); @@ -338,8 +332,8 @@ fn check<'tcx>( diag.span_suggestion_verbose( usage.stmt.span.shrink_to_lo(), - &format!("declare `{}` here", binding_name), - format!("{} = ", let_snippet), + &format!("declare `{binding_name}` here"), + format!("{let_snippet} = "), applicability, ); diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 4f46872439c3..178c973981b1 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -12,17 +12,17 @@ use rustc_hir::{ BindingAnnotation, Body, FnDecl, GenericArg, HirId, Impl, ItemKind, Mutability, Node, PatKind, QPath, TyKind, }; use rustc_hir::{HirIdMap, HirIdSet}; +use rustc_hir_analysis::expr_use_visitor as euv; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::FakeReadCause; use rustc_middle::ty::{self, TypeVisitable}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::kw; -use rustc_span::{sym, Span}; +use rustc_span::{sym, Span, DUMMY_SP}; use rustc_target::spec::abi::Abi; use rustc_trait_selection::traits; use rustc_trait_selection::traits::misc::can_type_implement_copy; -use rustc_hir_analysis::expr_use_visitor as euv; use std::borrow::Cow; declare_clippy_lint! { @@ -186,6 +186,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { if !is_self(arg); if !ty.is_mutable_ptr(); if !is_copy(cx, ty); + if ty.is_sized(cx.tcx.at(DUMMY_SP), cx.param_env); if !allowed_traits.iter().any(|&t| implements_trait(cx, ty, t, &[])); if !implements_borrow_trait; if !all_borrowable_trait; @@ -236,7 +237,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { snippet_opt(cx, span) .map_or( "change the call to".into(), - |x| Cow::from(format!("change `{}` to", x)), + |x| Cow::from(format!("change `{x}` to")), ) .as_ref(), suggestion, @@ -266,7 +267,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { snippet_opt(cx, span) .map_or( "change the call to".into(), - |x| Cow::from(format!("change `{}` to", x)) + |x| Cow::from(format!("change `{x}` to")) ) .as_ref(), suggestion, @@ -341,5 +342,11 @@ impl<'tcx> euv::Delegate<'tcx> for MovedVariablesCtxt { fn mutate(&mut self, _: &euv::PlaceWithHirId<'tcx>, _: HirId) {} - fn fake_read(&mut self, _: &rustc_hir_analysis::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} + fn fake_read( + &mut self, + _: &rustc_hir_analysis::expr_use_visitor::PlaceWithHirId<'tcx>, + _: FakeReadCause, + _: HirId, + ) { + } } diff --git a/clippy_lints/src/needless_question_mark.rs b/clippy_lints/src/needless_question_mark.rs index 8f85b00596c0..97c8cfbd3eb7 100644 --- a/clippy_lints/src/needless_question_mark.rs +++ b/clippy_lints/src/needless_question_mark.rs @@ -1,11 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_lang_ctor; +use clippy_utils::path_res; use clippy_utils::source::snippet; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::LangItem::{OptionSome, ResultOk}; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::{AsyncGeneratorKind, Block, Body, Expr, ExprKind, GeneratorKind, LangItem, MatchSource, QPath}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::DefIdTree; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { @@ -112,11 +113,12 @@ impl LateLintPass<'_> for NeedlessQuestionMark { fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { - if let ExprKind::Call(path, [arg]) = &expr.kind; - if let ExprKind::Path(ref qpath) = &path.kind; - let sugg_remove = if is_lang_ctor(cx, qpath, OptionSome) { + if let ExprKind::Call(path, [arg]) = expr.kind; + if let Res::Def(DefKind::Ctor(..), ctor_id) = path_res(cx, path); + if let Some(variant_id) = cx.tcx.opt_parent(ctor_id); + let sugg_remove = if cx.tcx.lang_items().option_some_variant() == Some(variant_id) { "Some()" - } else if is_lang_ctor(cx, qpath, ResultOk) { + } else if cx.tcx.lang_items().result_ok_variant() == Some(variant_id) { "Ok()" } else { return; @@ -134,7 +136,7 @@ fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { NEEDLESS_QUESTION_MARK, expr.span, "question mark operator is useless here", - &format!("try removing question mark and `{}`", sugg_remove), + &format!("try removing question mark and `{sugg_remove}`"), format!("{}", snippet(cx, inner_expr.span, r#""...""#)), Applicability::MachineApplicable, ); diff --git a/clippy_lints/src/neg_multiply.rs b/clippy_lints/src/neg_multiply.rs index b087cfb36b11..fb9a4abd0b4b 100644 --- a/clippy_lints/src/neg_multiply.rs +++ b/clippy_lints/src/neg_multiply.rs @@ -62,9 +62,9 @@ fn check_mul(cx: &LateContext<'_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) { let mut applicability = Applicability::MachineApplicable; let snip = snippet_with_applicability(cx, exp.span, "..", &mut applicability); let suggestion = if exp.precedence().order() < PREC_PREFIX && !has_enclosing_paren(&snip) { - format!("-({})", snip) + format!("-({snip})") } else { - format!("-{}", snip) + format!("-{snip}") }; span_lint_and_sugg( cx, diff --git a/clippy_lints/src/new_without_default.rs b/clippy_lints/src/new_without_default.rs index 357a71693d2c..6017117e1ecc 100644 --- a/clippy_lints/src/new_without_default.rs +++ b/clippy_lints/src/new_without_default.rs @@ -136,8 +136,7 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { id, impl_item.span, &format!( - "you should consider adding a `Default` implementation for `{}`", - self_type_snip + "you should consider adding a `Default` implementation for `{self_type_snip}`" ), |diag| { diag.suggest_prepend_item( @@ -161,9 +160,9 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { fn create_new_without_default_suggest_msg(self_type_snip: &str, generics_sugg: &str) -> String { #[rustfmt::skip] format!( -"impl{} Default for {} {{ +"impl{generics_sugg} Default for {self_type_snip} {{ fn default() -> Self {{ Self::new() }} -}}", generics_sugg, self_type_snip) +}}") } diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index 48ff737dae7b..2c839d029c6f 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -13,6 +13,7 @@ use rustc_hir::def_id::DefId; use rustc_hir::{ BodyId, Expr, ExprKind, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind, UnOp, }; +use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass, Lint}; use rustc_middle::mir; use rustc_middle::mir::interpret::{ConstValue, ErrorHandled}; @@ -20,7 +21,6 @@ use rustc_middle::ty::adjustment::Adjust; use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{sym, InnerSpan, Span, DUMMY_SP}; -use rustc_hir_analysis::hir_ty_to_ty; // FIXME: this is a correctness problem but there's no suitable // warn-by-default category. @@ -149,6 +149,9 @@ fn is_value_unfrozen_raw<'tcx>( // the fact that we have to dig into every structs to search enums // leads us to the point checking `UnsafeCell` directly is the only option. ty::Adt(ty_def, ..) if ty_def.is_unsafe_cell() => true, + // As of 2022-09-08 miri doesn't track which union field is active so there's no safe way to check the + // contained value. + ty::Adt(def, ..) if def.is_union() => false, ty::Array(..) | ty::Adt(..) | ty::Tuple(..) => { let val = cx.tcx.destructure_mir_constant(cx.param_env, val); val.fields.iter().any(|field| inner(cx, *field)) diff --git a/clippy_lints/src/non_expressive_names.rs b/clippy_lints/src/non_expressive_names.rs index a7cd1f6d0652..9f6917c146f6 100644 --- a/clippy_lints/src/non_expressive_names.rs +++ b/clippy_lints/src/non_expressive_names.rs @@ -112,10 +112,7 @@ impl<'a, 'tcx> SimilarNamesLocalVisitor<'a, 'tcx> { self.cx, MANY_SINGLE_CHAR_NAMES, span, - &format!( - "{} bindings with single-character names in scope", - num_single_char_names - ), + &format!("{num_single_char_names} bindings with single-character names in scope"), ); } } diff --git a/clippy_lints/src/non_octal_unix_permissions.rs b/clippy_lints/src/non_octal_unix_permissions.rs index 25fb4f0f4cff..1a765b14892f 100644 --- a/clippy_lints/src/non_octal_unix_permissions.rs +++ b/clippy_lints/src/non_octal_unix_permissions.rs @@ -1,12 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{snippet_opt, snippet_with_applicability}; -use clippy_utils::ty::match_type; +use clippy_utils::ty::{is_type_diagnostic_item, match_type}; use clippy_utils::{match_def_path, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -49,7 +50,7 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions { if_chain! { if (path.ident.name == sym!(mode) && (match_type(cx, obj_ty, &paths::OPEN_OPTIONS) - || match_type(cx, obj_ty, &paths::DIR_BUILDER))) + || is_type_diagnostic_item(cx, obj_ty, sym::DirBuilder))) || (path.ident.name == sym!(set_mode) && match_type(cx, obj_ty, &paths::PERMISSIONS)); if let ExprKind::Lit(_) = param.kind; diff --git a/clippy_lints/src/nonstandard_macro_braces.rs b/clippy_lints/src/nonstandard_macro_braces.rs index f86dfb6b8df8..0ca0befc1351 100644 --- a/clippy_lints/src/nonstandard_macro_braces.rs +++ b/clippy_lints/src/nonstandard_macro_braces.rs @@ -3,16 +3,17 @@ use std::{ hash::{Hash, Hasher}, }; -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_opt; use if_chain::if_chain; use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::hygiene::{ExpnKind, MacroKind}; -use rustc_span::{Span, Symbol}; +use rustc_span::Span; use serde::{de, Deserialize}; declare_clippy_lint! { @@ -39,8 +40,8 @@ declare_clippy_lint! { const BRACES: &[(&str, &str)] = &[("(", ")"), ("{", "}"), ("[", "]")]; -/// The (name, (open brace, close brace), source snippet) -type MacroInfo<'a> = (Symbol, &'a (String, String), String); +/// The (callsite span, (open brace, close brace), source snippet) +type MacroInfo<'a> = (Span, &'a (String, String), String); #[derive(Clone, Debug, Default)] pub struct MacroBraces { @@ -62,33 +63,29 @@ impl_lint_pass!(MacroBraces => [NONSTANDARD_MACRO_BRACES]); impl EarlyLintPass for MacroBraces { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { - if let Some((name, braces, snip)) = is_offending_macro(cx, item.span, self) { - let span = item.span.ctxt().outer_expn_data().call_site; - emit_help(cx, snip, braces, name, span); + if let Some((span, braces, snip)) = is_offending_macro(cx, item.span, self) { + emit_help(cx, &snip, braces, span); self.done.insert(span); } } fn check_stmt(&mut self, cx: &EarlyContext<'_>, stmt: &ast::Stmt) { - if let Some((name, braces, snip)) = is_offending_macro(cx, stmt.span, self) { - let span = stmt.span.ctxt().outer_expn_data().call_site; - emit_help(cx, snip, braces, name, span); + if let Some((span, braces, snip)) = is_offending_macro(cx, stmt.span, self) { + emit_help(cx, &snip, braces, span); self.done.insert(span); } } fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) { - if let Some((name, braces, snip)) = is_offending_macro(cx, expr.span, self) { - let span = expr.span.ctxt().outer_expn_data().call_site; - emit_help(cx, snip, braces, name, span); + if let Some((span, braces, snip)) = is_offending_macro(cx, expr.span, self) { + emit_help(cx, &snip, braces, span); self.done.insert(span); } } fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) { - if let Some((name, braces, snip)) = is_offending_macro(cx, ty.span, self) { - let span = ty.span.ctxt().outer_expn_data().call_site; - emit_help(cx, snip, braces, name, span); + if let Some((span, braces, snip)) = is_offending_macro(cx, ty.span, self) { + emit_help(cx, &snip, braces, span); self.done.insert(span); } } @@ -102,48 +99,44 @@ fn is_offending_macro<'a>(cx: &EarlyContext<'_>, span: Span, mac_braces: &'a Mac .last() .map_or(false, |e| e.macro_def_id.map_or(false, DefId::is_local)) }; + let span_call_site = span.ctxt().outer_expn_data().call_site; if_chain! { if let ExpnKind::Macro(MacroKind::Bang, mac_name) = span.ctxt().outer_expn_data().kind; let name = mac_name.as_str(); if let Some(braces) = mac_braces.macro_braces.get(name); - if let Some(snip) = snippet_opt(cx, span.ctxt().outer_expn_data().call_site); + if let Some(snip) = snippet_opt(cx, span_call_site); // we must check only invocation sites // https://github.com/rust-lang/rust-clippy/issues/7422 - if snip.starts_with(&format!("{}!", name)); + if snip.starts_with(&format!("{name}!")); if unnested_or_local(); // make formatting consistent let c = snip.replace(' ', ""); - if !c.starts_with(&format!("{}!{}", name, braces.0)); - if !mac_braces.done.contains(&span.ctxt().outer_expn_data().call_site); + if !c.starts_with(&format!("{name}!{}", braces.0)); + if !mac_braces.done.contains(&span_call_site); then { - Some((mac_name, braces, snip)) + Some((span_call_site, braces, snip)) } else { None } } } -fn emit_help(cx: &EarlyContext<'_>, snip: String, braces: &(String, String), name: Symbol, span: Span) { - let with_space = &format!("! {}", braces.0); - let without_space = &format!("!{}", braces.0); - let mut help = snip; - for b in BRACES.iter().filter(|b| b.0 != braces.0) { - help = help.replace(b.0, &braces.0).replace(b.1, &braces.1); - // Only `{` traditionally has space before the brace - if braces.0 != "{" && help.contains(with_space) { - help = help.replace(with_space, without_space); - } else if braces.0 == "{" && help.contains(without_space) { - help = help.replace(without_space, with_space); - } +fn emit_help(cx: &EarlyContext<'_>, snip: &str, braces: &(String, String), span: Span) { + if let Some((macro_name, macro_args_str)) = snip.split_once('!') { + let mut macro_args = macro_args_str.trim().to_string(); + // now remove the wrong braces + macro_args.remove(0); + macro_args.pop(); + span_lint_and_sugg( + cx, + NONSTANDARD_MACRO_BRACES, + span, + &format!("use of irregular braces for `{macro_name}!` macro"), + "consider writing", + format!("{macro_name}!{}{macro_args}{}", braces.0, braces.1), + Applicability::MachineApplicable, + ); } - span_lint_and_help( - cx, - NONSTANDARD_MACRO_BRACES, - span, - &format!("use of irregular braces for `{}!` macro", name), - Some(span), - &format!("consider writing `{}`", help), - ); } fn macro_braces(conf: FxHashSet) -> FxHashMap { @@ -273,9 +266,7 @@ impl<'de> Deserialize<'de> for MacroMatcher { .iter() .find(|b| b.0 == brace) .map(|(o, c)| ((*o).to_owned(), (*c).to_owned())) - .ok_or_else(|| { - de::Error::custom(&format!("expected one of `(`, `{{`, `[` found `{}`", brace)) - })?, + .ok_or_else(|| de::Error::custom(&format!("expected one of `(`, `{{`, `[` found `{brace}`")))?, }) } } diff --git a/clippy_lints/src/octal_escapes.rs b/clippy_lints/src/octal_escapes.rs index bffbf20b4d28..f380a5065827 100644 --- a/clippy_lints/src/octal_escapes.rs +++ b/clippy_lints/src/octal_escapes.rs @@ -102,7 +102,7 @@ fn check_lit(cx: &EarlyContext<'_>, lit: &Lit, span: Span, is_string: bool) { // construct a replacement escape // the maximum value is \077, or \x3f, so u8 is sufficient here if let Ok(n) = u8::from_str_radix(&contents[from + 1..to], 8) { - write!(suggest_1, "\\x{:02x}", n).unwrap(); + write!(suggest_1, "\\x{n:02x}").unwrap(); } // append the null byte as \x00 and the following digits literally diff --git a/clippy_lints/src/operators/absurd_extreme_comparisons.rs b/clippy_lints/src/operators/absurd_extreme_comparisons.rs index 1ec4240afefe..d29ca37eaeb8 100644 --- a/clippy_lints/src/operators/absurd_extreme_comparisons.rs +++ b/clippy_lints/src/operators/absurd_extreme_comparisons.rs @@ -34,13 +34,12 @@ pub(super) fn check<'tcx>( }; let help = format!( - "because `{}` is the {} value for this type, {}", + "because `{}` is the {} value for this type, {conclusion}", snippet(cx, culprit.expr.span, "x"), match culprit.which { ExtremeType::Minimum => "minimum", ExtremeType::Maximum => "maximum", - }, - conclusion + } ); span_lint_and_help(cx, ABSURD_EXTREME_COMPARISONS, expr.span, msg, None, &help); diff --git a/clippy_lints/src/operators/arithmetic_side_effects.rs b/clippy_lints/src/operators/arithmetic_side_effects.rs index d24c57c0a4b8..8827daaa3ee7 100644 --- a/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -1,8 +1,3 @@ -#![allow( - // False positive - clippy::match_same_arms -)] - use super::ARITHMETIC_SIDE_EFFECTS; use clippy_utils::{consts::constant_simple, diagnostics::span_lint}; use rustc_ast as ast; @@ -14,11 +9,12 @@ use rustc_session::impl_lint_pass; use rustc_span::source_map::{Span, Spanned}; const HARD_CODED_ALLOWED: &[&str] = &[ + "&str", "f32", "f64", "std::num::Saturating", - "std::string::String", "std::num::Wrapping", + "std::string::String", ]; #[derive(Debug)] @@ -45,61 +41,59 @@ impl ArithmeticSideEffects { /// Assuming that `expr` is a literal integer, checks operators (+=, -=, *, /) in a /// non-constant environment that won't overflow. fn has_valid_op(op: &Spanned, expr: &hir::Expr<'_>) -> bool { - if let hir::BinOpKind::Add | hir::BinOpKind::Sub = op.node - && let hir::ExprKind::Lit(ref lit) = expr.kind - && let ast::LitKind::Int(0, _) = lit.node + if let hir::ExprKind::Lit(ref lit) = expr.kind && + let ast::LitKind::Int(value, _) = lit.node { - return true; + match (&op.node, value) { + (hir::BinOpKind::Div | hir::BinOpKind::Rem, 0) => false, + (hir::BinOpKind::Add | hir::BinOpKind::Sub, 0) + | (hir::BinOpKind::Div | hir::BinOpKind::Rem, _) + | (hir::BinOpKind::Mul, 0 | 1) => true, + _ => false, + } + } else { + false } - if let hir::BinOpKind::Div | hir::BinOpKind::Rem = op.node - && let hir::ExprKind::Lit(ref lit) = expr.kind - && !matches!(lit.node, ast::LitKind::Int(0, _)) - { - return true; - } - if let hir::BinOpKind::Mul = op.node - && let hir::ExprKind::Lit(ref lit) = expr.kind - && let ast::LitKind::Int(0 | 1, _) = lit.node - { - return true; - } - false } /// Checks if the given `expr` has any of the inner `allowed` elements. - fn is_allowed_ty(&self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { - self.allowed.contains( - cx.typeck_results() - .expr_ty(expr) - .to_string() - .split('<') - .next() - .unwrap_or_default(), - ) + fn is_allowed_ty(&self, ty: Ty<'_>) -> bool { + self.allowed + .contains(ty.to_string().split('<').next().unwrap_or_default()) } - /// Explicit integers like `1` or `i32::MAX`. Does not take into consideration references. - fn is_literal_integer(expr: &hir::Expr<'_>, expr_refs: Ty<'_>) -> bool { - let is_integral = expr_refs.is_integral(); - let is_literal = matches!(expr.kind, hir::ExprKind::Lit(_)); - is_integral && is_literal + // For example, 8i32 or &i64::MAX. + fn is_integral(ty: Ty<'_>) -> bool { + ty.peel_refs().is_integral() } + // Common entry-point to avoid code duplication. fn issue_lint(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) { let msg = "arithmetic operation that can potentially result in unexpected side-effects"; span_lint(cx, ARITHMETIC_SIDE_EFFECTS, expr.span, msg); self.expr_span = Some(expr.span); } + /// If `expr` does not match any variant of `LiteralIntegerTy`, returns `None`. + fn literal_integer<'expr, 'tcx>(expr: &'expr hir::Expr<'tcx>) -> Option> { + if matches!(expr.kind, hir::ExprKind::Lit(_)) { + return Some(LiteralIntegerTy::Value(expr)); + } + if let hir::ExprKind::AddrOf(.., inn) = expr.kind && let hir::ExprKind::Lit(_) = inn.kind { + return Some(LiteralIntegerTy::Ref(inn)); + } + None + } + /// Manages when the lint should be triggered. Operations in constant environments, hard coded /// types, custom allowed types and non-constant operations that won't overflow are ignored. - fn manage_bin_ops( + fn manage_bin_ops<'tcx>( &mut self, - cx: &LateContext<'_>, - expr: &hir::Expr<'_>, + cx: &LateContext<'tcx>, + expr: &hir::Expr<'tcx>, op: &Spanned, - lhs: &hir::Expr<'_>, - rhs: &hir::Expr<'_>, + lhs: &hir::Expr<'tcx>, + rhs: &hir::Expr<'tcx>, ) { if constant_simple(cx, cx.typeck_results(), expr).is_some() { return; @@ -116,17 +110,20 @@ impl ArithmeticSideEffects { ) { return; }; - if self.is_allowed_ty(cx, lhs) || self.is_allowed_ty(cx, rhs) { + let lhs_ty = cx.typeck_results().expr_ty(lhs); + let rhs_ty = cx.typeck_results().expr_ty(rhs); + let lhs_and_rhs_have_the_same_ty = lhs_ty == rhs_ty; + if lhs_and_rhs_have_the_same_ty && self.is_allowed_ty(lhs_ty) && self.is_allowed_ty(rhs_ty) { return; } - let has_valid_op = match ( - Self::is_literal_integer(lhs, cx.typeck_results().expr_ty(lhs).peel_refs()), - Self::is_literal_integer(rhs, cx.typeck_results().expr_ty(rhs).peel_refs()), - ) { - (true, true) => true, - (true, false) => Self::has_valid_op(op, lhs), - (false, true) => Self::has_valid_op(op, rhs), - (false, false) => false, + let has_valid_op = if Self::is_integral(lhs_ty) && Self::is_integral(rhs_ty) { + match (Self::literal_integer(lhs), Self::literal_integer(rhs)) { + (None, Some(lit_int_ty)) | (Some(lit_int_ty), None) => Self::has_valid_op(op, lit_int_ty.into()), + (Some(LiteralIntegerTy::Value(_)), Some(LiteralIntegerTy::Value(_))) => true, + (None, None) | (Some(_), Some(_)) => false, + } + } else { + false }; if !has_valid_op { self.issue_lint(cx, expr); @@ -135,7 +132,7 @@ impl ArithmeticSideEffects { } impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &hir::Expr<'tcx>) { if self.expr_span.is_some() || self.const_span.map_or(false, |sp| sp.contains(expr.span)) { return; } @@ -180,3 +177,22 @@ impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects { } } } + +/// Tells if an expression is a integer declared by value or by reference. +/// +/// If `LiteralIntegerTy::Ref`, then the contained value will be `hir::ExprKind::Lit` rather +/// than `hirExprKind::Addr`. +enum LiteralIntegerTy<'expr, 'tcx> { + /// For example, `&199` + Ref(&'expr hir::Expr<'tcx>), + /// For example, `1` or `i32::MAX` + Value(&'expr hir::Expr<'tcx>), +} + +impl<'expr, 'tcx> From> for &'expr hir::Expr<'tcx> { + fn from(from: LiteralIntegerTy<'expr, 'tcx>) -> Self { + match from { + LiteralIntegerTy::Ref(elem) | LiteralIntegerTy::Value(elem) => elem, + } + } +} diff --git a/clippy_lints/src/operators/assign_op_pattern.rs b/clippy_lints/src/operators/assign_op_pattern.rs index f134c6c4cdba..26bca7c306a8 100644 --- a/clippy_lints/src/operators/assign_op_pattern.rs +++ b/clippy_lints/src/operators/assign_op_pattern.rs @@ -2,16 +2,17 @@ use clippy_utils::binop_traits; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_opt; use clippy_utils::ty::implements_trait; +use clippy_utils::visitors::for_each_expr; use clippy_utils::{eq_expr_value, trait_ref_of_method}; +use core::ops::ControlFlow; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_hir::intravisit::{walk_expr, Visitor}; +use rustc_hir_analysis::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use rustc_lint::LateContext; use rustc_middle::mir::FakeReadCause; use rustc_middle::ty::BorrowKind; use rustc_trait_selection::infer::TyCtxtInferExt; -use rustc_hir_analysis::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use super::ASSIGN_OP_PATTERN; @@ -55,7 +56,7 @@ pub(super) fn check<'tcx>( diag.span_suggestion( expr.span, "replace it with", - format!("{} {}= {}", snip_a, op.node.as_str(), snip_r), + format!("{snip_a} {}= {snip_r}", op.node.as_str()), Applicability::MachineApplicable, ); } @@ -65,15 +66,19 @@ pub(super) fn check<'tcx>( } }; - let mut visitor = ExprVisitor { - assignee, - counter: 0, - cx, - }; + let mut found = false; + let found_multiple = for_each_expr(e, |e| { + if eq_expr_value(cx, assignee, e) { + if found { + return ControlFlow::Break(()); + } + found = true; + } + ControlFlow::Continue(()) + }) + .is_some(); - walk_expr(&mut visitor, e); - - if visitor.counter == 1 { + if found && !found_multiple { // a = a op b if eq_expr_value(cx, assignee, l) { lint(assignee, r); @@ -98,22 +103,6 @@ pub(super) fn check<'tcx>( } } -struct ExprVisitor<'a, 'tcx> { - assignee: &'a hir::Expr<'a>, - counter: u8, - cx: &'a LateContext<'tcx>, -} - -impl<'a, 'tcx> Visitor<'tcx> for ExprVisitor<'a, 'tcx> { - fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { - if eq_expr_value(self.cx, self.assignee, expr) { - self.counter += 1; - } - - walk_expr(self, expr); - } -} - fn imm_borrows_in_expr(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> hir::HirIdSet { struct S(hir::HirIdSet); impl Delegate<'_> for S { diff --git a/clippy_lints/src/operators/bit_mask.rs b/clippy_lints/src/operators/bit_mask.rs index 74387fbc87be..1369b3e74625 100644 --- a/clippy_lints/src/operators/bit_mask.rs +++ b/clippy_lints/src/operators/bit_mask.rs @@ -64,10 +64,7 @@ fn check_bit_mask( cx, BAD_BIT_MASK, span, - &format!( - "incompatible bit mask: `_ & {}` can never be equal to `{}`", - mask_value, cmp_value - ), + &format!("incompatible bit mask: `_ & {mask_value}` can never be equal to `{cmp_value}`"), ); } } else if mask_value == 0 { @@ -80,10 +77,7 @@ fn check_bit_mask( cx, BAD_BIT_MASK, span, - &format!( - "incompatible bit mask: `_ | {}` can never be equal to `{}`", - mask_value, cmp_value - ), + &format!("incompatible bit mask: `_ | {mask_value}` can never be equal to `{cmp_value}`"), ); } }, @@ -96,10 +90,7 @@ fn check_bit_mask( cx, BAD_BIT_MASK, span, - &format!( - "incompatible bit mask: `_ & {}` will always be lower than `{}`", - mask_value, cmp_value - ), + &format!("incompatible bit mask: `_ & {mask_value}` will always be lower than `{cmp_value}`"), ); } else if mask_value == 0 { span_lint(cx, BAD_BIT_MASK, span, "&-masking with zero"); @@ -111,10 +102,7 @@ fn check_bit_mask( cx, BAD_BIT_MASK, span, - &format!( - "incompatible bit mask: `_ | {}` will never be lower than `{}`", - mask_value, cmp_value - ), + &format!("incompatible bit mask: `_ | {mask_value}` will never be lower than `{cmp_value}`"), ); } else { check_ineffective_lt(cx, span, mask_value, cmp_value, "|"); @@ -130,10 +118,7 @@ fn check_bit_mask( cx, BAD_BIT_MASK, span, - &format!( - "incompatible bit mask: `_ & {}` will never be higher than `{}`", - mask_value, cmp_value - ), + &format!("incompatible bit mask: `_ & {mask_value}` will never be higher than `{cmp_value}`"), ); } else if mask_value == 0 { span_lint(cx, BAD_BIT_MASK, span, "&-masking with zero"); @@ -145,10 +130,7 @@ fn check_bit_mask( cx, BAD_BIT_MASK, span, - &format!( - "incompatible bit mask: `_ | {}` will always be higher than `{}`", - mask_value, cmp_value - ), + &format!("incompatible bit mask: `_ | {mask_value}` will always be higher than `{cmp_value}`"), ); } else { check_ineffective_gt(cx, span, mask_value, cmp_value, "|"); @@ -167,10 +149,7 @@ fn check_ineffective_lt(cx: &LateContext<'_>, span: Span, m: u128, c: u128, op: cx, INEFFECTIVE_BIT_MASK, span, - &format!( - "ineffective bit mask: `x {} {}` compared to `{}`, is the same as x compared directly", - op, m, c - ), + &format!("ineffective bit mask: `x {op} {m}` compared to `{c}`, is the same as x compared directly"), ); } } @@ -181,10 +160,7 @@ fn check_ineffective_gt(cx: &LateContext<'_>, span: Span, m: u128, c: u128, op: cx, INEFFECTIVE_BIT_MASK, span, - &format!( - "ineffective bit mask: `x {} {}` compared to `{}`, is the same as x compared directly", - op, m, c - ), + &format!("ineffective bit mask: `x {op} {m}` compared to `{c}`, is the same as x compared directly"), ); } } diff --git a/clippy_lints/src/operators/cmp_owned.rs b/clippy_lints/src/operators/cmp_owned.rs index 638a514ff9b3..c9c777f1bd8d 100644 --- a/clippy_lints/src/operators/cmp_owned.rs +++ b/clippy_lints/src/operators/cmp_owned.rs @@ -99,7 +99,7 @@ fn check_op(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) let expr_snip; let eq_impl; if with_deref.is_implemented() { - expr_snip = format!("*{}", arg_snip); + expr_snip = format!("*{arg_snip}"); eq_impl = with_deref; } else { expr_snip = arg_snip.to_string(); @@ -121,17 +121,15 @@ fn check_op(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) }; if eq_impl.ty_eq_other { hint = format!( - "{}{}{}", - expr_snip, + "{expr_snip}{}{}", snippet(cx, cmp_span, ".."), snippet(cx, other.span, "..") ); } else { hint = format!( - "{}{}{}", + "{}{}{expr_snip}", snippet(cx, other.span, ".."), - snippet(cx, cmp_span, ".."), - expr_snip + snippet(cx, cmp_span, "..") ); } } diff --git a/clippy_lints/src/operators/duration_subsec.rs b/clippy_lints/src/operators/duration_subsec.rs index 827a2b267093..49e662cacb0c 100644 --- a/clippy_lints/src/operators/duration_subsec.rs +++ b/clippy_lints/src/operators/duration_subsec.rs @@ -31,12 +31,11 @@ pub(crate) fn check<'tcx>( cx, DURATION_SUBSEC, expr.span, - &format!("calling `{}()` is more concise than this calculation", suggested_fn), + &format!("calling `{suggested_fn}()` is more concise than this calculation"), "try", format!( - "{}.{}()", - snippet_with_applicability(cx, self_arg.span, "_", &mut applicability), - suggested_fn + "{}.{suggested_fn}()", + snippet_with_applicability(cx, self_arg.span, "_", &mut applicability) ), applicability, ); diff --git a/clippy_lints/src/operators/eq_op.rs b/clippy_lints/src/operators/eq_op.rs index 44cf0bb06120..67913f7392c0 100644 --- a/clippy_lints/src/operators/eq_op.rs +++ b/clippy_lints/src/operators/eq_op.rs @@ -22,7 +22,7 @@ pub(crate) fn check_assert<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { cx, EQ_OP, lhs.span.to(rhs.span), - &format!("identical args used in this `{}!` macro call", macro_name), + &format!("identical args used in this `{macro_name}!` macro call"), ); } } diff --git a/clippy_lints/src/operators/misrefactored_assign_op.rs b/clippy_lints/src/operators/misrefactored_assign_op.rs index 0024384d9278..ae805147f07a 100644 --- a/clippy_lints/src/operators/misrefactored_assign_op.rs +++ b/clippy_lints/src/operators/misrefactored_assign_op.rs @@ -47,18 +47,14 @@ fn lint_misrefactored_assign_op( if let (Some(snip_a), Some(snip_r)) = (snippet_opt(cx, assignee.span), snippet_opt(cx, rhs_other.span)) { let a = &sugg::Sugg::hir(cx, assignee, ".."); let r = &sugg::Sugg::hir(cx, rhs, ".."); - let long = format!("{} = {}", snip_a, sugg::make_binop(op.into(), a, r)); + let long = format!("{snip_a} = {}", sugg::make_binop(op.into(), a, r)); diag.span_suggestion( expr.span, &format!( - "did you mean `{} = {} {} {}` or `{}`? Consider replacing it with", - snip_a, - snip_a, - op.as_str(), - snip_r, - long + "did you mean `{snip_a} = {snip_a} {} {snip_r}` or `{long}`? Consider replacing it with", + op.as_str() ), - format!("{} {}= {}", snip_a, op.as_str(), snip_r), + format!("{snip_a} {}= {snip_r}", op.as_str()), Applicability::MaybeIncorrect, ); diag.span_suggestion( diff --git a/clippy_lints/src/operators/mod.rs b/clippy_lints/src/operators/mod.rs index c32b4df4f75c..b8a20d5ebe9b 100644 --- a/clippy_lints/src/operators/mod.rs +++ b/clippy_lints/src/operators/mod.rs @@ -67,7 +67,7 @@ declare_clippy_lint! { /// Reference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#overflow), /// or can panic (`/`, `%`). /// - /// Known safe built-in types like `Wrapping` or `Saturing`, floats, operations in constant + /// Known safe built-in types like `Wrapping` or `Saturating`, floats, operations in constant /// environments, allowed types and non-constant operations that won't overflow are ignored. /// /// ### Why is this bad? diff --git a/clippy_lints/src/operators/needless_bitwise_bool.rs b/clippy_lints/src/operators/needless_bitwise_bool.rs index e902235a014e..ab5fb1787004 100644 --- a/clippy_lints/src/operators/needless_bitwise_bool.rs +++ b/clippy_lints/src/operators/needless_bitwise_bool.rs @@ -27,7 +27,7 @@ pub(super) fn check(cx: &LateContext<'_>, e: &Expr<'_>, op: BinOpKind, lhs: &Exp if let Some(lhs_snip) = snippet_opt(cx, lhs.span) && let Some(rhs_snip) = snippet_opt(cx, rhs.span) { - let sugg = format!("{} {} {}", lhs_snip, op_str, rhs_snip); + let sugg = format!("{lhs_snip} {op_str} {rhs_snip}"); diag.span_suggestion(e.span, "try", sugg, Applicability::MachineApplicable); } }, diff --git a/clippy_lints/src/operators/numeric_arithmetic.rs b/clippy_lints/src/operators/numeric_arithmetic.rs index b6097710dc68..0830a106f556 100644 --- a/clippy_lints/src/operators/numeric_arithmetic.rs +++ b/clippy_lints/src/operators/numeric_arithmetic.rs @@ -1,5 +1,6 @@ use clippy_utils::consts::constant_simple; use clippy_utils::diagnostics::span_lint; +use clippy_utils::is_integer_literal; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::source_map::Span; @@ -50,11 +51,9 @@ impl Context { hir::BinOpKind::Div | hir::BinOpKind::Rem => match &r.kind { hir::ExprKind::Lit(_lit) => (), hir::ExprKind::Unary(hir::UnOp::Neg, expr) => { - if let hir::ExprKind::Lit(lit) = &expr.kind { - if let rustc_ast::ast::LitKind::Int(1, _) = lit.node { - span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected"); - self.expr_id = Some(expr.hir_id); - } + if is_integer_literal(expr, 1) { + span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected"); + self.expr_id = Some(expr.hir_id); } }, _ => { diff --git a/clippy_lints/src/operators/ptr_eq.rs b/clippy_lints/src/operators/ptr_eq.rs index 1aefc2741c21..1229c202f5a0 100644 --- a/clippy_lints/src/operators/ptr_eq.rs +++ b/clippy_lints/src/operators/ptr_eq.rs @@ -34,7 +34,7 @@ pub(super) fn check<'tcx>( expr.span, LINT_MSG, "try", - format!("std::ptr::eq({}, {})", left_snip, right_snip), + format!("std::ptr::eq({left_snip}, {right_snip})"), Applicability::MachineApplicable, ); } diff --git a/clippy_lints/src/operators/self_assignment.rs b/clippy_lints/src/operators/self_assignment.rs index 9d6bec05bf09..7c9d5320a3a8 100644 --- a/clippy_lints/src/operators/self_assignment.rs +++ b/clippy_lints/src/operators/self_assignment.rs @@ -14,7 +14,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, lhs: &'tcx cx, SELF_ASSIGNMENT, e.span, - &format!("self-assignment of `{}` to `{}`", rhs, lhs), + &format!("self-assignment of `{rhs}` to `{lhs}`"), ); } } diff --git a/clippy_lints/src/operators/verbose_bit_mask.rs b/clippy_lints/src/operators/verbose_bit_mask.rs index ff85fd554298..fbf65e92b322 100644 --- a/clippy_lints/src/operators/verbose_bit_mask.rs +++ b/clippy_lints/src/operators/verbose_bit_mask.rs @@ -35,7 +35,7 @@ pub(super) fn check<'tcx>( diag.span_suggestion( e.span, "try", - format!("{}.trailing_zeros() >= {}", sugg, n.count_ones()), + format!("{sugg}.trailing_zeros() >= {}", n.count_ones()), Applicability::MaybeIncorrect, ); }, diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 0315678bf97a..4eb42da1fed0 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::sugg::Sugg; use clippy_utils::{ - can_move_expr_to_closure, eager_or_lazy, higher, in_constant, is_else_clause, is_lang_ctor, peel_blocks, + can_move_expr_to_closure, eager_or_lazy, higher, in_constant, is_else_clause, is_res_lang_ctor, peel_blocks, peel_hir_expr_while, CaptureKind, }; use if_chain::if_chain; @@ -88,7 +88,7 @@ declare_lint_pass!(OptionIfLetElse => [OPTION_IF_LET_ELSE]); /// None/_ => {..} /// } /// ``` -struct OptionOccurence { +struct OptionOccurrence { option: String, method_sugg: String, some_expr: String, @@ -109,13 +109,13 @@ fn format_option_in_sugg(cx: &LateContext<'_>, cond_expr: &Expr<'_>, as_ref: boo ) } -fn try_get_option_occurence<'tcx>( +fn try_get_option_occurrence<'tcx>( cx: &LateContext<'tcx>, pat: &Pat<'tcx>, expr: &Expr<'_>, if_then: &'tcx Expr<'_>, if_else: &'tcx Expr<'_>, -) -> Option { +) -> Option { let cond_expr = match expr.kind { ExprKind::Unary(UnOp::Deref, inner_expr) | ExprKind::AddrOf(_, _, inner_expr) => inner_expr, _ => expr, @@ -160,10 +160,10 @@ fn try_get_option_occurence<'tcx>( } } - return Some(OptionOccurence { + return Some(OptionOccurrence { option: format_option_in_sugg(cx, cond_expr, as_ref, as_mut), method_sugg: method_sugg.to_string(), - some_expr: format!("|{}{}| {}", capture_mut, capture_name, Sugg::hir_with_macro_callsite(cx, some_body, "..")), + some_expr: format!("|{capture_mut}{capture_name}| {}", Sugg::hir_with_macro_callsite(cx, some_body, "..")), none_expr: format!("{}{}", if method_sugg == "map_or" { "" } else { "|| " }, Sugg::hir_with_macro_callsite(cx, none_body, "..")), }); } @@ -174,7 +174,8 @@ fn try_get_option_occurence<'tcx>( fn try_get_inner_pat<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>) -> Option<&'tcx Pat<'tcx>> { if let PatKind::TupleStruct(ref qpath, [inner_pat], ..) = pat.kind { - if is_lang_ctor(cx, qpath, OptionSome) || is_lang_ctor(cx, qpath, ResultOk) { + let res = cx.qpath_res(qpath, pat.hir_id); + if is_res_lang_ctor(cx, res, OptionSome) || is_res_lang_ctor(cx, res, ResultOk) { return Some(inner_pat); } } @@ -182,9 +183,9 @@ fn try_get_inner_pat<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>) -> Option<&' } /// If this expression is the option if let/else construct we're detecting, then -/// this function returns an `OptionOccurence` struct with details if +/// this function returns an `OptionOccurrence` struct with details if /// this construct is found, or None if this construct is not found. -fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option { +fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option { if let Some(higher::IfLet { let_pat, let_expr, @@ -193,16 +194,16 @@ fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> }) = higher::IfLet::hir(cx, expr) { if !is_else_clause(cx.tcx, expr) { - return try_get_option_occurence(cx, let_pat, let_expr, if_then, if_else); + return try_get_option_occurrence(cx, let_pat, let_expr, if_then, if_else); } } None } -fn detect_option_match<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option { +fn detect_option_match<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option { if let ExprKind::Match(ex, arms, MatchSource::Normal) = expr.kind { if let Some((let_pat, if_then, if_else)) = try_convert_match(cx, arms) { - return try_get_option_occurence(cx, let_pat, ex, if_then, if_else); + return try_get_option_occurrence(cx, let_pat, ex, if_then, if_else); } } None @@ -226,9 +227,10 @@ fn try_convert_match<'tcx>( fn is_none_or_err_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { match arm.pat.kind { - PatKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone), + PatKind::Path(ref qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), OptionNone), PatKind::TupleStruct(ref qpath, [first_pat], _) => { - is_lang_ctor(cx, qpath, ResultErr) && matches!(first_pat.kind, PatKind::Wild) + is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), ResultErr) + && matches!(first_pat.kind, PatKind::Wild) }, PatKind::Wild => true, _ => false, diff --git a/clippy_lints/src/panic_in_result_fn.rs b/clippy_lints/src/panic_in_result_fn.rs index 4aa0d9227aba..efec12489a9b 100644 --- a/clippy_lints/src/panic_in_result_fn.rs +++ b/clippy_lints/src/panic_in_result_fn.rs @@ -2,9 +2,10 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::return_ty; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::visitors::expr_visitor_no_bodies; +use clippy_utils::visitors::{for_each_expr, Descend}; +use core::ops::ControlFlow; use rustc_hir as hir; -use rustc_hir::intravisit::{FnKind, Visitor}; +use rustc_hir::intravisit::FnKind; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{sym, Span}; @@ -58,18 +59,20 @@ impl<'tcx> LateLintPass<'tcx> for PanicInResultFn { fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir::Body<'tcx>) { let mut panics = Vec::new(); - expr_visitor_no_bodies(|expr| { - let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return true }; + let _: Option = for_each_expr(body.value, |e| { + let Some(macro_call) = root_macro_call_first_node(cx, e) else { + return ControlFlow::Continue(Descend::Yes); + }; if matches!( cx.tcx.item_name(macro_call.def_id).as_str(), "unimplemented" | "unreachable" | "panic" | "todo" | "assert" | "assert_eq" | "assert_ne" ) { panics.push(macro_call.span); - return false; + ControlFlow::Continue(Descend::No) + } else { + ControlFlow::Continue(Descend::Yes) } - true - }) - .visit_expr(body.value); + }); if !panics.is_empty() { span_lint_and_then( cx, diff --git a/clippy_lints/src/partialeq_to_none.rs b/clippy_lints/src/partialeq_to_none.rs index 000b0ba7a148..6810a2431758 100644 --- a/clippy_lints/src/partialeq_to_none.rs +++ b/clippy_lints/src/partialeq_to_none.rs @@ -1,5 +1,5 @@ use clippy_utils::{ - diagnostics::span_lint_and_sugg, is_lang_ctor, peel_hir_expr_refs, peel_ref_operators, sugg, + diagnostics::span_lint_and_sugg, is_res_lang_ctor, path_res, peel_hir_expr_refs, peel_ref_operators, sugg, ty::is_type_diagnostic_item, }; use rustc_errors::Applicability; @@ -54,8 +54,7 @@ impl<'tcx> LateLintPass<'tcx> for PartialeqToNone { // If the expression is a literal `Option::None` let is_none_ctor = |expr: &Expr<'_>| { !expr.span.from_expansion() - && matches!(&peel_hir_expr_refs(expr).0.kind, - ExprKind::Path(p) if is_lang_ctor(cx, p, LangItem::OptionNone)) + && is_res_lang_ctor(cx, path_res(cx, peel_hir_expr_refs(expr).0), LangItem::OptionNone) }; let mut applicability = Applicability::MachineApplicable; diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs index 6b2eea489322..45e98de10ace 100644 --- a/clippy_lints/src/pass_by_ref_or_value.rs +++ b/clippy_lints/src/pass_by_ref_or_value.rs @@ -209,7 +209,7 @@ impl<'tcx> PassByRefOrValue { cx, TRIVIALLY_COPY_PASS_BY_REF, input.span, - &format!("this argument ({} byte) is passed by reference, but would be more efficient if passed by value (limit: {} byte)", size, self.ref_min_size), + &format!("this argument ({size} byte) is passed by reference, but would be more efficient if passed by value (limit: {} byte)", self.ref_min_size), "consider passing by value instead", value_type, Applicability::Unspecified, @@ -237,7 +237,7 @@ impl<'tcx> PassByRefOrValue { cx, LARGE_TYPES_PASSED_BY_VALUE, input.span, - &format!("this argument ({} byte) is passed by value, but might be more efficient if passed by reference (limit: {} byte)", size, self.value_max_size), + &format!("this argument ({size} byte) is passed by value, but might be more efficient if passed by reference (limit: {} byte)", self.value_max_size), "consider passing by reference instead", format!("&{}", snippet(cx, input.span, "_")), Applicability::MaybeIncorrect, diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 41d1baba64f8..d296a150b46d 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -463,7 +463,7 @@ fn check_fn_args<'cx, 'tcx: 'cx>( diag.span_suggestion( hir_ty.span, "change this to", - format!("&{}{}", mutability.prefix_str(), ty_name), + format!("&{}{ty_name}", mutability.prefix_str()), Applicability::Unspecified, ); } @@ -669,7 +669,7 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args: } fn get_rptr_lm<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutability, Span)> { - if let TyKind::Rptr(ref lt, ref m) = ty.kind { + if let TyKind::Rptr(lt, ref m) = ty.kind { Some((lt, m.mutbl, ty.span)) } else { None diff --git a/clippy_lints/src/ptr_offset_with_cast.rs b/clippy_lints/src/ptr_offset_with_cast.rs index 4dc65da3ea1f..b0a5d1a67582 100644 --- a/clippy_lints/src/ptr_offset_with_cast.rs +++ b/clippy_lints/src/ptr_offset_with_cast.rs @@ -60,7 +60,7 @@ impl<'tcx> LateLintPass<'tcx> for PtrOffsetWithCast { None => return, }; - let msg = format!("use of `{}` with a `usize` casted to an `isize`", method); + let msg = format!("use of `{method}` with a `usize` casted to an `isize`"); if let Some(sugg) = build_suggestion(cx, method, receiver_expr, cast_lhs_expr) { span_lint_and_sugg( cx, @@ -124,7 +124,7 @@ fn build_suggestion<'tcx>( ) -> Option { let receiver = snippet_opt(cx, receiver_expr.span)?; let cast_lhs = snippet_opt(cx, cast_lhs_expr.span)?; - Some(format!("{}.{}({})", receiver, method.suggestion(), cast_lhs)) + Some(format!("{receiver}.{}({cast_lhs})", method.suggestion())) } #[derive(Copy, Clone)] diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index 569870ab2b7f..328371fd602f 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -3,11 +3,12 @@ use clippy_utils::higher; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{ - eq_expr_value, get_parent_node, is_else_clause, is_lang_ctor, path_to_local, path_to_local_id, peel_blocks, - peel_blocks_with_stmt, + eq_expr_value, get_parent_node, in_constant, is_else_clause, is_res_lang_ctor, path_to_local, path_to_local_id, + peel_blocks, peel_blocks_with_stmt, }; use if_chain::if_chain; use rustc_errors::Applicability; +use rustc_hir::def::Res; use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; use rustc_hir::{BindingAnnotation, ByRef, Expr, ExprKind, Node, PatKind, PathSegment, QPath}; use rustc_lint::{LateContext, LateLintPass}; @@ -58,7 +59,7 @@ enum IfBlockType<'hir> { /// Contains: let_pat_qpath (Xxx), let_pat_type, let_pat_sym (a), let_expr (b), if_then (c), /// if_else (d) IfLet( - &'hir QPath<'hir>, + Res, Ty<'hir>, Symbol, &'hir Expr<'hir>, @@ -97,12 +98,12 @@ fn check_is_none_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: &Ex !matches!(caller.kind, ExprKind::Call(..) | ExprKind::MethodCall(..)); let sugg = if let Some(else_inner) = r#else { if eq_expr_value(cx, caller, peel_blocks(else_inner)) { - format!("Some({}?)", receiver_str) + format!("Some({receiver_str}?)") } else { return; } } else { - format!("{}{}?;", receiver_str, if by_ref { ".as_ref()" } else { "" }) + format!("{receiver_str}{}?;", if by_ref { ".as_ref()" } else { "" }) }; span_lint_and_sugg( @@ -126,7 +127,14 @@ fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: if ddpos.as_opt_usize().is_none(); if let PatKind::Binding(BindingAnnotation(by_ref, _), bind_id, ident, None) = field.kind; let caller_ty = cx.typeck_results().expr_ty(let_expr); - let if_block = IfBlockType::IfLet(path1, caller_ty, ident.name, let_expr, if_then, if_else); + let if_block = IfBlockType::IfLet( + cx.qpath_res(path1, let_pat.hir_id), + caller_ty, + ident.name, + let_expr, + if_then, + if_else + ); if (is_early_return(sym::Option, cx, &if_block) && path_to_local_id(peel_blocks(if_then), bind_id)) || is_early_return(sym::Result, cx, &if_block); if if_else.map(|e| eq_expr_value(cx, let_expr, peel_blocks(e))).filter(|e| *e).is_none(); @@ -135,8 +143,7 @@ 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!(get_parent_node(cx.tcx, expr.hir_id), Some(Node::Stmt(_))); let sugg = format!( - "{}{}?{}", - receiver_str, + "{receiver_str}{}?{}", if by_ref == ByRef::Yes { ".as_ref()" } else { "" }, if requires_semi { ";" } else { "" } ); @@ -166,21 +173,21 @@ fn is_early_return(smbl: Symbol, cx: &LateContext<'_>, if_block: &IfBlockType<'_ _ => false, } }, - IfBlockType::IfLet(qpath, let_expr_ty, let_pat_sym, let_expr, if_then, if_else) => { + IfBlockType::IfLet(res, let_expr_ty, let_pat_sym, let_expr, if_then, if_else) => { is_type_diagnostic_item(cx, let_expr_ty, 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_lang_ctor(cx, qpath, OptionSome) + is_res_lang_ctor(cx, res, OptionSome) && if_else.is_some() && expr_return_none_or_err(smbl, cx, if_else.unwrap(), let_expr, None) }, sym::Result => { - (is_lang_ctor(cx, qpath, ResultOk) + (is_res_lang_ctor(cx, res, ResultOk) && if_else.is_some() && expr_return_none_or_err(smbl, cx, if_else.unwrap(), let_expr, Some(let_pat_sym))) - || is_lang_ctor(cx, qpath, ResultErr) + || is_res_lang_ctor(cx, res, ResultErr) && expr_return_none_or_err(smbl, cx, if_then, let_expr, Some(let_pat_sym)) }, _ => false, @@ -199,7 +206,7 @@ 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_lang_ctor(cx, qpath, OptionNone), + 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), _ => false, }, @@ -224,7 +231,9 @@ fn expr_return_none_or_err( impl<'tcx> LateLintPass<'tcx> for QuestionMark { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - check_is_none_or_err_and_early_return(cx, expr); - check_if_let_some_or_err_and_early_return(cx, expr); + if !in_constant(cx, expr.hir_id) { + check_is_none_or_err_and_early_return(cx, expr); + check_if_let_some_or_err_and_early_return(cx, expr); + } } } diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 918d624eec6f..c6fbb5e805ab 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -243,9 +243,9 @@ fn check_possible_range_contains( cx, MANUAL_RANGE_CONTAINS, span, - &format!("manual `{}::contains` implementation", range_type), + &format!("manual `{range_type}::contains` implementation"), "use", - format!("({}{}{}{}).contains(&{})", lo, space, range_op, hi, name), + format!("({lo}{space}{range_op}{hi}).contains(&{name})"), applicability, ); } else if !combine_and && ord == Some(l.ord) { @@ -273,9 +273,9 @@ fn check_possible_range_contains( cx, MANUAL_RANGE_CONTAINS, span, - &format!("manual `!{}::contains` implementation", range_type), + &format!("manual `!{range_type}::contains` implementation"), "use", - format!("!({}{}{}{}).contains(&{})", lo, space, range_op, hi, name), + format!("!({lo}{space}{range_op}{hi}).contains(&{name})"), applicability, ); } @@ -372,14 +372,14 @@ fn check_exclusive_range_plus_one(cx: &LateContext<'_>, expr: &Expr<'_>) { diag.span_suggestion( span, "use", - format!("({}..={})", start, end), + format!("({start}..={end})"), Applicability::MaybeIncorrect, ); } else { diag.span_suggestion( span, "use", - format!("{}..={}", start, end), + format!("{start}..={end}"), Applicability::MachineApplicable, // snippet ); } @@ -408,7 +408,7 @@ fn check_inclusive_range_minus_one(cx: &LateContext<'_>, expr: &Expr<'_>) { diag.span_suggestion( expr.span, "use", - format!("{}..{}", start, end), + format!("{start}..{end}"), Applicability::MachineApplicable, // snippet ); }, @@ -486,7 +486,7 @@ fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) { expr.span, "consider using the following if you are attempting to iterate over this \ range in reverse", - format!("({}{}{}).rev()", end_snippet, dots, start_snippet), + format!("({end_snippet}{dots}{start_snippet}).rev()"), Applicability::MaybeIncorrect, ); } diff --git a/clippy_lints/src/read_zero_byte_vec.rs b/clippy_lints/src/read_zero_byte_vec.rs index 94dec191103c..fa107858863a 100644 --- a/clippy_lints/src/read_zero_byte_vec.rs +++ b/clippy_lints/src/read_zero_byte_vec.rs @@ -2,9 +2,10 @@ use clippy_utils::{ diagnostics::{span_lint, span_lint_and_sugg}, higher::{get_vec_init_kind, VecInitKind}, source::snippet, - visitors::expr_visitor_no_bodies, + visitors::for_each_expr, }; -use hir::{intravisit::Visitor, ExprKind, Local, PatKind, PathSegment, QPath, StmtKind}; +use core::ops::ControlFlow; +use hir::{Expr, ExprKind, Local, PatKind, PathSegment, QPath, StmtKind}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; @@ -58,10 +59,8 @@ impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec { && let PatKind::Binding(_, _, ident, _) = pat.kind && let Some(vec_init_kind) = get_vec_init_kind(cx, init) { - // finds use of `_.read(&mut v)` - let mut read_found = false; - let mut visitor = expr_visitor_no_bodies(|expr| { - if let ExprKind::MethodCall(path, _self, [arg], _) = expr.kind + let visitor = |expr: &Expr<'_>| { + if let ExprKind::MethodCall(path, _, [arg], _) = expr.kind && let PathSegment { ident: read_or_read_exact, .. } = *path && matches!(read_or_read_exact.as_str(), "read" | "read_exact") && let ExprKind::AddrOf(_, hir::Mutability::Mut, inner) = arg.kind @@ -69,27 +68,22 @@ impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec { && let [inner_seg] = inner_path.segments && ident.name == inner_seg.ident.name { - read_found = true; - } - !read_found - }); - - let next_stmt_span; - if idx == block.stmts.len() - 1 { - // case { .. stmt; expr } - if let Some(e) = block.expr { - visitor.visit_expr(e); - next_stmt_span = e.span; + ControlFlow::Break(()) } else { - return; + ControlFlow::Continue(()) } - } else { + }; + + let (read_found, next_stmt_span) = + if let Some(next_stmt) = block.stmts.get(idx + 1) { // case { .. stmt; stmt; .. } - let next_stmt = &block.stmts[idx + 1]; - visitor.visit_stmt(next_stmt); - next_stmt_span = next_stmt.span; - } - drop(visitor); + (for_each_expr(next_stmt, visitor).is_some(), next_stmt.span) + } else if let Some(e) = block.expr { + // case { .. stmt; expr } + (for_each_expr(e, visitor).is_some(), e.span) + } else { + return + }; if read_found && !next_stmt_span.from_expansion() { let applicability = Applicability::MaybeIncorrect; @@ -101,9 +95,8 @@ impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec { next_stmt_span, "reading zero byte data to `Vec`", "try", - format!("{}.resize({}, 0); {}", + format!("{}.resize({len}, 0); {}", ident.as_str(), - len, snippet(cx, next_stmt_span, "..") ), applicability, diff --git a/clippy_lints/src/redundant_pub_crate.rs b/clippy_lints/src/redundant_pub_crate.rs index 3c6ca9d98975..464f6827e1d5 100644 --- a/clippy_lints/src/redundant_pub_crate.rs +++ b/clippy_lints/src/redundant_pub_crate.rs @@ -56,7 +56,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantPubCrate { cx, REDUNDANT_PUB_CRATE, span, - &format!("pub(crate) {} inside private module", descr), + &format!("pub(crate) {descr} inside private module"), |diag| { diag.span_suggestion( item.vis_span, diff --git a/clippy_lints/src/redundant_slicing.rs b/clippy_lints/src/redundant_slicing.rs index 8693ca9af830..245a02ea26e6 100644 --- a/clippy_lints/src/redundant_slicing.rs +++ b/clippy_lints/src/redundant_slicing.rs @@ -127,9 +127,9 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing { let snip = snippet_with_context(cx, indexed.span, ctxt, "..", &mut app).0; let sugg = if (deref_count != 0 || !reborrow_str.is_empty()) && needs_parens_for_prefix { - format!("({}{}{})", reborrow_str, "*".repeat(deref_count), snip) + format!("({reborrow_str}{}{snip})", "*".repeat(deref_count)) } else { - format!("{}{}{}", reborrow_str, "*".repeat(deref_count), snip) + format!("{reborrow_str}{}{snip}", "*".repeat(deref_count)) }; (lint, help_str, sugg) @@ -141,9 +141,9 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing { if deref_ty == expr_ty { let snip = snippet_with_context(cx, indexed.span, ctxt, "..", &mut app).0; let sugg = if needs_parens_for_prefix { - format!("(&{}{}*{})", mutability.prefix_str(), "*".repeat(indexed_ref_count), snip) + format!("(&{}{}*{snip})", mutability.prefix_str(), "*".repeat(indexed_ref_count)) } else { - format!("&{}{}*{}", mutability.prefix_str(), "*".repeat(indexed_ref_count), snip) + format!("&{}{}*{snip}", mutability.prefix_str(), "*".repeat(indexed_ref_count)) }; (DEREF_BY_SLICING_LINT, "dereference the original value instead", sugg) } else { diff --git a/clippy_lints/src/redundant_static_lifetimes.rs b/clippy_lints/src/redundant_static_lifetimes.rs index 2d751c274679..60ba62c4a433 100644 --- a/clippy_lints/src/redundant_static_lifetimes.rs +++ b/clippy_lints/src/redundant_static_lifetimes.rs @@ -67,7 +67,7 @@ impl RedundantStaticLifetimes { TyKind::Path(..) | TyKind::Slice(..) | TyKind::Array(..) | TyKind::Tup(..) => { if lifetime.ident.name == rustc_span::symbol::kw::StaticLifetime { let snip = snippet(cx, borrow_type.ty.span, ""); - let sugg = format!("&{}", snip); + let sugg = format!("&{snip}"); span_lint_and_then( cx, REDUNDANT_STATIC_LIFETIMES, diff --git a/clippy_lints/src/regex.rs b/clippy_lints/src/regex.rs index 6bcae0da8f48..1fda58fa54de 100644 --- a/clippy_lints/src/regex.rs +++ b/clippy_lints/src/regex.rs @@ -172,7 +172,7 @@ fn check_regex<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, utf8: bool) { ); }, Err(e) => { - span_lint(cx, INVALID_REGEX, expr.span, &format!("regex syntax error: {}", e)); + span_lint(cx, INVALID_REGEX, expr.span, &format!("regex syntax error: {e}")); }, } } @@ -200,7 +200,7 @@ fn check_regex<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, utf8: bool) { ); }, Err(e) => { - span_lint(cx, INVALID_REGEX, expr.span, &format!("regex syntax error: {}", e)); + span_lint(cx, INVALID_REGEX, expr.span, &format!("regex syntax error: {e}")); }, } } diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 91553240e3c9..2b2a41d16011 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -1,9 +1,11 @@ -use clippy_utils::diagnostics::span_lint_hir_and_then; +use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::source::{snippet_opt, snippet_with_context}; +use clippy_utils::visitors::{for_each_expr, Descend}; use clippy_utils::{fn_def_id, path_to_local_id}; +use core::ops::ControlFlow; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::intravisit::{walk_expr, FnKind, Visitor}; +use rustc_hir::intravisit::FnKind; use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, HirId, MatchSource, PatKind, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -72,6 +74,27 @@ enum RetReplacement { Unit, } +impl RetReplacement { + fn sugg_help(self) -> &'static str { + match self { + Self::Empty => "remove `return`", + Self::Block => "replace `return` with an empty block", + Self::Unit => "replace `return` with a unit value", + } + } +} + +impl ToString for RetReplacement { + fn to_string(&self) -> String { + match *self { + Self::Empty => "", + Self::Block => "{}", + Self::Unit => "()", + } + .to_string() + } +} + declare_lint_pass!(Return => [LET_AND_RETURN, NEEDLESS_RETURN]); impl<'tcx> LateLintPass<'tcx> for Return { @@ -139,26 +162,35 @@ impl<'tcx> LateLintPass<'tcx> for Return { } else { RetReplacement::Empty }; - check_final_expr(cx, body.value, Some(body.value.span), replacement); + check_final_expr(cx, body.value, vec![], replacement); }, FnKind::ItemFn(..) | FnKind::Method(..) => { - if let ExprKind::Block(block, _) = body.value.kind { - check_block_return(cx, block); - } + check_block_return(cx, &body.value.kind, vec![]); }, } } } -fn check_block_return<'tcx>(cx: &LateContext<'tcx>, block: &Block<'tcx>) { - if let Some(expr) = block.expr { - check_final_expr(cx, expr, Some(expr.span), RetReplacement::Empty); - } else if let Some(stmt) = block.stmts.iter().last() { - match stmt.kind { - StmtKind::Expr(expr) | StmtKind::Semi(expr) => { - check_final_expr(cx, expr, Some(stmt.span), RetReplacement::Empty); - }, - _ => (), +// if `expr` is a block, check if there are needless returns in it +fn check_block_return<'tcx>(cx: &LateContext<'tcx>, expr_kind: &ExprKind<'tcx>, semi_spans: Vec) { + if let ExprKind::Block(block, _) = expr_kind { + if let Some(block_expr) = block.expr { + check_final_expr(cx, block_expr, semi_spans, RetReplacement::Empty); + } else if let Some(stmt) = block.stmts.iter().last() { + match stmt.kind { + StmtKind::Expr(expr) => { + check_final_expr(cx, expr, semi_spans, RetReplacement::Empty); + }, + StmtKind::Semi(semi_expr) => { + let mut semi_spans_and_this_one = semi_spans; + // we only want the span containing the semicolon so we can remove it later. From `entry.rs:382` + if let Some(semicolon_span) = stmt.span.trim_start(semi_expr.span) { + semi_spans_and_this_one.push(semicolon_span); + check_final_expr(cx, semi_expr, semi_spans_and_this_one, RetReplacement::Empty); + } + }, + _ => (), + } } } } @@ -166,10 +198,12 @@ fn check_block_return<'tcx>(cx: &LateContext<'tcx>, block: &Block<'tcx>) { fn check_final_expr<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, - span: Option, + semi_spans: Vec, /* containing all the places where we would need to remove semicolons if finding an + * needless return */ replacement: RetReplacement, ) { - match expr.kind { + let peeled_drop_expr = expr.peel_drop_temps(); + match &peeled_drop_expr.kind { // simple return is always "bad" ExprKind::Ret(ref inner) => { if cx.tcx.hir().attrs(expr.hir_id).is_empty() { @@ -177,24 +211,18 @@ fn check_final_expr<'tcx>( if !borrows { emit_return_lint( cx, - inner.map_or(expr.hir_id, |inner| inner.hir_id), - span.expect("`else return` is not possible"), + peeled_drop_expr.span, + semi_spans, inner.as_ref().map(|i| i.span), replacement, ); } } }, - // a whole block? check it! - ExprKind::Block(block, _) => { - check_block_return(cx, block); - }, ExprKind::If(_, then, else_clause_opt) => { - if let ExprKind::Block(ifblock, _) = then.kind { - check_block_return(cx, ifblock); - } + check_block_return(cx, &then.kind, semi_spans.clone()); if let Some(else_clause) = else_clause_opt { - check_final_expr(cx, else_clause, None, RetReplacement::Empty); + check_block_return(cx, &else_clause.kind, semi_spans); } }, // a match expr, check all arms @@ -203,123 +231,61 @@ fn check_final_expr<'tcx>( // (except for unit type functions) so we don't match it ExprKind::Match(_, arms, MatchSource::Normal) => { for arm in arms.iter() { - check_final_expr(cx, arm.body, Some(arm.body.span), RetReplacement::Unit); + check_final_expr(cx, arm.body, semi_spans.clone(), RetReplacement::Unit); } }, - ExprKind::DropTemps(expr) => check_final_expr(cx, expr, None, RetReplacement::Empty), - _ => (), + // if it's a whole block, check it + other_expr_kind => check_block_return(cx, other_expr_kind, semi_spans), } } fn emit_return_lint( cx: &LateContext<'_>, - emission_place: HirId, ret_span: Span, + semi_spans: Vec, inner_span: Option, replacement: RetReplacement, ) { if ret_span.from_expansion() { return; } - match inner_span { - Some(inner_span) => { - let mut applicability = Applicability::MachineApplicable; - span_lint_hir_and_then( - cx, - NEEDLESS_RETURN, - emission_place, - ret_span, - "unneeded `return` statement", - |diag| { - let (snippet, _) = snippet_with_context(cx, inner_span, ret_span.ctxt(), "..", &mut applicability); - diag.span_suggestion(ret_span, "remove `return`", snippet, applicability); - }, - ); + let mut applicability = Applicability::MachineApplicable; + let return_replacement = inner_span.map_or_else( + || replacement.to_string(), + |inner_span| { + let (snippet, _) = snippet_with_context(cx, inner_span, ret_span.ctxt(), "..", &mut applicability); + snippet.to_string() }, - None => match replacement { - RetReplacement::Empty => { - span_lint_hir_and_then( - cx, - NEEDLESS_RETURN, - emission_place, - ret_span, - "unneeded `return` statement", - |diag| { - diag.span_suggestion( - ret_span, - "remove `return`", - String::new(), - Applicability::MachineApplicable, - ); - }, - ); - }, - RetReplacement::Block => { - span_lint_hir_and_then( - cx, - NEEDLESS_RETURN, - emission_place, - ret_span, - "unneeded `return` statement", - |diag| { - diag.span_suggestion( - ret_span, - "replace `return` with an empty block", - "{}".to_string(), - Applicability::MachineApplicable, - ); - }, - ); - }, - RetReplacement::Unit => { - span_lint_hir_and_then( - cx, - NEEDLESS_RETURN, - emission_place, - ret_span, - "unneeded `return` statement", - |diag| { - diag.span_suggestion( - ret_span, - "replace `return` with a unit value", - "()".to_string(), - Applicability::MachineApplicable, - ); - }, - ); - }, - }, - } + ); + let sugg_help = if inner_span.is_some() { + "remove `return`" + } else { + replacement.sugg_help() + }; + span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded `return` statement", |diag| { + diag.span_suggestion_hidden(ret_span, sugg_help, return_replacement, applicability); + // for each parent statement, we need to remove the semicolon + for semi_stmt_span in semi_spans { + diag.tool_only_span_suggestion(semi_stmt_span, "remove this semicolon", "", applicability); + } + }); } fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { - let mut visitor = BorrowVisitor { cx, borrows: false }; - walk_expr(&mut visitor, expr); - visitor.borrows -} - -struct BorrowVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - borrows: bool, -} - -impl<'tcx> Visitor<'tcx> for BorrowVisitor<'_, 'tcx> { - fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if self.borrows || expr.span.from_expansion() { - return; - } - - if let Some(def_id) = fn_def_id(self.cx, expr) { - self.borrows = self - .cx + for_each_expr(expr, |e| { + if let Some(def_id) = fn_def_id(cx, e) + && cx .tcx .fn_sig(def_id) - .output() .skip_binder() + .output() .walk() - .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_))); + .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_))) + { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(Descend::from(!expr.span.from_expansion())) } - - walk_expr(self, expr); - } + }) + .is_some() } diff --git a/clippy_lints/src/same_name_method.rs b/clippy_lints/src/same_name_method.rs index 20184d54b76e..4249063d2d47 100644 --- a/clippy_lints/src/same_name_method.rs +++ b/clippy_lints/src/same_name_method.rs @@ -108,7 +108,7 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod { |diag| { diag.span_note( trait_method_span, - &format!("existing `{}` defined here", method_name), + &format!("existing `{method_name}` defined here"), ); }, ); @@ -151,7 +151,7 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod { // iterate on trait_spans? diag.span_note( trait_spans[0], - &format!("existing `{}` defined here", method_name), + &format!("existing `{method_name}` defined here"), ); }, ); diff --git a/clippy_lints/src/semicolon_if_nothing_returned.rs b/clippy_lints/src/semicolon_if_nothing_returned.rs index 729694da46d5..66638eed9983 100644 --- a/clippy_lints/src/semicolon_if_nothing_returned.rs +++ b/clippy_lints/src/semicolon_if_nothing_returned.rs @@ -54,7 +54,7 @@ impl<'tcx> LateLintPass<'tcx> for SemicolonIfNothingReturned { } let sugg = sugg::Sugg::hir_with_macro_callsite(cx, expr, ".."); - let suggestion = format!("{0};", sugg); + let suggestion = format!("{sugg};"); span_lint_and_sugg( cx, SEMICOLON_IF_NOTHING_RETURNED, diff --git a/clippy_lints/src/slow_vector_initialization.rs b/clippy_lints/src/slow_vector_initialization.rs index c07aa00a1278..760399231513 100644 --- a/clippy_lints/src/slow_vector_initialization.rs +++ b/clippy_lints/src/slow_vector_initialization.rs @@ -1,9 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{get_enclosing_block, is_expr_path_def_path, path_to_local, path_to_local_id, paths, SpanlessEq}; +use clippy_utils::{ + get_enclosing_block, is_integer_literal, is_path_diagnostic_item, path_to_local, path_to_local_id, SpanlessEq, +}; use if_chain::if_chain; -use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_block, walk_expr, walk_stmt, Visitor}; use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, PatKind, QPath, Stmt, StmtKind}; @@ -174,7 +175,7 @@ impl SlowVectorInit { diag.span_suggestion( vec_alloc.allocation_expr.span, "consider replace allocation with", - format!("vec![0; {}]", len_expr), + format!("vec![0; {len_expr}]"), Applicability::Unspecified, ); }); @@ -219,8 +220,7 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> { && path_to_local_id(self_arg, self.vec_alloc.local_id) && path.ident.name == sym!(resize) // Check that is filled with 0 - && let ExprKind::Lit(ref lit) = fill_arg.kind - && let LitKind::Int(0, _) = lit.node { + && is_integer_literal(fill_arg, 0) { // Check that len expression is equals to `with_capacity` expression if SpanlessEq::new(self.cx).eq_expr(len_arg, self.vec_alloc.len_expr) { self.slow_expression = Some(InitializationType::Resize(expr)); @@ -254,10 +254,8 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> { fn is_repeat_zero(&self, expr: &Expr<'_>) -> bool { if_chain! { if let ExprKind::Call(fn_expr, [repeat_arg]) = expr.kind; - if is_expr_path_def_path(self.cx, fn_expr, &paths::ITER_REPEAT); - if let ExprKind::Lit(ref lit) = repeat_arg.kind; - if let LitKind::Int(0, _) = lit.node; - + if is_path_diagnostic_item(self.cx, fn_expr, sym::iter_repeat); + if is_integer_literal(repeat_arg, 0); then { true } else { diff --git a/clippy_lints/src/std_instead_of_core.rs b/clippy_lints/src/std_instead_of_core.rs index ffd63cc687a1..d6b336bef943 100644 --- a/clippy_lints/src/std_instead_of_core.rs +++ b/clippy_lints/src/std_instead_of_core.rs @@ -1,6 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_help; +use rustc_hir::def_id::DefId; use rustc_hir::{def::Res, HirId, Path, PathSegment}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::DefIdTree; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{sym, symbol::kw, Span}; @@ -94,6 +96,7 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports { fn check_path(&mut self, cx: &LateContext<'tcx>, path: &Path<'tcx>, _: HirId) { if let Res::Def(_, def_id) = path.res && let Some(first_segment) = get_first_segment(path) + && is_stable(cx, def_id) { let (lint, msg, help) = match first_segment.ident.name { sym::std => match cx.tcx.crate_name(def_id.krate) { @@ -146,3 +149,22 @@ fn get_first_segment<'tcx>(path: &Path<'tcx>) -> Option<&'tcx PathSegment<'tcx>> _ => None, } } + +/// Checks if all ancestors of `def_id` are stable, to avoid linting +/// [unstable moves](https://github.com/rust-lang/rust/pull/95956) +fn is_stable(cx: &LateContext<'_>, mut def_id: DefId) -> bool { + loop { + if cx + .tcx + .lookup_stability(def_id) + .map_or(false, |stability| stability.is_unstable()) + { + return false; + } + + match cx.tcx.opt_parent(def_id) { + Some(parent) => def_id = parent, + None => return true, + } + } +} diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 662d399ca538..d356c99c8fc4 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -284,7 +284,7 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { e.span, "calling a slice of `as_bytes()` with `from_utf8` should be not necessary", "try", - format!("Some(&{}[{}])", snippet_app, snippet(cx, right.span, "..")), + format!("Some(&{snippet_app}[{}])", snippet(cx, right.span, "..")), applicability ) } @@ -500,8 +500,8 @@ impl<'tcx> LateLintPass<'tcx> for TrimSplitWhitespace { cx, TRIM_SPLIT_WHITESPACE, trim_span.with_hi(split_ws_span.lo()), - &format!("found call to `str::{}` before `str::split_whitespace`", trim_fn_name), - &format!("remove `{}()`", trim_fn_name), + &format!("found call to `str::{trim_fn_name}` before `str::split_whitespace`"), + &format!("remove `{trim_fn_name}()`"), String::new(), Applicability::MachineApplicable, ); diff --git a/clippy_lints/src/strlen_on_c_strings.rs b/clippy_lints/src/strlen_on_c_strings.rs index 78403d9fdb7e..03324c66e8ef 100644 --- a/clippy_lints/src/strlen_on_c_strings.rs +++ b/clippy_lints/src/strlen_on_c_strings.rs @@ -79,7 +79,7 @@ impl<'tcx> LateLintPass<'tcx> for StrlenOnCStrings { span, "using `libc::strlen` on a `CString` or `CStr` value", "try this", - format!("{}.{}().len()", val_name, method_name), + format!("{val_name}.{method_name}().len()"), app, ); } diff --git a/clippy_lints/src/suspicious_operation_groupings.rs b/clippy_lints/src/suspicious_operation_groupings.rs index 5d36f0f5ff8b..eef9bdc78494 100644 --- a/clippy_lints/src/suspicious_operation_groupings.rs +++ b/clippy_lints/src/suspicious_operation_groupings.rs @@ -326,8 +326,7 @@ fn replace_left_sugg( applicability: &mut Applicability, ) -> String { format!( - "{} {} {}", - left_suggestion, + "{left_suggestion} {} {}", binop.op.to_string(), snippet_with_applicability(cx, binop.right.span, "..", applicability), ) @@ -340,10 +339,9 @@ fn replace_right_sugg( applicability: &mut Applicability, ) -> String { format!( - "{} {} {}", + "{} {} {right_suggestion}", snippet_with_applicability(cx, binop.left.span, "..", applicability), binop.op.to_string(), - right_suggestion, ) } @@ -676,9 +674,8 @@ fn suggestion_with_swapped_ident( } Some(format!( - "{}{}{}", + "{}{new_ident}{}", snippet_with_applicability(cx, expr.span.with_hi(current_ident.span.lo()), "..", applicability), - new_ident, snippet_with_applicability(cx, expr.span.with_lo(current_ident.span.hi()), "..", applicability), )) }) diff --git a/clippy_lints/src/suspicious_trait_impl.rs b/clippy_lints/src/suspicious_trait_impl.rs index d47ed459387e..b57b484bdc89 100644 --- a/clippy_lints/src/suspicious_trait_impl.rs +++ b/clippy_lints/src/suspicious_trait_impl.rs @@ -1,8 +1,9 @@ use clippy_utils::diagnostics::span_lint; +use clippy_utils::visitors::for_each_expr; use clippy_utils::{binop_traits, trait_ref_of_method, BINOP_TRAITS, OP_ASSIGN_TRAITS}; +use core::ops::ControlFlow; use if_chain::if_chain; use rustc_hir as hir; -use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -92,25 +93,17 @@ impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl { } fn count_binops(expr: &hir::Expr<'_>) -> u32 { - let mut visitor = BinaryExprVisitor::default(); - visitor.visit_expr(expr); - visitor.nb_binops -} - -#[derive(Default)] -struct BinaryExprVisitor { - nb_binops: u32, -} - -impl<'tcx> Visitor<'tcx> for BinaryExprVisitor { - fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { - match expr.kind { + let mut count = 0u32; + let _: Option = for_each_expr(expr, |e| { + if matches!( + e.kind, hir::ExprKind::Binary(..) - | hir::ExprKind::Unary(hir::UnOp::Not | hir::UnOp::Neg, _) - | hir::ExprKind::AssignOp(..) => self.nb_binops += 1, - _ => {}, + | hir::ExprKind::Unary(hir::UnOp::Not | hir::UnOp::Neg, _) + | hir::ExprKind::AssignOp(..) + ) { + count += 1; } - - walk_expr(self, expr); - } + ControlFlow::Continue(()) + }); + count } diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index 1885f3ca414d..f46c21e12655 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -96,7 +96,7 @@ fn generate_swap_warning(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>, spa cx, MANUAL_SWAP, span, - &format!("this looks like you are swapping elements of `{}` manually", slice), + &format!("this looks like you are swapping elements of `{slice}` manually"), "try", format!( "{}.swap({}, {})", @@ -121,16 +121,16 @@ fn generate_swap_warning(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>, spa cx, MANUAL_SWAP, span, - &format!("this looks like you are swapping `{}` and `{}` manually", first, second), + &format!("this looks like you are swapping `{first}` and `{second}` manually"), |diag| { diag.span_suggestion( span, "try", - format!("{}::mem::swap({}, {})", sugg, first.mut_addr(), second.mut_addr()), + format!("{sugg}::mem::swap({}, {})", first.mut_addr(), second.mut_addr()), applicability, ); if !is_xor_based { - diag.note(&format!("or maybe you should use `{}::mem::replace`?", sugg)); + diag.note(&format!("or maybe you should use `{sugg}::mem::replace`?")); } }, ); @@ -182,7 +182,7 @@ fn check_suspicious_swap(cx: &LateContext<'_>, block: &Block<'_>) { let rhs0 = Sugg::hir_opt(cx, rhs0); let (what, lhs, rhs) = if let (Some(first), Some(second)) = (lhs0, rhs0) { ( - format!(" `{}` and `{}`", first, second), + format!(" `{first}` and `{second}`"), first.mut_addr().to_string(), second.mut_addr().to_string(), ) @@ -196,22 +196,19 @@ fn check_suspicious_swap(cx: &LateContext<'_>, block: &Block<'_>) { span_lint_and_then(cx, ALMOST_SWAPPED, span, - &format!("this looks like you are trying to swap{}", what), + &format!("this looks like you are trying to swap{what}"), |diag| { if !what.is_empty() { diag.span_suggestion( span, "try", format!( - "{}::mem::swap({}, {})", - sugg, - lhs, - rhs, + "{sugg}::mem::swap({lhs}, {rhs})", ), Applicability::MaybeIncorrect, ); diag.note( - &format!("or maybe you should use `{}::mem::replace`?", sugg) + &format!("or maybe you should use `{sugg}::mem::replace`?") ); } }); diff --git a/clippy_lints/src/swap_ptr_to_ref.rs b/clippy_lints/src/swap_ptr_to_ref.rs index 3cbbda80f3a9..d085dda3582b 100644 --- a/clippy_lints/src/swap_ptr_to_ref.rs +++ b/clippy_lints/src/swap_ptr_to_ref.rs @@ -58,7 +58,7 @@ impl LateLintPass<'_> for SwapPtrToRef { let mut app = Applicability::MachineApplicable; let snip1 = snippet_with_context(cx, arg1_span.unwrap_or(arg1.span), ctxt, "..", &mut app).0; let snip2 = snippet_with_context(cx, arg2_span.unwrap_or(arg2.span), ctxt, "..", &mut app).0; - diag.span_suggestion(e.span, "use ptr::swap", format!("core::ptr::swap({}, {})", snip1, snip2), app); + diag.span_suggestion(e.span, "use ptr::swap", format!("core::ptr::swap({snip1}, {snip2})"), app); } } ); diff --git a/clippy_lints/src/to_digit_is_some.rs b/clippy_lints/src/to_digit_is_some.rs index 651201f34ed2..2512500a6be7 100644 --- a/clippy_lints/src/to_digit_is_some.rs +++ b/clippy_lints/src/to_digit_is_some.rs @@ -84,9 +84,9 @@ impl<'tcx> LateLintPass<'tcx> for ToDigitIsSome { "use of `.to_digit(..).is_some()`", "try this", if is_method_call { - format!("{}.is_digit({})", char_arg_snip, radix_snip) + format!("{char_arg_snip}.is_digit({radix_snip})") } else { - format!("char::is_digit({}, {})", char_arg_snip, radix_snip) + format!("char::is_digit({char_arg_snip}, {radix_snip})") }, applicability, ); diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index 2be22884027e..b5f11b4acae0 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -215,9 +215,8 @@ impl TraitBounds { .map(|(_, _, span)| snippet_with_applicability(cx, span, "..", &mut applicability)) .join(" + "); let hint_string = format!( - "consider combining the bounds: `{}: {}`", + "consider combining the bounds: `{}: {trait_bounds}`", snippet(cx, p.bounded_ty.span, "_"), - trait_bounds, ); span_lint_and_help( cx, diff --git a/clippy_lints/src/transmute/crosspointer_transmute.rs b/clippy_lints/src/transmute/crosspointer_transmute.rs index 25d0543c8611..c4b9d82fc735 100644 --- a/clippy_lints/src/transmute/crosspointer_transmute.rs +++ b/clippy_lints/src/transmute/crosspointer_transmute.rs @@ -13,10 +13,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty cx, CROSSPOINTER_TRANSMUTE, e.span, - &format!( - "transmute from a type (`{}`) to the type that it points to (`{}`)", - from_ty, to_ty - ), + &format!("transmute from a type (`{from_ty}`) to the type that it points to (`{to_ty}`)"), ); true }, @@ -25,10 +22,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty cx, CROSSPOINTER_TRANSMUTE, e.span, - &format!( - "transmute from a type (`{}`) to a pointer to that type (`{}`)", - from_ty, to_ty - ), + &format!("transmute from a type (`{from_ty}`) to a pointer to that type (`{to_ty}`)"), ); true }, diff --git a/clippy_lints/src/transmute/transmute_float_to_int.rs b/clippy_lints/src/transmute/transmute_float_to_int.rs index 1bde977cfa27..5ecba512b0fd 100644 --- a/clippy_lints/src/transmute/transmute_float_to_int.rs +++ b/clippy_lints/src/transmute/transmute_float_to_int.rs @@ -24,7 +24,7 @@ pub(super) fn check<'tcx>( cx, TRANSMUTE_FLOAT_TO_INT, e.span, - &format!("transmute from a `{}` to a `{}`", from_ty, to_ty), + &format!("transmute from a `{from_ty}` to a `{to_ty}`"), |diag| { let mut sugg = sugg::Sugg::hir(cx, arg, ".."); @@ -38,7 +38,7 @@ pub(super) fn check<'tcx>( if let ExprKind::Lit(lit) = &arg.kind; if let ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) = lit.node; then { - let op = format!("{}{}", sugg, float_ty.name_str()).into(); + let op = format!("{sugg}{}", float_ty.name_str()).into(); match sugg { sugg::Sugg::MaybeParen(_) => sugg = sugg::Sugg::MaybeParen(op), _ => sugg = sugg::Sugg::NonParen(op) diff --git a/clippy_lints/src/transmute/transmute_int_to_bool.rs b/clippy_lints/src/transmute/transmute_int_to_bool.rs index 8c50b58ca4b8..58227c53de2f 100644 --- a/clippy_lints/src/transmute/transmute_int_to_bool.rs +++ b/clippy_lints/src/transmute/transmute_int_to_bool.rs @@ -23,7 +23,7 @@ pub(super) fn check<'tcx>( cx, TRANSMUTE_INT_TO_BOOL, e.span, - &format!("transmute from a `{}` to a `bool`", from_ty), + &format!("transmute from a `{from_ty}` to a `bool`"), |diag| { let arg = sugg::Sugg::hir(cx, arg, ".."); let zero = sugg::Sugg::NonParen(Cow::from("0")); diff --git a/clippy_lints/src/transmute/transmute_int_to_char.rs b/clippy_lints/src/transmute/transmute_int_to_char.rs index 9e1823c373bf..7d31c375f8cf 100644 --- a/clippy_lints/src/transmute/transmute_int_to_char.rs +++ b/clippy_lints/src/transmute/transmute_int_to_char.rs @@ -23,7 +23,7 @@ pub(super) fn check<'tcx>( cx, TRANSMUTE_INT_TO_CHAR, e.span, - &format!("transmute from a `{}` to a `char`", from_ty), + &format!("transmute from a `{from_ty}` to a `char`"), |diag| { let arg = sugg::Sugg::hir(cx, arg, ".."); let arg = if let ty::Int(_) = from_ty.kind() { @@ -34,7 +34,7 @@ pub(super) fn check<'tcx>( diag.span_suggestion( e.span, "consider using", - format!("std::char::from_u32({}).unwrap()", arg), + format!("std::char::from_u32({arg}).unwrap()"), Applicability::Unspecified, ); }, diff --git a/clippy_lints/src/transmute/transmute_int_to_float.rs b/clippy_lints/src/transmute/transmute_int_to_float.rs index b8703052e6c8..cc3422edbbf1 100644 --- a/clippy_lints/src/transmute/transmute_int_to_float.rs +++ b/clippy_lints/src/transmute/transmute_int_to_float.rs @@ -22,7 +22,7 @@ pub(super) fn check<'tcx>( cx, TRANSMUTE_INT_TO_FLOAT, e.span, - &format!("transmute from a `{}` to a `{}`", from_ty, to_ty), + &format!("transmute from a `{from_ty}` to a `{to_ty}`"), |diag| { let arg = sugg::Sugg::hir(cx, arg, ".."); let arg = if let ty::Int(int_ty) = from_ty.kind() { @@ -36,7 +36,7 @@ pub(super) fn check<'tcx>( diag.span_suggestion( e.span, "consider using", - format!("{}::from_bits({})", to_ty, arg), + format!("{to_ty}::from_bits({arg})"), Applicability::Unspecified, ); }, diff --git a/clippy_lints/src/transmute/transmute_num_to_bytes.rs b/clippy_lints/src/transmute/transmute_num_to_bytes.rs index 52d193d11e1a..009d5a7c8ae1 100644 --- a/clippy_lints/src/transmute/transmute_num_to_bytes.rs +++ b/clippy_lints/src/transmute/transmute_num_to_bytes.rs @@ -31,13 +31,13 @@ pub(super) fn check<'tcx>( cx, TRANSMUTE_NUM_TO_BYTES, e.span, - &format!("transmute from a `{}` to a `{}`", from_ty, to_ty), + &format!("transmute from a `{from_ty}` to a `{to_ty}`"), |diag| { let arg = sugg::Sugg::hir(cx, arg, ".."); diag.span_suggestion( e.span, "consider using `to_ne_bytes()`", - format!("{}.to_ne_bytes()", arg), + format!("{arg}.to_ne_bytes()"), Applicability::Unspecified, ); }, diff --git a/clippy_lints/src/transmute/transmute_ptr_to_ref.rs b/clippy_lints/src/transmute/transmute_ptr_to_ref.rs index 5eb03275b8ec..12d0b866e1c9 100644 --- a/clippy_lints/src/transmute/transmute_ptr_to_ref.rs +++ b/clippy_lints/src/transmute/transmute_ptr_to_ref.rs @@ -25,10 +25,7 @@ pub(super) fn check<'tcx>( cx, TRANSMUTE_PTR_TO_REF, e.span, - &format!( - "transmute from a pointer type (`{}`) to a reference type (`{}`)", - from_ty, to_ty - ), + &format!("transmute from a pointer type (`{from_ty}`) to a reference type (`{to_ty}`)"), |diag| { let arg = sugg::Sugg::hir(cx, arg, ".."); let (deref, cast) = if *mutbl == Mutability::Mut { @@ -41,26 +38,25 @@ pub(super) fn check<'tcx>( let sugg = if let Some(ty) = get_explicit_type(path) { let ty_snip = snippet_with_applicability(cx, ty.span, "..", &mut app); if meets_msrv(msrv, msrvs::POINTER_CAST) { - format!("{}{}.cast::<{}>()", deref, arg.maybe_par(), ty_snip) + format!("{deref}{}.cast::<{ty_snip}>()", arg.maybe_par()) } else if from_ptr_ty.has_erased_regions() { - sugg::make_unop(deref, arg.as_ty(format!("{} () as {} {}", cast, cast, ty_snip))) - .to_string() + sugg::make_unop(deref, arg.as_ty(format!("{cast} () as {cast} {ty_snip}"))).to_string() } else { - sugg::make_unop(deref, arg.as_ty(format!("{} {}", cast, ty_snip))).to_string() + sugg::make_unop(deref, arg.as_ty(format!("{cast} {ty_snip}"))).to_string() } } else if from_ptr_ty.ty == *to_ref_ty { if from_ptr_ty.has_erased_regions() { if meets_msrv(msrv, msrvs::POINTER_CAST) { - format!("{}{}.cast::<{}>()", deref, arg.maybe_par(), to_ref_ty) + format!("{deref}{}.cast::<{to_ref_ty}>()", arg.maybe_par()) } else { - sugg::make_unop(deref, arg.as_ty(format!("{} () as {} {}", cast, cast, to_ref_ty))) + sugg::make_unop(deref, arg.as_ty(format!("{cast} () as {cast} {to_ref_ty}"))) .to_string() } } else { sugg::make_unop(deref, arg).to_string() } } else { - sugg::make_unop(deref, arg.as_ty(format!("{} {}", cast, to_ref_ty))).to_string() + sugg::make_unop(deref, arg.as_ty(format!("{cast} {to_ref_ty}"))).to_string() }; diag.span_suggestion(e.span, "try", sugg, app); diff --git a/clippy_lints/src/transmute/transmute_ref_to_ref.rs b/clippy_lints/src/transmute/transmute_ref_to_ref.rs index 707a11d361c0..afb7f2e13269 100644 --- a/clippy_lints/src/transmute/transmute_ref_to_ref.rs +++ b/clippy_lints/src/transmute/transmute_ref_to_ref.rs @@ -38,7 +38,7 @@ pub(super) fn check<'tcx>( cx, TRANSMUTE_BYTES_TO_STR, e.span, - &format!("transmute from a `{}` to a `{}`", from_ty, to_ty), + &format!("transmute from a `{from_ty}` to a `{to_ty}`"), "consider using", if const_context { format!("std::str::from_utf8_unchecked{postfix}({snippet})") diff --git a/clippy_lints/src/transmute/transmute_undefined_repr.rs b/clippy_lints/src/transmute/transmute_undefined_repr.rs index ae55a6bf5586..1c99a02e6c71 100644 --- a/clippy_lints/src/transmute/transmute_undefined_repr.rs +++ b/clippy_lints/src/transmute/transmute_undefined_repr.rs @@ -75,10 +75,10 @@ pub(super) fn check<'tcx>( cx, TRANSMUTE_UNDEFINED_REPR, e.span, - &format!("transmute from `{}` which has an undefined layout", from_ty_orig), + &format!("transmute from `{from_ty_orig}` which has an undefined layout"), |diag| { if from_ty_orig.peel_refs() != from_ty.peel_refs() { - diag.note(&format!("the contained type `{}` has an undefined layout", from_ty)); + diag.note(&format!("the contained type `{from_ty}` has an undefined layout")); } }, ); @@ -89,10 +89,10 @@ pub(super) fn check<'tcx>( cx, TRANSMUTE_UNDEFINED_REPR, e.span, - &format!("transmute to `{}` which has an undefined layout", to_ty_orig), + &format!("transmute to `{to_ty_orig}` which has an undefined layout"), |diag| { if to_ty_orig.peel_refs() != to_ty.peel_refs() { - diag.note(&format!("the contained type `{}` has an undefined layout", to_ty)); + diag.note(&format!("the contained type `{to_ty}` has an undefined layout")); } }, ); @@ -116,8 +116,7 @@ pub(super) fn check<'tcx>( TRANSMUTE_UNDEFINED_REPR, e.span, &format!( - "transmute from `{}` to `{}`, both of which have an undefined layout", - from_ty_orig, to_ty_orig + "transmute from `{from_ty_orig}` to `{to_ty_orig}`, both of which have an undefined layout" ), |diag| { if let Some(same_adt_did) = same_adt_did { @@ -127,10 +126,10 @@ pub(super) fn check<'tcx>( )); } else { if from_ty_orig.peel_refs() != from_ty { - diag.note(&format!("the contained type `{}` has an undefined layout", from_ty)); + diag.note(&format!("the contained type `{from_ty}` has an undefined layout")); } if to_ty_orig.peel_refs() != to_ty { - diag.note(&format!("the contained type `{}` has an undefined layout", to_ty)); + diag.note(&format!("the contained type `{to_ty}` has an undefined layout")); } } }, @@ -145,10 +144,10 @@ pub(super) fn check<'tcx>( cx, TRANSMUTE_UNDEFINED_REPR, e.span, - &format!("transmute from `{}` which has an undefined layout", from_ty_orig), + &format!("transmute from `{from_ty_orig}` which has an undefined layout"), |diag| { if from_ty_orig.peel_refs() != from_ty { - diag.note(&format!("the contained type `{}` has an undefined layout", from_ty)); + diag.note(&format!("the contained type `{from_ty}` has an undefined layout")); } }, ); @@ -162,10 +161,10 @@ pub(super) fn check<'tcx>( cx, TRANSMUTE_UNDEFINED_REPR, e.span, - &format!("transmute into `{}` which has an undefined layout", to_ty_orig), + &format!("transmute into `{to_ty_orig}` which has an undefined layout"), |diag| { if to_ty_orig.peel_refs() != to_ty { - diag.note(&format!("the contained type `{}` has an undefined layout", to_ty)); + diag.note(&format!("the contained type `{to_ty}` has an undefined layout")); } }, ); diff --git a/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs b/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs index 626d7cd46fc4..6b444922a7cc 100644 --- a/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs +++ b/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs @@ -21,10 +21,7 @@ pub(super) fn check<'tcx>( cx, TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS, e.span, - &format!( - "transmute from `{}` to `{}` which could be expressed as a pointer cast instead", - from_ty, to_ty - ), + &format!("transmute from `{from_ty}` to `{to_ty}` which could be expressed as a pointer cast instead"), |diag| { if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) { let sugg = arg.as_ty(&to_ty.to_string()).to_string(); diff --git a/clippy_lints/src/transmute/transmuting_null.rs b/clippy_lints/src/transmute/transmuting_null.rs index d8e349af7af8..19ce5ae72c24 100644 --- a/clippy_lints/src/transmute/transmuting_null.rs +++ b/clippy_lints/src/transmute/transmuting_null.rs @@ -1,8 +1,6 @@ use clippy_utils::consts::{constant_context, Constant}; use clippy_utils::diagnostics::span_lint; -use clippy_utils::is_path_diagnostic_item; -use if_chain::if_chain; -use rustc_ast::LitKind; +use clippy_utils::{is_integer_literal, is_path_diagnostic_item}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::Ty; @@ -19,37 +17,28 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t // Catching transmute over constants that resolve to `null`. let mut const_eval_context = constant_context(cx, cx.typeck_results()); - if_chain! { - if let ExprKind::Path(ref _qpath) = arg.kind; - if let Some(Constant::RawPtr(x)) = const_eval_context.expr(arg); - if x == 0; - then { - span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG); - return true; - } + if let ExprKind::Path(ref _qpath) = arg.kind && + let Some(Constant::RawPtr(x)) = const_eval_context.expr(arg) && + x == 0 + { + span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG); + return true; } // Catching: // `std::mem::transmute(0 as *const i32)` - if_chain! { - if let ExprKind::Cast(inner_expr, _cast_ty) = arg.kind; - if let ExprKind::Lit(ref lit) = inner_expr.kind; - if let LitKind::Int(0, _) = lit.node; - then { - span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG); - return true; - } + if let ExprKind::Cast(inner_expr, _cast_ty) = arg.kind && is_integer_literal(inner_expr, 0) { + span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG); + return true; } // Catching: // `std::mem::transmute(std::ptr::null::())` - if_chain! { - if let ExprKind::Call(func1, []) = arg.kind; - if is_path_diagnostic_item(cx, func1, sym::ptr_null); - then { - span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG); - return true; - } + if let ExprKind::Call(func1, []) = arg.kind && + is_path_diagnostic_item(cx, func1, sym::ptr_null) + { + span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG); + return true; } // FIXME: diff --git a/clippy_lints/src/transmute/unsound_collection_transmute.rs b/clippy_lints/src/transmute/unsound_collection_transmute.rs index 831b0d450d20..b1445311b711 100644 --- a/clippy_lints/src/transmute/unsound_collection_transmute.rs +++ b/clippy_lints/src/transmute/unsound_collection_transmute.rs @@ -37,10 +37,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty cx, UNSOUND_COLLECTION_TRANSMUTE, e.span, - &format!( - "transmute from `{}` to `{}` with mismatched layout is unsound", - from_ty, to_ty - ), + &format!("transmute from `{from_ty}` to `{to_ty}` with mismatched layout is unsound"), ); true } else { diff --git a/clippy_lints/src/transmute/useless_transmute.rs b/clippy_lints/src/transmute/useless_transmute.rs index 8122cd716e01..f919bbd5afca 100644 --- a/clippy_lints/src/transmute/useless_transmute.rs +++ b/clippy_lints/src/transmute/useless_transmute.rs @@ -21,7 +21,7 @@ pub(super) fn check<'tcx>( cx, USELESS_TRANSMUTE, e.span, - &format!("transmute from a type (`{}`) to itself", from_ty), + &format!("transmute from a type (`{from_ty}`) to itself"), ); true }, diff --git a/clippy_lints/src/transmute/utils.rs b/clippy_lints/src/transmute/utils.rs index fdf847bf4459..b567d92230bb 100644 --- a/clippy_lints/src/transmute/utils.rs +++ b/clippy_lints/src/transmute/utils.rs @@ -1,8 +1,11 @@ use rustc_hir::Expr; +use rustc_hir_analysis::check::{ + cast::{self, CastCheckResult}, + FnCtxt, Inherited, +}; use rustc_lint::LateContext; use rustc_middle::ty::{cast::CastKind, Ty}; use rustc_span::DUMMY_SP; -use rustc_hir_analysis::check::{cast::{self, CastCheckResult}, FnCtxt, Inherited}; // check if the component types of the transmuted collection and the result have different ABI, // size or alignment diff --git a/clippy_lints/src/transmute/wrong_transmute.rs b/clippy_lints/src/transmute/wrong_transmute.rs index 2118f3d69500..d1965565b926 100644 --- a/clippy_lints/src/transmute/wrong_transmute.rs +++ b/clippy_lints/src/transmute/wrong_transmute.rs @@ -13,7 +13,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty cx, WRONG_TRANSMUTE, e.span, - &format!("transmute from a `{}` to a pointer", from_ty), + &format!("transmute from a `{from_ty}` to a pointer"), ); true }, diff --git a/clippy_lints/src/types/borrowed_box.rs b/clippy_lints/src/types/borrowed_box.rs index 1268c23206a6..9c6629958401 100644 --- a/clippy_lints/src/types/borrowed_box.rs +++ b/clippy_lints/src/types/borrowed_box.rs @@ -49,15 +49,15 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, m let inner_snippet = snippet(cx, inner.span, ".."); let suggestion = match &inner.kind { TyKind::TraitObject(bounds, lt_bound, _) if bounds.len() > 1 || !lt_bound.is_elided() => { - format!("&{}({})", ltopt, &inner_snippet) + format!("&{ltopt}({})", &inner_snippet) }, TyKind::Path(qpath) if get_bounds_if_impl_trait(cx, qpath, inner.hir_id) .map_or(false, |bounds| bounds.len() > 1) => { - format!("&{}({})", ltopt, &inner_snippet) + format!("&{ltopt}({})", &inner_snippet) }, - _ => format!("&{}{}", ltopt, &inner_snippet), + _ => format!("&{ltopt}{}", &inner_snippet), }; span_lint_and_sugg( cx, diff --git a/clippy_lints/src/types/box_collection.rs b/clippy_lints/src/types/box_collection.rs index ba51404d2148..08020ce66381 100644 --- a/clippy_lints/src/types/box_collection.rs +++ b/clippy_lints/src/types/box_collection.rs @@ -16,7 +16,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ _ => "<..>", }; - let box_content = format!("{outer}{generic}", outer = item_type); + let box_content = format!("{item_type}{generic}"); span_lint_and_help( cx, BOX_COLLECTION, diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index aca55817c525..79c31efb9fcd 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -352,8 +352,10 @@ impl<'tcx> LateLintPass<'tcx> for Types { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) { match item.kind { ImplItemKind::Const(ty, _) => { - let is_in_trait_impl = if let Some(hir::Node::Item(item)) = - cx.tcx.hir().find_by_def_id(cx.tcx.hir().get_parent_item(item.hir_id()).def_id) + let is_in_trait_impl = if let Some(hir::Node::Item(item)) = cx + .tcx + .hir() + .find_by_def_id(cx.tcx.hir().get_parent_item(item.hir_id()).def_id) { matches!(item.kind, ItemKind::Impl(hir::Impl { of_trait: Some(_), .. })) } else { @@ -535,7 +537,7 @@ impl Types { QPath::LangItem(..) => {}, } }, - TyKind::Rptr(ref lt, ref mut_ty) => { + TyKind::Rptr(lt, ref mut_ty) => { context.is_nested_call = true; if !borrowed_box::check(cx, hir_ty, lt, mut_ty) { self.check_ty(cx, mut_ty.ty, context); diff --git a/clippy_lints/src/types/rc_buffer.rs b/clippy_lints/src/types/rc_buffer.rs index 4d72a29e8c74..6b9de64e24c9 100644 --- a/clippy_lints/src/types/rc_buffer.rs +++ b/clippy_lints/src/types/rc_buffer.rs @@ -17,7 +17,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ hir_ty.span, "usage of `Rc` when T is a buffer type", "try", - format!("Rc<{}>", alternate), + format!("Rc<{alternate}>"), Applicability::MachineApplicable, ); } else { @@ -57,7 +57,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ hir_ty.span, "usage of `Arc` when T is a buffer type", "try", - format!("Arc<{}>", alternate), + format!("Arc<{alternate}>"), Applicability::MachineApplicable, ); } else if let Some(ty) = qpath_generic_tys(qpath).next() { diff --git a/clippy_lints/src/types/redundant_allocation.rs b/clippy_lints/src/types/redundant_allocation.rs index d81c5c83845d..ecb672005390 100644 --- a/clippy_lints/src/types/redundant_allocation.rs +++ b/clippy_lints/src/types/redundant_allocation.rs @@ -3,9 +3,9 @@ use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::{path_def_id, qpath_generic_tys}; use rustc_errors::Applicability; use rustc_hir::{self as hir, def_id::DefId, QPath, TyKind}; +use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::LateContext; use rustc_span::symbol::sym; -use rustc_hir_analysis::hir_ty_to_ty; use super::{utils, REDUNDANT_ALLOCATION}; @@ -27,13 +27,11 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ cx, REDUNDANT_ALLOCATION, hir_ty.span, - &format!("usage of `{}<{}>`", outer_sym, generic_snippet), + &format!("usage of `{outer_sym}<{generic_snippet}>`"), |diag| { - diag.span_suggestion(hir_ty.span, "try", format!("{}", generic_snippet), applicability); + diag.span_suggestion(hir_ty.span, "try", format!("{generic_snippet}"), applicability); diag.note(&format!( - "`{generic}` is already a pointer, `{outer}<{generic}>` allocates a pointer on the heap", - outer = outer_sym, - generic = generic_snippet + "`{generic_snippet}` is already a pointer, `{outer_sym}<{generic_snippet}>` allocates a pointer on the heap" )); }, ); @@ -72,19 +70,16 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ cx, REDUNDANT_ALLOCATION, hir_ty.span, - &format!("usage of `{}<{}<{}>>`", outer_sym, inner_sym, generic_snippet), + &format!("usage of `{outer_sym}<{inner_sym}<{generic_snippet}>>`"), |diag| { diag.span_suggestion( hir_ty.span, "try", - format!("{}<{}>", outer_sym, generic_snippet), + format!("{outer_sym}<{generic_snippet}>"), applicability, ); diag.note(&format!( - "`{inner}<{generic}>` is already on the heap, `{outer}<{inner}<{generic}>>` makes an extra allocation", - outer = outer_sym, - inner = inner_sym, - generic = generic_snippet + "`{inner_sym}<{generic_snippet}>` is already on the heap, `{outer_sym}<{inner_sym}<{generic_snippet}>>` makes an extra allocation" )); }, ); @@ -94,19 +89,13 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ cx, REDUNDANT_ALLOCATION, hir_ty.span, - &format!("usage of `{}<{}<{}>>`", outer_sym, inner_sym, generic_snippet), + &format!("usage of `{outer_sym}<{inner_sym}<{generic_snippet}>>`"), |diag| { diag.note(&format!( - "`{inner}<{generic}>` is already on the heap, `{outer}<{inner}<{generic}>>` makes an extra allocation", - outer = outer_sym, - inner = inner_sym, - generic = generic_snippet + "`{inner_sym}<{generic_snippet}>` is already on the heap, `{outer_sym}<{inner_sym}<{generic_snippet}>>` makes an extra allocation" )); diag.help(&format!( - "consider using just `{outer}<{generic}>` or `{inner}<{generic}>`", - outer = outer_sym, - inner = inner_sym, - generic = generic_snippet + "consider using just `{outer_sym}<{generic_snippet}>` or `{inner_sym}<{generic_snippet}>`" )); }, ); diff --git a/clippy_lints/src/types/vec_box.rs b/clippy_lints/src/types/vec_box.rs index 236f9955722d..6c329d8cdf19 100644 --- a/clippy_lints/src/types/vec_box.rs +++ b/clippy_lints/src/types/vec_box.rs @@ -4,11 +4,11 @@ use clippy_utils::source::snippet; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{self as hir, def_id::DefId, GenericArg, QPath, TyKind}; +use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::LateContext; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::TypeVisitable; use rustc_span::symbol::sym; -use rustc_hir_analysis::hir_ty_to_ty; use super::VEC_BOX; diff --git a/clippy_lints/src/uninit_vec.rs b/clippy_lints/src/uninit_vec.rs index 3f99bd3f3156..1ab0162a8813 100644 --- a/clippy_lints/src/uninit_vec.rs +++ b/clippy_lints/src/uninit_vec.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::higher::{get_vec_init_kind, VecInitKind}; use clippy_utils::ty::{is_type_diagnostic_item, is_uninit_value_valid_for_ty}; -use clippy_utils::{is_lint_allowed, path_to_local_id, peel_hir_expr_while, SpanlessEq}; +use clippy_utils::{is_integer_literal, is_lint_allowed, path_to_local_id, peel_hir_expr_while, SpanlessEq}; use rustc_hir::{Block, Expr, ExprKind, HirId, PatKind, PathSegment, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; @@ -211,9 +211,12 @@ fn extract_set_len_self<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Opt } }); match expr.kind { - ExprKind::MethodCall(path, self_expr, [_], _) => { + ExprKind::MethodCall(path, self_expr, [arg], _) => { let self_type = cx.typeck_results().expr_ty(self_expr).peel_refs(); - if is_type_diagnostic_item(cx, self_type, sym::Vec) && path.ident.name.as_str() == "set_len" { + if is_type_diagnostic_item(cx, self_type, sym::Vec) + && path.ident.name.as_str() == "set_len" + && !is_integer_literal(arg, 0) + { Some((self_expr, expr.span)) } else { None diff --git a/clippy_lints/src/unit_return_expecting_ord.rs b/clippy_lints/src/unit_return_expecting_ord.rs index c0a4f3fbacd6..57aff5367dd1 100644 --- a/clippy_lints/src/unit_return_expecting_ord.rs +++ b/clippy_lints/src/unit_return_expecting_ord.rs @@ -157,8 +157,7 @@ impl<'tcx> LateLintPass<'tcx> for UnitReturnExpectingOrd { span, &format!( "this closure returns \ - the unit type which also implements {}", - trait_name + the unit type which also implements {trait_name}" ), ); }, @@ -169,8 +168,7 @@ impl<'tcx> LateLintPass<'tcx> for UnitReturnExpectingOrd { span, &format!( "this closure returns \ - the unit type which also implements {}", - trait_name + the unit type which also implements {trait_name}" ), Some(last_semi), "probably caused by this trailing semicolon", diff --git a/clippy_lints/src/unit_types/unit_arg.rs b/clippy_lints/src/unit_types/unit_arg.rs index a6f777abc6e9..f6d3fb00f4ee 100644 --- a/clippy_lints/src/unit_types/unit_arg.rs +++ b/clippy_lints/src/unit_types/unit_arg.rs @@ -74,7 +74,7 @@ fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Exp cx, UNIT_ARG, expr.span, - &format!("passing {}unit value{} to a function", singular, plural), + &format!("passing {singular}unit value{plural} to a function"), |db| { let mut or = ""; args_to_recover @@ -129,7 +129,7 @@ fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Exp if arg_snippets_without_empty_blocks.is_empty() { db.multipart_suggestion( - &format!("use {}unit literal{} instead", singular, plural), + &format!("use {singular}unit literal{plural} instead"), args_to_recover .iter() .map(|arg| (arg.span, "()".to_string())) @@ -143,8 +143,7 @@ fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Exp db.span_suggestion( expr.span, &format!( - "{}move the expression{} in front of the call and replace {} with the unit literal `()`", - or, empty_or_s, it_or_them + "{or}move the expression{empty_or_s} in front of the call and replace {it_or_them} with the unit literal `()`" ), sugg, applicability, diff --git a/clippy_lints/src/unit_types/unit_cmp.rs b/clippy_lints/src/unit_types/unit_cmp.rs index 1dd8895ebd07..226495dcbda3 100644 --- a/clippy_lints/src/unit_types/unit_cmp.rs +++ b/clippy_lints/src/unit_types/unit_cmp.rs @@ -22,7 +22,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { cx, UNIT_CMP, macro_call.span, - &format!("`{}` of unit values detected. This will always {}", macro_name, result), + &format!("`{macro_name}` of unit values detected. This will always {result}"), ); } return; @@ -40,9 +40,8 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { UNIT_CMP, expr.span, &format!( - "{}-comparison of unit values detected. This will always be {}", - op.as_str(), - result + "{}-comparison of unit values detected. This will always be {result}", + op.as_str() ), ); } diff --git a/clippy_lints/src/unnecessary_owned_empty_strings.rs b/clippy_lints/src/unnecessary_owned_empty_strings.rs index 8a4f4c0ad971..016aacbf9da3 100644 --- a/clippy_lints/src/unnecessary_owned_empty_strings.rs +++ b/clippy_lints/src/unnecessary_owned_empty_strings.rs @@ -3,7 +3,7 @@ use clippy_utils::{match_def_path, paths}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; -use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability}; +use rustc_hir::{BorrowKind, Expr, ExprKind, LangItem, Mutability}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -55,7 +55,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryOwnedEmptyStrings { ); } else { if_chain! { - if match_def_path(cx, fun_def_id, &paths::FROM_FROM); + if cx.tcx.lang_items().require(LangItem::FromFrom).ok() == Some(fun_def_id); if let [.., last_arg] = args; if let ExprKind::Lit(spanned) = &last_arg.kind; if let LitKind::Str(symbol, _) = spanned.node; diff --git a/clippy_lints/src/unnecessary_self_imports.rs b/clippy_lints/src/unnecessary_self_imports.rs index 839a4bdab09d..bc0dd263d88a 100644 --- a/clippy_lints/src/unnecessary_self_imports.rs +++ b/clippy_lints/src/unnecessary_self_imports.rs @@ -57,7 +57,7 @@ impl EarlyLintPass for UnnecessarySelfImports { format!( "{}{};", last_segment.ident, - if let UseTreeKind::Simple(Some(alias), ..) = self_tree.kind { format!(" as {}", alias) } else { String::new() }, + if let UseTreeKind::Simple(Some(alias), ..) = self_tree.kind { format!(" as {alias}") } else { String::new() }, ), Applicability::MaybeIncorrect, ); diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs index 2c40827db0e7..7211e6864f3a 100644 --- a/clippy_lints/src/unnecessary_wraps.rs +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; -use clippy_utils::{contains_return, is_lang_ctor, return_ty, visitors::find_all_ret_expressions}; +use clippy_utils::{contains_return, is_res_lang_ctor, path_res, return_ty, visitors::find_all_ret_expressions}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; @@ -120,9 +120,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { if !ret_expr.span.from_expansion(); // Check if a function call. if let ExprKind::Call(func, [arg]) = ret_expr.kind; - // Check if OPTION_SOME or RESULT_OK, depending on return type. - if let ExprKind::Path(qpath) = &func.kind; - if is_lang_ctor(cx, qpath, lang_item); + if is_res_lang_ctor(cx, path_res(cx, func), lang_item); // Make sure the function argument does not contain a return expression. if !contains_return(arg); then { @@ -153,11 +151,8 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { ) } else { ( - format!( - "this function's return value is unnecessarily wrapped by `{}`", - return_type_label - ), - format!("remove `{}` from the return type...", return_type_label), + format!("this function's return value is unnecessarily wrapped by `{return_type_label}`"), + format!("remove `{return_type_label}` from the return type..."), inner_type.to_string(), "...and then change returning expressions", ) diff --git a/clippy_lints/src/unsafe_removed_from_name.rs b/clippy_lints/src/unsafe_removed_from_name.rs index 64f7a055cd9b..32cd46812014 100644 --- a/clippy_lints/src/unsafe_removed_from_name.rs +++ b/clippy_lints/src/unsafe_removed_from_name.rs @@ -65,10 +65,7 @@ fn unsafe_to_safe_check(old_name: Ident, new_name: Ident, cx: &EarlyContext<'_>, cx, UNSAFE_REMOVED_FROM_NAME, span, - &format!( - "removed `unsafe` from the name of `{}` in use as `{}`", - old_str, new_str - ), + &format!("removed `unsafe` from the name of `{old_str}` in use as `{new_str}`"), ); } } diff --git a/clippy_lints/src/unused_io_amount.rs b/clippy_lints/src/unused_io_amount.rs index b38d71784fcf..8bcdff66331d 100644 --- a/clippy_lints/src/unused_io_amount.rs +++ b/clippy_lints/src/unused_io_amount.rs @@ -1,8 +1,9 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; -use clippy_utils::{is_try, match_trait_method, paths}; +use clippy_utils::{is_trait_method, is_try, match_trait_method, paths}; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -116,13 +117,13 @@ fn check_method_call(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Exp match_trait_method(cx, call, &paths::FUTURES_IO_ASYNCREADEXT) || match_trait_method(cx, call, &paths::TOKIO_IO_ASYNCREADEXT) } else { - match_trait_method(cx, call, &paths::IO_READ) + is_trait_method(cx, call, sym::IoRead) }; let write_trait = if is_await { match_trait_method(cx, call, &paths::FUTURES_IO_ASYNCWRITEEXT) || match_trait_method(cx, call, &paths::TOKIO_IO_ASYNCWRITEEXT) } else { - match_trait_method(cx, call, &paths::IO_WRITE) + is_trait_method(cx, call, sym::IoWrite) }; match (read_trait, write_trait, symbol, is_await) { diff --git a/clippy_lints/src/unused_rounding.rs b/clippy_lints/src/unused_rounding.rs index b8a5d4ea8c9f..3164937293b6 100644 --- a/clippy_lints/src/unused_rounding.rs +++ b/clippy_lints/src/unused_rounding.rs @@ -58,8 +58,8 @@ impl EarlyLintPass for UnusedRounding { cx, UNUSED_ROUNDING, expr.span, - &format!("used the `{}` method with a whole number float", method_name), - &format!("remove the `{}` method call", method_name), + &format!("used the `{method_name}` method with a whole number float"), + &format!("remove the `{method_name}` method call"), float, Applicability::MachineApplicable, ); diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index 3ef265580797..ea878043c04e 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -257,9 +257,8 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> { expr.hir_id, expr.span, &format!( - "called `{}` on `{}` after checking its variant with `{}`", + "called `{}` on `{unwrappable_variable_name}` after checking its variant with `{}`", method_name.ident.name, - unwrappable_variable_name, unwrappable.check_name.ident.as_str(), ), |diag| { @@ -268,9 +267,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> { unwrappable.check.span.with_lo(unwrappable.if_expr.span.lo()), "try", format!( - "if let {} = {}", - suggested_pattern, - unwrappable_variable_name, + "if let {suggested_pattern} = {unwrappable_variable_name}", ), // We don't track how the unwrapped value is used inside the // block or suggest deleting the unwrap, so we can't offer a diff --git a/clippy_lints/src/unwrap_in_result.rs b/clippy_lints/src/unwrap_in_result.rs index baa53ba664f6..a69719b127b2 100644 --- a/clippy_lints/src/unwrap_in_result.rs +++ b/clippy_lints/src/unwrap_in_result.rs @@ -1,12 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::visitors::for_each_expr; use clippy_utils::{method_chain_args, return_ty}; +use core::ops::ControlFlow; use if_chain::if_chain; use rustc_hir as hir; -use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{Expr, ImplItemKind}; +use rustc_hir::ImplItemKind; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{sym, Span}; @@ -73,51 +73,37 @@ impl<'tcx> LateLintPass<'tcx> for UnwrapInResult { } } -struct FindExpectUnwrap<'a, 'tcx> { - lcx: &'a LateContext<'tcx>, - typeck_results: &'tcx ty::TypeckResults<'tcx>, - result: Vec, -} - -impl<'a, 'tcx> Visitor<'tcx> for FindExpectUnwrap<'a, 'tcx> { - fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - // check for `expect` - if let Some(arglists) = method_chain_args(expr, &["expect"]) { - 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) - { - self.result.push(expr.span); - } - } - - // check for `unwrap` - if let Some(arglists) = method_chain_args(expr, &["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) - { - self.result.push(expr.span); - } - } - - // and check sub-expressions - intravisit::walk_expr(self, expr); - } -} - fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_item: &'tcx hir::ImplItem<'_>) { if let ImplItemKind::Fn(_, body_id) = impl_item.kind { let body = cx.tcx.hir().body(body_id); - let mut fpu = FindExpectUnwrap { - lcx: cx, - typeck_results: cx.tcx.typeck(impl_item.def_id.def_id), - result: Vec::new(), - }; - fpu.visit_expr(body.value); + let typeck = cx.tcx.typeck(impl_item.def_id.def_id); + let mut result = Vec::new(); + let _: Option = for_each_expr(body.value, |e| { + // check for `expect` + if let Some(arglists) = method_chain_args(e, &["expect"]) { + let receiver_ty = typeck.expr_ty(arglists[0].0).peel_refs(); + if is_type_diagnostic_item(cx, receiver_ty, sym::Option) + || is_type_diagnostic_item(cx, receiver_ty, sym::Result) + { + result.push(e.span); + } + } + + // check for `unwrap` + if let Some(arglists) = method_chain_args(e, &["unwrap"]) { + let receiver_ty = typeck.expr_ty(arglists[0].0).peel_refs(); + if is_type_diagnostic_item(cx, receiver_ty, sym::Option) + || is_type_diagnostic_item(cx, receiver_ty, sym::Result) + { + result.push(e.span); + } + } + + ControlFlow::Continue(()) + }); // if we've found one, lint - if !fpu.result.is_empty() { + if !result.is_empty() { span_lint_and_then( cx, UNWRAP_IN_RESULT, @@ -125,7 +111,7 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_item: &'tc "used unwrap or expect in a function that returns result or option", move |diag| { diag.help("unwrap and expect should not be used in a function that returns result or option"); - diag.span_note(fpu.result, "potential non-recoverable error(s)"); + diag.span_note(result, "potential non-recoverable error(s)"); }, ); } diff --git a/clippy_lints/src/upper_case_acronyms.rs b/clippy_lints/src/upper_case_acronyms.rs index 2c71f35d490c..654ea306793b 100644 --- a/clippy_lints/src/upper_case_acronyms.rs +++ b/clippy_lints/src/upper_case_acronyms.rs @@ -93,7 +93,7 @@ fn check_ident(cx: &LateContext<'_>, ident: &Ident, be_aggressive: bool) { cx, UPPER_CASE_ACRONYMS, span, - &format!("name `{}` contains a capitalized acronym", ident), + &format!("name `{ident}` contains a capitalized acronym"), "consider making the acronym lowercase, except the initial letter", corrected, Applicability::MaybeIncorrect, @@ -114,6 +114,7 @@ impl LateLintPass<'_> for UpperCaseAcronyms { check_ident(cx, &it.ident, self.upper_case_acronyms_aggressive); }, ItemKind::Enum(ref enumdef, _) => { + check_ident(cx, &it.ident, self.upper_case_acronyms_aggressive); // check enum variants separately because again we only want to lint on private enums and // the fn check_variant does not know about the vis of the enum of its variants enumdef diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index 2c4f5075e980..65f1b5462081 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -12,11 +12,11 @@ use rustc_hir::{ Expr, ExprKind, FnRetTy, FnSig, GenericArg, HirId, Impl, ImplItemKind, Item, ItemKind, Pat, PatKind, Path, QPath, TyKind, }; +use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass}; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Span; -use rustc_hir_analysis::hir_ty_to_ty; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index f1b6463ad0f7..a82643a59f97 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -5,7 +5,7 @@ use clippy_utils::ty::{is_type_diagnostic_item, same_type_and_consts}; use clippy_utils::{get_parent_expr, is_trait_method, match_def_path, paths}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, HirId, MatchSource}; +use rustc_hir::{Expr, ExprKind, HirId, LangItem, MatchSource}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -74,7 +74,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - &format!("useless conversion to the same type: `{}`", b), + &format!("useless conversion to the same type: `{b}`"), "consider removing `.into()`", sugg, Applicability::MachineApplicable, // snippet @@ -97,7 +97,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - &format!("useless conversion to the same type: `{}`", b), + &format!("useless conversion to the same type: `{b}`"), "consider removing `.into_iter()`", sugg, Applicability::MachineApplicable, // snippet @@ -118,7 +118,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - &format!("useless conversion to the same type: `{}`", b), + &format!("useless conversion to the same type: `{b}`"), None, "consider removing `.try_into()`", ); @@ -146,7 +146,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - &format!("useless conversion to the same type: `{}`", b), + &format!("useless conversion to the same type: `{b}`"), None, &hint, ); @@ -154,7 +154,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { } if_chain! { - if match_def_path(cx, def_id, &paths::FROM_FROM); + if cx.tcx.lang_items().require(LangItem::FromFrom).ok() == Some(def_id); if same_type_and_consts(a, b); then { @@ -165,7 +165,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - &format!("useless conversion to the same type: `{}`", b), + &format!("useless conversion to the same type: `{b}`"), &sugg_msg, sugg.to_string(), Applicability::MachineApplicable, // snippet diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index 1df3135c962d..e069de8cb5c7 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -739,7 +739,7 @@ fn path_to_string(path: &QPath<'_>) -> String { *s += ", "; write!(s, "{:?}", segment.ident.as_str()).unwrap(); }, - other => write!(s, "/* unimplemented: {:?}*/", other).unwrap(), + other => write!(s, "/* unimplemented: {other:?}*/").unwrap(), }, QPath::LangItem(..) => panic!("path_to_string: called for lang item qpath"), } diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 2be3fa99c811..668123e4d6e3 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -39,28 +39,28 @@ pub struct Rename { pub rename: String, } -/// A single disallowed method, used by the `DISALLOWED_METHODS` lint. #[derive(Clone, Debug, Deserialize)] #[serde(untagged)] -pub enum DisallowedMethod { +pub enum DisallowedPath { Simple(String), WithReason { path: String, reason: Option }, } -impl DisallowedMethod { +impl DisallowedPath { pub fn path(&self) -> &str { let (Self::Simple(path) | Self::WithReason { path, .. }) = self; path } -} -/// A single disallowed type, used by the `DISALLOWED_TYPES` lint. -#[derive(Clone, Debug, Deserialize)] -#[serde(untagged)] -pub enum DisallowedType { - Simple(String), - WithReason { path: String, reason: Option }, + pub fn reason(&self) -> Option<&str> { + match self { + Self::WithReason { + reason: Some(reason), .. + } => Some(reason), + _ => None, + } + } } /// Conf with parse errors @@ -213,7 +213,7 @@ define_Conf! { /// /// Suppress lints whenever the suggested change would cause breakage for other crates. (avoid_breaking_exported_api: bool = true), - /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED. + /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED, UNINLINED_FORMAT_ARGS, MANUAL_CLAMP. /// /// The minimum rust version that the project supports (msrv: Option = None), @@ -315,14 +315,18 @@ define_Conf! { /// /// Whether to allow certain wildcard imports (prelude, super in tests). (warn_on_all_wildcard_imports: bool = false), + /// Lint: DISALLOWED_MACROS. + /// + /// The list of disallowed macros, written as fully qualified paths. + (disallowed_macros: Vec = Vec::new()), /// Lint: DISALLOWED_METHODS. /// /// The list of disallowed methods, written as fully qualified paths. - (disallowed_methods: Vec = Vec::new()), + (disallowed_methods: Vec = Vec::new()), /// Lint: DISALLOWED_TYPES. /// /// The list of disallowed types, written as fully qualified paths. - (disallowed_types: Vec = Vec::new()), + (disallowed_types: Vec = Vec::new()), /// Lint: UNREADABLE_LITERAL. /// /// Should the fraction of a decimal be linted to include separators. @@ -362,7 +366,7 @@ define_Conf! { /// For example, `[_, _, _, e, ..]` is a slice pattern with 4 elements. (max_suggested_slice_pattern_length: u64 = 3), /// Lint: AWAIT_HOLDING_INVALID_TYPE - (await_holding_invalid_types: Vec = Vec::new()), + (await_holding_invalid_types: Vec = Vec::new()), /// Lint: LARGE_INCLUDE_FILE. /// /// The maximum size of a file included via `include_bytes!()` or `include_str!()`, in bytes @@ -482,16 +486,13 @@ pub fn format_error(error: Box) -> String { let field = fields.get(index).copied().unwrap_or_default(); write!( msg, - "{:separator_width$}{:field_width$}", - " ", - field, - separator_width = SEPARATOR_WIDTH, - field_width = column_width + "{:SEPARATOR_WIDTH$}{field:column_width$}", + " " ) .unwrap(); } } - write!(msg, "\n{}", suffix).unwrap(); + write!(msg, "\n{suffix}").unwrap(); msg } else { s diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 78c036186f50..85bcbc7ad236 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -2,11 +2,11 @@ use crate::utils::internal_lints::metadata_collector::is_deprecated_lint; use clippy_utils::consts::{constant_simple, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::macros::root_macro_call_first_node; -use clippy_utils::source::snippet; +use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::ty::match_type; use clippy_utils::{ - def_path_res, higher, is_else_clause, is_expn_of, is_expr_path_def_path, is_lint_allowed, match_def_path, - method_calls, paths, peel_blocks_with_stmt, SpanlessEq, + def_path_res, higher, is_else_clause, is_expn_of, is_expr_path_def_path, is_lint_allowed, match_any_def_paths, + match_def_path, method_calls, paths, peel_blocks_with_stmt, peel_hir_expr_refs, SpanlessEq, }; use if_chain::if_chain; use rustc_ast as ast; @@ -15,26 +15,29 @@ use rustc_ast::visit::FnKind; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def::{DefKind, Namespace, Res}; use rustc_hir::def_id::DefId; use rustc_hir::hir_id::CRATE_HIR_ID; use rustc_hir::intravisit::Visitor; use rustc_hir::{ - BinOpKind, Block, Closure, Expr, ExprKind, HirId, Item, Local, MutTy, Mutability, Node, Path, Stmt, StmtKind, Ty, + BinOpKind, Block, Closure, Expr, ExprKind, HirId, Item, Local, MutTy, Mutability, Node, Path, Stmt, StmtKind, TyKind, UnOp, }; +use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; use rustc_middle::hir::nested_filter; -use rustc_middle::mir::interpret::ConstValue; -use rustc_middle::ty::{self, fast_reject::SimplifiedTypeGen, subst::GenericArgKind, FloatTy}; +use rustc_middle::mir::interpret::{Allocation, ConstValue, GlobalAlloc}; +use rustc_middle::ty::{ + self, fast_reject::SimplifiedTypeGen, subst::GenericArgKind, AssocKind, DefIdTree, FloatTy, Ty, +}; use rustc_semver::RustcVersion; use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Spanned; -use rustc_span::symbol::Symbol; +use rustc_span::symbol::{Ident, Symbol}; use rustc_span::{sym, BytePos, Span}; -use rustc_hir_analysis::hir_ty_to_ty; use std::borrow::{Borrow, Cow}; +use std::str; #[cfg(feature = "internal")] pub mod metadata_collector; @@ -226,11 +229,11 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for calls to `utils::match_type()` on a type diagnostic item - /// and suggests to use `utils::is_type_diagnostic_item()` instead. + /// Checks for usages of def paths when a diagnostic item or a `LangItem` could be used. /// /// ### Why is this bad? - /// `utils::is_type_diagnostic_item()` does not require hardcoded paths. + /// The path for an item is subject to change and is less efficient to look up than a + /// diagnostic item or a `LangItem`. /// /// ### Example /// ```rust,ignore @@ -241,9 +244,9 @@ declare_clippy_lint! { /// ```rust,ignore /// utils::is_type_diagnostic_item(cx, ty, sym::Vec) /// ``` - pub MATCH_TYPE_ON_DIAGNOSTIC_ITEM, + pub UNNECESSARY_DEF_PATH, internal, - "using `utils::match_type()` instead of `utils::is_type_diagnostic_item()`" + "using a def path when a diagnostic item or a `LangItem` is available" } declare_clippy_lint! { @@ -530,14 +533,14 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass { cx, LINT_WITHOUT_LINT_PASS, lint_span, - &format!("the lint `{}` is not added to any `LintPass`", lint_name), + &format!("the lint `{lint_name}` is not added to any `LintPass`"), ); } } } } -fn is_lint_ref_type<'tcx>(cx: &LateContext<'tcx>, ty: &Ty<'_>) -> bool { +fn is_lint_ref_type<'tcx>(cx: &LateContext<'tcx>, ty: &hir::Ty<'_>) -> bool { if let TyKind::Rptr( _, MutTy { @@ -666,7 +669,7 @@ impl<'tcx> LateLintPass<'tcx> for CompilerLintFunctions { path.ident.span, "usage of a compiler lint function", None, - &format!("please use the Clippy variant of this function: `{}`", sugg), + &format!("please use the Clippy variant of this function: `{sugg}`"), ); } } @@ -854,13 +857,8 @@ fn suggest_help( "this call is collapsible", "collapse into", format!( - "span_lint_and_help({}, {}, {}, {}, {}, {})", - and_then_snippets.cx, - and_then_snippets.lint, - and_then_snippets.span, - and_then_snippets.msg, - &option_span, - help + "span_lint_and_help({}, {}, {}, {}, {}, {help})", + and_then_snippets.cx, and_then_snippets.lint, and_then_snippets.span, and_then_snippets.msg, &option_span, ), Applicability::MachineApplicable, ); @@ -886,107 +884,238 @@ fn suggest_note( "this call is collapsible", "collapse into", format!( - "span_lint_and_note({}, {}, {}, {}, {}, {})", - and_then_snippets.cx, - and_then_snippets.lint, - and_then_snippets.span, - and_then_snippets.msg, - note_span, - note + "span_lint_and_note({}, {}, {}, {}, {note_span}, {note})", + and_then_snippets.cx, and_then_snippets.lint, and_then_snippets.span, and_then_snippets.msg, ), Applicability::MachineApplicable, ); } -declare_lint_pass!(MatchTypeOnDiagItem => [MATCH_TYPE_ON_DIAGNOSTIC_ITEM]); +declare_lint_pass!(UnnecessaryDefPath => [UNNECESSARY_DEF_PATH]); -impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem { +#[allow(clippy::too_many_lines)] +impl<'tcx> LateLintPass<'tcx> for UnnecessaryDefPath { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - if is_lint_allowed(cx, MATCH_TYPE_ON_DIAGNOSTIC_ITEM, expr.hir_id) { + enum Item { + LangItem(Symbol), + DiagnosticItem(Symbol), + } + static PATHS: &[&[&str]] = &[ + &["clippy_utils", "match_def_path"], + &["clippy_utils", "match_trait_method"], + &["clippy_utils", "ty", "match_type"], + &["clippy_utils", "is_expr_path_def_path"], + ]; + + if is_lint_allowed(cx, UNNECESSARY_DEF_PATH, expr.hir_id) { return; } if_chain! { - // Check if this is a call to utils::match_type() - if let ExprKind::Call(fn_path, [context, ty, ty_path]) = expr.kind; - if is_expr_path_def_path(cx, fn_path, &["clippy_utils", "ty", "match_type"]); + if let ExprKind::Call(func, [cx_arg, def_arg, args@..]) = expr.kind; + if let ExprKind::Path(path) = &func.kind; + if let Some(id) = cx.qpath_res(path, func.hir_id).opt_def_id(); + if let Some(which_path) = match_any_def_paths(cx, id, PATHS); + let item_arg = if which_path == 4 { &args[1] } else { &args[0] }; // Extract the path to the matched type - if let Some(segments) = path_to_matched_type(cx, ty_path); - let segments: Vec<&str> = segments.iter().map(Symbol::as_str).collect(); - if let Some(ty_did) = def_path_res(cx, &segments[..]).opt_def_id(); - // Check if the matched type is a diagnostic item - if let Some(item_name) = cx.tcx.get_diagnostic_name(ty_did); + if let Some(segments) = path_to_matched_type(cx, item_arg); + let segments: Vec<&str> = segments.iter().map(|sym| &**sym).collect(); + if let Some(def_id) = def_path_res(cx, &segments[..], None).opt_def_id(); then { - // TODO: check paths constants from external crates. - let cx_snippet = snippet(cx, context.span, "_"); - let ty_snippet = snippet(cx, ty.span, "_"); + // def_path_res will match field names before anything else, but for this we want to match + // inherent functions first. + let def_id = if cx.tcx.def_kind(def_id) == DefKind::Field { + let method_name = *segments.last().unwrap(); + cx.tcx.def_key(def_id).parent + .and_then(|parent_idx| + cx.tcx.inherent_impls(DefId { index: parent_idx, krate: def_id.krate }).iter() + .find_map(|impl_id| cx.tcx.associated_items(*impl_id) + .find_by_name_and_kind( + cx.tcx, + Ident::from_str(method_name), + AssocKind::Fn, + *impl_id, + ) + ) + ) + .map_or(def_id, |item| item.def_id) + } else { + def_id + }; - span_lint_and_sugg( + // Check if the target item is a diagnostic item or LangItem. + let (msg, item) = if let Some(item_name) + = cx.tcx.diagnostic_items(def_id.krate).id_to_name.get(&def_id) + { + ( + "use of a def path to a diagnostic item", + Item::DiagnosticItem(*item_name), + ) + } else if let Some(lang_item) = cx.tcx.lang_items().items().iter().position(|id| *id == Some(def_id)) { + let lang_items = def_path_res(cx, &["rustc_hir", "lang_items", "LangItem"], Some(Namespace::TypeNS)).def_id(); + let item_name = cx.tcx.adt_def(lang_items).variants().iter().nth(lang_item).unwrap().name; + ( + "use of a def path to a `LangItem`", + Item::LangItem(item_name), + ) + } else { + return; + }; + + let has_ctor = match cx.tcx.def_kind(def_id) { + DefKind::Struct => { + let variant = cx.tcx.adt_def(def_id).non_enum_variant(); + variant.ctor_def_id.is_some() && variant.fields.iter().all(|f| f.vis.is_public()) + } + DefKind::Variant => { + let variant = cx.tcx.adt_def(cx.tcx.parent(def_id)).variant_with_id(def_id); + variant.ctor_def_id.is_some() && variant.fields.iter().all(|f| f.vis.is_public()) + } + _ => false, + }; + + let mut app = Applicability::MachineApplicable; + let cx_snip = snippet_with_applicability(cx, cx_arg.span, "..", &mut app); + let def_snip = snippet_with_applicability(cx, def_arg.span, "..", &mut app); + let (sugg, with_note) = match (which_path, item) { + // match_def_path + (0, Item::DiagnosticItem(item)) => + (format!("{cx_snip}.tcx.is_diagnostic_item(sym::{item}, {def_snip})"), has_ctor), + (0, Item::LangItem(item)) => ( + format!("{cx_snip}.tcx.lang_items().require(LangItem::{item}).ok() == Some({def_snip})"), + has_ctor + ), + // match_trait_method + (1, Item::DiagnosticItem(item)) => + (format!("is_trait_method({cx_snip}, {def_snip}, sym::{item})"), false), + // match_type + (2, Item::DiagnosticItem(item)) => + (format!("is_type_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"), false), + (2, Item::LangItem(item)) => + (format!("is_type_lang_item({cx_snip}, {def_snip}, LangItem::{item})"), false), + // is_expr_path_def_path + (3, Item::DiagnosticItem(item)) if has_ctor => ( + format!( + "is_res_diag_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), sym::{item})", + ), + false, + ), + (3, Item::LangItem(item)) if has_ctor => ( + format!( + "is_res_lang_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), LangItem::{item})", + ), + false, + ), + (3, Item::DiagnosticItem(item)) => + (format!("is_path_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"), false), + (3, Item::LangItem(item)) => ( + format!( + "path_res({cx_snip}, {def_snip}).opt_def_id()\ + .map_or(false, |id| {cx_snip}.tcx.lang_items().require(LangItem::{item}).ok() == Some(id))", + ), + false, + ), + _ => return, + }; + + span_lint_and_then( cx, - MATCH_TYPE_ON_DIAGNOSTIC_ITEM, + UNNECESSARY_DEF_PATH, expr.span, - "usage of `clippy_utils::ty::match_type()` on a type diagnostic item", - "try", - format!("clippy_utils::ty::is_type_diagnostic_item({}, {}, sym::{})", cx_snippet, ty_snippet, item_name), - Applicability::MaybeIncorrect, + msg, + |diag| { + diag.span_suggestion(expr.span, "try", sugg, app); + if with_note { + diag.help( + "if this `DefId` came from a constructor expression or pattern then the \ + parent `DefId` should be used instead" + ); + } + }, ); } } } } -fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option> { - use rustc_hir::ItemKind; - - match &expr.kind { - ExprKind::AddrOf(.., expr) => return path_to_matched_type(cx, expr), - ExprKind::Path(qpath) => match cx.qpath_res(qpath, expr.hir_id) { +fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option> { + match peel_hir_expr_refs(expr).0.kind { + ExprKind::Path(ref qpath) => match cx.qpath_res(qpath, expr.hir_id) { Res::Local(hir_id) => { let parent_id = cx.tcx.hir().get_parent_node(hir_id); - if let Some(Node::Local(local)) = cx.tcx.hir().find(parent_id) { - if let Some(init) = local.init { - return path_to_matched_type(cx, init); - } - } - }, - Res::Def(DefKind::Const | DefKind::Static(..), def_id) => { - if let Some(Node::Item(item)) = cx.tcx.hir().get_if_local(def_id) { - if let ItemKind::Const(.., body_id) | ItemKind::Static(.., body_id) = item.kind { - let body = cx.tcx.hir().body(body_id); - return path_to_matched_type(cx, body.value); - } - } - }, - _ => {}, - }, - ExprKind::Array(exprs) => { - let segments: Vec = exprs - .iter() - .filter_map(|expr| { - if let ExprKind::Lit(lit) = &expr.kind { - if let LitKind::Str(sym, _) = lit.node { - return Some(sym); - } - } - + if let Some(Node::Local(Local { init: Some(init), .. })) = cx.tcx.hir().find(parent_id) { + path_to_matched_type(cx, init) + } else { None - }) - .collect(); - - if segments.len() == exprs.len() { - return Some(segments); - } + } + }, + Res::Def(DefKind::Static(_), def_id) => read_mir_alloc_def_path( + cx, + cx.tcx.eval_static_initializer(def_id).ok()?.inner(), + cx.tcx.type_of(def_id), + ), + Res::Def(DefKind::Const, def_id) => match cx.tcx.const_eval_poly(def_id).ok()? { + ConstValue::ByRef { alloc, offset } if offset.bytes() == 0 => { + read_mir_alloc_def_path(cx, alloc.inner(), cx.tcx.type_of(def_id)) + }, + _ => None, + }, + _ => None, }, - _ => {}, - } + ExprKind::Array(exprs) => exprs + .iter() + .map(|expr| { + if let ExprKind::Lit(lit) = &expr.kind { + if let LitKind::Str(sym, _) = lit.node { + return Some((*sym.as_str()).to_owned()); + } + } - None + None + }) + .collect(), + _ => None, + } +} + +fn read_mir_alloc_def_path<'tcx>(cx: &LateContext<'tcx>, alloc: &'tcx Allocation, ty: Ty<'_>) -> Option> { + let (alloc, ty) = if let ty::Ref(_, ty, Mutability::Not) = *ty.kind() { + let &alloc = alloc.provenance().values().next()?; + if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc) { + (alloc.inner(), ty) + } else { + return None; + } + } else { + (alloc, ty) + }; + + if let ty::Array(ty, _) | ty::Slice(ty) = *ty.kind() + && let ty::Ref(_, ty, Mutability::Not) = *ty.kind() + && ty.is_str() + { + alloc + .provenance() + .values() + .map(|&alloc| { + if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc) { + let alloc = alloc.inner(); + str::from_utf8(alloc.inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len())) + .ok().map(ToOwned::to_owned) + } else { + None + } + }) + .collect() + } else { + None + } } // This is not a complete resolver for paths. It works on all the paths currently used in the paths // module. That's all it does and all it needs to do. pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool { - if def_path_res(cx, path) != Res::Err { + if def_path_res(cx, path, None) != Res::Err { return true; } @@ -1077,7 +1206,7 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { } for &module in &[&paths::KW_MODULE, &paths::SYM_MODULE] { - if let Some(def_id) = def_path_res(cx, module).opt_def_id() { + if let Some(def_id) = def_path_res(cx, module, None).opt_def_id() { for item in cx.tcx.module_children(def_id).iter() { if_chain! { if let Res::Def(DefKind::Const, item_def_id) = item.res; diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index 342f627e3827..c84191bb0103 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -64,46 +64,6 @@ const DEFAULT_LINT_LEVELS: &[(&str, &str)] = &[ /// This prefix is in front of the lint groups in the lint store. The prefix will be trimmed /// to only keep the actual lint group in the output. const CLIPPY_LINT_GROUP_PREFIX: &str = "clippy::"; - -/// This template will be used to format the configuration section in the lint documentation. -/// The `configurations` parameter will be replaced with one or multiple formatted -/// `ClippyConfiguration` instances. See `CONFIGURATION_VALUE_TEMPLATE` for further customizations -macro_rules! CONFIGURATION_SECTION_TEMPLATE { - () => { - r#" -### Configuration -This lint has the following configuration variables: - -{configurations} -"# - }; -} -/// This template will be used to format an individual `ClippyConfiguration` instance in the -/// lint documentation. -/// -/// The format function will provide strings for the following parameters: `name`, `ty`, `doc` and -/// `default` -macro_rules! CONFIGURATION_VALUE_TEMPLATE { - () => { - "* `{name}`: `{ty}`: {doc} (defaults to `{default}`)\n" - }; -} - -macro_rules! RENAMES_SECTION_TEMPLATE { - () => { - r#" -### Past names - -{names} -"# - }; -} -macro_rules! RENAME_VALUE_TEMPLATE { - () => { - "* `{name}`\n" - }; -} - const LINT_EMISSION_FUNCTIONS: [&[&str]; 7] = [ &["clippy_utils", "diagnostics", "span_lint"], &["clippy_utils", "diagnostics", "span_lint_and_help"], @@ -205,7 +165,16 @@ impl MetadataCollector { .filter(|config| config.lints.iter().any(|lint| lint == lint_name)) .map(ToString::to_string) .reduce(|acc, x| acc + &x) - .map(|configurations| format!(CONFIGURATION_SECTION_TEMPLATE!(), configurations = configurations)) + .map(|configurations| { + format!( + r#" +### Configuration +This lint has the following configuration variables: + +{configurations} +"# + ) + }) } } @@ -291,16 +260,13 @@ fn replace_produces(lint_name: &str, docs: &mut String, clippy_project_root: &Pa continue; } - panic!("lint `{}` has an unterminated code block", lint_name) + panic!("lint `{lint_name}` has an unterminated code block") } break; }, Some(line) if line.trim_start() == "{{produces}}" => { - panic!( - "lint `{}` has marker {{{{produces}}}} with an ignored or missing code block", - lint_name - ) + panic!("lint `{lint_name}` has marker {{{{produces}}}} with an ignored or missing code block") }, Some(line) => { let line = line.trim(); @@ -319,7 +285,7 @@ fn replace_produces(lint_name: &str, docs: &mut String, clippy_project_root: &Pa match lines.next() { Some(line) if line.trim_start() == "```" => break, Some(line) => example.push(line), - None => panic!("lint `{}` has an unterminated code block", lint_name), + None => panic!("lint `{lint_name}` has an unterminated code block"), } } @@ -336,10 +302,9 @@ fn replace_produces(lint_name: &str, docs: &mut String, clippy_project_root: &Pa Produces\n\ \n\ ```text\n\ - {}\n\ + {output}\n\ ```\n\ - ", - output + " ), ); @@ -394,7 +359,7 @@ fn get_lint_output(lint_name: &str, example: &[&mut String], clippy_project_root panic!("failed to write to `{}`: {e}", file.as_path().to_string_lossy()); } - let prefixed_name = format!("{}{lint_name}", CLIPPY_LINT_GROUP_PREFIX); + let prefixed_name = format!("{CLIPPY_LINT_GROUP_PREFIX}{lint_name}"); let mut cmd = Command::new("cargo"); @@ -417,7 +382,7 @@ fn get_lint_output(lint_name: &str, example: &[&mut String], clippy_project_root let output = cmd .arg(file.as_path()) .output() - .unwrap_or_else(|e| panic!("failed to run `{:?}`: {e}", cmd)); + .unwrap_or_else(|e| panic!("failed to run `{cmd:?}`: {e}")); let tmp_file_path = file.to_string_lossy(); let stderr = std::str::from_utf8(&output.stderr).unwrap(); @@ -441,8 +406,7 @@ fn get_lint_output(lint_name: &str, example: &[&mut String], clippy_project_root let rendered: Vec<&str> = msgs.iter().filter_map(|msg| msg["rendered"].as_str()).collect(); let non_json: Vec<&str> = stderr.lines().filter(|line| !line.starts_with('{')).collect(); panic!( - "did not find lint `{}` in output of example, got:\n{}\n{}", - lint_name, + "did not find lint `{lint_name}` in output of example, got:\n{}\n{}", non_json.join("\n"), rendered.join("\n") ); @@ -588,13 +552,10 @@ fn to_kebab(config_name: &str) -> String { impl fmt::Display for ClippyConfiguration { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result { - write!( + writeln!( f, - CONFIGURATION_VALUE_TEMPLATE!(), - name = self.name, - ty = self.config_type, - doc = self.doc, - default = self.default + "* `{}`: `{}`: {} (defaults to `{}`)", + self.name, self.config_type, self.doc, self.default ) } } @@ -811,7 +772,7 @@ fn get_lint_group_and_level_or_lint( lint_collection_error_item( cx, item, - &format!("Unable to determine lint level for found group `{}`", group), + &format!("Unable to determine lint level for found group `{group}`"), ); None } @@ -869,7 +830,7 @@ fn collect_renames(lints: &mut Vec) { if name == lint_name; if let Some(past_name) = k.strip_prefix(CLIPPY_LINT_GROUP_PREFIX); then { - write!(collected, RENAME_VALUE_TEMPLATE!(), name = past_name).unwrap(); + writeln!(collected, "* `{past_name}`").unwrap(); names.push(past_name.to_string()); } } @@ -882,7 +843,15 @@ fn collect_renames(lints: &mut Vec) { } if !collected.is_empty() { - write!(&mut lint.docs, RENAMES_SECTION_TEMPLATE!(), names = collected).unwrap(); + write!( + &mut lint.docs, + r#" +### Past names + +{collected} +"# + ) + .unwrap(); } } } @@ -895,7 +864,7 @@ fn lint_collection_error_item(cx: &LateContext<'_>, item: &Item<'_>, message: &s cx, INTERNAL_METADATA_COLLECTOR, item.ident.span, - &format!("metadata collection error for `{}`: {}", item.ident.name, message), + &format!("metadata collection error for `{}`: {message}", item.ident.name), ); } diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index 2604b1ee7c56..301eed9a1fbf 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -173,7 +173,7 @@ impl LateLintPass<'_> for WildcardImports { let sugg = if braced_glob { imports_string } else { - format!("{}::{}", import_source_snippet, imports_string) + format!("{import_source_snippet}::{imports_string}") }; let (lint, message) = if let Res::Def(DefKind::Enum, _) = use_path.res { diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 06e7d7017017..36574198f917 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -1,12 +1,12 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn, MacroCall}; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::{expand_past_previous_comma, snippet_opt}; use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, HirIdMap, Impl, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::{sym, BytePos, Span}; +use rustc_span::{sym, BytePos}; declare_clippy_lint! { /// ### What it does @@ -475,11 +475,11 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, name: & value.span, "literal with an empty format string", |diag| { - if let Some(replacement) = replacement { + if let Some(replacement) = replacement // `format!("{}", "a")`, `format!("{named}", named = "b") // ~~~~~ ~~~~~~~~~~~~~ - let value_span = expand_past_previous_comma(cx, value.span); - + && let Some(value_span) = format_args.value_with_prev_comma_span(value.hir_id) + { let replacement = replacement.replace('{', "{{").replace('}', "}}"); diag.multipart_suggestion( "try this", @@ -542,10 +542,3 @@ fn conservative_unescape(literal: &str) -> Result { if err { Err(UnescapeErr::Lint) } else { Ok(unescaped) } } - -// Expand from `writeln!(o, "")` to `writeln!(o, "")` -// ^^ ^^^^ -fn expand_past_previous_comma(cx: &LateContext<'_>, span: Span) -> Span { - let extended = cx.sess().source_map().span_extend_to_prev_char(span, ',', true); - extended.with_lo(extended.lo() - BytePos(1)) -} diff --git a/clippy_lints/src/zero_div_zero.rs b/clippy_lints/src/zero_div_zero.rs index 50d3c079fe67..9b3de35dbd3c 100644 --- a/clippy_lints/src/zero_div_zero.rs +++ b/clippy_lints/src/zero_div_zero.rs @@ -57,8 +57,7 @@ impl<'tcx> LateLintPass<'tcx> for ZeroDiv { "constant division of `0.0` with `0.0` will always result in NaN", None, &format!( - "consider using `{}::NAN` if you would like a constant representing NaN", - float_type, + "consider using `{float_type}::NAN` if you would like a constant representing NaN", ), ); } diff --git a/clippy_lints/src/zero_sized_map_values.rs b/clippy_lints/src/zero_sized_map_values.rs index 703ba2ef4b05..6cf2a955fd5c 100644 --- a/clippy_lints/src/zero_sized_map_values.rs +++ b/clippy_lints/src/zero_sized_map_values.rs @@ -2,12 +2,12 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::{is_normalizable, is_type_diagnostic_item}; use if_chain::if_chain; use rustc_hir::{self as hir, HirId, ItemKind, Node}; +use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::layout::LayoutOf as _; use rustc_middle::ty::{Adt, Ty, TypeVisitable}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; -use rustc_hir_analysis::hir_ty_to_ty; declare_clippy_lint! { /// ### What it does @@ -69,10 +69,7 @@ impl LateLintPass<'_> for ZeroSizedMapValues { fn in_trait_impl(cx: &LateContext<'_>, hir_id: HirId) -> bool { let parent_id = cx.tcx.hir().get_parent_item(hir_id); - let second_parent_id = cx - .tcx - .hir() - .get_parent_item(parent_id.into()).def_id; + let second_parent_id = cx.tcx.hir().get_parent_item(parent_id.into()).def_id; if let Some(Node::Item(item)) = cx.tcx.hir().find_by_def_id(second_parent_id) { if let ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) = item.kind { return true; diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index c36bca06507d..83fee7bb39c2 100644 --- a/clippy_utils/Cargo.toml +++ b/clippy_utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_utils" -version = "0.1.65" +version = "0.1.66" edition = "2021" publish = false diff --git a/clippy_utils/src/attrs.rs b/clippy_utils/src/attrs.rs index 8ab77c881663..d9b22664fd25 100644 --- a/clippy_utils/src/attrs.rs +++ b/clippy_utils/src/attrs.rs @@ -131,12 +131,12 @@ pub fn get_unique_inner_attr(sess: &Session, attrs: &[ast::Attribute], name: &'s match attr.style { ast::AttrStyle::Inner if unique_attr.is_none() => unique_attr = Some(attr.clone()), ast::AttrStyle::Inner => { - sess.struct_span_err(attr.span, &format!("`{}` is defined multiple times", name)) + sess.struct_span_err(attr.span, &format!("`{name}` is defined multiple times")) .span_note(unique_attr.as_ref().unwrap().span, "first definition found here") .emit(); }, ast::AttrStyle::Outer => { - sess.span_err(attr.span, &format!("`{}` cannot be an outer attribute", name)); + sess.span_err(attr.span, &format!("`{name}` cannot be an outer attribute")); }, } } diff --git a/clippy_utils/src/diagnostics.rs b/clippy_utils/src/diagnostics.rs index 78960d1ab1da..78f93755b72d 100644 --- a/clippy_utils/src/diagnostics.rs +++ b/clippy_utils/src/diagnostics.rs @@ -18,12 +18,11 @@ fn docs_link(diag: &mut Diagnostic, lint: &'static Lint) { if env::var("CLIPPY_DISABLE_DOCS_LINKS").is_err() { if let Some(lint) = lint.name_lower().strip_prefix("clippy::") { diag.help(&format!( - "for further information visit https://rust-lang.github.io/rust-clippy/{}/index.html#{}", + "for further information visit https://rust-lang.github.io/rust-clippy/{}/index.html#{lint}", &option_env!("RUST_RELEASE_NUM").map_or("master".to_string(), |n| { // extract just major + minor version and ignore patch versions format!("rust-{}", n.rsplit_once('.').unwrap().1) - }), - lint + }) )); } } diff --git a/clippy_utils/src/eager_or_lazy.rs b/clippy_utils/src/eager_or_lazy.rs index 91c9c382c236..8724a4cd651d 100644 --- a/clippy_utils/src/eager_or_lazy.rs +++ b/clippy_utils/src/eager_or_lazy.rs @@ -113,7 +113,17 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS }, args, ) => match self.cx.qpath_res(path, hir_id) { - Res::Def(DefKind::Ctor(..) | DefKind::Variant, _) | Res::SelfCtor(_) => (), + Res::Def(DefKind::Ctor(..) | DefKind::Variant, _) | Res::SelfCtor(_) => { + if self + .cx + .typeck_results() + .expr_ty(e) + .has_significant_drop(self.cx.tcx, self.cx.param_env) + { + self.eagerness = Lazy; + return; + } + }, Res::Def(_, id) if self.cx.tcx.is_promotable_const_fn(id) => (), // No need to walk the arguments here, `is_const_evaluatable` already did Res::Def(..) if is_const_evaluatable(self.cx, e) => { diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index 7212d9cd7445..cf24ec8b67b9 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -962,7 +962,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { mut_ty.mutbl.hash(&mut self.s); }, TyKind::Rptr(lifetime, ref mut_ty) => { - self.hash_lifetime(*lifetime); + self.hash_lifetime(lifetime); self.hash_ty(mut_ty.ty); mut_ty.mutbl.hash(&mut self.s); }, @@ -992,7 +992,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { in_trait.hash(&mut self.s); }, TyKind::TraitObject(_, lifetime, _) => { - self.hash_lifetime(*lifetime); + self.hash_lifetime(lifetime); }, TyKind::Typeof(anon_const) => { self.hash_body(anon_const.body); diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 8f79c07c9772..42374fdd7baf 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -3,6 +3,7 @@ #![feature(control_flow_enum)] #![feature(let_chains)] #![feature(lint_reasons)] +#![feature(never_type)] #![feature(once_cell)] #![feature(rustc_private)] #![recursion_limit = "512"] @@ -23,6 +24,7 @@ extern crate rustc_attr; extern crate rustc_data_structures; extern crate rustc_errors; extern crate rustc_hir; +extern crate rustc_hir_analysis; extern crate rustc_infer; extern crate rustc_lexer; extern crate rustc_lint; @@ -32,7 +34,6 @@ extern crate rustc_session; extern crate rustc_span; extern crate rustc_target; extern crate rustc_trait_selection; -extern crate rustc_hir_analysis; #[macro_use] pub mod sym_helper; @@ -65,6 +66,7 @@ pub use self::hir_utils::{ both, count_eq, eq_expr_value, hash_expr, hash_stmt, over, HirEqInterExpr, SpanlessEq, SpanlessHash, }; +use core::ops::ControlFlow; use std::collections::hash_map::Entry; use std::hash::BuildHasherDefault; use std::sync::OnceLock; @@ -76,7 +78,7 @@ use rustc_ast::Attribute; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::unhash::UnhashMap; use rustc_hir as hir; -use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def::{DefKind, Namespace, Res}; use rustc_hir::def_id::{CrateNum, DefId, LocalDefId}; use rustc_hir::hir_id::{HirIdMap, HirIdSet}; use rustc_hir::intravisit::{walk_expr, FnKind, Visitor}; @@ -113,14 +115,14 @@ use rustc_target::abi::Integer; use crate::consts::{constant, Constant}; use crate::ty::{can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type, ty_is_fn_once_param}; -use crate::visitors::expr_visitor_no_bodies; +use crate::visitors::for_each_expr; pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option) -> Option { if let Ok(version) = RustcVersion::parse(msrv) { return Some(version); } else if let Some(sess) = sess { if let Some(span) = span { - sess.span_err(span, &format!("`{}` is not a valid Rust version", msrv)); + sess.span_err(span, &format!("`{msrv}` is not a valid Rust version")); } } None @@ -238,19 +240,69 @@ pub fn in_constant(cx: &LateContext<'_>, id: HirId) -> bool { } } -/// Checks if a `QPath` resolves to a constructor of a `LangItem`. +/// 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_lang_ctor(cx: &LateContext<'_>, qpath: &QPath<'_>, lang_item: LangItem) -> bool { +pub fn is_res_lang_ctor(cx: &LateContext<'_>, res: Res, lang_item: LangItem) -> bool { + if let Res::Def(DefKind::Ctor(..), id) = res + && let Ok(lang_id) = cx.tcx.lang_items().require(lang_item) + && let Some(id) = cx.tcx.opt_parent(id) + { + id == lang_id + } else { + false + } +} + +pub fn is_res_diagnostic_ctor(cx: &LateContext<'_>, res: Res, diag_item: Symbol) -> bool { + if let Res::Def(DefKind::Ctor(..), id) = res + && let Some(id) = cx.tcx.opt_parent(id) + { + cx.tcx.is_diagnostic_item(diag_item, id) + } else { + false + } +} + +/// Checks if a `QPath` resolves to a constructor of a diagnostic item. +pub fn is_diagnostic_ctor(cx: &LateContext<'_>, qpath: &QPath<'_>, diagnostic_item: Symbol) -> bool { if let QPath::Resolved(_, path) = qpath { if let Res::Def(DefKind::Ctor(..), ctor_id) = path.res { - if let Ok(item_id) = cx.tcx.lang_items().require(lang_item) { - return cx.tcx.parent(ctor_id) == item_id; - } + return cx.tcx.is_diagnostic_item(diagnostic_item, cx.tcx.parent(ctor_id)); } } false } +/// Checks if the `DefId` matches the given diagnostic item or it's constructor. +pub fn is_diagnostic_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: Symbol) -> bool { + let did = match cx.tcx.def_kind(did) { + DefKind::Ctor(..) => cx.tcx.parent(did), + // Constructors for types in external crates seem to have `DefKind::Variant` + DefKind::Variant => match cx.tcx.opt_parent(did) { + Some(did) if matches!(cx.tcx.def_kind(did), DefKind::Variant) => did, + _ => did, + }, + _ => did, + }; + + cx.tcx.is_diagnostic_item(item, did) +} + +/// Checks if the `DefId` matches the given `LangItem` or it's constructor. +pub fn is_lang_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: LangItem) -> bool { + let did = match cx.tcx.def_kind(did) { + DefKind::Ctor(..) => cx.tcx.parent(did), + // Constructors for types in external crates seem to have `DefKind::Variant` + DefKind::Variant => match cx.tcx.opt_parent(did) { + Some(did) if matches!(cx.tcx.def_kind(did), DefKind::Variant) => did, + _ => did, + }, + _ => did, + }; + + cx.tcx.lang_items().require(item).map_or(false, |id| id == did) +} + pub fn is_unit_expr(expr: &Expr<'_>) -> bool { matches!( expr.kind, @@ -470,15 +522,49 @@ pub fn path_def_id<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx> path_res(cx, maybe_path).opt_def_id() } -/// Resolves a def path like `std::vec::Vec`. +fn find_primitive<'tcx>(tcx: TyCtxt<'tcx>, name: &str) -> impl Iterator + 'tcx { + let single = |ty| tcx.incoherent_impls(ty).iter().copied(); + let empty = || [].iter().copied(); + match name { + "bool" => single(BoolSimplifiedType), + "char" => single(CharSimplifiedType), + "str" => single(StrSimplifiedType), + "array" => single(ArraySimplifiedType), + "slice" => single(SliceSimplifiedType), + // FIXME: rustdoc documents these two using just `pointer`. + // + // Maybe this is something we should do here too. + "const_ptr" => single(PtrSimplifiedType(Mutability::Not)), + "mut_ptr" => single(PtrSimplifiedType(Mutability::Mut)), + "isize" => single(IntSimplifiedType(IntTy::Isize)), + "i8" => single(IntSimplifiedType(IntTy::I8)), + "i16" => single(IntSimplifiedType(IntTy::I16)), + "i32" => single(IntSimplifiedType(IntTy::I32)), + "i64" => single(IntSimplifiedType(IntTy::I64)), + "i128" => single(IntSimplifiedType(IntTy::I128)), + "usize" => single(UintSimplifiedType(UintTy::Usize)), + "u8" => single(UintSimplifiedType(UintTy::U8)), + "u16" => single(UintSimplifiedType(UintTy::U16)), + "u32" => single(UintSimplifiedType(UintTy::U32)), + "u64" => single(UintSimplifiedType(UintTy::U64)), + "u128" => single(UintSimplifiedType(UintTy::U128)), + "f32" => single(FloatSimplifiedType(FloatTy::F32)), + "f64" => single(FloatSimplifiedType(FloatTy::F64)), + _ => empty(), + } +} + +/// Resolves a def path like `std::vec::Vec`. `namespace_hint` can be supplied to disambiguate +/// between `std::vec` the module and `std::vec` the macro +/// /// This function is expensive and should be used sparingly. -pub fn def_path_res(cx: &LateContext<'_>, path: &[&str]) -> Res { - fn item_child_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: &str) -> Option { +pub fn def_path_res(cx: &LateContext<'_>, path: &[&str], namespace_hint: Option) -> Res { + fn item_child_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: &str, matches_ns: impl Fn(Res) -> bool) -> Option { match tcx.def_kind(def_id) { DefKind::Mod | DefKind::Enum | DefKind::Trait => tcx .module_children(def_id) .iter() - .find(|item| item.ident.name.as_str() == name) + .find(|item| item.ident.name.as_str() == name && matches_ns(item.res.expect_non_local())) .map(|child| child.res.expect_non_local()), DefKind::Impl => tcx .associated_item_def_ids(def_id) @@ -486,40 +572,17 @@ pub fn def_path_res(cx: &LateContext<'_>, path: &[&str]) -> Res { .copied() .find(|assoc_def_id| tcx.item_name(*assoc_def_id).as_str() == name) .map(|assoc_def_id| Res::Def(tcx.def_kind(assoc_def_id), assoc_def_id)), + DefKind::Struct | DefKind::Union => tcx + .adt_def(def_id) + .non_enum_variant() + .fields + .iter() + .find(|f| f.name.as_str() == name) + .map(|f| Res::Def(DefKind::Field, f.did)), _ => None, } } - fn find_primitive<'tcx>(tcx: TyCtxt<'tcx>, name: &str) -> impl Iterator + 'tcx { - let single = |ty| tcx.incoherent_impls(ty).iter().copied(); - let empty = || [].iter().copied(); - match name { - "bool" => single(BoolSimplifiedType), - "char" => single(CharSimplifiedType), - "str" => single(StrSimplifiedType), - "array" => single(ArraySimplifiedType), - "slice" => single(SliceSimplifiedType), - // FIXME: rustdoc documents these two using just `pointer`. - // - // Maybe this is something we should do here too. - "const_ptr" => single(PtrSimplifiedType(Mutability::Not)), - "mut_ptr" => single(PtrSimplifiedType(Mutability::Mut)), - "isize" => single(IntSimplifiedType(IntTy::Isize)), - "i8" => single(IntSimplifiedType(IntTy::I8)), - "i16" => single(IntSimplifiedType(IntTy::I16)), - "i32" => single(IntSimplifiedType(IntTy::I32)), - "i64" => single(IntSimplifiedType(IntTy::I64)), - "i128" => single(IntSimplifiedType(IntTy::I128)), - "usize" => single(UintSimplifiedType(UintTy::Usize)), - "u8" => single(UintSimplifiedType(UintTy::U8)), - "u16" => single(UintSimplifiedType(UintTy::U16)), - "u32" => single(UintSimplifiedType(UintTy::U32)), - "u64" => single(UintSimplifiedType(UintTy::U64)), - "u128" => single(UintSimplifiedType(UintTy::U128)), - "f32" => single(FloatSimplifiedType(FloatTy::F32)), - "f64" => single(FloatSimplifiedType(FloatTy::F64)), - _ => empty(), - } - } + fn find_crate(tcx: TyCtxt<'_>, name: &str) -> Option { tcx.crates(()) .iter() @@ -528,32 +591,45 @@ pub fn def_path_res(cx: &LateContext<'_>, path: &[&str]) -> Res { .map(CrateNum::as_def_id) } - let (base, first, path) = match *path { - [base, first, ref path @ ..] => (base, first, path), + let (base, path) = match *path { [primitive] => { return PrimTy::from_name(Symbol::intern(primitive)).map_or(Res::Err, Res::PrimTy); }, + [base, ref path @ ..] => (base, path), _ => return Res::Err, }; let tcx = cx.tcx; let starts = find_primitive(tcx, base) .chain(find_crate(tcx, base)) - .filter_map(|id| item_child_by_name(tcx, id, first)); + .map(|id| Res::Def(tcx.def_kind(id), id)); for first in starts { let last = path .iter() .copied() + .enumerate() // for each segment, find the child item - .try_fold(first, |res, segment| { + .try_fold(first, |res, (idx, segment)| { + let matches_ns = |res: Res| { + // If at the last segment in the path, respect the namespace hint + if idx == path.len() - 1 { + match namespace_hint { + Some(ns) => res.matches_ns(ns), + None => true, + } + } else { + res.matches_ns(Namespace::TypeNS) + } + }; + let def_id = res.def_id(); - if let Some(item) = item_child_by_name(tcx, def_id, segment) { + if let Some(item) = item_child_by_name(tcx, def_id, segment, matches_ns) { Some(item) } else if matches!(res, Res::Def(DefKind::Enum | DefKind::Struct, _)) { // it is not a child item so check inherent impl items tcx.inherent_impls(def_id) .iter() - .find_map(|&impl_def_id| item_child_by_name(tcx, impl_def_id, segment)) + .find_map(|&impl_def_id| item_child_by_name(tcx, impl_def_id, segment, matches_ns)) } else { None } @@ -569,8 +645,10 @@ pub fn def_path_res(cx: &LateContext<'_>, path: &[&str]) -> Res { /// Convenience function to get the `DefId` of a trait by path. /// It could be a trait or trait alias. +/// +/// This function is expensive and should be used sparingly. pub fn get_trait_def_id(cx: &LateContext<'_>, path: &[&str]) -> Option { - match def_path_res(cx, path) { + match def_path_res(cx, path, Some(Namespace::TypeNS)) { Res::Def(DefKind::Trait | DefKind::TraitAlias, trait_id) => Some(trait_id), _ => None, } @@ -738,7 +816,7 @@ pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { } }, ExprKind::Call(repl_func, _) => is_default_equivalent_call(cx, repl_func), - ExprKind::Path(qpath) => is_lang_ctor(cx, qpath, OptionNone), + ExprKind::Path(qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, e.hir_id), OptionNone), ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])), _ => false, } @@ -1136,17 +1214,14 @@ pub fn contains_name(name: Symbol, expr: &Expr<'_>) -> bool { /// Returns `true` if `expr` contains a return expression pub fn contains_return(expr: &hir::Expr<'_>) -> bool { - let mut found = false; - expr_visitor_no_bodies(|expr| { - if !found { - if let hir::ExprKind::Ret(..) = &expr.kind { - found = true; - } + for_each_expr(expr, |e| { + if matches!(e.kind, hir::ExprKind::Ret(..)) { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) } - !found }) - .visit_expr(expr); - found + .is_some() } /// Extends the span to the beginning of the spans line, incl. whitespaces. @@ -1386,8 +1461,8 @@ pub fn is_integer_literal(expr: &Expr<'_>, value: u128) -> bool { /// Examples of coercions can be found in the Nomicon at /// . /// -/// See `rustc_middle::ty::adjustment::Adjustment` and `rustc_hir_analysis::check::coercion` for more -/// information on adjustments and coercions. +/// See `rustc_middle::ty::adjustment::Adjustment` and `rustc_hir_analysis::check::coercion` for +/// more information on adjustments and coercions. pub fn is_adjusted(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { cx.typeck_results().adjustments().get(e.hir_id).is_some() } @@ -1553,7 +1628,7 @@ pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tc if_chain! { if let PatKind::TupleStruct(ref path, pat, ddpos) = arm.pat.kind; if ddpos.as_opt_usize().is_none(); - if is_lang_ctor(cx, path, ResultOk); + if is_res_lang_ctor(cx, cx.qpath_res(path, arm.pat.hir_id), ResultOk); if let PatKind::Binding(_, hir_id, _, None) = pat[0].kind; if path_to_local_id(arm.body, hir_id); then { @@ -1565,7 +1640,7 @@ 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_lang_ctor(cx, path, ResultErr) + is_res_lang_ctor(cx, cx.qpath_res(path, arm.pat.hir_id), ResultErr) } else { false } @@ -2295,6 +2370,29 @@ pub fn span_contains_comment(sm: &SourceMap, span: Span) -> bool { }); } +/// Return all the comments a given span contains +/// Comments are returned wrapped with their relevant delimiters +pub fn span_extract_comment(sm: &SourceMap, span: Span) -> String { + let snippet = sm.span_to_snippet(span).unwrap_or_default(); + let mut comments_buf: Vec = Vec::new(); + let mut index: usize = 0; + + for token in tokenize(&snippet) { + let token_range = index..(index + token.len as usize); + index += token.len as usize; + match token.kind { + TokenKind::BlockComment { .. } | TokenKind::LineComment { .. } => { + if let Some(comment) = snippet.get(token_range) { + comments_buf.push(comment.to_string()); + } + }, + _ => (), + } + } + + comments_buf.join("\n") +} + macro_rules! op_utils { ($($name:ident $assign:ident)*) => { /// Binary operation traits like `LangItem::Add` diff --git a/clippy_utils/src/macros.rs b/clippy_utils/src/macros.rs index a1808c097200..dd0ce1da6575 100644 --- a/clippy_utils/src/macros.rs +++ b/clippy_utils/src/macros.rs @@ -2,7 +2,7 @@ use crate::is_path_diagnostic_item; use crate::source::snippet_opt; -use crate::visitors::expr_visitor_no_bodies; +use crate::visitors::{for_each_expr, Descend}; use arrayvec::ArrayVec; use itertools::{izip, Either, Itertools}; @@ -16,6 +16,7 @@ use rustc_parse_format::{self as rpf, Alignment}; use rustc_span::def_id::DefId; use rustc_span::hygiene::{self, MacroKind, SyntaxContext}; use rustc_span::{sym, BytePos, ExpnData, ExpnId, ExpnKind, Pos, Span, SpanData, Symbol}; +use std::iter::{once, zip}; use std::ops::ControlFlow; const FORMAT_MACRO_DIAG_ITEMS: &[Symbol] = &[ @@ -270,20 +271,19 @@ fn find_assert_args_inner<'a, const N: usize>( }; let mut args = ArrayVec::new(); let mut panic_expn = None; - expr_visitor_no_bodies(|e| { + let _: Option = for_each_expr(expr, |e| { if args.is_full() { if panic_expn.is_none() && e.span.ctxt() != expr.span.ctxt() { panic_expn = PanicExpn::parse(cx, e); } - panic_expn.is_none() + ControlFlow::Continue(Descend::from(panic_expn.is_none())) } else if is_assert_arg(cx, e, expn) { args.push(e); - false + ControlFlow::Continue(Descend::No) } else { - true + ControlFlow::Continue(Descend::Yes) } - }) - .visit_expr(expr); + }); let args = args.into_inner().ok()?; // if no `panic!(..)` is found, use `PanicExpn::Empty` // to indicate that the default assertion message is used @@ -297,22 +297,19 @@ fn find_assert_within_debug_assert<'a>( expn: ExpnId, assert_name: Symbol, ) -> Option<(&'a Expr<'a>, ExpnId)> { - let mut found = None; - expr_visitor_no_bodies(|e| { - if found.is_some() || !e.span.from_expansion() { - return false; + for_each_expr(expr, |e| { + if !e.span.from_expansion() { + return ControlFlow::Continue(Descend::No); } let e_expn = e.span.ctxt().outer_expn(); if e_expn == expn { - return true; + ControlFlow::Continue(Descend::Yes) + } else if e_expn.expn_data().macro_def_id.map(|id| cx.tcx.item_name(id)) == Some(assert_name) { + ControlFlow::Break((e, e_expn)) + } else { + ControlFlow::Continue(Descend::No) } - if e_expn.expn_data().macro_def_id.map(|id| cx.tcx.item_name(id)) == Some(assert_name) { - found = Some((e, e_expn)); - } - false }) - .visit_expr(expr); - found } fn is_assert_arg(cx: &LateContext<'_>, expr: &Expr<'_>, assert_expn: ExpnId) -> bool { @@ -392,20 +389,18 @@ impl FormatString { unescape_literal(inner, mode, &mut |_, ch| match ch { Ok(ch) => unescaped.push(ch), Err(e) if !e.is_fatal() => (), - Err(e) => panic!("{:?}", e), + Err(e) => panic!("{e:?}"), }); let mut parts = Vec::new(); - expr_visitor_no_bodies(|expr| { - if let ExprKind::Lit(lit) = &expr.kind { - if let LitKind::Str(symbol, _) = lit.node { - parts.push(symbol); - } + let _: Option = for_each_expr(pieces, |expr| { + if let ExprKind::Lit(lit) = &expr.kind + && let LitKind::Str(symbol, _) = lit.node + { + parts.push(symbol); } - - true - }) - .visit_expr(pieces); + ControlFlow::Continue(()) + }); Some(Self { span, @@ -418,7 +413,8 @@ impl FormatString { } struct FormatArgsValues<'tcx> { - /// See `FormatArgsExpn::value_args` + /// Values passed after the format string and implicit captures. `[1, z + 2, x]` for + /// `format!("{x} {} {y}", 1, z + 2)`. value_args: Vec<&'tcx Expr<'tcx>>, /// Maps an `rt::v1::Argument::position` or an `rt::v1::Count::Param` to its index in /// `value_args` @@ -431,7 +427,7 @@ impl<'tcx> FormatArgsValues<'tcx> { fn new(args: &'tcx Expr<'tcx>, format_string_span: SpanData) -> Self { let mut pos_to_value_index = Vec::new(); let mut value_args = Vec::new(); - expr_visitor_no_bodies(|expr| { + let _: Option = for_each_expr(args, |expr| { if expr.span.ctxt() == args.span.ctxt() { // ArgumentV1::new_() // ArgumentV1::from_usize() @@ -453,16 +449,13 @@ impl<'tcx> FormatArgsValues<'tcx> { pos_to_value_index.push(val_idx); } - - true + ControlFlow::Continue(Descend::Yes) } else { // assume that any expr with a differing span is a value value_args.push(expr); - - false + ControlFlow::Continue(Descend::No) } - }) - .visit_expr(args); + }); Self { value_args, @@ -545,19 +538,32 @@ fn span_from_inner(base: SpanData, inner: rpf::InnerSpan) -> Span { ) } +/// How a format parameter is used in the format string #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum FormatParamKind { /// An implicit parameter , such as `{}` or `{:?}`. Implicit, - /// A parameter with an explicit number, or an asterisk precision. e.g. `{1}`, `{0:?}`, - /// `{:.0$}` or `{:.*}`. + /// A parameter with an explicit number, e.g. `{1}`, `{0:?}`, or `{:.0$}` Numbered, + /// A parameter with an asterisk precision. e.g. `{:.*}`. + Starred, /// A named parameter with a named `value_arg`, such as the `x` in `format!("{x}", x = 1)`. Named(Symbol), /// An implicit named parameter, such as the `y` in `format!("{y}")`. NamedInline(Symbol), } +/// Where a format parameter is being used in the format string +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum FormatParamUsage { + /// Appears as an argument, e.g. `format!("{}", foo)` + Argument, + /// Appears as a width, e.g. `format!("{:width$}", foo, width = 1)` + Width, + /// Appears as a precision, e.g. `format!("{:.precision$}", foo, precision = 1)` + Precision, +} + /// A `FormatParam` is any place in a `FormatArgument` that refers to a supplied value, e.g. /// /// ``` @@ -573,6 +579,8 @@ pub struct FormatParam<'tcx> { pub value: &'tcx Expr<'tcx>, /// How this parameter refers to its `value`. pub kind: FormatParamKind, + /// Where this format param is being used - argument/width/precision + pub usage: FormatParamUsage, /// Span of the parameter, may be zero width. Includes the whitespace of implicit parameters. /// /// ```text @@ -585,6 +593,7 @@ pub struct FormatParam<'tcx> { impl<'tcx> FormatParam<'tcx> { fn new( mut kind: FormatParamKind, + usage: FormatParamUsage, position: usize, inner: rpf::InnerSpan, values: &FormatArgsValues<'tcx>, @@ -599,7 +608,12 @@ impl<'tcx> FormatParam<'tcx> { kind = FormatParamKind::NamedInline(name); } - Some(Self { value, kind, span }) + Some(Self { + value, + kind, + usage, + span, + }) } } @@ -618,6 +632,7 @@ pub enum Count<'tcx> { impl<'tcx> Count<'tcx> { fn new( + usage: FormatParamUsage, count: rpf::Count<'_>, position: Option, inner: Option, @@ -625,15 +640,27 @@ impl<'tcx> Count<'tcx> { ) -> Option { Some(match count { rpf::Count::CountIs(val) => Self::Is(val, span_from_inner(values.format_string_span, inner?)), - rpf::Count::CountIsName(name, span) => Self::Param(FormatParam::new( + rpf::Count::CountIsName(name, _) => Self::Param(FormatParam::new( FormatParamKind::Named(Symbol::intern(name)), + usage, position?, - span, + inner?, + values, + )?), + rpf::Count::CountIsParam(_) => Self::Param(FormatParam::new( + FormatParamKind::Numbered, + usage, + position?, + inner?, + values, + )?), + rpf::Count::CountIsStar(_) => Self::Param(FormatParam::new( + FormatParamKind::Starred, + usage, + position?, + inner?, values, )?), - rpf::Count::CountIsParam(_) | rpf::Count::CountIsStar(_) => { - Self::Param(FormatParam::new(FormatParamKind::Numbered, position?, inner?, values)?) - }, rpf::Count::CountImplied => Self::Implied, }) } @@ -676,8 +703,20 @@ impl<'tcx> FormatSpec<'tcx> { fill: spec.fill, align: spec.align, flags: spec.flags, - precision: Count::new(spec.precision, positions.precision, spec.precision_span, values)?, - width: Count::new(spec.width, positions.width, spec.width_span, values)?, + precision: Count::new( + FormatParamUsage::Precision, + spec.precision, + positions.precision, + spec.precision_span, + values, + )?, + width: Count::new( + FormatParamUsage::Width, + spec.width, + positions.width, + spec.width_span, + values, + )?, r#trait: match spec.ty { "" => sym::Display, "?" => sym::Debug, @@ -723,17 +762,87 @@ pub struct FormatArg<'tcx> { pub struct FormatArgsExpn<'tcx> { /// The format string literal. pub format_string: FormatString, - // The format arguments, such as `{:?}`. + /// The format arguments, such as `{:?}`. pub args: Vec>, /// Has an added newline due to `println!()`/`writeln!()`/etc. The last format string part will /// include this added newline. pub newline: bool, - /// Values passed after the format string and implicit captures. `[1, z + 2, x]` for + /// Spans of the commas between the format string and explicit values, excluding any trailing + /// comma + /// + /// ```ignore + /// format!("..", 1, 2, 3,) + /// // ^ ^ ^ + /// ``` + comma_spans: Vec, + /// Explicit values passed after the format string, ignoring implicit captures. `[1, z + 2]` for /// `format!("{x} {} {y}", 1, z + 2)`. - value_args: Vec<&'tcx Expr<'tcx>>, + explicit_values: Vec<&'tcx Expr<'tcx>>, } impl<'tcx> FormatArgsExpn<'tcx> { + /// Gets the spans of the commas inbetween the format string and explicit args, not including + /// any trailing comma + /// + /// ```ignore + /// format!("{} {}", a, b) + /// // ^ ^ + /// ``` + /// + /// Ensures that the format string and values aren't coming from a proc macro that sets the + /// output span to that of its input + fn comma_spans(cx: &LateContext<'_>, explicit_values: &[&Expr<'_>], fmt_span: Span) -> Option> { + // `format!("{} {} {c}", "one", "two", c = "three")` + // ^^^^^ ^^^^^ ^^^^^^^ + let value_spans = explicit_values + .iter() + .map(|val| hygiene::walk_chain(val.span, fmt_span.ctxt())); + + // `format!("{} {} {c}", "one", "two", c = "three")` + // ^^ ^^ ^^^^^^ + let between_spans = once(fmt_span) + .chain(value_spans) + .tuple_windows() + .map(|(start, end)| start.between(end)); + + let mut comma_spans = Vec::new(); + for between_span in between_spans { + let mut offset = 0; + let mut seen_comma = false; + + for token in tokenize(&snippet_opt(cx, between_span)?) { + match token.kind { + TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace => {}, + TokenKind::Comma if !seen_comma => { + seen_comma = true; + + let base = between_span.data(); + comma_spans.push(Span::new( + base.lo + BytePos(offset), + base.lo + BytePos(offset + 1), + base.ctxt, + base.parent, + )); + }, + // named arguments, `start_val, name = end_val` + // ^^^^^^^^^ between_span + TokenKind::Ident | TokenKind::Eq if seen_comma => {}, + // An unexpected token usually indicates the format string or a value came from a proc macro output + // that sets the span of its output to an input, e.g. `println!(some_proc_macro!("input"), ..)` that + // emits a string literal with the span set to that of `"input"` + _ => return None, + } + offset += token.len; + } + + if !seen_comma { + return None; + } + } + + Some(comma_spans) + } + pub fn parse(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option { let macro_name = macro_backtrace(expr.span) .map(|macro_call| cx.tcx.item_name(macro_call.def_id)) @@ -797,6 +906,7 @@ impl<'tcx> FormatArgsExpn<'tcx> { // NamedInline is handled by `FormatParam::new()` rpf::Position::ArgumentNamed(name) => FormatParamKind::Named(Symbol::intern(name)), }, + FormatParamUsage::Argument, position.value, parsed_arg.position_span, &values, @@ -807,11 +917,22 @@ impl<'tcx> FormatArgsExpn<'tcx> { }) .collect::>>()?; + let mut explicit_values = values.value_args; + // remove values generated for implicitly captured vars + let len = explicit_values + .iter() + .take_while(|val| !format_string.span.contains(val.span)) + .count(); + explicit_values.truncate(len); + + let comma_spans = Self::comma_spans(cx, &explicit_values, format_string.span)?; + Some(Self { format_string, args, - value_args: values.value_args, newline, + comma_spans, + explicit_values, }) } else { None @@ -819,27 +940,25 @@ impl<'tcx> FormatArgsExpn<'tcx> { } pub fn find_nested(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, expn_id: ExpnId) -> Option { - let mut format_args = None; - expr_visitor_no_bodies(|e| { - if format_args.is_some() { - return false; - } + for_each_expr(expr, |e| { let e_ctxt = e.span.ctxt(); if e_ctxt == expr.span.ctxt() { - return true; + ControlFlow::Continue(Descend::Yes) + } else if e_ctxt.outer_expn().is_descendant_of(expn_id) { + if let Some(args) = FormatArgsExpn::parse(cx, e) { + ControlFlow::Break(args) + } else { + ControlFlow::Continue(Descend::No) + } + } else { + ControlFlow::Continue(Descend::No) } - if e_ctxt.outer_expn().is_descendant_of(expn_id) { - format_args = FormatArgsExpn::parse(cx, e); - } - false }) - .visit_expr(expr); - format_args } /// Source callsite span of all inputs pub fn inputs_span(&self) -> Span { - match *self.value_args { + match *self.explicit_values { [] => self.format_string.span, [.., last] => self .format_string @@ -848,6 +967,22 @@ impl<'tcx> FormatArgsExpn<'tcx> { } } + /// Get the span of a value expanded to the previous comma, e.g. for the value `10` + /// + /// ```ignore + /// format("{}.{}", 10, 11) + /// // ^^^^ + /// ``` + pub fn value_with_prev_comma_span(&self, value_id: HirId) -> Option { + for (comma_span, value) in zip(&self.comma_spans, &self.explicit_values) { + if value.hir_id == value_id { + return Some(comma_span.to(hygiene::walk_chain(value.span, comma_span.ctxt()))); + } + } + + None + } + /// Iterator of all format params, both values and those referenced by `width`/`precision`s. pub fn params(&'tcx self) -> impl Iterator> { self.args diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 62020e21c815..8b843732a236 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -13,10 +13,11 @@ macro_rules! msrv_aliases { // names may refer to stabilized feature flags or library items msrv_aliases! { 1,62,0 { BOOL_THEN_SOME } + 1,58,0 { FORMAT_ARGS_CAPTURE } 1,53,0 { OR_PATTERNS, MANUAL_BITS, BTREE_MAP_RETAIN, BTREE_SET_RETAIN, ARRAY_INTO_ITERATOR } 1,52,0 { STR_SPLIT_ONCE, REM_EUCLID_CONST } 1,51,0 { BORROW_AS_PTR, UNSIGNED_ABS } - 1,50,0 { BOOL_THEN } + 1,50,0 { BOOL_THEN, CLAMP } 1,47,0 { TAU } 1,46,0 { CONST_IF_MATCH } 1,45,0 { STR_STRIP_PREFIX } diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 07170e2df12a..13938645fc3e 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -34,7 +34,6 @@ pub const DEFAULT_TRAIT_METHOD: [&str; 4] = ["core", "default", "Default", "defa pub const DEREF_MUT_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "DerefMut", "deref_mut"]; /// Preferably use the diagnostic item `sym::deref_method` where possible pub const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"]; -pub const DIR_BUILDER: [&str; 3] = ["std", "fs", "DirBuilder"]; pub const DISPLAY_TRAIT: [&str; 3] = ["core", "fmt", "Display"]; #[cfg(feature = "internal")] pub const EARLY_CONTEXT: [&str; 2] = ["rustc_lint", "EarlyContext"]; @@ -64,8 +63,6 @@ pub const IDENT_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Ident", "as_str"]; pub const INDEX: [&str; 3] = ["core", "ops", "Index"]; pub const INDEX_MUT: [&str; 3] = ["core", "ops", "IndexMut"]; pub const INSERT_STR: [&str; 4] = ["alloc", "string", "String", "insert_str"]; -pub const IO_READ: [&str; 3] = ["std", "io", "Read"]; -pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"]; pub const ITER_COUNT: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "count"]; pub const ITER_EMPTY: [&str; 5] = ["core", "iter", "sources", "empty", "Empty"]; pub const ITER_REPEAT: [&str; 5] = ["core", "iter", "sources", "repeat", "repeat"]; diff --git a/clippy_utils/src/ptr.rs b/clippy_utils/src/ptr.rs index 0226f74906b5..88837d8a143e 100644 --- a/clippy_utils/src/ptr.rs +++ b/clippy_utils/src/ptr.rs @@ -1,7 +1,7 @@ use crate::source::snippet; -use crate::visitors::expr_visitor_no_bodies; +use crate::visitors::{for_each_expr, Descend}; use crate::{path_to_local_id, strip_pat_refs}; -use rustc_hir::intravisit::Visitor; +use core::ops::ControlFlow; use rustc_hir::{Body, BodyId, ExprKind, HirId, PatKind}; use rustc_lint::LateContext; use rustc_span::Span; @@ -30,28 +30,23 @@ fn extract_clone_suggestions<'tcx>( replace: &[(&'static str, &'static str)], body: &'tcx Body<'_>, ) -> Option)>> { - let mut abort = false; let mut spans = Vec::new(); - expr_visitor_no_bodies(|expr| { - if abort { - return false; - } - if let ExprKind::MethodCall(seg, recv, [], _) = expr.kind { - if path_to_local_id(recv, id) { - if seg.ident.name.as_str() == "capacity" { - abort = true; - return false; - } - for &(fn_name, suffix) in replace { - if seg.ident.name.as_str() == fn_name { - spans.push((expr.span, snippet(cx, recv.span, "_") + suffix)); - return false; - } + for_each_expr(body, |e| { + if let ExprKind::MethodCall(seg, recv, [], _) = e.kind + && path_to_local_id(recv, id) + { + if seg.ident.as_str() == "capacity" { + return ControlFlow::Break(()); + } + for &(fn_name, suffix) in replace { + if seg.ident.as_str() == fn_name { + spans.push((e.span, snippet(cx, recv.span, "_") + suffix)); + return ControlFlow::Continue(Descend::No); } } } - !abort + ControlFlow::Continue(Descend::Yes) }) - .visit_body(body); - if abort { None } else { Some(spans) } + .is_none() + .then_some(spans) } diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index f7ce71917726..5a0721486e33 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -33,10 +33,10 @@ pub fn is_min_const_fn<'a, 'tcx>(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, msrv: | ty::PredicateKind::ConstEquate(..) | ty::PredicateKind::Trait(..) | ty::PredicateKind::TypeWellFormedFromEnv(..) => continue, - ty::PredicateKind::ObjectSafe(_) => panic!("object safe predicate on function: {:#?}", predicate), - ty::PredicateKind::ClosureKind(..) => panic!("closure kind predicate on function: {:#?}", predicate), - ty::PredicateKind::Subtype(_) => panic!("subtype predicate on function: {:#?}", predicate), - ty::PredicateKind::Coerce(_) => panic!("coerce predicate on function: {:#?}", predicate), + ty::PredicateKind::ObjectSafe(_) => panic!("object safe predicate on function: {predicate:#?}"), + ty::PredicateKind::ClosureKind(..) => panic!("closure kind predicate on function: {predicate:#?}"), + ty::PredicateKind::Subtype(_) => panic!("subtype predicate on function: {predicate:#?}"), + ty::PredicateKind::Coerce(_) => panic!("coerce predicate on function: {predicate:#?}"), } } match predicates.parent { @@ -319,8 +319,7 @@ fn check_terminator<'a, 'tcx>( span, format!( "can only call other `const fn` within a `const fn`, \ - but `{:?}` is not stable as `const fn`", - func, + but `{func:?}` is not stable as `const fn`", ) .into(), )); @@ -368,8 +367,9 @@ fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option) -> bo // function could be removed if `rustc` provided a MSRV-aware version of `is_const_fn`. // as a part of an unimplemented MSRV check https://github.com/rust-lang/rust/issues/65262. - // HACK(nilstrieb): CURRENT_RUSTC_VERSION can return versions like 1.66.0-dev. `rustc-semver` doesn't accept - // the `-dev` version number so we have to strip it off. + // HACK(nilstrieb): CURRENT_RUSTC_VERSION can return versions like 1.66.0-dev. `rustc-semver` + // doesn't accept the `-dev` version number so we have to strip it + // off. let short_version = since .as_str() .split('-') @@ -380,8 +380,9 @@ fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option) -> bo crate::meets_msrv( msrv, - RustcVersion::parse(since.as_str()) - .unwrap_or_else(|err| panic!("`rustc_attr::StabilityLevel::Stable::since` is ill-formatted: `{since}`, {err:?}")), + RustcVersion::parse(since.as_str()).unwrap_or_else(|err| { + panic!("`rustc_attr::StabilityLevel::Stable::since` is ill-formatted: `{since}`, {err:?}") + }), ) } else { // Unstable const fn with the feature enabled. diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index d85f591fb9a4..d28bd92d708b 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -25,11 +25,11 @@ pub fn expr_block<'a, T: LintContext>( if expr.span.from_expansion() { Cow::Owned(format!("{{ {} }}", snippet_with_macro_callsite(cx, expr.span, default))) } else if let ExprKind::Block(_, _) = expr.kind { - Cow::Owned(format!("{}{}", code, string)) + Cow::Owned(format!("{code}{string}")) } else if string.is_empty() { - Cow::Owned(format!("{{ {} }}", code)) + Cow::Owned(format!("{{ {code} }}")) } else { - Cow::Owned(format!("{{\n{};\n{}\n}}", code, string)) + Cow::Owned(format!("{{\n{code};\n{string}\n}}")) } } @@ -392,6 +392,16 @@ pub fn trim_span(sm: &SourceMap, span: Span) -> Span { .span() } +/// Expand a span to include a preceding comma +/// ```rust,ignore +/// writeln!(o, "") -> writeln!(o, "") +/// ^^ ^^^^ +/// ``` +pub fn expand_past_previous_comma(cx: &LateContext<'_>, span: Span) -> Span { + let extended = cx.sess().source_map().span_extend_to_prev_char(span, ',', true); + extended.with_lo(extended.lo() - BytePos(1)) +} + #[cfg(test)] mod test { use super::{reindent_multiline, without_block_comments}; @@ -466,7 +476,7 @@ mod test { #[test] fn test_without_block_comments_lines_without_block_comments() { let result = without_block_comments(vec!["/*", "", "*/"]); - println!("result: {:?}", result); + println!("result: {result:?}"); assert!(result.is_empty()); let result = without_block_comments(vec!["", "/*", "", "*/", "#[crate_type = \"lib\"]", "/*", "", "*/", ""]); diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index e53c40e95760..ef836e84829b 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -10,13 +10,13 @@ use rustc_ast_pretty::pprust::token_kind_to_string; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::{Closure, ExprKind, HirId, MutTy, TyKind}; +use rustc_hir_analysis::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{EarlyContext, LateContext, LintContext}; use rustc_middle::hir::place::ProjectionKind; use rustc_middle::mir::{FakeReadCause, Mutability}; use rustc_middle::ty; use rustc_span::source_map::{BytePos, CharPos, Pos, Span, SyntaxContext}; -use rustc_hir_analysis::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use std::borrow::Cow; use std::fmt::{Display, Write as _}; use std::ops::{Add, Neg, Not, Sub}; @@ -310,19 +310,19 @@ impl<'a> Sugg<'a> { /// Convenience method to transform suggestion into a return call pub fn make_return(self) -> Sugg<'static> { - Sugg::NonParen(Cow::Owned(format!("return {}", self))) + Sugg::NonParen(Cow::Owned(format!("return {self}"))) } /// Convenience method to transform suggestion into a block /// where the suggestion is a trailing expression pub fn blockify(self) -> Sugg<'static> { - Sugg::NonParen(Cow::Owned(format!("{{ {} }}", self))) + Sugg::NonParen(Cow::Owned(format!("{{ {self} }}"))) } /// Convenience method to prefix the expression with the `async` keyword. /// Can be used after `blockify` to create an async block. pub fn asyncify(self) -> Sugg<'static> { - Sugg::NonParen(Cow::Owned(format!("async {}", self))) + Sugg::NonParen(Cow::Owned(format!("async {self}"))) } /// Convenience method to create the `..` or `...` @@ -346,12 +346,12 @@ impl<'a> Sugg<'a> { if has_enclosing_paren(&sugg) { Sugg::MaybeParen(sugg) } else { - Sugg::NonParen(format!("({})", sugg).into()) + Sugg::NonParen(format!("({sugg})").into()) } }, Sugg::BinOp(op, lhs, rhs) => { let sugg = binop_to_string(op, &lhs, &rhs); - Sugg::NonParen(format!("({})", sugg).into()) + Sugg::NonParen(format!("({sugg})").into()) }, } } @@ -379,20 +379,18 @@ fn binop_to_string(op: AssocOp, lhs: &str, rhs: &str) -> String { | AssocOp::Greater | AssocOp::GreaterEqual => { format!( - "{} {} {}", - lhs, - op.to_ast_binop().expect("Those are AST ops").to_string(), - rhs + "{lhs} {} {rhs}", + op.to_ast_binop().expect("Those are AST ops").to_string() ) }, - AssocOp::Assign => format!("{} = {}", lhs, rhs), + AssocOp::Assign => format!("{lhs} = {rhs}"), AssocOp::AssignOp(op) => { - format!("{} {}= {}", lhs, token_kind_to_string(&token::BinOp(op)), rhs) + format!("{lhs} {}= {rhs}", token_kind_to_string(&token::BinOp(op))) }, - AssocOp::As => format!("{} as {}", lhs, rhs), - AssocOp::DotDot => format!("{}..{}", lhs, rhs), - AssocOp::DotDotEq => format!("{}..={}", lhs, rhs), - AssocOp::Colon => format!("{}: {}", lhs, rhs), + AssocOp::As => format!("{lhs} as {rhs}"), + AssocOp::DotDot => format!("{lhs}..{rhs}"), + AssocOp::DotDotEq => format!("{lhs}..={rhs}"), + AssocOp::Colon => format!("{lhs}: {rhs}"), } } @@ -523,7 +521,7 @@ impl Display for ParenHelper { /// operators have the same /// precedence. pub fn make_unop(op: &str, expr: Sugg<'_>) -> Sugg<'static> { - Sugg::MaybeParen(format!("{}{}", op, expr.maybe_par()).into()) + Sugg::MaybeParen(format!("{op}{}", expr.maybe_par()).into()) } /// Builds the string for ` ` adding parenthesis when necessary. @@ -744,7 +742,7 @@ impl DiagnosticExt for rustc_errors::Diagnostic { if let Some(indent) = indentation(cx, item) { let span = item.with_hi(item.lo()); - self.span_suggestion(span, msg, format!("{}\n{}", attr, indent), applicability); + self.span_suggestion(span, msg, format!("{attr}\n{indent}"), applicability); } } @@ -758,14 +756,14 @@ impl DiagnosticExt for rustc_errors::Diagnostic { .map(|l| { if first { first = false; - format!("{}\n", l) + format!("{l}\n") } else { - format!("{}{}\n", indent, l) + format!("{indent}{l}\n") } }) .collect::(); - self.span_suggestion(span, msg, format!("{}\n{}", new_item, indent), applicability); + self.span_suggestion(span, msg, format!("{new_item}\n{indent}"), applicability); } } @@ -863,7 +861,7 @@ impl<'tcx> DerefDelegate<'_, 'tcx> { pub fn finish(&mut self) -> String { let end_span = Span::new(self.next_pos, self.closure_span.hi(), self.closure_span.ctxt(), None); let end_snip = snippet_with_applicability(self.cx, end_span, "..", &mut self.applicability); - let sugg = format!("{}{}", self.suggestion_start, end_snip); + let sugg = format!("{}{end_snip}", self.suggestion_start); if self.closure_arg_is_type_annotated_double_ref { sugg.replacen('&', "", 1) } else { @@ -925,7 +923,7 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { if cmt.place.projections.is_empty() { // handle item without any projection, that needs an explicit borrowing // i.e.: suggest `&x` instead of `x` - let _ = write!(self.suggestion_start, "{}&{}", start_snip, ident_str); + let _ = write!(self.suggestion_start, "{start_snip}&{ident_str}"); } else { // cases where a parent `Call` or `MethodCall` is using the item // i.e.: suggest `.contains(&x)` for `.find(|x| [1, 2, 3].contains(x)).is_none()` @@ -940,7 +938,7 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { // given expression is the self argument and will be handled completely by the compiler // i.e.: `|x| x.is_something()` ExprKind::MethodCall(_, self_expr, ..) if self_expr.hir_id == cmt.hir_id => { - let _ = write!(self.suggestion_start, "{}{}", start_snip, ident_str_with_proj); + let _ = write!(self.suggestion_start, "{start_snip}{ident_str_with_proj}"); self.next_pos = span.hi(); return; }, @@ -973,9 +971,9 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { } else { ident_str }; - format!("{}{}", start_snip, ident) + format!("{start_snip}{ident}") } else { - format!("{}&{}", start_snip, ident_str) + format!("{start_snip}&{ident_str}") }; self.suggestion_start.push_str(&ident_sugg); self.next_pos = span.hi(); @@ -1042,13 +1040,13 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { for item in projections { if item.kind == ProjectionKind::Deref { - replacement_str = format!("*{}", replacement_str); + replacement_str = format!("*{replacement_str}"); } } } } - let _ = write!(self.suggestion_start, "{}{}", start_snip, replacement_str); + let _ = write!(self.suggestion_start, "{start_snip}{replacement_str}"); } self.next_pos = span.hi(); } @@ -1056,7 +1054,13 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { fn mutate(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {} - fn fake_read(&mut self, _: &rustc_hir_analysis::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} + fn fake_read( + &mut self, + _: &rustc_hir_analysis::expr_use_visitor::PlaceWithHirId<'tcx>, + _: FakeReadCause, + _: HirId, + ) { + } } #[cfg(test)] diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 56343880320d..934470bd135b 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -12,11 +12,11 @@ use rustc_hir::{Expr, FnDecl, LangItem, TyKind, Unsafety}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::mir::interpret::{ConstValue, Scalar}; -use rustc_middle::ty::{GenericArg, GenericArgKind}; use rustc_middle::ty::{ self, AdtDef, Binder, BoundRegion, DefIdTree, FnSig, IntTy, ParamEnv, Predicate, PredicateKind, ProjectionTy, Region, RegionKind, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, UintTy, VariantDef, VariantDiscr, }; +use rustc_middle::ty::{GenericArg, GenericArgKind}; use rustc_span::symbol::Ident; use rustc_span::{sym, Span, Symbol, DUMMY_SP}; use rustc_target::abi::{Size, VariantIdx}; diff --git a/clippy_utils/src/usage.rs b/clippy_utils/src/usage.rs index 76bfec75726d..b5ec3fef3e0b 100644 --- a/clippy_utils/src/usage.rs +++ b/clippy_utils/src/usage.rs @@ -1,15 +1,16 @@ use crate as utils; -use crate::visitors::{expr_visitor, expr_visitor_no_bodies}; +use crate::visitors::{for_each_expr, for_each_expr_with_closures, Descend}; +use core::ops::ControlFlow; use rustc_hir as hir; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::HirIdSet; use rustc_hir::{Expr, ExprKind, HirId, Node}; +use rustc_hir_analysis::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; use rustc_middle::mir::FakeReadCause; use rustc_middle::ty; -use rustc_hir_analysis::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; /// Returns a set of mutated local variable IDs, or `None` if mutations could not be determined. pub fn mutated_variables<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> Option { @@ -73,7 +74,13 @@ impl<'tcx> Delegate<'tcx> for MutVarsDelegate { self.update(cmt); } - fn fake_read(&mut self, _: &rustc_hir_analysis::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} + fn fake_read( + &mut self, + _: &rustc_hir_analysis::expr_use_visitor::PlaceWithHirId<'tcx>, + _: FakeReadCause, + _: HirId, + ) { + } } pub struct ParamBindingIdCollector { @@ -142,28 +149,17 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for BindingUsageFinder<'a, 'tcx> { } pub fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool { - let mut seen_return_break_continue = false; - expr_visitor_no_bodies(|ex| { - if seen_return_break_continue { - return false; - } - match &ex.kind { - ExprKind::Ret(..) | ExprKind::Break(..) | ExprKind::Continue(..) => { - seen_return_break_continue = true; - }, + for_each_expr(expression, |e| { + match e.kind { + ExprKind::Ret(..) | ExprKind::Break(..) | ExprKind::Continue(..) => ControlFlow::Break(()), // Something special could be done here to handle while or for loop // desugaring, as this will detect a break if there's a while loop // or a for loop inside the expression. - _ => { - if ex.span.from_expansion() { - seen_return_break_continue = true; - } - }, + _ if e.span.from_expansion() => ControlFlow::Break(()), + _ => ControlFlow::Continue(()), } - !seen_return_break_continue }) - .visit_expr(expression); - seen_return_break_continue + .is_some() } pub fn local_used_after_expr(cx: &LateContext<'_>, local_id: HirId, after: &Expr<'_>) -> bool { @@ -194,23 +190,16 @@ pub fn local_used_after_expr(cx: &LateContext<'_>, local_id: HirId, after: &Expr return true; } - let mut used_after_expr = false; let mut past_expr = false; - expr_visitor(cx, |expr| { - if used_after_expr { - return false; - } - - if expr.hir_id == after.hir_id { + for_each_expr_with_closures(cx, block, |e| { + if e.hir_id == after.hir_id { past_expr = true; - return false; + ControlFlow::Continue(Descend::No) + } else if past_expr && utils::path_to_local_id(e, local_id) { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(Descend::Yes) } - - if past_expr && utils::path_to_local_id(expr, local_id) { - used_after_expr = true; - } - !used_after_expr }) - .visit_block(block); - used_after_expr + .is_some() } diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs index 232d571902b6..d4294f18fd50 100644 --- a/clippy_utils/src/visitors.rs +++ b/clippy_utils/src/visitors.rs @@ -5,14 +5,13 @@ use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::intravisit::{self, walk_block, walk_expr, Visitor}; use rustc_hir::{ - Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, Let, Pat, QPath, Stmt, UnOp, - UnsafeSource, Unsafety, + AnonConst, Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, Let, Pat, QPath, + Stmt, UnOp, UnsafeSource, Unsafety, }; use rustc_lint::LateContext; -use rustc_middle::hir::map::Map; use rustc_middle::hir::nested_filter; use rustc_middle::ty::adjustment::Adjust; -use rustc_middle::ty::{self, Ty, TypeckResults}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeckResults}; use rustc_span::Span; mod internal { @@ -48,6 +47,26 @@ impl Continue for Descend { } } +/// A type which can be visited. +pub trait Visitable<'tcx> { + /// Calls the corresponding `visit_*` function on the visitor. + fn visit>(self, visitor: &mut V); +} +macro_rules! visitable_ref { + ($t:ident, $f:ident) => { + impl<'tcx> Visitable<'tcx> for &'tcx $t<'tcx> { + fn visit>(self, visitor: &mut V) { + visitor.$f(self); + } + } + }; +} +visitable_ref!(Arm, visit_arm); +visitable_ref!(Block, visit_block); +visitable_ref!(Body, visit_body); +visitable_ref!(Expr, visit_expr); +visitable_ref!(Stmt, visit_stmt); + /// Calls the given function once for each expression contained. This does not enter any bodies or /// nested items. pub fn for_each_expr<'tcx, B, C: Continue>( @@ -82,57 +101,63 @@ pub fn for_each_expr<'tcx, B, C: Continue>( v.res } -/// Convenience method for creating a `Visitor` with just `visit_expr` overridden and nested -/// bodies (i.e. closures) are visited. -/// If the callback returns `true`, the expr just provided to the callback is walked. -#[must_use] -pub fn expr_visitor<'tcx>(cx: &LateContext<'tcx>, f: impl FnMut(&'tcx Expr<'tcx>) -> bool) -> impl Visitor<'tcx> { - struct V<'tcx, F> { - hir: Map<'tcx>, +/// Calls the given function once for each expression contained. This will enter bodies, but not +/// nested items. +pub fn for_each_expr_with_closures<'tcx, B, C: Continue>( + cx: &LateContext<'tcx>, + node: impl Visitable<'tcx>, + f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow, +) -> Option { + struct V<'tcx, B, F> { + tcx: TyCtxt<'tcx>, f: F, + res: Option, } - impl<'tcx, F: FnMut(&'tcx Expr<'tcx>) -> bool> Visitor<'tcx> for V<'tcx, F> { + impl<'tcx, B, C: Continue, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow> Visitor<'tcx> for V<'tcx, B, F> { type NestedFilter = nested_filter::OnlyBodies; fn nested_visit_map(&mut self) -> Self::Map { - self.hir + self.tcx.hir() } - fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { - if (self.f)(expr) { - walk_expr(self, expr); + fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) { + if self.res.is_some() { + return; + } + match (self.f)(e) { + ControlFlow::Continue(c) if c.descend() => walk_expr(self, e), + ControlFlow::Break(b) => self.res = Some(b), + ControlFlow::Continue(_) => (), } } - } - V { hir: cx.tcx.hir(), f } -} -/// Convenience method for creating a `Visitor` with just `visit_expr` overridden and nested -/// bodies (i.e. closures) are not visited. -/// If the callback returns `true`, the expr just provided to the callback is walked. -#[must_use] -pub fn expr_visitor_no_bodies<'tcx>(f: impl FnMut(&'tcx Expr<'tcx>) -> bool) -> impl Visitor<'tcx> { - struct V(F); - impl<'tcx, F: FnMut(&'tcx Expr<'tcx>) -> bool> Visitor<'tcx> for V { - fn visit_expr(&mut self, e: &'tcx Expr<'_>) { - if (self.0)(e) { - walk_expr(self, e); - } - } + // Only walk closures + fn visit_anon_const(&mut self, _: &'tcx AnonConst) {} + // Avoid unnecessary `walk_*` calls. + fn visit_ty(&mut self, _: &'tcx hir::Ty<'tcx>) {} + fn visit_pat(&mut self, _: &'tcx Pat<'tcx>) {} + fn visit_qpath(&mut self, _: &'tcx QPath<'tcx>, _: HirId, _: Span) {} + // Avoid monomorphising all `visit_*` functions. + fn visit_nested_item(&mut self, _: ItemId) {} } - V(f) + let mut v = V { + tcx: cx.tcx, + f, + res: None, + }; + node.visit(&mut v); + v.res } /// returns `true` if expr contains match expr desugared from try fn contains_try(expr: &hir::Expr<'_>) -> bool { - let mut found = false; - expr_visitor_no_bodies(|e| { - if !found { - found = matches!(e.kind, hir::ExprKind::Match(_, _, hir::MatchSource::TryDesugar)); + for_each_expr(expr, |e| { + if matches!(e.kind, hir::ExprKind::Match(_, _, hir::MatchSource::TryDesugar)) { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) } - !found }) - .visit_expr(expr); - found + .is_some() } pub fn find_all_ret_expressions<'hir, F>(_cx: &LateContext<'_>, expr: &'hir hir::Expr<'hir>, callback: F) -> bool @@ -228,68 +253,29 @@ where } } -/// A type which can be visited. -pub trait Visitable<'tcx> { - /// Calls the corresponding `visit_*` function on the visitor. - fn visit>(self, visitor: &mut V); -} -macro_rules! visitable_ref { - ($t:ident, $f:ident) => { - impl<'tcx> Visitable<'tcx> for &'tcx $t<'tcx> { - fn visit>(self, visitor: &mut V) { - visitor.$f(self); - } - } - }; -} -visitable_ref!(Arm, visit_arm); -visitable_ref!(Block, visit_block); -visitable_ref!(Body, visit_body); -visitable_ref!(Expr, visit_expr); -visitable_ref!(Stmt, visit_stmt); - -// impl<'tcx, I: IntoIterator> Visitable<'tcx> for I -// where -// I::Item: Visitable<'tcx>, -// { -// fn visit>(self, visitor: &mut V) { -// for x in self { -// x.visit(visitor); -// } -// } -// } - /// Checks if the given resolved path is used in the given body. pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool { - let mut found = false; - expr_visitor(cx, |e| { - if found { - return false; - } - + for_each_expr_with_closures(cx, cx.tcx.hir().body(body).value, |e| { if let ExprKind::Path(p) = &e.kind { if cx.qpath_res(p, e.hir_id) == res { - found = true; + return ControlFlow::Break(()); } } - !found + ControlFlow::Continue(()) }) - .visit_expr(cx.tcx.hir().body(body).value); - found + .is_some() } /// Checks if the given local is used. pub fn is_local_used<'tcx>(cx: &LateContext<'tcx>, visitable: impl Visitable<'tcx>, id: HirId) -> bool { - let mut is_used = false; - let mut visitor = expr_visitor(cx, |expr| { - if !is_used { - is_used = path_to_local_id(expr, id); + for_each_expr_with_closures(cx, visitable, |e| { + if path_to_local_id(e, id) { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) } - !is_used - }); - visitable.visit(&mut visitor); - drop(visitor); - is_used + }) + .is_some() } /// Checks if the given expression is a constant. diff --git a/lintcheck/Cargo.toml b/lintcheck/Cargo.toml index 737c845c0451..de31c16b819e 100644 --- a/lintcheck/Cargo.toml +++ b/lintcheck/Cargo.toml @@ -12,9 +12,11 @@ publish = false [dependencies] cargo_metadata = "0.14" clap = "3.2" +crossbeam-channel = "0.5.6" flate2 = "1.0" rayon = "1.5.1" serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0.85" tar = "0.4" toml = "0.5" ureq = "2.2" diff --git a/lintcheck/README.md b/lintcheck/README.md index 6f3d23382ce1..6142de5e3130 100644 --- a/lintcheck/README.md +++ b/lintcheck/README.md @@ -69,9 +69,27 @@ is checked. is explicitly specified in the options. ### Fix mode -You can run `./lintcheck/target/debug/lintcheck --fix` which will run Clippy with `--fix` and +You can run `cargo lintcheck --fix` which will run Clippy with `--fix` and print a warning if Clippy's suggestions fail to apply (if the resulting code does not build). This lets us spot bad suggestions or false positives automatically in some cases. Please note that the target dir should be cleaned afterwards since clippy will modify the downloaded sources which can lead to unexpected results when running lintcheck again afterwards. + +### Recursive mode +You can run `cargo lintcheck --recursive` to also run Clippy on the dependencies +of the crates listed in the crates source `.toml`. e.g. adding `rand 0.8.5` +would also lint `rand_core`, `rand_chacha`, etc. + +Particularly slow crates in the dependency graph can be ignored using +`recursive.ignore`: + +```toml +[crates] +cargo = {name = "cargo", versions = ['0.64.0']} + +[recursive] +ignore = [ + "unicode-normalization", +] +``` diff --git a/lintcheck/lintcheck_crates.toml b/lintcheck/lintcheck_crates.toml index ebbe9c9ae675..52f7fee47b61 100644 --- a/lintcheck/lintcheck_crates.toml +++ b/lintcheck/lintcheck_crates.toml @@ -33,3 +33,11 @@ cfg-expr = {name = "cfg-expr", versions = ['0.7.1']} puffin = {name = "puffin", git_url = "https://github.com/EmbarkStudios/puffin", git_hash = "02dd4a3"} rpmalloc = {name = "rpmalloc", versions = ['0.2.0']} tame-oidc = {name = "tame-oidc", versions = ['0.1.0']} + +[recursive] +ignore = [ + # Takes ~30s to lint + "combine", + # Has 1.2 million `clippy::match_same_arms`s + "unicode-normalization", +] diff --git a/lintcheck/src/config.rs b/lintcheck/src/config.rs index 1742cf677c0f..b344db634f61 100644 --- a/lintcheck/src/config.rs +++ b/lintcheck/src/config.rs @@ -34,11 +34,16 @@ fn get_clap_config() -> ArgMatches { Arg::new("markdown") .long("markdown") .help("Change the reports table to use markdown links"), + Arg::new("recursive") + .long("--recursive") + .help("Run clippy on the dependencies of crates specified in crates-toml") + .conflicts_with("threads") + .conflicts_with("fix"), ]) .get_matches() } -#[derive(Debug)] +#[derive(Debug, Clone)] pub(crate) struct LintcheckConfig { /// max number of jobs to spawn (default 1) pub max_jobs: usize, @@ -54,6 +59,8 @@ pub(crate) struct LintcheckConfig { pub lint_filter: Vec, /// Indicate if the output should support markdown syntax pub markdown: bool, + /// Run clippy on the dependencies of crates + pub recursive: bool, } impl LintcheckConfig { @@ -119,6 +126,7 @@ impl LintcheckConfig { fix: clap_config.contains_id("fix"), lint_filter, markdown, + recursive: clap_config.contains_id("recursive"), } } } diff --git a/lintcheck/src/driver.rs b/lintcheck/src/driver.rs new file mode 100644 index 000000000000..63221bab32d3 --- /dev/null +++ b/lintcheck/src/driver.rs @@ -0,0 +1,67 @@ +use crate::recursive::{deserialize_line, serialize_line, DriverInfo}; + +use std::io::{self, BufReader, Write}; +use std::net::TcpStream; +use std::process::{self, Command, Stdio}; +use std::{env, mem}; + +/// 1. Sends [DriverInfo] to the [crate::recursive::LintcheckServer] running on `addr` +/// 2. Receives [bool] from the server, if `false` returns `None` +/// 3. Otherwise sends the stderr of running `clippy-driver` to the server +fn run_clippy(addr: &str) -> Option { + let driver_info = DriverInfo { + package_name: env::var("CARGO_PKG_NAME").ok()?, + crate_name: env::var("CARGO_CRATE_NAME").ok()?, + version: env::var("CARGO_PKG_VERSION").ok()?, + }; + + let mut stream = BufReader::new(TcpStream::connect(addr).unwrap()); + + serialize_line(&driver_info, stream.get_mut()); + + let should_run = deserialize_line::(&mut stream); + if !should_run { + return None; + } + + // Remove --cap-lints allow so that clippy runs and lints are emitted + let mut include_next = true; + let args = env::args().skip(1).filter(|arg| match arg.as_str() { + "--cap-lints=allow" => false, + "--cap-lints" => { + include_next = false; + false + }, + _ => mem::replace(&mut include_next, true), + }); + + let output = Command::new(env::var("CLIPPY_DRIVER").expect("missing env CLIPPY_DRIVER")) + .args(args) + .stdout(Stdio::inherit()) + .output() + .expect("failed to run clippy-driver"); + + stream + .get_mut() + .write_all(&output.stderr) + .unwrap_or_else(|e| panic!("{e:?} in {driver_info:?}")); + + match output.status.code() { + Some(0) => Some(0), + code => { + io::stderr().write_all(&output.stderr).unwrap(); + Some(code.expect("killed by signal")) + }, + } +} + +pub fn drive(addr: &str) { + process::exit(run_clippy(addr).unwrap_or_else(|| { + Command::new("rustc") + .args(env::args_os().skip(2)) + .status() + .unwrap() + .code() + .unwrap() + })) +} diff --git a/lintcheck/src/main.rs b/lintcheck/src/main.rs index 9ee25280f046..cc2b3e1acec7 100644 --- a/lintcheck/src/main.rs +++ b/lintcheck/src/main.rs @@ -8,13 +8,17 @@ #![allow(clippy::collapsible_else_if)] mod config; +mod driver; +mod recursive; -use config::LintcheckConfig; +use crate::config::LintcheckConfig; +use crate::recursive::LintcheckServer; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::env; +use std::env::consts::EXE_SUFFIX; use std::fmt::Write as _; -use std::fs::write; +use std::fs; use std::io::ErrorKind; use std::path::{Path, PathBuf}; use std::process::Command; @@ -22,22 +26,12 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use std::thread; use std::time::Duration; -use cargo_metadata::diagnostic::DiagnosticLevel; +use cargo_metadata::diagnostic::{Diagnostic, DiagnosticLevel}; use cargo_metadata::Message; use rayon::prelude::*; use serde::{Deserialize, Serialize}; use walkdir::{DirEntry, WalkDir}; -#[cfg(not(windows))] -const CLIPPY_DRIVER_PATH: &str = "target/debug/clippy-driver"; -#[cfg(not(windows))] -const CARGO_CLIPPY_PATH: &str = "target/debug/cargo-clippy"; - -#[cfg(windows)] -const CLIPPY_DRIVER_PATH: &str = "target/debug/clippy-driver.exe"; -#[cfg(windows)] -const CARGO_CLIPPY_PATH: &str = "target/debug/cargo-clippy.exe"; - const LINTCHECK_DOWNLOADS: &str = "target/lintcheck/downloads"; const LINTCHECK_SOURCES: &str = "target/lintcheck/sources"; @@ -45,6 +39,13 @@ const LINTCHECK_SOURCES: &str = "target/lintcheck/sources"; #[derive(Debug, Serialize, Deserialize)] struct SourceList { crates: HashMap, + #[serde(default)] + recursive: RecursiveOptions, +} + +#[derive(Debug, Serialize, Deserialize, Default)] +struct RecursiveOptions { + ignore: HashSet, } /// A crate source stored inside the .toml @@ -105,12 +106,7 @@ struct ClippyWarning { #[allow(unused)] impl ClippyWarning { - fn new(cargo_message: Message, krate: &Crate) -> Option { - let diag = match cargo_message { - Message::CompilerMessage(message) => message.message, - _ => return None, - }; - + fn new(diag: Diagnostic, crate_name: &str, crate_version: &str) -> Option { let lint_type = diag.code?.code; if !(lint_type.contains("clippy") || diag.message.contains("clippy")) || diag.message.contains("could not read cargo metadata") @@ -124,12 +120,12 @@ impl ClippyWarning { Ok(stripped) => format!("$CARGO_HOME/{}", stripped.display()), Err(_) => format!( "target/lintcheck/sources/{}-{}/{}", - krate.name, krate.version, span.file_name + crate_name, crate_version, span.file_name ), }; Some(Self { - crate_name: krate.name.clone(), + crate_name: crate_name.to_owned(), file, line: span.line_start, column: span.column_start, @@ -142,8 +138,6 @@ impl ClippyWarning { fn to_output(&self, markdown: bool) -> String { let file_with_pos = format!("{}:{}:{}", &self.file, &self.line, &self.column); if markdown { - let lint = format!("`{}`", self.lint_type); - let mut file = self.file.clone(); if !file.starts_with('$') { file.insert_str(0, "../"); @@ -151,7 +145,7 @@ impl ClippyWarning { let mut output = String::from("| "); let _ = write!(output, "[`{}`]({}#L{})", file_with_pos, file, self.line); - let _ = write!(output, r#" | {:<50} | "{}" |"#, lint, self.message); + let _ = write!(output, r#" | `{:<50}` | "{}" |"#, self.lint_type, self.message); output.push('\n'); output } else { @@ -243,6 +237,7 @@ impl CrateSource { } // check out the commit/branch/whatever if !Command::new("git") + .args(["-c", "advice.detachedHead=false"]) .arg("checkout") .arg(commit) .current_dir(&repo_path) @@ -309,10 +304,12 @@ impl Crate { fn run_clippy_lints( &self, cargo_clippy_path: &Path, + clippy_driver_path: &Path, target_dir_index: &AtomicUsize, total_crates_to_lint: usize, config: &LintcheckConfig, lint_filter: &Vec, + server: &Option, ) -> Vec { // advance the atomic index by one let index = target_dir_index.fetch_add(1, Ordering::SeqCst); @@ -336,36 +333,67 @@ impl Crate { let shared_target_dir = clippy_project_root().join("target/lintcheck/shared_target_dir"); - let mut args = if config.fix { + let mut cargo_clippy_args = if config.fix { vec!["--fix", "--"] } else { vec!["--", "--message-format=json", "--"] }; + let mut clippy_args = Vec::<&str>::new(); if let Some(options) = &self.options { for opt in options { - args.push(opt); + clippy_args.push(opt); } } else { - args.extend(&["-Wclippy::pedantic", "-Wclippy::cargo"]) + clippy_args.extend(&["-Wclippy::pedantic", "-Wclippy::cargo"]) } if lint_filter.is_empty() { - args.push("--cap-lints=warn"); + clippy_args.push("--cap-lints=warn"); } else { - args.push("--cap-lints=allow"); - args.extend(lint_filter.iter().map(|filter| filter.as_str())) + clippy_args.push("--cap-lints=allow"); + clippy_args.extend(lint_filter.iter().map(|filter| filter.as_str())) } - let all_output = std::process::Command::new(&cargo_clippy_path) + if let Some(server) = server { + let target = shared_target_dir.join("recursive"); + + // `cargo clippy` is a wrapper around `cargo check` that mainly sets `RUSTC_WORKSPACE_WRAPPER` to + // `clippy-driver`. We do the same thing here with a couple changes: + // + // `RUSTC_WRAPPER` is used instead of `RUSTC_WORKSPACE_WRAPPER` so that we can lint all crate + // dependencies rather than only workspace members + // + // The wrapper is set to the `lintcheck` so we can force enable linting and ignore certain crates + // (see `crate::driver`) + let status = Command::new("cargo") + .arg("check") + .arg("--quiet") + .current_dir(&self.path) + .env("CLIPPY_ARGS", clippy_args.join("__CLIPPY_HACKERY__")) + .env("CARGO_TARGET_DIR", target) + .env("RUSTC_WRAPPER", env::current_exe().unwrap()) + // Pass the absolute path so `crate::driver` can find `clippy-driver`, as it's executed in various + // different working directories + .env("CLIPPY_DRIVER", clippy_driver_path) + .env("LINTCHECK_SERVER", server.local_addr.to_string()) + .status() + .expect("failed to run cargo"); + + assert_eq!(status.code(), Some(0)); + + return Vec::new(); + } + + cargo_clippy_args.extend(clippy_args); + + let all_output = Command::new(&cargo_clippy_path) // use the looping index to create individual target dirs .env( "CARGO_TARGET_DIR", shared_target_dir.join(format!("_{:?}", thread_index)), ) - // lint warnings will look like this: - // src/cargo/ops/cargo_compile.rs:127:35: warning: usage of `FromIterator::from_iter` - .args(&args) + .args(&cargo_clippy_args) .current_dir(&self.path) .output() .unwrap_or_else(|error| { @@ -404,7 +432,10 @@ impl Crate { // get all clippy warnings and ICEs let warnings: Vec = Message::parse_stream(stdout.as_bytes()) - .filter_map(|msg| ClippyWarning::new(msg.unwrap(), &self)) + .filter_map(|msg| match msg { + Ok(Message::CompilerMessage(message)) => ClippyWarning::new(message.message, &self.name, &self.version), + _ => None, + }) .collect(); warnings @@ -423,8 +454,8 @@ fn build_clippy() { } } -/// Read a `toml` file and return a list of `CrateSources` that we want to check with clippy -fn read_crates(toml_path: &Path) -> Vec { +/// Read a `lintcheck_crates.toml` file +fn read_crates(toml_path: &Path) -> (Vec, RecursiveOptions) { let toml_content: String = std::fs::read_to_string(&toml_path).unwrap_or_else(|_| panic!("Failed to read {}", toml_path.display())); let crate_list: SourceList = @@ -484,7 +515,7 @@ fn read_crates(toml_path: &Path) -> Vec { // sort the crates crate_sources.sort(); - crate_sources + (crate_sources, crate_list.recursive) } /// Generate a short list of occurring lints-types and their count @@ -516,20 +547,20 @@ fn gather_stats(clippy_warnings: &[ClippyWarning]) -> (String, HashMap<&String, /// check if the latest modification of the logfile is older than the modification date of the /// clippy binary, if this is true, we should clean the lintchec shared target directory and recheck -fn lintcheck_needs_rerun(lintcheck_logs_path: &Path) -> bool { +fn lintcheck_needs_rerun(lintcheck_logs_path: &Path, paths: [&Path; 2]) -> bool { if !lintcheck_logs_path.exists() { return true; } let clippy_modified: std::time::SystemTime = { - let mut times = [CLIPPY_DRIVER_PATH, CARGO_CLIPPY_PATH].iter().map(|p| { + let [cargo, driver] = paths.map(|p| { std::fs::metadata(p) .expect("failed to get metadata of file") .modified() .expect("failed to get modification date") }); // the oldest modification of either of the binaries - std::cmp::max(times.next().unwrap(), times.next().unwrap()) + std::cmp::max(cargo, driver) }; let logs_modified: std::time::SystemTime = std::fs::metadata(lintcheck_logs_path) @@ -543,6 +574,11 @@ fn lintcheck_needs_rerun(lintcheck_logs_path: &Path) -> bool { } fn main() { + // We're being executed as a `RUSTC_WRAPPER` as part of `--recursive` + if let Ok(addr) = env::var("LINTCHECK_SERVER") { + driver::drive(&addr); + } + // assert that we launch lintcheck from the repo root (via cargo lintcheck) if std::fs::metadata("lintcheck/Cargo.toml").is_err() { eprintln!("lintcheck needs to be run from clippy's repo root!\nUse `cargo lintcheck` alternatively."); @@ -555,9 +591,15 @@ fn main() { build_clippy(); println!("Done compiling"); + let cargo_clippy_path = fs::canonicalize(format!("target/debug/cargo-clippy{EXE_SUFFIX}")).unwrap(); + let clippy_driver_path = fs::canonicalize(format!("target/debug/clippy-driver{EXE_SUFFIX}")).unwrap(); + // if the clippy bin is newer than our logs, throw away target dirs to force clippy to // refresh the logs - if lintcheck_needs_rerun(&config.lintcheck_results_path) { + if lintcheck_needs_rerun( + &config.lintcheck_results_path, + [&cargo_clippy_path, &clippy_driver_path], + ) { let shared_target_dir = "target/lintcheck/shared_target_dir"; // if we get an Err here, the shared target dir probably does simply not exist if let Ok(metadata) = std::fs::metadata(&shared_target_dir) { @@ -569,10 +611,6 @@ fn main() { } } - let cargo_clippy_path: PathBuf = PathBuf::from(CARGO_CLIPPY_PATH) - .canonicalize() - .expect("failed to canonicalize path to clippy binary"); - // assert that clippy is found assert!( cargo_clippy_path.is_file(), @@ -580,7 +618,7 @@ fn main() { cargo_clippy_path.display() ); - let clippy_ver = std::process::Command::new(CARGO_CLIPPY_PATH) + let clippy_ver = std::process::Command::new(&cargo_clippy_path) .arg("--version") .output() .map(|o| String::from_utf8_lossy(&o.stdout).into_owned()) @@ -589,7 +627,7 @@ fn main() { // download and extract the crates, then run clippy on them and collect clippy's warnings // flatten into one big list of warnings - let crates = read_crates(&config.sources_toml_path); + let (crates, recursive_options) = read_crates(&config.sources_toml_path); let old_stats = read_stats_from_file(&config.lintcheck_results_path); let counter = AtomicUsize::new(1); @@ -639,11 +677,31 @@ fn main() { .build_global() .unwrap(); - let clippy_warnings: Vec = crates + let server = config.recursive.then(|| { + let _ = fs::remove_dir_all("target/lintcheck/shared_target_dir/recursive"); + + LintcheckServer::spawn(recursive_options) + }); + + let mut clippy_warnings: Vec = crates .par_iter() - .flat_map(|krate| krate.run_clippy_lints(&cargo_clippy_path, &counter, crates.len(), &config, &lint_filter)) + .flat_map(|krate| { + krate.run_clippy_lints( + &cargo_clippy_path, + &clippy_driver_path, + &counter, + crates.len(), + &config, + &lint_filter, + &server, + ) + }) .collect(); + if let Some(server) = server { + clippy_warnings.extend(server.warnings()); + } + // if we are in --fix mode, don't change the log files, terminate here if config.fix { return; @@ -681,8 +739,8 @@ fn main() { } println!("Writing logs to {}", config.lintcheck_results_path.display()); - std::fs::create_dir_all(config.lintcheck_results_path.parent().unwrap()).unwrap(); - write(&config.lintcheck_results_path, text).unwrap(); + fs::create_dir_all(config.lintcheck_results_path.parent().unwrap()).unwrap(); + fs::write(&config.lintcheck_results_path, text).unwrap(); print_stats(old_stats, new_stats, &config.lint_filter); } diff --git a/lintcheck/src/recursive.rs b/lintcheck/src/recursive.rs new file mode 100644 index 000000000000..67dcfc2b199c --- /dev/null +++ b/lintcheck/src/recursive.rs @@ -0,0 +1,123 @@ +//! In `--recursive` mode we set the `lintcheck` binary as the `RUSTC_WRAPPER` of `cargo check`, +//! this allows [crate::driver] to be run for every dependency. The driver connects to +//! [LintcheckServer] to ask if it should be skipped, and if not sends the stderr of running clippy +//! on the crate to the server + +use crate::ClippyWarning; +use crate::RecursiveOptions; + +use std::collections::HashSet; +use std::io::{BufRead, BufReader, Read, Write}; +use std::net::{SocketAddr, TcpListener, TcpStream}; +use std::sync::{Arc, Mutex}; +use std::thread; + +use cargo_metadata::diagnostic::Diagnostic; +use crossbeam_channel::{Receiver, Sender}; +use serde::de::DeserializeOwned; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Eq, Hash, PartialEq, Clone, Serialize, Deserialize)] +pub(crate) struct DriverInfo { + pub package_name: String, + pub crate_name: String, + pub version: String, +} + +pub(crate) fn serialize_line(value: &T, writer: &mut W) +where + T: Serialize, + W: Write, +{ + let mut buf = serde_json::to_vec(&value).expect("failed to serialize"); + buf.push(b'\n'); + writer.write_all(&buf).expect("write_all failed"); +} + +pub(crate) fn deserialize_line(reader: &mut R) -> T +where + T: DeserializeOwned, + R: BufRead, +{ + let mut string = String::new(); + reader.read_line(&mut string).expect("read_line failed"); + serde_json::from_str(&string).expect("failed to deserialize") +} + +fn process_stream( + stream: TcpStream, + sender: &Sender, + options: &RecursiveOptions, + seen: &Mutex>, +) { + let mut stream = BufReader::new(stream); + + let driver_info: DriverInfo = deserialize_line(&mut stream); + + let unseen = seen.lock().unwrap().insert(driver_info.clone()); + let ignored = options.ignore.contains(&driver_info.package_name); + let should_run = unseen && !ignored; + + serialize_line(&should_run, stream.get_mut()); + + let mut stderr = String::new(); + stream.read_to_string(&mut stderr).unwrap(); + + let messages = stderr + .lines() + .filter_map(|json_msg| serde_json::from_str::(json_msg).ok()) + .filter_map(|diag| ClippyWarning::new(diag, &driver_info.package_name, &driver_info.version)); + + for message in messages { + sender.send(message).unwrap(); + } +} + +pub(crate) struct LintcheckServer { + pub local_addr: SocketAddr, + receiver: Receiver, + sender: Arc>, +} + +impl LintcheckServer { + pub fn spawn(options: RecursiveOptions) -> Self { + let listener = TcpListener::bind("localhost:0").unwrap(); + let local_addr = listener.local_addr().unwrap(); + + let (sender, receiver) = crossbeam_channel::unbounded::(); + let sender = Arc::new(sender); + // The spawned threads hold a `Weak` so that they don't keep the channel connected + // indefinitely + let sender_weak = Arc::downgrade(&sender); + + // Ignore dependencies multiple times, e.g. for when it's both checked and compiled for a + // build dependency + let seen = Mutex::default(); + + thread::spawn(move || { + thread::scope(|s| { + s.spawn(|| { + while let Ok((stream, _)) = listener.accept() { + let sender = sender_weak.upgrade().expect("received connection after server closed"); + let options = &options; + let seen = &seen; + s.spawn(move || process_stream(stream, &sender, options, seen)); + } + }); + }); + }); + + Self { + local_addr, + sender, + receiver, + } + } + + pub fn warnings(self) -> impl Iterator { + // causes the channel to become disconnected so that the receiver iterator ends + drop(self.sender); + + self.receiver.into_iter() + } +} diff --git a/rust-toolchain b/rust-toolchain index b6976366dafc..49b13cb54e71 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2022-09-08" +channel = "nightly-2022-10-06" components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/rustc_tools_util/Cargo.toml b/rustc_tools_util/Cargo.toml index 9554d4d6c003..89c3d6aaa89e 100644 --- a/rustc_tools_util/Cargo.toml +++ b/rustc_tools_util/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rustc_tools_util" -version = "0.2.0" +version = "0.2.1" description = "small helper to generate version information for git packages" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/rustc_tools_util/README.md b/rustc_tools_util/README.md index 01891b51d3b0..e947f9c7e66e 100644 --- a/rustc_tools_util/README.md +++ b/rustc_tools_util/README.md @@ -6,17 +6,17 @@ for packages installed from a git repo ## Usage Add a `build.rs` file to your repo and list it in `Cargo.toml` -```` +````toml build = "build.rs" ```` List rustc_tools_util as regular AND build dependency. -```` +````toml [dependencies] -rustc_tools_util = "0.1" +rustc_tools_util = "0.2.1" [build-dependencies] -rustc_tools_util = "0.1" +rustc_tools_util = "0.2.1" ```` In `build.rs`, generate the data in your `main()` diff --git a/rustc_tools_util/src/lib.rs b/rustc_tools_util/src/lib.rs index 429dddc42ea9..01d25c53126f 100644 --- a/rustc_tools_util/src/lib.rs +++ b/rustc_tools_util/src/lib.rs @@ -48,8 +48,8 @@ impl std::fmt::Display for VersionInfo { if (hash_trimmed.len() + date_trimmed.len()) > 0 { write!( f, - "{} {}.{}.{} ({} {})", - self.crate_name, self.major, self.minor, self.patch, hash_trimmed, date_trimmed, + "{} {}.{}.{} ({hash_trimmed} {date_trimmed})", + self.crate_name, self.major, self.minor, self.patch, )?; } else { write!(f, "{} {}.{}.{}", self.crate_name, self.major, self.minor, self.patch)?; @@ -137,7 +137,7 @@ mod test { let vi = get_version_info!(); assert_eq!(vi.major, 0); assert_eq!(vi.minor, 2); - assert_eq!(vi.patch, 0); + assert_eq!(vi.patch, 1); assert_eq!(vi.crate_name, "rustc_tools_util"); // hard to make positive tests for these since they will always change assert!(vi.commit_hash.is_none()); @@ -147,16 +147,16 @@ mod test { #[test] fn test_display_local() { let vi = get_version_info!(); - assert_eq!(vi.to_string(), "rustc_tools_util 0.2.0"); + assert_eq!(vi.to_string(), "rustc_tools_util 0.2.1"); } #[test] fn test_debug_local() { let vi = get_version_info!(); - let s = format!("{:?}", vi); + let s = format!("{vi:?}"); assert_eq!( s, - "VersionInfo { crate_name: \"rustc_tools_util\", major: 0, minor: 2, patch: 0 }" + "VersionInfo { crate_name: \"rustc_tools_util\", major: 0, minor: 2, patch: 1 }" ); } } diff --git a/src/docs.rs b/src/docs.rs index 6c89b4dde372..3bf488ab4779 100644 --- a/src/docs.rs +++ b/src/docs.rs @@ -48,6 +48,7 @@ docs! { "borrow_interior_mutable_const", "borrowed_box", "box_collection", + "box_default", "boxed_local", "branches_sharing_code", "builtin_type_shadow", @@ -105,6 +106,7 @@ docs! { "derive_hash_xor_eq", "derive_ord_xor_partial_ord", "derive_partial_eq_without_eq", + "disallowed_macros", "disallowed_methods", "disallowed_names", "disallowed_script_idents", @@ -190,6 +192,7 @@ docs! { "implicit_clone", "implicit_hasher", "implicit_return", + "implicit_saturating_add", "implicit_saturating_sub", "imprecise_flops", "inconsistent_digit_grouping", @@ -254,6 +257,7 @@ docs! { "manual_assert", "manual_async_fn", "manual_bits", + "manual_clamp", "manual_filter_map", "manual_find", "manual_find_map", @@ -521,6 +525,7 @@ docs! { "unimplemented", "uninit_assumed_init", "uninit_vec", + "uninlined_format_args", "unit_arg", "unit_cmp", "unit_hash", diff --git a/src/docs/arithmetic_side_effects.txt b/src/docs/arithmetic_side_effects.txt index 6c7d51a4989e..4ae8bce88ad5 100644 --- a/src/docs/arithmetic_side_effects.txt +++ b/src/docs/arithmetic_side_effects.txt @@ -5,7 +5,7 @@ Operators like `+`, `-`, `*` or `<<` are usually capable of overflowing accordin Reference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#overflow), or can panic (`/`, `%`). -Known safe built-in types like `Wrapping` or `Saturing`, floats, operations in constant +Known safe built-in types like `Wrapping` or `Saturating`, floats, operations in constant environments, allowed types and non-constant operations that won't overflow are ignored. ### Why is this bad? diff --git a/src/docs/box_default.txt b/src/docs/box_default.txt new file mode 100644 index 000000000000..ffac894d0c50 --- /dev/null +++ b/src/docs/box_default.txt @@ -0,0 +1,23 @@ +### What it does +checks for `Box::new(T::default())`, which is better written as +`Box::::default()`. + +### Why is this bad? +First, it's more complex, involving two calls instead of one. +Second, `Box::default()` can be faster +[in certain cases](https://nnethercote.github.io/perf-book/standard-library-types.html#box). + +### Known problems +The lint may miss some cases (e.g. Box::new(String::from(""))). +On the other hand, it will trigger on cases where the `default` +code comes from a macro that does something different based on +e.g. target operating system. + +### Example +``` +let x: Box = Box::new(Default::default()); +``` +Use instead: +``` +let x: Box = Box::default(); +``` \ No newline at end of file diff --git a/src/docs/disallowed_macros.txt b/src/docs/disallowed_macros.txt new file mode 100644 index 000000000000..96fa15afabfd --- /dev/null +++ b/src/docs/disallowed_macros.txt @@ -0,0 +1,36 @@ +### What it does +Denies the configured macros in clippy.toml + +Note: Even though this lint is warn-by-default, it will only trigger if +macros are defined in the clippy.toml file. + +### Why is this bad? +Some macros are undesirable in certain contexts, and it's beneficial to +lint for them as needed. + +### Example +An example clippy.toml configuration: +``` +disallowed-macros = [ + # Can use a string as the path of the disallowed macro. + "std::print", + # Can also use an inline table with a `path` key. + { path = "std::println" }, + # When using an inline table, can add a `reason` for why the macro + # is disallowed. + { path = "serde::Serialize", reason = "no serializing" }, +] +``` +``` +use serde::Serialize; + +// Example code where clippy issues a warning +println!("warns"); + +// The diagnostic will contain the message "no serializing" +#[derive(Serialize)] +struct Data { + name: String, + value: usize, +} +``` \ No newline at end of file diff --git a/src/docs/implicit_saturating_add.txt b/src/docs/implicit_saturating_add.txt new file mode 100644 index 000000000000..5883a5363e2b --- /dev/null +++ b/src/docs/implicit_saturating_add.txt @@ -0,0 +1,20 @@ +### What it does +Checks for implicit saturating addition. + +### Why is this bad? +The built-in function is more readable and may be faster. + +### Example +``` +let mut u:u32 = 7000; + +if u != u32::MAX { + u += 1; +} +``` +Use instead: +``` +let mut u:u32 = 7000; + +u = u.saturating_add(1); +``` \ No newline at end of file diff --git a/src/docs/manual_clamp.txt b/src/docs/manual_clamp.txt new file mode 100644 index 000000000000..8993f6683adf --- /dev/null +++ b/src/docs/manual_clamp.txt @@ -0,0 +1,46 @@ +### What it does +Identifies good opportunities for a clamp function from std or core, and suggests using it. + +### Why is this bad? +clamp is much shorter, easier to read, and doesn't use any control flow. + +### Known issue(s) +If the clamped variable is NaN this suggestion will cause the code to propagate NaN +rather than returning either `max` or `min`. + +`clamp` functions will panic if `max < min`, `max.is_nan()`, or `min.is_nan()`. +Some may consider panicking in these situations to be desirable, but it also may +introduce panicking where there wasn't any before. + +### Examples +``` +if input > max { + max +} else if input < min { + min +} else { + input +} +``` + +``` +input.max(min).min(max) +``` + +``` +match input { + x if x > max => max, + x if x < min => min, + x => x, +} +``` + +``` +let mut x = input; +if x < min { x = min; } +if x > max { x = max; } +``` +Use instead: +``` +input.clamp(min, max) +``` \ No newline at end of file diff --git a/src/docs/needless_borrowed_reference.txt b/src/docs/needless_borrowed_reference.txt index 55faa0cf5719..152459ba1c9d 100644 --- a/src/docs/needless_borrowed_reference.txt +++ b/src/docs/needless_borrowed_reference.txt @@ -1,30 +1,22 @@ ### What it does -Checks for bindings that destructure a reference and borrow the inner +Checks for bindings that needlessly destructure a reference and borrow the inner value with `&ref`. ### Why is this bad? This pattern has no effect in almost all cases. -### Known problems -In some cases, `&ref` is needed to avoid a lifetime mismatch error. -Example: -``` -fn foo(a: &Option, b: &Option) { - match (a, b) { - (None, &ref c) | (&ref c, None) => (), - (&Some(ref c), _) => (), - }; -} -``` - ### Example ``` let mut v = Vec::::new(); v.iter_mut().filter(|&ref a| a.is_empty()); + +if let &[ref first, ref second] = v.as_slice() {} ``` Use instead: ``` let mut v = Vec::::new(); v.iter_mut().filter(|a| a.is_empty()); + +if let [first, second] = v.as_slice() {} ``` \ No newline at end of file diff --git a/src/docs/similar_names.txt b/src/docs/similar_names.txt index 13aca9c0bb75..f9eff21b6793 100644 --- a/src/docs/similar_names.txt +++ b/src/docs/similar_names.txt @@ -1,6 +1,10 @@ ### What it does Checks for names that are very similar and thus confusing. +Note: this lint looks for similar names throughout each +scope. To allow it, you need to allow it on the scope +level, not on the name that is reported. + ### Why is this bad? It's hard to distinguish between names that differ only by a single character. diff --git a/src/docs/uninlined_format_args.txt b/src/docs/uninlined_format_args.txt new file mode 100644 index 000000000000..3d2966c84dbe --- /dev/null +++ b/src/docs/uninlined_format_args.txt @@ -0,0 +1,36 @@ +### What it does +Detect when a variable is not inlined in a format string, +and suggests to inline it. + +### Why is this bad? +Non-inlined code is slightly more difficult to read and understand, +as it requires arguments to be matched against the format string. +The inlined syntax, where allowed, is simpler. + +### Example +``` +format!("{}", var); +format!("{v:?}", v = var); +format!("{0} {0}", var); +format!("{0:1$}", var, width); +format!("{:.*}", prec, var); +``` +Use instead: +``` +format!("{var}"); +format!("{var:?}"); +format!("{var} {var}"); +format!("{var:width$}"); +format!("{var:.prec$}"); +``` + +### Known Problems + +There may be a false positive if the format string is expanded from certain proc macros: + +``` +println!(indoc!("{}"), var); +``` + +If a format string contains a numbered argument that cannot be inlined +nothing will be suggested, e.g. `println!("{0}={1}", var, 1+2)`. \ No newline at end of file diff --git a/src/driver.rs b/src/driver.rs index 235eae5af1ec..b12208ac62a8 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -193,8 +193,8 @@ fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) { let xs: Vec> = vec![ "the compiler unexpectedly panicked. this is a bug.".into(), - format!("we would appreciate a bug report: {}", bug_report_url).into(), - format!("Clippy version: {}", version_info).into(), + format!("we would appreciate a bug report: {bug_report_url}").into(), + format!("Clippy version: {version_info}").into(), ]; for note in &xs { @@ -290,7 +290,7 @@ pub fn main() { if orig_args.iter().any(|a| a == "--version" || a == "-V") { let version_info = rustc_tools_util::get_version_info!(); - println!("{}", version_info); + println!("{version_info}"); exit(0); } diff --git a/src/main.rs b/src/main.rs index 4a32e0e54a81..fce3cdfc462e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -37,12 +37,12 @@ You can use tool lints to allow or deny lints from your code, eg.: "#; fn show_help() { - println!("{}", CARGO_CLIPPY_HELP); + println!("{CARGO_CLIPPY_HELP}"); } fn show_version() { let version_info = rustc_tools_util::get_version_info!(); - println!("{}", version_info); + println!("{version_info}"); } pub fn main() { @@ -133,7 +133,7 @@ impl ClippyCmd { let clippy_args: String = self .clippy_args .iter() - .map(|arg| format!("{}__CLIPPY_HACKERY__", arg)) + .map(|arg| format!("{arg}__CLIPPY_HACKERY__")) .collect(); // Currently, `CLIPPY_TERMINAL_WIDTH` is used only to format "unknown field" error messages. diff --git a/tests/compile-test.rs b/tests/compile-test.rs index ba6186e599e9..fa769222d1af 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -111,15 +111,14 @@ static EXTERN_FLAGS: LazyLock = LazyLock::new(|| { .collect(); assert!( not_found.is_empty(), - "dependencies not found in depinfo: {:?}\n\ + "dependencies not found in depinfo: {not_found:?}\n\ help: Make sure the `-Z binary-dep-depinfo` rust flag is enabled\n\ help: Try adding to dev-dependencies in Cargo.toml\n\ help: Be sure to also add `extern crate ...;` to tests/compile-test.rs", - not_found, ); crates .into_iter() - .map(|(name, path)| format!(" --extern {}={}", name, path)) + .map(|(name, path)| format!(" --extern {name}={path}")) .collect() }); @@ -150,9 +149,8 @@ fn base_config(test_dir: &str) -> compiletest::Config { .map(|p| format!(" -L dependency={}", Path::new(p).join("deps").display())) .unwrap_or_default(); config.target_rustcflags = Some(format!( - "--emit=metadata -Dwarnings -Zui-testing -L dependency={}{}{}", + "--emit=metadata -Dwarnings -Zui-testing -L dependency={}{host_libs}{}", deps_path.display(), - host_libs, &*EXTERN_FLAGS, )); @@ -239,7 +237,7 @@ fn run_ui_toml() { Ok(true) => {}, Ok(false) => panic!("Some tests failed"), Err(e) => { - panic!("I/O failure during tests: {:?}", e); + panic!("I/O failure during tests: {e:?}"); }, } } @@ -348,7 +346,7 @@ fn run_ui_cargo() { Ok(true) => {}, Ok(false) => panic!("Some tests failed"), Err(e) => { - panic!("I/O failure during tests: {:?}", e); + panic!("I/O failure during tests: {e:?}"); }, } } @@ -419,16 +417,15 @@ fn check_rustfix_coverage() { if rs_path.starts_with("tests/ui/crashes") { continue; } - assert!(rs_path.starts_with("tests/ui/"), "{:?}", rs_file); + assert!(rs_path.starts_with("tests/ui/"), "{rs_file:?}"); let filename = rs_path.strip_prefix("tests/ui/").unwrap(); assert!( RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS .binary_search_by_key(&filename, Path::new) .is_ok(), - "`{}` runs `MachineApplicable` diagnostics but is missing a `run-rustfix` annotation. \ + "`{rs_file}` runs `MachineApplicable` diagnostics but is missing a `run-rustfix` annotation. \ Please either add `// run-rustfix` at the top of the file or add the file to \ `RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS` in `tests/compile-test.rs`.", - rs_file, ); } } @@ -478,15 +475,13 @@ fn ui_cargo_toml_metadata() { .map(|component| component.as_os_str().to_string_lossy().replace('-', "_")) .any(|s| *s == name) || path.starts_with(&cargo_common_metadata_path), - "{:?} has incorrect package name", - path + "{path:?} has incorrect package name" ); let publish = package.get("publish").and_then(toml::Value::as_bool).unwrap_or(true); assert!( !publish || publish_exceptions.contains(&path.parent().unwrap().to_path_buf()), - "{:?} lacks `publish = false`", - path + "{path:?} lacks `publish = false`" ); } } diff --git a/tests/integration.rs b/tests/integration.rs index 23a9bef3ccce..818ff70b33f4 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -6,10 +6,15 @@ use std::env; use std::ffi::OsStr; use std::process::Command; +#[cfg(not(windows))] +const CARGO_CLIPPY: &str = "cargo-clippy"; +#[cfg(windows)] +const CARGO_CLIPPY: &str = "cargo-clippy.exe"; + #[cfg_attr(feature = "integration", test)] fn integration_test() { let repo_name = env::var("INTEGRATION").expect("`INTEGRATION` var not set"); - let repo_url = format!("https://github.com/{}", repo_name); + let repo_url = format!("https://github.com/{repo_name}"); let crate_name = repo_name .split('/') .nth(1) @@ -31,7 +36,7 @@ fn integration_test() { let root_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); let target_dir = std::path::Path::new(&root_dir).join("target"); - let clippy_binary = target_dir.join(env!("PROFILE")).join("cargo-clippy"); + let clippy_binary = target_dir.join(env!("PROFILE")).join(CARGO_CLIPPY); let output = Command::new(clippy_binary) .current_dir(repo_dir) @@ -51,17 +56,15 @@ fn integration_test() { .expect("unable to run clippy"); let stderr = String::from_utf8_lossy(&output.stderr); - if stderr.contains("internal compiler error") { - let backtrace_start = stderr - .find("thread 'rustc' panicked at") - .expect("start of backtrace not found"); - let backtrace_end = stderr - .rfind("error: internal compiler error") + if let Some(backtrace_start) = stderr.find("error: internal compiler error") { + static BACKTRACE_END_MSG: &str = "end of query stack"; + let backtrace_end = stderr[backtrace_start..] + .find(BACKTRACE_END_MSG) .expect("end of backtrace not found"); panic!( "internal compiler error\nBacktrace:\n\n{}", - &stderr[backtrace_start..backtrace_end] + &stderr[backtrace_start..backtrace_start + backtrace_end + BACKTRACE_END_MSG.len()] ); } else if stderr.contains("query stack during panic") { panic!("query stack during panic in the output"); @@ -83,7 +86,7 @@ fn integration_test() { match output.status.code() { Some(0) => println!("Compilation successful"), - Some(code) => eprintln!("Compilation failed. Exit code: {}", code), + Some(code) => eprintln!("Compilation failed. Exit code: {code}"), None => panic!("Process terminated by signal"), } } diff --git a/tests/lint_message_convention.rs b/tests/lint_message_convention.rs index 2e0f4e76075b..abd0d1bc5934 100644 --- a/tests/lint_message_convention.rs +++ b/tests/lint_message_convention.rs @@ -102,7 +102,7 @@ fn lint_message_convention() { "error: the test '{}' contained the following nonconforming lines :", message.path.display() ); - message.bad_lines.iter().for_each(|line| eprintln!("{}", line)); + message.bad_lines.iter().for_each(|line| eprintln!("{line}")); eprintln!("\n\n"); } diff --git a/tests/missing-test-files.rs b/tests/missing-test-files.rs index 7d6edc2b1e09..caedd5d76cd6 100644 --- a/tests/missing-test-files.rs +++ b/tests/missing-test-files.rs @@ -17,7 +17,7 @@ fn test_missing_tests() { "Didn't see a test file for the following files:\n\n{}\n", missing_files .iter() - .map(|s| format!("\t{}", s)) + .map(|s| format!("\t{s}")) .collect::>() .join("\n") ); diff --git a/tests/ui-cargo/duplicate_mod/fail/src/main.stderr b/tests/ui-cargo/duplicate_mod/fail/src/main.stderr index b450a2b18f25..3b80d89a6865 100644 --- a/tests/ui-cargo/duplicate_mod/fail/src/main.stderr +++ b/tests/ui-cargo/duplicate_mod/fail/src/main.stderr @@ -7,8 +7,8 @@ LL | / #[path = "b.rs"] LL | | mod b2; | |_______^ loaded again here | - = note: `-D clippy::duplicate-mod` implied by `-D warnings` = help: replace all but one `mod` item with `use` items + = note: `-D clippy::duplicate-mod` implied by `-D warnings` error: file is loaded as a module multiple times: `$DIR/c.rs` --> $DIR/main.rs:9:1 diff --git a/tests/ui-cargo/feature_name/fail/src/main.stderr b/tests/ui-cargo/feature_name/fail/src/main.stderr index b9e6cb49bc98..c6a11fa93eb5 100644 --- a/tests/ui-cargo/feature_name/fail/src/main.stderr +++ b/tests/ui-cargo/feature_name/fail/src/main.stderr @@ -1,7 +1,7 @@ error: the "no-" prefix in the feature name "no-qaq" is negative | - = note: `-D clippy::negative-feature-names` implied by `-D warnings` = help: consider renaming the feature to "qaq", but make sure the feature adds functionality + = note: `-D clippy::negative-feature-names` implied by `-D warnings` error: the "no_" prefix in the feature name "no_qaq" is negative | @@ -17,8 +17,8 @@ error: the "not_" prefix in the feature name "not_orz" is negative error: the "-support" suffix in the feature name "qvq-support" is redundant | - = note: `-D clippy::redundant-feature-names` implied by `-D warnings` = help: consider renaming the feature to "qvq" + = note: `-D clippy::redundant-feature-names` implied by `-D warnings` error: the "_support" suffix in the feature name "qvq_support" is redundant | diff --git a/tests/ui-cargo/module_style/fail_mod/src/main.stderr b/tests/ui-cargo/module_style/fail_mod/src/main.stderr index e2010e998131..697c8b57c4a2 100644 --- a/tests/ui-cargo/module_style/fail_mod/src/main.stderr +++ b/tests/ui-cargo/module_style/fail_mod/src/main.stderr @@ -4,8 +4,8 @@ error: `mod.rs` files are required, found `bad/inner.rs` LL | pub mod stuff; | ^ | - = note: `-D clippy::self-named-module-files` implied by `-D warnings` = help: move `bad/inner.rs` to `bad/inner/mod.rs` + = note: `-D clippy::self-named-module-files` implied by `-D warnings` error: `mod.rs` files are required, found `bad/inner/stuff.rs` --> $DIR/bad/inner/stuff.rs:1:1 diff --git a/tests/ui-cargo/module_style/fail_mod_remap/src/main.stderr b/tests/ui-cargo/module_style/fail_mod_remap/src/main.stderr index 46991ff662e5..ea6ea98064a7 100644 --- a/tests/ui-cargo/module_style/fail_mod_remap/src/main.stderr +++ b/tests/ui-cargo/module_style/fail_mod_remap/src/main.stderr @@ -4,8 +4,8 @@ error: `mod.rs` files are required, found `bad.rs` LL | pub mod inner; | ^ | - = note: `-D clippy::self-named-module-files` implied by `-D warnings` = help: move `bad.rs` to `bad/mod.rs` + = note: `-D clippy::self-named-module-files` implied by `-D warnings` error: aborting due to previous error diff --git a/tests/ui-cargo/module_style/fail_no_mod/src/main.stderr b/tests/ui-cargo/module_style/fail_no_mod/src/main.stderr index f91940209383..f40ceea234b9 100644 --- a/tests/ui-cargo/module_style/fail_no_mod/src/main.stderr +++ b/tests/ui-cargo/module_style/fail_no_mod/src/main.stderr @@ -4,8 +4,8 @@ error: `mod.rs` files are not allowed, found `bad/mod.rs` LL | pub struct Thing; | ^ | - = note: `-D clippy::mod-module-files` implied by `-D warnings` = help: move `bad/mod.rs` to `bad.rs` + = note: `-D clippy::mod-module-files` implied by `-D warnings` error: aborting due to previous error diff --git a/tests/ui-internal/auxiliary/paths.rs b/tests/ui-internal/auxiliary/paths.rs new file mode 100644 index 000000000000..52fcaec4df32 --- /dev/null +++ b/tests/ui-internal/auxiliary/paths.rs @@ -0,0 +1,2 @@ +pub static OPTION: [&str; 3] = ["core", "option", "Option"]; +pub const RESULT: &[&str] = &["core", "result", "Result"]; diff --git a/tests/ui-internal/check_clippy_version_attribute.stderr b/tests/ui-internal/check_clippy_version_attribute.stderr index 2aa4de490bcf..fd8c8379f5bf 100644 --- a/tests/ui-internal/check_clippy_version_attribute.stderr +++ b/tests/ui-internal/check_clippy_version_attribute.stderr @@ -10,13 +10,13 @@ LL | | report_in_external_macro: true LL | | } | |_^ | + = help: please use a valid semantic version, see `doc/adding_lints.md` note: the lint level is defined here --> $DIR/check_clippy_version_attribute.rs:1:9 | LL | #![deny(clippy::internal)] | ^^^^^^^^^^^^^^^^ = note: `#[deny(clippy::invalid_clippy_version_attribute)]` implied by `#[deny(clippy::internal)]` - = help: please use a valid semantic version, see `doc/adding_lints.md` = note: this error originates in the macro `$crate::declare_tool_lint` which comes from the expansion of the macro `declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info) error: this item has an invalid `clippy::version` attribute @@ -46,8 +46,8 @@ LL | | report_in_external_macro: true LL | | } | |_^ | - = note: `#[deny(clippy::missing_clippy_version_attribute)]` implied by `#[deny(clippy::internal)]` = help: please use a `clippy::version` attribute, see `doc/adding_lints.md` + = note: `#[deny(clippy::missing_clippy_version_attribute)]` implied by `#[deny(clippy::internal)]` = note: this error originates in the macro `$crate::declare_tool_lint` which comes from the expansion of the macro `declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info) error: this lint is missing the `clippy::version` attribute or version value diff --git a/tests/ui-internal/if_chain_style.stderr b/tests/ui-internal/if_chain_style.stderr index 24106510e73b..d8f1ffb21ba6 100644 --- a/tests/ui-internal/if_chain_style.stderr +++ b/tests/ui-internal/if_chain_style.stderr @@ -10,12 +10,12 @@ LL | | } LL | | } | |_____^ | - = note: `-D clippy::if-chain-style` implied by `-D warnings` help: this `let` statement can also be in the `if_chain!` --> $DIR/if_chain_style.rs:10:9 | LL | let x = ""; | ^^^^^^^^^^^ + = note: `-D clippy::if-chain-style` implied by `-D warnings` error: `if a && b;` should be `if a; if b;` --> $DIR/if_chain_style.rs:19:12 diff --git a/tests/ui-internal/match_type_on_diag_item.rs b/tests/ui-internal/match_type_on_diag_item.rs deleted file mode 100644 index 4b41ff15e80f..000000000000 --- a/tests/ui-internal/match_type_on_diag_item.rs +++ /dev/null @@ -1,39 +0,0 @@ -#![deny(clippy::internal)] -#![allow(clippy::missing_clippy_version_attribute)] -#![feature(rustc_private)] - -extern crate clippy_utils; -extern crate rustc_hir; -extern crate rustc_lint; -extern crate rustc_middle; - -#[macro_use] -extern crate rustc_session; -use clippy_utils::{paths, ty::match_type}; -use rustc_hir::Expr; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::Ty; - -declare_lint! { - pub TEST_LINT, - Warn, - "" -} - -declare_lint_pass!(Pass => [TEST_LINT]); - -static OPTION: [&str; 3] = ["core", "option", "Option"]; - -impl<'tcx> LateLintPass<'tcx> for Pass { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr) { - let ty = cx.typeck_results().expr_ty(expr); - - let _ = match_type(cx, ty, &OPTION); - let _ = match_type(cx, ty, &["core", "result", "Result"]); - - let rc_path = &["alloc", "rc", "Rc"]; - let _ = clippy_utils::ty::match_type(cx, ty, rc_path); - } -} - -fn main() {} diff --git a/tests/ui-internal/match_type_on_diag_item.stderr b/tests/ui-internal/match_type_on_diag_item.stderr deleted file mode 100644 index e3cb6b6c22ea..000000000000 --- a/tests/ui-internal/match_type_on_diag_item.stderr +++ /dev/null @@ -1,27 +0,0 @@ -error: usage of `clippy_utils::ty::match_type()` on a type diagnostic item - --> $DIR/match_type_on_diag_item.rs:31:17 - | -LL | let _ = match_type(cx, ty, &OPTION); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clippy_utils::ty::is_type_diagnostic_item(cx, ty, sym::Option)` - | -note: the lint level is defined here - --> $DIR/match_type_on_diag_item.rs:1:9 - | -LL | #![deny(clippy::internal)] - | ^^^^^^^^^^^^^^^^ - = note: `#[deny(clippy::match_type_on_diagnostic_item)]` implied by `#[deny(clippy::internal)]` - -error: usage of `clippy_utils::ty::match_type()` on a type diagnostic item - --> $DIR/match_type_on_diag_item.rs:32:17 - | -LL | let _ = match_type(cx, ty, &["core", "result", "Result"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clippy_utils::ty::is_type_diagnostic_item(cx, ty, sym::Result)` - -error: usage of `clippy_utils::ty::match_type()` on a type diagnostic item - --> $DIR/match_type_on_diag_item.rs:35:17 - | -LL | let _ = clippy_utils::ty::match_type(cx, ty, rc_path); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clippy_utils::ty::is_type_diagnostic_item(cx, ty, sym::Rc)` - -error: aborting due to 3 previous errors - diff --git a/tests/ui-internal/unnecessary_def_path.fixed b/tests/ui-internal/unnecessary_def_path.fixed new file mode 100644 index 000000000000..4c050332f2cc --- /dev/null +++ b/tests/ui-internal/unnecessary_def_path.fixed @@ -0,0 +1,62 @@ +// run-rustfix +// aux-build:paths.rs +#![deny(clippy::internal)] +#![feature(rustc_private)] + +extern crate clippy_utils; +extern crate paths; +extern crate rustc_hir; +extern crate rustc_lint; +extern crate rustc_middle; +extern crate rustc_span; + +#[allow(unused)] +use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item, match_type}; +#[allow(unused)] +use clippy_utils::{ + is_expr_path_def_path, is_path_diagnostic_item, is_res_diagnostic_ctor, is_res_lang_ctor, is_trait_method, + match_def_path, match_trait_method, path_res, +}; + +#[allow(unused)] +use rustc_hir::LangItem; +#[allow(unused)] +use rustc_span::sym; + +use rustc_hir::def_id::DefId; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_middle::ty::Ty; + +#[allow(unused)] +static OPTION: [&str; 3] = ["core", "option", "Option"]; +#[allow(unused)] +const RESULT: &[&str] = &["core", "result", "Result"]; + +fn _f<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, did: DefId, expr: &Expr<'_>) { + let _ = is_type_diagnostic_item(cx, ty, sym::Option); + let _ = is_type_diagnostic_item(cx, ty, sym::Result); + let _ = is_type_diagnostic_item(cx, ty, sym::Result); + + #[allow(unused)] + let rc_path = &["alloc", "rc", "Rc"]; + let _ = is_type_diagnostic_item(cx, ty, sym::Rc); + + let _ = is_type_diagnostic_item(cx, ty, sym::Option); + let _ = is_type_diagnostic_item(cx, ty, sym::Result); + + let _ = is_type_lang_item(cx, ty, LangItem::OwnedBox); + let _ = is_type_diagnostic_item(cx, ty, sym::maybe_uninit_uninit); + + let _ = cx.tcx.lang_items().require(LangItem::OwnedBox).ok() == Some(did); + let _ = cx.tcx.is_diagnostic_item(sym::Option, did); + let _ = cx.tcx.lang_items().require(LangItem::OptionSome).ok() == Some(did); + + let _ = is_trait_method(cx, expr, sym::AsRef); + + let _ = is_path_diagnostic_item(cx, expr, sym::Option); + let _ = path_res(cx, expr).opt_def_id().map_or(false, |id| cx.tcx.lang_items().require(LangItem::IteratorNext).ok() == Some(id)); + let _ = is_res_lang_ctor(cx, path_res(cx, expr), LangItem::OptionSome); +} + +fn main() {} diff --git a/tests/ui-internal/unnecessary_def_path.rs b/tests/ui-internal/unnecessary_def_path.rs new file mode 100644 index 000000000000..6506f1f164ac --- /dev/null +++ b/tests/ui-internal/unnecessary_def_path.rs @@ -0,0 +1,62 @@ +// run-rustfix +// aux-build:paths.rs +#![deny(clippy::internal)] +#![feature(rustc_private)] + +extern crate clippy_utils; +extern crate paths; +extern crate rustc_hir; +extern crate rustc_lint; +extern crate rustc_middle; +extern crate rustc_span; + +#[allow(unused)] +use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item, match_type}; +#[allow(unused)] +use clippy_utils::{ + is_expr_path_def_path, is_path_diagnostic_item, is_res_diagnostic_ctor, is_res_lang_ctor, is_trait_method, + match_def_path, match_trait_method, path_res, +}; + +#[allow(unused)] +use rustc_hir::LangItem; +#[allow(unused)] +use rustc_span::sym; + +use rustc_hir::def_id::DefId; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_middle::ty::Ty; + +#[allow(unused)] +static OPTION: [&str; 3] = ["core", "option", "Option"]; +#[allow(unused)] +const RESULT: &[&str] = &["core", "result", "Result"]; + +fn _f<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, did: DefId, expr: &Expr<'_>) { + let _ = match_type(cx, ty, &OPTION); + let _ = match_type(cx, ty, RESULT); + let _ = match_type(cx, ty, &["core", "result", "Result"]); + + #[allow(unused)] + let rc_path = &["alloc", "rc", "Rc"]; + let _ = clippy_utils::ty::match_type(cx, ty, rc_path); + + let _ = match_type(cx, ty, &paths::OPTION); + let _ = match_type(cx, ty, paths::RESULT); + + let _ = match_type(cx, ty, &["alloc", "boxed", "Box"]); + let _ = match_type(cx, ty, &["core", "mem", "maybe_uninit", "MaybeUninit", "uninit"]); + + let _ = match_def_path(cx, did, &["alloc", "boxed", "Box"]); + let _ = match_def_path(cx, did, &["core", "option", "Option"]); + let _ = match_def_path(cx, did, &["core", "option", "Option", "Some"]); + + let _ = match_trait_method(cx, expr, &["core", "convert", "AsRef"]); + + let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option"]); + let _ = is_expr_path_def_path(cx, expr, &["core", "iter", "traits", "Iterator", "next"]); + let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option", "Some"]); +} + +fn main() {} diff --git a/tests/ui-internal/unnecessary_def_path.stderr b/tests/ui-internal/unnecessary_def_path.stderr new file mode 100644 index 000000000000..a99a8f71fa6a --- /dev/null +++ b/tests/ui-internal/unnecessary_def_path.stderr @@ -0,0 +1,101 @@ +error: use of a def path to a diagnostic item + --> $DIR/unnecessary_def_path.rs:37:13 + | +LL | let _ = match_type(cx, ty, &OPTION); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Option)` + | +note: the lint level is defined here + --> $DIR/unnecessary_def_path.rs:3:9 + | +LL | #![deny(clippy::internal)] + | ^^^^^^^^^^^^^^^^ + = note: `#[deny(clippy::unnecessary_def_path)]` implied by `#[deny(clippy::internal)]` + +error: use of a def path to a diagnostic item + --> $DIR/unnecessary_def_path.rs:38:13 + | +LL | let _ = match_type(cx, ty, RESULT); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Result)` + +error: use of a def path to a diagnostic item + --> $DIR/unnecessary_def_path.rs:39:13 + | +LL | let _ = match_type(cx, ty, &["core", "result", "Result"]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Result)` + +error: use of a def path to a diagnostic item + --> $DIR/unnecessary_def_path.rs:43:13 + | +LL | let _ = clippy_utils::ty::match_type(cx, ty, rc_path); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Rc)` + +error: use of a def path to a diagnostic item + --> $DIR/unnecessary_def_path.rs:45:13 + | +LL | let _ = match_type(cx, ty, &paths::OPTION); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Option)` + +error: use of a def path to a diagnostic item + --> $DIR/unnecessary_def_path.rs:46:13 + | +LL | let _ = match_type(cx, ty, paths::RESULT); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Result)` + +error: use of a def path to a `LangItem` + --> $DIR/unnecessary_def_path.rs:48:13 + | +LL | let _ = match_type(cx, ty, &["alloc", "boxed", "Box"]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_lang_item(cx, ty, LangItem::OwnedBox)` + +error: use of a def path to a diagnostic item + --> $DIR/unnecessary_def_path.rs:49:13 + | +LL | let _ = match_type(cx, ty, &["core", "mem", "maybe_uninit", "MaybeUninit", "uninit"]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::maybe_uninit_uninit)` + +error: use of a def path to a `LangItem` + --> $DIR/unnecessary_def_path.rs:51:13 + | +LL | let _ = match_def_path(cx, did, &["alloc", "boxed", "Box"]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.lang_items().require(LangItem::OwnedBox).ok() == Some(did)` + +error: use of a def path to a diagnostic item + --> $DIR/unnecessary_def_path.rs:52:13 + | +LL | let _ = match_def_path(cx, did, &["core", "option", "Option"]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.is_diagnostic_item(sym::Option, did)` + +error: use of a def path to a `LangItem` + --> $DIR/unnecessary_def_path.rs:53:13 + | +LL | let _ = match_def_path(cx, did, &["core", "option", "Option", "Some"]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.lang_items().require(LangItem::OptionSome).ok() == Some(did)` + | + = help: if this `DefId` came from a constructor expression or pattern then the parent `DefId` should be used instead + +error: use of a def path to a diagnostic item + --> $DIR/unnecessary_def_path.rs:55:13 + | +LL | let _ = match_trait_method(cx, expr, &["core", "convert", "AsRef"]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_trait_method(cx, expr, sym::AsRef)` + +error: use of a def path to a diagnostic item + --> $DIR/unnecessary_def_path.rs:57:13 + | +LL | let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option"]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_path_diagnostic_item(cx, expr, sym::Option)` + +error: use of a def path to a `LangItem` + --> $DIR/unnecessary_def_path.rs:58:13 + | +LL | let _ = is_expr_path_def_path(cx, expr, &["core", "iter", "traits", "Iterator", "next"]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `path_res(cx, expr).opt_def_id().map_or(false, |id| cx.tcx.lang_items().require(LangItem::IteratorNext).ok() == Some(id))` + +error: use of a def path to a `LangItem` + --> $DIR/unnecessary_def_path.rs:59:13 + | +LL | let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option", "Some"]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_res_lang_ctor(cx, path_res(cx, expr), LangItem::OptionSome)` + +error: aborting due to 15 previous errors + diff --git a/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.rs b/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.rs index b4e677ea124b..7f1c512d7c97 100644 --- a/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.rs +++ b/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.rs @@ -1,3 +1,5 @@ +#![allow(clippy::uninlined_format_args)] + fn main() {} #[warn(clippy::cognitive_complexity)] diff --git a/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr b/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr index b2b57bdde89c..630bad07c5b7 100644 --- a/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr +++ b/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr @@ -3,7 +3,7 @@ warning: error reading Clippy's configuration file `$DIR/clippy.toml`: deprecate warning: error reading Clippy's configuration file `$DIR/clippy.toml`: deprecated field `blacklisted-names`. Please use `disallowed-names` instead error: the function has a cognitive complexity of (3/2) - --> $DIR/conf_deprecated_key.rs:4:4 + --> $DIR/conf_deprecated_key.rs:6:4 | LL | fn cognitive_complexity() { | ^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui-toml/disallowed_macros/auxiliary/macros.rs b/tests/ui-toml/disallowed_macros/auxiliary/macros.rs new file mode 100644 index 000000000000..fcaeace0e989 --- /dev/null +++ b/tests/ui-toml/disallowed_macros/auxiliary/macros.rs @@ -0,0 +1,32 @@ +#[macro_export] +macro_rules! expr { + () => { + 1 + }; +} + +#[macro_export] +macro_rules! stmt { + () => { + let _x = (); + }; +} + +#[macro_export] +macro_rules! ty { + () => { &'static str }; +} + +#[macro_export] +macro_rules! pat { + () => { + _ + }; +} + +#[macro_export] +macro_rules! item { + () => { + const ITEM: usize = 1; + }; +} diff --git a/tests/ui-toml/disallowed_macros/clippy.toml b/tests/ui-toml/disallowed_macros/clippy.toml new file mode 100644 index 000000000000..c8fe8be9a770 --- /dev/null +++ b/tests/ui-toml/disallowed_macros/clippy.toml @@ -0,0 +1,11 @@ +disallowed-macros = [ + "std::println", + "std::vec", + { path = "std::cfg" }, + { path = "serde::Serialize", reason = "no serializing" }, + "macros::expr", + "macros::stmt", + "macros::ty", + "macros::pat", + "macros::item", +] diff --git a/tests/ui-toml/disallowed_macros/disallowed_macros.rs b/tests/ui-toml/disallowed_macros/disallowed_macros.rs new file mode 100644 index 000000000000..2bb5376076e2 --- /dev/null +++ b/tests/ui-toml/disallowed_macros/disallowed_macros.rs @@ -0,0 +1,39 @@ +// aux-build:macros.rs + +#![allow(unused)] + +extern crate macros; + +use serde::Serialize; + +fn main() { + println!("one"); + println!("two"); + cfg!(unix); + vec![1, 2, 3]; + + #[derive(Serialize)] + struct Derive; + + let _ = macros::expr!(); + macros::stmt!(); + let macros::pat!() = 1; + let _: macros::ty!() = ""; + macros::item!(); + + eprintln!("allowed"); +} + +struct S; + +impl S { + macros::item!(); +} + +trait Y { + macros::item!(); +} + +impl Y for S { + macros::item!(); +} diff --git a/tests/ui-toml/disallowed_macros/disallowed_macros.stderr b/tests/ui-toml/disallowed_macros/disallowed_macros.stderr new file mode 100644 index 000000000000..aed9feb6f796 --- /dev/null +++ b/tests/ui-toml/disallowed_macros/disallowed_macros.stderr @@ -0,0 +1,84 @@ +error: use of a disallowed macro `std::println` + --> $DIR/disallowed_macros.rs:10:5 + | +LL | println!("one"); + | ^^^^^^^^^^^^^^^ + | + = note: `-D clippy::disallowed-macros` implied by `-D warnings` + +error: use of a disallowed macro `std::println` + --> $DIR/disallowed_macros.rs:11:5 + | +LL | println!("two"); + | ^^^^^^^^^^^^^^^ + +error: use of a disallowed macro `std::cfg` + --> $DIR/disallowed_macros.rs:12:5 + | +LL | cfg!(unix); + | ^^^^^^^^^^ + +error: use of a disallowed macro `std::vec` + --> $DIR/disallowed_macros.rs:13:5 + | +LL | vec![1, 2, 3]; + | ^^^^^^^^^^^^^ + +error: use of a disallowed macro `serde::Serialize` + --> $DIR/disallowed_macros.rs:15:14 + | +LL | #[derive(Serialize)] + | ^^^^^^^^^ + | + = note: no serializing (from clippy.toml) + +error: use of a disallowed macro `macros::expr` + --> $DIR/disallowed_macros.rs:18:13 + | +LL | let _ = macros::expr!(); + | ^^^^^^^^^^^^^^^ + +error: use of a disallowed macro `macros::stmt` + --> $DIR/disallowed_macros.rs:19:5 + | +LL | macros::stmt!(); + | ^^^^^^^^^^^^^^^ + +error: use of a disallowed macro `macros::pat` + --> $DIR/disallowed_macros.rs:20:9 + | +LL | let macros::pat!() = 1; + | ^^^^^^^^^^^^^^ + +error: use of a disallowed macro `macros::ty` + --> $DIR/disallowed_macros.rs:21:12 + | +LL | let _: macros::ty!() = ""; + | ^^^^^^^^^^^^^ + +error: use of a disallowed macro `macros::item` + --> $DIR/disallowed_macros.rs:22:5 + | +LL | macros::item!(); + | ^^^^^^^^^^^^^^^ + +error: use of a disallowed macro `macros::item` + --> $DIR/disallowed_macros.rs:30:5 + | +LL | macros::item!(); + | ^^^^^^^^^^^^^^^ + +error: use of a disallowed macro `macros::item` + --> $DIR/disallowed_macros.rs:34:5 + | +LL | macros::item!(); + | ^^^^^^^^^^^^^^^ + +error: use of a disallowed macro `macros::item` + --> $DIR/disallowed_macros.rs:38:5 + | +LL | macros::item!(); + | ^^^^^^^^^^^^^^^ + +error: aborting due to 13 previous errors + diff --git a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.fixed b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.fixed new file mode 100644 index 000000000000..01d135764dff --- /dev/null +++ b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.fixed @@ -0,0 +1,62 @@ +// aux-build:proc_macro_derive.rs +// run-rustfix + +#![warn(clippy::nonstandard_macro_braces)] + +extern crate proc_macro_derive; +extern crate quote; + +use quote::quote; + +#[derive(proc_macro_derive::DeriveSomething)] +pub struct S; + +proc_macro_derive::foo_bar!(); + +#[rustfmt::skip] +macro_rules! test { + () => { + vec![0, 0, 0] + }; +} + +#[rustfmt::skip] +macro_rules! test2 { + ($($arg:tt)*) => { + format_args!($($arg)*) + }; +} + +macro_rules! type_pos { + ($what:ty) => { + Vec<$what> + }; +} + +macro_rules! printlnfoo { + ($thing:expr) => { + println!("{}", $thing) + }; +} + +#[rustfmt::skip] +fn main() { + let _ = vec![1, 2, 3]; + let _ = format!("ugh {} stop being such a good compiler", "hello"); + let _ = matches!({}, ()); + let _ = quote!{let x = 1;}; + let _ = quote::quote!{match match match}; + let _ = test!(); // trigger when macro def is inside our own crate + let _ = vec![1,2,3]; + + let _ = quote::quote! {true || false}; + let _ = vec! [0 ,0 ,0]; + let _ = format!("fds{}fds", 10); + let _ = test2!["{}{}{}", 1, 2, 3]; + + let _: type_pos![usize] = vec![]; + + eprint!["test if user config overrides defaults"]; + + printlnfoo!["test if printlnfoo is triggered by println"]; +} diff --git a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs index ed8161acc0ef..72883e8270c3 100644 --- a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs +++ b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs @@ -1,4 +1,5 @@ // aux-build:proc_macro_derive.rs +// run-rustfix #![warn(clippy::nonstandard_macro_braces)] diff --git a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr index 15fa4f42f9ba..7ae3815978c7 100644 --- a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr +++ b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr @@ -1,106 +1,57 @@ error: use of irregular braces for `vec!` macro - --> $DIR/conf_nonstandard_macro_braces.rs:43:13 + --> $DIR/conf_nonstandard_macro_braces.rs:44:13 | LL | let _ = vec! {1, 2, 3}; - | ^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^ help: consider writing: `vec![1, 2, 3]` | -help: consider writing `vec![1, 2, 3]` - --> $DIR/conf_nonstandard_macro_braces.rs:43:13 - | -LL | let _ = vec! {1, 2, 3}; - | ^^^^^^^^^^^^^^ = note: `-D clippy::nonstandard-macro-braces` implied by `-D warnings` error: use of irregular braces for `format!` macro - --> $DIR/conf_nonstandard_macro_braces.rs:44:13 + --> $DIR/conf_nonstandard_macro_braces.rs:45:13 | LL | let _ = format!["ugh {} stop being such a good compiler", "hello"]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: consider writing `format!("ugh () stop being such a good compiler", "hello")` - --> $DIR/conf_nonstandard_macro_braces.rs:44:13 - | -LL | let _ = format!["ugh {} stop being such a good compiler", "hello"]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `format!("ugh {} stop being such a good compiler", "hello")` error: use of irregular braces for `matches!` macro - --> $DIR/conf_nonstandard_macro_braces.rs:45:13 + --> $DIR/conf_nonstandard_macro_braces.rs:46:13 | LL | let _ = matches!{{}, ()}; - | ^^^^^^^^^^^^^^^^ - | -help: consider writing `matches!((), ())` - --> $DIR/conf_nonstandard_macro_braces.rs:45:13 - | -LL | let _ = matches!{{}, ()}; - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ help: consider writing: `matches!({}, ())` error: use of irregular braces for `quote!` macro - --> $DIR/conf_nonstandard_macro_braces.rs:46:13 + --> $DIR/conf_nonstandard_macro_braces.rs:47:13 | LL | let _ = quote!(let x = 1;); - | ^^^^^^^^^^^^^^^^^^ - | -help: consider writing `quote! {let x = 1;}` - --> $DIR/conf_nonstandard_macro_braces.rs:46:13 - | -LL | let _ = quote!(let x = 1;); - | ^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^ help: consider writing: `quote!{let x = 1;}` error: use of irregular braces for `quote::quote!` macro - --> $DIR/conf_nonstandard_macro_braces.rs:47:13 + --> $DIR/conf_nonstandard_macro_braces.rs:48:13 | LL | let _ = quote::quote!(match match match); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: consider writing `quote::quote! {match match match}` - --> $DIR/conf_nonstandard_macro_braces.rs:47:13 - | -LL | let _ = quote::quote!(match match match); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `quote::quote!{match match match}` error: use of irregular braces for `vec!` macro - --> $DIR/conf_nonstandard_macro_braces.rs:18:9 + --> $DIR/conf_nonstandard_macro_braces.rs:19:9 | LL | vec!{0, 0, 0} - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^ help: consider writing: `vec![0, 0, 0]` ... LL | let _ = test!(); // trigger when macro def is inside our own crate | ------- in this macro invocation | -help: consider writing `vec![0, 0, 0]` - --> $DIR/conf_nonstandard_macro_braces.rs:18:9 - | -LL | vec!{0, 0, 0} - | ^^^^^^^^^^^^^ -... -LL | let _ = test!(); // trigger when macro def is inside our own crate - | ------- in this macro invocation = note: this error originates in the macro `test` (in Nightly builds, run with -Z macro-backtrace for more info) error: use of irregular braces for `type_pos!` macro - --> $DIR/conf_nonstandard_macro_braces.rs:56:12 + --> $DIR/conf_nonstandard_macro_braces.rs:57:12 | LL | let _: type_pos!(usize) = vec![]; - | ^^^^^^^^^^^^^^^^ - | -help: consider writing `type_pos![usize]` - --> $DIR/conf_nonstandard_macro_braces.rs:56:12 - | -LL | let _: type_pos!(usize) = vec![]; - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ help: consider writing: `type_pos![usize]` error: use of irregular braces for `eprint!` macro - --> $DIR/conf_nonstandard_macro_braces.rs:58:5 + --> $DIR/conf_nonstandard_macro_braces.rs:59:5 | LL | eprint!("test if user config overrides defaults"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: consider writing `eprint!["test if user config overrides defaults"]` - --> $DIR/conf_nonstandard_macro_braces.rs:58:5 - | -LL | eprint!("test if user config overrides defaults"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `eprint!["test if user config overrides defaults"]` error: aborting due to 8 previous errors diff --git a/tests/ui-toml/toml_disallowed_methods/clippy.toml b/tests/ui-toml/toml_disallowed_methods/clippy.toml index c902d21123dc..28774db625bb 100644 --- a/tests/ui-toml/toml_disallowed_methods/clippy.toml +++ b/tests/ui-toml/toml_disallowed_methods/clippy.toml @@ -3,6 +3,7 @@ disallowed-methods = [ "std::iter::Iterator::sum", "f32::clamp", "slice::sort_unstable", + "futures::stream::select_all", # can give path and reason with an inline table { path = "regex::Regex::is_match", reason = "no matching allowed" }, # can use an inline table but omit reason diff --git a/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs b/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs index 3397fa1ec692..b483f1600289 100644 --- a/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs +++ b/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs @@ -1,6 +1,9 @@ #![warn(clippy::disallowed_methods)] +extern crate futures; extern crate regex; + +use futures::stream::{empty, select_all}; use regex::Regex; fn main() { @@ -20,4 +23,7 @@ fn main() { let in_call = Box::new(f32::clamp); let in_method_call = ["^", "$"].into_iter().map(Regex::new); + + // resolve ambiguity between `futures::stream::select_all` the module and the function + let same_name_as_module = select_all(vec![empty::<()>()]); } diff --git a/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr b/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr index 5cbb567546c0..6d78c32e127e 100644 --- a/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr +++ b/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr @@ -1,5 +1,5 @@ error: use of a disallowed method `regex::Regex::new` - --> $DIR/conf_disallowed_methods.rs:7:14 + --> $DIR/conf_disallowed_methods.rs:10:14 | LL | let re = Regex::new(r"ab.*c").unwrap(); | ^^^^^^^^^^^^^^^^^^^^ @@ -7,7 +7,7 @@ LL | let re = Regex::new(r"ab.*c").unwrap(); = note: `-D clippy::disallowed-methods` implied by `-D warnings` error: use of a disallowed method `regex::Regex::is_match` - --> $DIR/conf_disallowed_methods.rs:8:5 + --> $DIR/conf_disallowed_methods.rs:11:5 | LL | re.is_match("abc"); | ^^^^^^^^^^^^^^^^^^ @@ -15,40 +15,46 @@ LL | re.is_match("abc"); = note: no matching allowed (from clippy.toml) error: use of a disallowed method `std::iter::Iterator::sum` - --> $DIR/conf_disallowed_methods.rs:11:5 + --> $DIR/conf_disallowed_methods.rs:14:5 | LL | a.iter().sum::(); | ^^^^^^^^^^^^^^^^^^^^^ error: use of a disallowed method `slice::sort_unstable` - --> $DIR/conf_disallowed_methods.rs:13:5 + --> $DIR/conf_disallowed_methods.rs:16:5 | LL | a.sort_unstable(); | ^^^^^^^^^^^^^^^^^ error: use of a disallowed method `f32::clamp` - --> $DIR/conf_disallowed_methods.rs:15:13 + --> $DIR/conf_disallowed_methods.rs:18:13 | LL | let _ = 2.0f32.clamp(3.0f32, 4.0f32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: use of a disallowed method `regex::Regex::new` - --> $DIR/conf_disallowed_methods.rs:18:61 + --> $DIR/conf_disallowed_methods.rs:21:61 | LL | let indirect: fn(&str) -> Result = Regex::new; | ^^^^^^^^^^ error: use of a disallowed method `f32::clamp` - --> $DIR/conf_disallowed_methods.rs:21:28 + --> $DIR/conf_disallowed_methods.rs:24:28 | LL | let in_call = Box::new(f32::clamp); | ^^^^^^^^^^ error: use of a disallowed method `regex::Regex::new` - --> $DIR/conf_disallowed_methods.rs:22:53 + --> $DIR/conf_disallowed_methods.rs:25:53 | LL | let in_method_call = ["^", "$"].into_iter().map(Regex::new); | ^^^^^^^^^^ -error: aborting due to 8 previous errors +error: use of a disallowed method `futures::stream::select_all` + --> $DIR/conf_disallowed_methods.rs:28:31 + | +LL | let same_name_as_module = select_all(vec![empty::<()>()]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 9 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 f27f78d15d3a..82ee80541321 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -11,6 +11,7 @@ error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown fie cargo-ignore-publish cognitive-complexity-threshold cyclomatic-complexity-threshold + disallowed-macros disallowed-methods disallowed-names disallowed-types diff --git a/tests/ui/arithmetic_side_effects.rs b/tests/ui/arithmetic_side_effects.rs index dd24f5aa592b..b25e68f13061 100644 --- a/tests/ui/arithmetic_side_effects.rs +++ b/tests/ui/arithmetic_side_effects.rs @@ -2,15 +2,41 @@ clippy::assign_op_pattern, clippy::erasing_op, clippy::identity_op, + clippy::op_ref, clippy::unnecessary_owned_empty_strings, arithmetic_overflow, unconditional_panic )] -#![feature(inline_const, saturating_int_impl)] +#![feature(const_mut_refs, inline_const, saturating_int_impl)] #![warn(clippy::arithmetic_side_effects)] use core::num::{Saturating, Wrapping}; +pub struct Custom; + +macro_rules! impl_arith { + ( $( $_trait:ident, $ty:ty, $method:ident; )* ) => { + $( + impl core::ops::$_trait<$ty> for Custom { + type Output = Self; + fn $method(self, _: $ty) -> Self::Output { Self } + } + )* + } +} + +impl_arith!( + Add, i32, add; + Div, i32, div; + Mul, i32, mul; + Sub, i32, sub; + + Add, f64, add; + Div, f64, div; + Mul, f64, mul; + Sub, f64, sub; +); + pub fn association_with_structures_should_not_trigger_the_lint() { enum Foo { Bar = -2, @@ -79,32 +105,48 @@ pub fn const_ops_should_not_trigger_the_lint() { const _: i32 = 1 + 1; let _ = const { 1 + 1 }; - const _: i32 = { let mut n = -1; n = -(-1); n = -n; n }; - let _ = const { let mut n = -1; n = -(-1); n = -n; n }; + const _: i32 = { let mut n = 1; n = -1; n = -(-1); n = -n; n }; + let _ = const { let mut n = 1; n = -1; n = -(-1); n = -n; n }; } -pub fn non_overflowing_runtime_ops_or_ops_already_handled_by_the_compiler() { +pub fn non_overflowing_ops_or_ops_already_handled_by_the_compiler_should_not_trigger_the_lint() { let mut _n = i32::MAX; // Assign _n += 0; + _n += &0; _n -= 0; + _n -= &0; _n /= 99; + _n /= &99; _n %= 99; + _n %= &99; _n *= 0; + _n *= &0; _n *= 1; + _n *= &1; // Binary _n = _n + 0; + _n = _n + &0; _n = 0 + _n; + _n = &0 + _n; _n = _n - 0; + _n = _n - &0; _n = 0 - _n; + _n = &0 - _n; _n = _n / 99; + _n = _n / &99; _n = _n % 99; + _n = _n % &99; _n = _n * 0; + _n = _n * &0; _n = 0 * _n; + _n = &0 * _n; _n = _n * 1; + _n = _n * &1; _n = 1 * _n; + _n = &1 * _n; _n = 23 + 85; // Unary @@ -112,28 +154,71 @@ pub fn non_overflowing_runtime_ops_or_ops_already_handled_by_the_compiler() { _n = -(-1); } -pub fn overflowing_runtime_ops() { +pub fn runtime_ops() { let mut _n = i32::MAX; // Assign _n += 1; + _n += &1; _n -= 1; + _n -= &1; _n /= 0; + _n /= &0; _n %= 0; + _n %= &0; _n *= 2; + _n *= &2; // Binary _n = _n + 1; + _n = _n + &1; _n = 1 + _n; + _n = &1 + _n; _n = _n - 1; + _n = _n - &1; _n = 1 - _n; + _n = &1 - _n; _n = _n / 0; + _n = _n / &0; _n = _n % 0; + _n = _n % &0; _n = _n * 2; + _n = _n * &2; _n = 2 * _n; + _n = &2 * _n; + _n = 23 + &85; + _n = &23 + 85; + _n = &23 + &85; + + // Custom + let _ = Custom + 0; + let _ = Custom + 1; + let _ = Custom + 2; + let _ = Custom + 0.0; + let _ = Custom + 1.0; + let _ = Custom + 2.0; + let _ = Custom - 0; + let _ = Custom - 1; + let _ = Custom - 2; + let _ = Custom - 0.0; + let _ = Custom - 1.0; + let _ = Custom - 2.0; + let _ = Custom / 0; + let _ = Custom / 1; + let _ = Custom / 2; + let _ = Custom / 0.0; + let _ = Custom / 1.0; + let _ = Custom / 2.0; + let _ = Custom * 0; + let _ = Custom * 1; + let _ = Custom * 2; + let _ = Custom * 0.0; + let _ = Custom * 1.0; + let _ = Custom * 2.0; // Unary _n = -_n; + _n = -&_n; } fn main() {} diff --git a/tests/ui/arithmetic_side_effects.stderr b/tests/ui/arithmetic_side_effects.stderr index a2a856efbffc..0f06e22bae96 100644 --- a/tests/ui/arithmetic_side_effects.stderr +++ b/tests/ui/arithmetic_side_effects.stderr @@ -1,88 +1,352 @@ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:119:5 + --> $DIR/arithmetic_side_effects.rs:78:13 | -LL | _n += 1; - | ^^^^^^^ +LL | let _ = String::new() + ""; + | ^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::arithmetic-side-effects` implied by `-D warnings` error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:120:5 + --> $DIR/arithmetic_side_effects.rs:86:27 + | +LL | let inferred_string = string + ""; + | ^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:90:13 + | +LL | let _ = inferred_string + ""; + | ^^^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:161:5 + | +LL | _n += 1; + | ^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:162:5 + | +LL | _n += &1; + | ^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:163:5 | LL | _n -= 1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:121:5 + --> $DIR/arithmetic_side_effects.rs:164:5 + | +LL | _n -= &1; + | ^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:165:5 | LL | _n /= 0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:122:5 + --> $DIR/arithmetic_side_effects.rs:166:5 + | +LL | _n /= &0; + | ^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:167:5 | LL | _n %= 0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:123:5 + --> $DIR/arithmetic_side_effects.rs:168:5 + | +LL | _n %= &0; + | ^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:169:5 | LL | _n *= 2; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:126:10 + --> $DIR/arithmetic_side_effects.rs:170:5 + | +LL | _n *= &2; + | ^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:173:10 | LL | _n = _n + 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:127:10 + --> $DIR/arithmetic_side_effects.rs:174:10 + | +LL | _n = _n + &1; + | ^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:175:10 | LL | _n = 1 + _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:128:10 + --> $DIR/arithmetic_side_effects.rs:176:10 + | +LL | _n = &1 + _n; + | ^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:177:10 | LL | _n = _n - 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:129:10 + --> $DIR/arithmetic_side_effects.rs:178:10 + | +LL | _n = _n - &1; + | ^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:179:10 | LL | _n = 1 - _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:130:10 + --> $DIR/arithmetic_side_effects.rs:180:10 + | +LL | _n = &1 - _n; + | ^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:181:10 | LL | _n = _n / 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:131:10 + --> $DIR/arithmetic_side_effects.rs:182:10 + | +LL | _n = _n / &0; + | ^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:183:10 | LL | _n = _n % 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:132:10 + --> $DIR/arithmetic_side_effects.rs:184:10 + | +LL | _n = _n % &0; + | ^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:185:10 | LL | _n = _n * 2; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:133:10 + --> $DIR/arithmetic_side_effects.rs:186:10 + | +LL | _n = _n * &2; + | ^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:187:10 | LL | _n = 2 * _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:136:10 + --> $DIR/arithmetic_side_effects.rs:188:10 + | +LL | _n = &2 * _n; + | ^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:189:10 + | +LL | _n = 23 + &85; + | ^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:190:10 + | +LL | _n = &23 + 85; + | ^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:191:10 + | +LL | _n = &23 + &85; + | ^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:194:13 + | +LL | let _ = Custom + 0; + | ^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:195:13 + | +LL | let _ = Custom + 1; + | ^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:196:13 + | +LL | let _ = Custom + 2; + | ^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:197:13 + | +LL | let _ = Custom + 0.0; + | ^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:198:13 + | +LL | let _ = Custom + 1.0; + | ^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:199:13 + | +LL | let _ = Custom + 2.0; + | ^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:200:13 + | +LL | let _ = Custom - 0; + | ^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:201:13 + | +LL | let _ = Custom - 1; + | ^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:202:13 + | +LL | let _ = Custom - 2; + | ^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:203:13 + | +LL | let _ = Custom - 0.0; + | ^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:204:13 + | +LL | let _ = Custom - 1.0; + | ^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:205:13 + | +LL | let _ = Custom - 2.0; + | ^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:206:13 + | +LL | let _ = Custom / 0; + | ^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:207:13 + | +LL | let _ = Custom / 1; + | ^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:208:13 + | +LL | let _ = Custom / 2; + | ^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:209:13 + | +LL | let _ = Custom / 0.0; + | ^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:210:13 + | +LL | let _ = Custom / 1.0; + | ^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:211:13 + | +LL | let _ = Custom / 2.0; + | ^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:212:13 + | +LL | let _ = Custom * 0; + | ^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:213:13 + | +LL | let _ = Custom * 1; + | ^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:214:13 + | +LL | let _ = Custom * 2; + | ^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:215:13 + | +LL | let _ = Custom * 0.0; + | ^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:216:13 + | +LL | let _ = Custom * 1.0; + | ^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:217:13 + | +LL | let _ = Custom * 2.0; + | ^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:220:10 | LL | _n = -_n; | ^^^ -error: aborting due to 14 previous errors +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:221:10 + | +LL | _n = -&_n; + | ^^^^ + +error: aborting due to 58 previous errors diff --git a/tests/ui/assign_ops2.rs b/tests/ui/assign_ops2.rs index f6d3a8fa3f0d..2c876a96c55d 100644 --- a/tests/ui/assign_ops2.rs +++ b/tests/ui/assign_ops2.rs @@ -1,3 +1,5 @@ +#![allow(clippy::uninlined_format_args)] + #[allow(unused_assignments)] #[warn(clippy::misrefactored_assign_op, clippy::assign_op_pattern)] fn main() { diff --git a/tests/ui/assign_ops2.stderr b/tests/ui/assign_ops2.stderr index 04b1dc93d4a4..25e74602244d 100644 --- a/tests/ui/assign_ops2.stderr +++ b/tests/ui/assign_ops2.stderr @@ -1,5 +1,5 @@ error: variable appears on both sides of an assignment operation - --> $DIR/assign_ops2.rs:5:5 + --> $DIR/assign_ops2.rs:7:5 | LL | a += a + 1; | ^^^^^^^^^^ @@ -15,7 +15,7 @@ LL | a = a + a + 1; | ~~~~~~~~~~~~~ error: variable appears on both sides of an assignment operation - --> $DIR/assign_ops2.rs:6:5 + --> $DIR/assign_ops2.rs:8:5 | LL | a += 1 + a; | ^^^^^^^^^^ @@ -30,7 +30,7 @@ LL | a = a + 1 + a; | ~~~~~~~~~~~~~ error: variable appears on both sides of an assignment operation - --> $DIR/assign_ops2.rs:7:5 + --> $DIR/assign_ops2.rs:9:5 | LL | a -= a - 1; | ^^^^^^^^^^ @@ -45,7 +45,7 @@ LL | a = a - (a - 1); | ~~~~~~~~~~~~~~~ error: variable appears on both sides of an assignment operation - --> $DIR/assign_ops2.rs:8:5 + --> $DIR/assign_ops2.rs:10:5 | LL | a *= a * 99; | ^^^^^^^^^^^ @@ -60,7 +60,7 @@ LL | a = a * a * 99; | ~~~~~~~~~~~~~~ error: variable appears on both sides of an assignment operation - --> $DIR/assign_ops2.rs:9:5 + --> $DIR/assign_ops2.rs:11:5 | LL | a *= 42 * a; | ^^^^^^^^^^^ @@ -75,7 +75,7 @@ LL | a = a * 42 * a; | ~~~~~~~~~~~~~~ error: variable appears on both sides of an assignment operation - --> $DIR/assign_ops2.rs:10:5 + --> $DIR/assign_ops2.rs:12:5 | LL | a /= a / 2; | ^^^^^^^^^^ @@ -90,7 +90,7 @@ LL | a = a / (a / 2); | ~~~~~~~~~~~~~~~ error: variable appears on both sides of an assignment operation - --> $DIR/assign_ops2.rs:11:5 + --> $DIR/assign_ops2.rs:13:5 | LL | a %= a % 5; | ^^^^^^^^^^ @@ -105,7 +105,7 @@ LL | a = a % (a % 5); | ~~~~~~~~~~~~~~~ error: variable appears on both sides of an assignment operation - --> $DIR/assign_ops2.rs:12:5 + --> $DIR/assign_ops2.rs:14:5 | LL | a &= a & 1; | ^^^^^^^^^^ @@ -120,7 +120,7 @@ LL | a = a & a & 1; | ~~~~~~~~~~~~~ error: variable appears on both sides of an assignment operation - --> $DIR/assign_ops2.rs:13:5 + --> $DIR/assign_ops2.rs:15:5 | LL | a *= a * a; | ^^^^^^^^^^ @@ -135,7 +135,7 @@ LL | a = a * a * a; | ~~~~~~~~~~~~~ error: manual implementation of an assign operation - --> $DIR/assign_ops2.rs:50:5 + --> $DIR/assign_ops2.rs:52:5 | LL | buf = buf + cows.clone(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `buf += cows.clone()` diff --git a/tests/ui/auxiliary/proc_macro_attr.rs b/tests/ui/auxiliary/proc_macro_attr.rs index ae2cc2492f41..4914f14b58ff 100644 --- a/tests/ui/auxiliary/proc_macro_attr.rs +++ b/tests/ui/auxiliary/proc_macro_attr.rs @@ -4,7 +4,7 @@ #![crate_type = "proc-macro"] #![feature(repr128, proc_macro_hygiene, proc_macro_quote, box_patterns)] #![allow(incomplete_features)] -#![allow(clippy::useless_conversion)] +#![allow(clippy::useless_conversion, clippy::uninlined_format_args)] extern crate proc_macro; extern crate quote; diff --git a/tests/ui/bind_instead_of_map.fixed b/tests/ui/bind_instead_of_map.fixed index 5815550d7a6a..d94e2ac6072d 100644 --- a/tests/ui/bind_instead_of_map.fixed +++ b/tests/ui/bind_instead_of_map.fixed @@ -1,5 +1,6 @@ // run-rustfix #![deny(clippy::bind_instead_of_map)] +#![allow(clippy::uninlined_format_args)] // need a main anyway, use it get rid of unused warnings too pub fn main() { diff --git a/tests/ui/bind_instead_of_map.rs b/tests/ui/bind_instead_of_map.rs index 623b100a4ce7..86f31f58284a 100644 --- a/tests/ui/bind_instead_of_map.rs +++ b/tests/ui/bind_instead_of_map.rs @@ -1,5 +1,6 @@ // run-rustfix #![deny(clippy::bind_instead_of_map)] +#![allow(clippy::uninlined_format_args)] // need a main anyway, use it get rid of unused warnings too pub fn main() { diff --git a/tests/ui/bind_instead_of_map.stderr b/tests/ui/bind_instead_of_map.stderr index 24c6b7f9ef32..b6a81d21bb20 100644 --- a/tests/ui/bind_instead_of_map.stderr +++ b/tests/ui/bind_instead_of_map.stderr @@ -1,5 +1,5 @@ error: using `Option.and_then(Some)`, which is a no-op - --> $DIR/bind_instead_of_map.rs:8:13 + --> $DIR/bind_instead_of_map.rs:9:13 | LL | let _ = x.and_then(Some); | ^^^^^^^^^^^^^^^^ help: use the expression directly: `x` @@ -11,13 +11,13 @@ LL | #![deny(clippy::bind_instead_of_map)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)` - --> $DIR/bind_instead_of_map.rs:9:13 + --> $DIR/bind_instead_of_map.rs:10:13 | LL | let _ = x.and_then(|o| Some(o + 1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `x.map(|o| o + 1)` error: using `Result.and_then(Ok)`, which is a no-op - --> $DIR/bind_instead_of_map.rs:15:13 + --> $DIR/bind_instead_of_map.rs:16:13 | LL | let _ = x.and_then(Ok); | ^^^^^^^^^^^^^^ help: use the expression directly: `x` diff --git a/tests/ui/borrow_box.rs b/tests/ui/borrow_box.rs index 35ed87b0f182..3b5b6bf4c950 100644 --- a/tests/ui/borrow_box.rs +++ b/tests/ui/borrow_box.rs @@ -1,7 +1,6 @@ #![deny(clippy::borrowed_box)] -#![allow(clippy::disallowed_names)] -#![allow(unused_variables)] -#![allow(dead_code)] +#![allow(dead_code, unused_variables)] +#![allow(clippy::uninlined_format_args, clippy::disallowed_names)] use std::fmt::Display; diff --git a/tests/ui/borrow_box.stderr b/tests/ui/borrow_box.stderr index 3eac32815be3..99cb60a1ead9 100644 --- a/tests/ui/borrow_box.stderr +++ b/tests/ui/borrow_box.stderr @@ -1,5 +1,5 @@ error: you seem to be trying to use `&Box`. Consider using just `&T` - --> $DIR/borrow_box.rs:21:14 + --> $DIR/borrow_box.rs:20:14 | LL | let foo: &Box; | ^^^^^^^^^^ help: try: `&bool` @@ -11,55 +11,55 @@ LL | #![deny(clippy::borrowed_box)] | ^^^^^^^^^^^^^^^^^^^^ error: you seem to be trying to use `&Box`. Consider using just `&T` - --> $DIR/borrow_box.rs:25:10 + --> $DIR/borrow_box.rs:24:10 | LL | foo: &'a Box, | ^^^^^^^^^^^^^ help: try: `&'a bool` error: you seem to be trying to use `&Box`. Consider using just `&T` - --> $DIR/borrow_box.rs:29:17 + --> $DIR/borrow_box.rs:28:17 | LL | fn test4(a: &Box); | ^^^^^^^^^^ help: try: `&bool` error: you seem to be trying to use `&Box`. Consider using just `&T` - --> $DIR/borrow_box.rs:95:25 + --> $DIR/borrow_box.rs:94:25 | LL | pub fn test14(_display: &Box) {} | ^^^^^^^^^^^^^^^^^ help: try: `&dyn Display` error: you seem to be trying to use `&Box`. Consider using just `&T` - --> $DIR/borrow_box.rs:96:25 + --> $DIR/borrow_box.rs:95:25 | LL | pub fn test15(_display: &Box) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&(dyn Display + Send)` error: you seem to be trying to use `&Box`. Consider using just `&T` - --> $DIR/borrow_box.rs:97:29 + --> $DIR/borrow_box.rs:96:29 | LL | pub fn test16<'a>(_display: &'a Box) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&'a (dyn Display + 'a)` error: you seem to be trying to use `&Box`. Consider using just `&T` - --> $DIR/borrow_box.rs:99:25 + --> $DIR/borrow_box.rs:98:25 | LL | pub fn test17(_display: &Box) {} | ^^^^^^^^^^^^^^^^^^ help: try: `&impl Display` error: you seem to be trying to use `&Box`. Consider using just `&T` - --> $DIR/borrow_box.rs:100:25 + --> $DIR/borrow_box.rs:99:25 | LL | pub fn test18(_display: &Box) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&(impl Display + Send)` error: you seem to be trying to use `&Box`. Consider using just `&T` - --> $DIR/borrow_box.rs:101:29 + --> $DIR/borrow_box.rs:100:29 | LL | pub fn test19<'a>(_display: &'a Box) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&'a (impl Display + 'a)` error: you seem to be trying to use `&Box`. Consider using just `&T` - --> $DIR/borrow_box.rs:106:25 + --> $DIR/borrow_box.rs:105:25 | LL | pub fn test20(_display: &Box<(dyn Display + Send)>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&(dyn Display + Send)` diff --git a/tests/ui/box_collection.rs b/tests/ui/box_collection.rs index 0780c8f0586e..4c9947b9ae72 100644 --- a/tests/ui/box_collection.rs +++ b/tests/ui/box_collection.rs @@ -15,7 +15,7 @@ macro_rules! boxit { } fn test_macro() { - boxit!(Vec::new(), Vec); + boxit!(vec![1], Vec); } fn test1(foo: Box>) {} @@ -50,7 +50,7 @@ fn test_local_not_linted() { pub fn pub_test(foo: Box>) {} pub fn pub_test_ret() -> Box> { - Box::new(Vec::new()) + Box::default() } fn main() {} diff --git a/tests/ui/box_default.rs b/tests/ui/box_default.rs new file mode 100644 index 000000000000..dc522705bc62 --- /dev/null +++ b/tests/ui/box_default.rs @@ -0,0 +1,31 @@ +#![warn(clippy::box_default)] + +#[derive(Default)] +struct ImplementsDefault; + +struct OwnDefault; + +impl OwnDefault { + fn default() -> Self { + Self + } +} + +macro_rules! outer { + ($e: expr) => { + $e + }; +} + +fn main() { + let _string: Box = Box::new(Default::default()); + let _byte = Box::new(u8::default()); + let _vec = Box::new(Vec::::new()); + let _impl = Box::new(ImplementsDefault::default()); + let _impl2 = Box::new(::default()); + let _impl3: Box = Box::new(Default::default()); + let _own = Box::new(OwnDefault::default()); // should not lint + let _in_macro = outer!(Box::new(String::new())); + // false negative: default is from different expansion + let _vec2: Box> = Box::new(vec![]); +} diff --git a/tests/ui/box_default.stderr b/tests/ui/box_default.stderr new file mode 100644 index 000000000000..b2030e95acb1 --- /dev/null +++ b/tests/ui/box_default.stderr @@ -0,0 +1,59 @@ +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:21:32 + | +LL | let _string: Box = Box::new(Default::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Box::default()` instead + = note: `-D clippy::box-default` implied by `-D warnings` + +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:22:17 + | +LL | let _byte = Box::new(u8::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Box::default()` instead + +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:23:16 + | +LL | let _vec = Box::new(Vec::::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Box::default()` instead + +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:24:17 + | +LL | let _impl = Box::new(ImplementsDefault::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Box::default()` instead + +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:25:18 + | +LL | let _impl2 = Box::new(::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Box::default()` instead + +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:26:42 + | +LL | let _impl3: Box = Box::new(Default::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Box::default()` instead + +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:28:28 + | +LL | let _in_macro = outer!(Box::new(String::new())); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Box::default()` instead + +error: aborting due to 7 previous errors + diff --git a/tests/ui/branches_sharing_code/shared_at_bottom.rs b/tests/ui/branches_sharing_code/shared_at_bottom.rs index 12f550d9c9a8..6a63008b5a74 100644 --- a/tests/ui/branches_sharing_code/shared_at_bottom.rs +++ b/tests/ui/branches_sharing_code/shared_at_bottom.rs @@ -1,5 +1,6 @@ -#![allow(dead_code, clippy::equatable_if_let)] #![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] +#![allow(dead_code)] +#![allow(clippy::equatable_if_let, clippy::uninlined_format_args)] // This tests the branches_sharing_code lint at the end of blocks diff --git a/tests/ui/branches_sharing_code/shared_at_bottom.stderr b/tests/ui/branches_sharing_code/shared_at_bottom.stderr index b919812e098a..b9b113dc0c6a 100644 --- a/tests/ui/branches_sharing_code/shared_at_bottom.stderr +++ b/tests/ui/branches_sharing_code/shared_at_bottom.stderr @@ -1,5 +1,5 @@ error: all if blocks contain the same code at the end - --> $DIR/shared_at_bottom.rs:30:5 + --> $DIR/shared_at_bottom.rs:31:5 | LL | / let result = false; LL | | println!("Block end!"); @@ -9,7 +9,7 @@ LL | | }; | = note: the end suggestion probably needs some adjustments to use the expression result correctly note: the lint level is defined here - --> $DIR/shared_at_bottom.rs:2:36 + --> $DIR/shared_at_bottom.rs:1:36 | LL | #![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -22,7 +22,7 @@ LL ~ result; | error: all if blocks contain the same code at the end - --> $DIR/shared_at_bottom.rs:48:5 + --> $DIR/shared_at_bottom.rs:49:5 | LL | / println!("Same end of block"); LL | | } @@ -35,7 +35,7 @@ LL + println!("Same end of block"); | error: all if blocks contain the same code at the end - --> $DIR/shared_at_bottom.rs:65:5 + --> $DIR/shared_at_bottom.rs:66:5 | LL | / println!( LL | | "I'm moveable because I know: `outer_scope_value`: '{}'", @@ -54,7 +54,7 @@ LL + ); | error: all if blocks contain the same code at the end - --> $DIR/shared_at_bottom.rs:77:9 + --> $DIR/shared_at_bottom.rs:78:9 | LL | / println!("Hello World"); LL | | } @@ -67,7 +67,7 @@ LL + println!("Hello World"); | error: all if blocks contain the same code at the end - --> $DIR/shared_at_bottom.rs:93:5 + --> $DIR/shared_at_bottom.rs:94:5 | LL | / let later_used_value = "A string value"; LL | | println!("{}", later_used_value); @@ -84,7 +84,7 @@ LL + println!("{}", later_used_value); | error: all if blocks contain the same code at the end - --> $DIR/shared_at_bottom.rs:106:5 + --> $DIR/shared_at_bottom.rs:107:5 | LL | / let simple_examples = "I now identify as a &str :)"; LL | | println!("This is the new simple_example: {}", simple_examples); @@ -100,7 +100,7 @@ LL + println!("This is the new simple_example: {}", simple_examples); | error: all if blocks contain the same code at the end - --> $DIR/shared_at_bottom.rs:171:5 + --> $DIR/shared_at_bottom.rs:172:5 | LL | / x << 2 LL | | }; @@ -114,7 +114,7 @@ LL ~ x << 2; | error: all if blocks contain the same code at the end - --> $DIR/shared_at_bottom.rs:178:5 + --> $DIR/shared_at_bottom.rs:179:5 | LL | / x * 4 LL | | } @@ -128,7 +128,7 @@ LL + x * 4 | error: all if blocks contain the same code at the end - --> $DIR/shared_at_bottom.rs:190:44 + --> $DIR/shared_at_bottom.rs:191:44 | LL | if x == 17 { b = 1; a = 0x99; } else { a = 0x99; } | ^^^^^^^^^^^ diff --git a/tests/ui/branches_sharing_code/shared_at_top.rs b/tests/ui/branches_sharing_code/shared_at_top.rs index bdeb0a39558d..9e0b99f16665 100644 --- a/tests/ui/branches_sharing_code/shared_at_top.rs +++ b/tests/ui/branches_sharing_code/shared_at_top.rs @@ -1,5 +1,6 @@ -#![allow(dead_code, clippy::mixed_read_write_in_expression)] -#![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] +#![deny(clippy::branches_sharing_code, clippy::if_same_then_else)] +#![allow(dead_code)] +#![allow(clippy::mixed_read_write_in_expression, clippy::uninlined_format_args)] // This tests the branches_sharing_code lint at the start of blocks diff --git a/tests/ui/branches_sharing_code/shared_at_top.stderr b/tests/ui/branches_sharing_code/shared_at_top.stderr index fb3da641fb5e..3e3242a75d30 100644 --- a/tests/ui/branches_sharing_code/shared_at_top.stderr +++ b/tests/ui/branches_sharing_code/shared_at_top.stderr @@ -1,15 +1,15 @@ error: all if blocks contain the same code at the start - --> $DIR/shared_at_top.rs:10:5 + --> $DIR/shared_at_top.rs:11:5 | LL | / if true { LL | | println!("Hello World!"); | |_________________________________^ | note: the lint level is defined here - --> $DIR/shared_at_top.rs:2:36 + --> $DIR/shared_at_top.rs:1:9 | -LL | #![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![deny(clippy::branches_sharing_code, clippy::if_same_then_else)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider moving these statements before the if | LL ~ println!("Hello World!"); @@ -17,7 +17,7 @@ LL + if true { | error: all if blocks contain the same code at the start - --> $DIR/shared_at_top.rs:19:5 + --> $DIR/shared_at_top.rs:20:5 | LL | / if x == 0 { LL | | let y = 9; @@ -35,7 +35,7 @@ LL + if x == 0 { | error: all if blocks contain the same code at the start - --> $DIR/shared_at_top.rs:40:5 + --> $DIR/shared_at_top.rs:41:5 | LL | / let _ = if x == 7 { LL | | let y = 16; @@ -48,7 +48,7 @@ LL + let _ = if x == 7 { | error: all if blocks contain the same code at the start - --> $DIR/shared_at_top.rs:58:5 + --> $DIR/shared_at_top.rs:59:5 | LL | / if x == 10 { LL | | let used_value_name = "Different type"; @@ -64,7 +64,7 @@ LL + if x == 10 { | error: all if blocks contain the same code at the start - --> $DIR/shared_at_top.rs:72:5 + --> $DIR/shared_at_top.rs:73:5 | LL | / if x == 11 { LL | | let can_be_overridden = "Move me"; @@ -80,7 +80,7 @@ LL + if x == 11 { | error: all if blocks contain the same code at the start - --> $DIR/shared_at_top.rs:88:5 + --> $DIR/shared_at_top.rs:89:5 | LL | / if x == 2020 { LL | | println!("This should trigger the `SHARED_CODE_IN_IF_BLOCKS` lint."); @@ -95,7 +95,7 @@ LL + if x == 2020 { | error: this `if` has identical blocks - --> $DIR/shared_at_top.rs:96:18 + --> $DIR/shared_at_top.rs:97:18 | LL | if x == 2019 { | __________________^ @@ -104,7 +104,7 @@ LL | | } else { | |_____^ | note: same as this - --> $DIR/shared_at_top.rs:98:12 + --> $DIR/shared_at_top.rs:99:12 | LL | } else { | ____________^ @@ -112,10 +112,10 @@ LL | | println!("This should trigger `IS_SAME_THAN_ELSE` as usual"); LL | | } | |_____^ note: the lint level is defined here - --> $DIR/shared_at_top.rs:2:9 + --> $DIR/shared_at_top.rs:1:40 | -LL | #![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![deny(clippy::branches_sharing_code, clippy::if_same_then_else)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 7 previous errors diff --git a/tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs b/tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs index deefdad32c9d..93b8c6e10dae 100644 --- a/tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs +++ b/tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs @@ -1,5 +1,6 @@ +#![deny(clippy::branches_sharing_code, clippy::if_same_then_else)] #![allow(dead_code)] -#![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] +#![allow(clippy::uninlined_format_args)] // branches_sharing_code at the top and bottom of the if blocks diff --git a/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr b/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr index 3edb8e53a7d4..ccd697a42155 100644 --- a/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr +++ b/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr @@ -1,5 +1,5 @@ error: all if blocks contain the same code at both the start and the end - --> $DIR/shared_at_top_and_bottom.rs:16:5 + --> $DIR/shared_at_top_and_bottom.rs:17:5 | LL | / if x == 7 { LL | | let t = 7; @@ -8,16 +8,16 @@ LL | | let _overlap_end = 2 * t; | |_________________________________^ | note: this code is shared at the end - --> $DIR/shared_at_top_and_bottom.rs:28:5 + --> $DIR/shared_at_top_and_bottom.rs:29:5 | LL | / let _u = 9; LL | | } | |_____^ note: the lint level is defined here - --> $DIR/shared_at_top_and_bottom.rs:2:36 + --> $DIR/shared_at_top_and_bottom.rs:1:9 | -LL | #![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![deny(clippy::branches_sharing_code, clippy::if_same_then_else)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider moving these statements before the if | LL ~ let t = 7; @@ -32,7 +32,7 @@ LL + let _u = 9; | error: all if blocks contain the same code at both the start and the end - --> $DIR/shared_at_top_and_bottom.rs:32:5 + --> $DIR/shared_at_top_and_bottom.rs:33:5 | LL | / if x == 99 { LL | | let r = 7; @@ -41,7 +41,7 @@ LL | | let _overlap_middle = r * r; | |____________________________________^ | note: this code is shared at the end - --> $DIR/shared_at_top_and_bottom.rs:43:5 + --> $DIR/shared_at_top_and_bottom.rs:44:5 | LL | / let _overlap_end = r * r * r; LL | | let z = "end"; @@ -63,7 +63,7 @@ LL + let z = "end"; | error: all if blocks contain the same code at both the start and the end - --> $DIR/shared_at_top_and_bottom.rs:61:5 + --> $DIR/shared_at_top_and_bottom.rs:62:5 | LL | / if (x > 7 && y < 13) || (x + y) % 2 == 1 { LL | | let a = 0xcafe; @@ -72,7 +72,7 @@ LL | | let e_id = gen_id(a, b); | |________________________________^ | note: this code is shared at the end - --> $DIR/shared_at_top_and_bottom.rs:81:5 + --> $DIR/shared_at_top_and_bottom.rs:82:5 | LL | / let pack = DataPack { LL | | id: e_id, @@ -102,14 +102,14 @@ LL + process_data(pack); | error: all if blocks contain the same code at both the start and the end - --> $DIR/shared_at_top_and_bottom.rs:94:5 + --> $DIR/shared_at_top_and_bottom.rs:95:5 | LL | / let _ = if x == 7 { LL | | let _ = 19; | |___________________^ | note: this code is shared at the end - --> $DIR/shared_at_top_and_bottom.rs:103:5 + --> $DIR/shared_at_top_and_bottom.rs:104:5 | LL | / x << 2 LL | | }; @@ -127,14 +127,14 @@ LL ~ x << 2; | error: all if blocks contain the same code at both the start and the end - --> $DIR/shared_at_top_and_bottom.rs:106:5 + --> $DIR/shared_at_top_and_bottom.rs:107:5 | LL | / if x == 9 { LL | | let _ = 17; | |___________________^ | note: this code is shared at the end - --> $DIR/shared_at_top_and_bottom.rs:115:5 + --> $DIR/shared_at_top_and_bottom.rs:116:5 | LL | / x * 4 LL | | } diff --git a/tests/ui/branches_sharing_code/valid_if_blocks.rs b/tests/ui/branches_sharing_code/valid_if_blocks.rs index a26141be2373..2d6055eb6c42 100644 --- a/tests/ui/branches_sharing_code/valid_if_blocks.rs +++ b/tests/ui/branches_sharing_code/valid_if_blocks.rs @@ -1,5 +1,6 @@ -#![allow(dead_code, clippy::mixed_read_write_in_expression)] -#![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] +#![deny(clippy::branches_sharing_code, clippy::if_same_then_else)] +#![allow(dead_code)] +#![allow(clippy::mixed_read_write_in_expression, clippy::uninlined_format_args)] // This tests valid if blocks that shouldn't trigger the lint diff --git a/tests/ui/branches_sharing_code/valid_if_blocks.stderr b/tests/ui/branches_sharing_code/valid_if_blocks.stderr index d2acd6d9735c..ce7fff0122f1 100644 --- a/tests/ui/branches_sharing_code/valid_if_blocks.stderr +++ b/tests/ui/branches_sharing_code/valid_if_blocks.stderr @@ -1,5 +1,5 @@ error: this `if` has identical blocks - --> $DIR/valid_if_blocks.rs:104:14 + --> $DIR/valid_if_blocks.rs:105:14 | LL | if false { | ______________^ @@ -7,20 +7,20 @@ LL | | } else { | |_____^ | note: same as this - --> $DIR/valid_if_blocks.rs:105:12 + --> $DIR/valid_if_blocks.rs:106:12 | LL | } else { | ____________^ LL | | } | |_____^ note: the lint level is defined here - --> $DIR/valid_if_blocks.rs:2:9 + --> $DIR/valid_if_blocks.rs:1:40 | -LL | #![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![deny(clippy::branches_sharing_code, clippy::if_same_then_else)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: this `if` has identical blocks - --> $DIR/valid_if_blocks.rs:115:15 + --> $DIR/valid_if_blocks.rs:116:15 | LL | if x == 0 { | _______________^ @@ -31,7 +31,7 @@ LL | | } else { | |_____^ | note: same as this - --> $DIR/valid_if_blocks.rs:119:12 + --> $DIR/valid_if_blocks.rs:120:12 | LL | } else { | ____________^ @@ -42,19 +42,19 @@ LL | | } | |_____^ error: this `if` has identical blocks - --> $DIR/valid_if_blocks.rs:126:23 + --> $DIR/valid_if_blocks.rs:127:23 | LL | let _ = if x == 6 { 7 } else { 7 }; | ^^^^^ | note: same as this - --> $DIR/valid_if_blocks.rs:126:34 + --> $DIR/valid_if_blocks.rs:127:34 | LL | let _ = if x == 6 { 7 } else { 7 }; | ^^^^^ error: this `if` has identical blocks - --> $DIR/valid_if_blocks.rs:132:23 + --> $DIR/valid_if_blocks.rs:133:23 | LL | } else if x == 68 { | _______________________^ @@ -66,7 +66,7 @@ LL | | } else { | |_____^ | note: same as this - --> $DIR/valid_if_blocks.rs:137:12 + --> $DIR/valid_if_blocks.rs:138:12 | LL | } else { | ____________^ @@ -78,7 +78,7 @@ LL | | }; | |_____^ error: this `if` has identical blocks - --> $DIR/valid_if_blocks.rs:146:23 + --> $DIR/valid_if_blocks.rs:147:23 | LL | } else if x == 68 { | _______________________^ @@ -88,7 +88,7 @@ LL | | } else { | |_____^ | note: same as this - --> $DIR/valid_if_blocks.rs:149:12 + --> $DIR/valid_if_blocks.rs:150:12 | LL | } else { | ____________^ diff --git a/tests/ui/cast_abs_to_unsigned.fixed b/tests/ui/cast_abs_to_unsigned.fixed index 7ecefd7b1343..a37f3fec20f1 100644 --- a/tests/ui/cast_abs_to_unsigned.fixed +++ b/tests/ui/cast_abs_to_unsigned.fixed @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::cast_abs_to_unsigned)] +#![allow(clippy::uninlined_format_args)] fn main() { let x: i32 = -42; diff --git a/tests/ui/cast_abs_to_unsigned.rs b/tests/ui/cast_abs_to_unsigned.rs index 30c603fca9a1..5706930af5a0 100644 --- a/tests/ui/cast_abs_to_unsigned.rs +++ b/tests/ui/cast_abs_to_unsigned.rs @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::cast_abs_to_unsigned)] +#![allow(clippy::uninlined_format_args)] fn main() { let x: i32 = -42; diff --git a/tests/ui/cast_abs_to_unsigned.stderr b/tests/ui/cast_abs_to_unsigned.stderr index 045537745267..7cea11c183d2 100644 --- a/tests/ui/cast_abs_to_unsigned.stderr +++ b/tests/ui/cast_abs_to_unsigned.stderr @@ -1,5 +1,5 @@ error: casting the result of `i32::abs()` to u32 - --> $DIR/cast_abs_to_unsigned.rs:6:18 + --> $DIR/cast_abs_to_unsigned.rs:7:18 | LL | let y: u32 = x.abs() as u32; | ^^^^^^^^^^^^^^ help: replace with: `x.unsigned_abs()` @@ -7,97 +7,97 @@ LL | let y: u32 = x.abs() as u32; = note: `-D clippy::cast-abs-to-unsigned` implied by `-D warnings` error: casting the result of `i32::abs()` to usize - --> $DIR/cast_abs_to_unsigned.rs:10:20 + --> $DIR/cast_abs_to_unsigned.rs:11:20 | LL | let _: usize = a.abs() as usize; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i32::abs()` to usize - --> $DIR/cast_abs_to_unsigned.rs:11:20 + --> $DIR/cast_abs_to_unsigned.rs:12:20 | LL | let _: usize = a.abs() as _; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i32::abs()` to usize - --> $DIR/cast_abs_to_unsigned.rs:12:13 + --> $DIR/cast_abs_to_unsigned.rs:13:13 | LL | let _ = a.abs() as usize; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to usize - --> $DIR/cast_abs_to_unsigned.rs:15:13 + --> $DIR/cast_abs_to_unsigned.rs:16:13 | LL | let _ = a.abs() as usize; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u8 - --> $DIR/cast_abs_to_unsigned.rs:16:13 + --> $DIR/cast_abs_to_unsigned.rs:17:13 | LL | let _ = a.abs() as u8; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u16 - --> $DIR/cast_abs_to_unsigned.rs:17:13 + --> $DIR/cast_abs_to_unsigned.rs:18:13 | LL | let _ = a.abs() as u16; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u32 - --> $DIR/cast_abs_to_unsigned.rs:18:13 + --> $DIR/cast_abs_to_unsigned.rs:19:13 | LL | let _ = a.abs() as u32; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u64 - --> $DIR/cast_abs_to_unsigned.rs:19:13 + --> $DIR/cast_abs_to_unsigned.rs:20:13 | LL | let _ = a.abs() as u64; | ^^^^^^^^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u128 - --> $DIR/cast_abs_to_unsigned.rs:20:13 + --> $DIR/cast_abs_to_unsigned.rs:21:13 | LL | let _ = a.abs() as u128; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to usize - --> $DIR/cast_abs_to_unsigned.rs:23:13 + --> $DIR/cast_abs_to_unsigned.rs:24:13 | LL | let _ = a.abs() as usize; | ^^^^^^^^^^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to u8 - --> $DIR/cast_abs_to_unsigned.rs:24:13 + --> $DIR/cast_abs_to_unsigned.rs:25:13 | LL | let _ = a.abs() as u8; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to u16 - --> $DIR/cast_abs_to_unsigned.rs:25:13 + --> $DIR/cast_abs_to_unsigned.rs:26:13 | LL | let _ = a.abs() as u16; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to u32 - --> $DIR/cast_abs_to_unsigned.rs:26:13 + --> $DIR/cast_abs_to_unsigned.rs:27:13 | LL | let _ = a.abs() as u32; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to u64 - --> $DIR/cast_abs_to_unsigned.rs:27:13 + --> $DIR/cast_abs_to_unsigned.rs:28:13 | LL | let _ = a.abs() as u64; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to u128 - --> $DIR/cast_abs_to_unsigned.rs:28:13 + --> $DIR/cast_abs_to_unsigned.rs:29:13 | LL | let _ = a.abs() as u128; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u32 - --> $DIR/cast_abs_to_unsigned.rs:30:13 + --> $DIR/cast_abs_to_unsigned.rs:31:13 | LL | let _ = (x as i64 - y as i64).abs() as u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `(x as i64 - y as i64).unsigned_abs()` diff --git a/tests/ui/collapsible_match.rs b/tests/ui/collapsible_match.rs index 603ae7dc9eb1..7d53e08345d3 100644 --- a/tests/ui/collapsible_match.rs +++ b/tests/ui/collapsible_match.rs @@ -1,9 +1,10 @@ #![warn(clippy::collapsible_match)] #![allow( + clippy::equatable_if_let, clippy::needless_return, clippy::no_effect, clippy::single_match, - clippy::equatable_if_let + clippy::uninlined_format_args )] fn lint_cases(opt_opt: Option>, res_opt: Result, String>) { diff --git a/tests/ui/collapsible_match.stderr b/tests/ui/collapsible_match.stderr index 33562e8401ca..2580bef58091 100644 --- a/tests/ui/collapsible_match.stderr +++ b/tests/ui/collapsible_match.stderr @@ -1,5 +1,5 @@ error: this `match` can be collapsed into the outer `match` - --> $DIR/collapsible_match.rs:12:20 + --> $DIR/collapsible_match.rs:13:20 | LL | Ok(val) => match val { | ____________________^ @@ -9,7 +9,7 @@ LL | | }, | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> $DIR/collapsible_match.rs:12:12 + --> $DIR/collapsible_match.rs:13:12 | LL | Ok(val) => match val { | ^^^ replace this binding @@ -18,7 +18,7 @@ LL | Some(n) => foo(n), = note: `-D clippy::collapsible-match` implied by `-D warnings` error: this `match` can be collapsed into the outer `match` - --> $DIR/collapsible_match.rs:21:20 + --> $DIR/collapsible_match.rs:22:20 | LL | Ok(val) => match val { | ____________________^ @@ -28,7 +28,7 @@ LL | | }, | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> $DIR/collapsible_match.rs:21:12 + --> $DIR/collapsible_match.rs:22:12 | LL | Ok(val) => match val { | ^^^ replace this binding @@ -36,7 +36,7 @@ LL | Some(n) => foo(n), | ^^^^^^^ with this pattern error: this `if let` can be collapsed into the outer `if let` - --> $DIR/collapsible_match.rs:30:9 + --> $DIR/collapsible_match.rs:31:9 | LL | / if let Some(n) = val { LL | | take(n); @@ -44,7 +44,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> $DIR/collapsible_match.rs:29:15 + --> $DIR/collapsible_match.rs:30:15 | LL | if let Ok(val) = res_opt { | ^^^ replace this binding @@ -52,7 +52,7 @@ LL | if let Some(n) = val { | ^^^^^^^ with this pattern error: this `if let` can be collapsed into the outer `if let` - --> $DIR/collapsible_match.rs:37:9 + --> $DIR/collapsible_match.rs:38:9 | LL | / if let Some(n) = val { LL | | take(n); @@ -62,7 +62,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> $DIR/collapsible_match.rs:36:15 + --> $DIR/collapsible_match.rs:37:15 | LL | if let Ok(val) = res_opt { | ^^^ replace this binding @@ -70,7 +70,7 @@ LL | if let Some(n) = val { | ^^^^^^^ with this pattern error: this `match` can be collapsed into the outer `if let` - --> $DIR/collapsible_match.rs:48:9 + --> $DIR/collapsible_match.rs:49:9 | LL | / match val { LL | | Some(n) => foo(n), @@ -79,7 +79,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> $DIR/collapsible_match.rs:47:15 + --> $DIR/collapsible_match.rs:48:15 | LL | if let Ok(val) = res_opt { | ^^^ replace this binding @@ -88,7 +88,7 @@ LL | Some(n) => foo(n), | ^^^^^^^ with this pattern error: this `if let` can be collapsed into the outer `match` - --> $DIR/collapsible_match.rs:57:13 + --> $DIR/collapsible_match.rs:58:13 | LL | / if let Some(n) = val { LL | | take(n); @@ -96,7 +96,7 @@ LL | | } | |_____________^ | help: the outer pattern can be modified to include the inner pattern - --> $DIR/collapsible_match.rs:56:12 + --> $DIR/collapsible_match.rs:57:12 | LL | Ok(val) => { | ^^^ replace this binding @@ -104,7 +104,7 @@ LL | if let Some(n) = val { | ^^^^^^^ with this pattern error: this `match` can be collapsed into the outer `if let` - --> $DIR/collapsible_match.rs:66:9 + --> $DIR/collapsible_match.rs:67:9 | LL | / match val { LL | | Some(n) => foo(n), @@ -113,7 +113,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> $DIR/collapsible_match.rs:65:15 + --> $DIR/collapsible_match.rs:66:15 | LL | if let Ok(val) = res_opt { | ^^^ replace this binding @@ -122,7 +122,7 @@ LL | Some(n) => foo(n), | ^^^^^^^ with this pattern error: this `if let` can be collapsed into the outer `match` - --> $DIR/collapsible_match.rs:77:13 + --> $DIR/collapsible_match.rs:78:13 | LL | / if let Some(n) = val { LL | | take(n); @@ -132,7 +132,7 @@ LL | | } | |_____________^ | help: the outer pattern can be modified to include the inner pattern - --> $DIR/collapsible_match.rs:76:12 + --> $DIR/collapsible_match.rs:77:12 | LL | Ok(val) => { | ^^^ replace this binding @@ -140,7 +140,7 @@ LL | if let Some(n) = val { | ^^^^^^^ with this pattern error: this `match` can be collapsed into the outer `match` - --> $DIR/collapsible_match.rs:88:20 + --> $DIR/collapsible_match.rs:89:20 | LL | Ok(val) => match val { | ____________________^ @@ -150,7 +150,7 @@ LL | | }, | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> $DIR/collapsible_match.rs:88:12 + --> $DIR/collapsible_match.rs:89:12 | LL | Ok(val) => match val { | ^^^ replace this binding @@ -158,7 +158,7 @@ LL | Some(n) => foo(n), | ^^^^^^^ with this pattern error: this `match` can be collapsed into the outer `match` - --> $DIR/collapsible_match.rs:97:22 + --> $DIR/collapsible_match.rs:98:22 | LL | Some(val) => match val { | ______________________^ @@ -168,7 +168,7 @@ LL | | }, | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> $DIR/collapsible_match.rs:97:14 + --> $DIR/collapsible_match.rs:98:14 | LL | Some(val) => match val { | ^^^ replace this binding diff --git a/tests/ui/crashes/ice-4775.rs b/tests/ui/crashes/ice-4775.rs index 405e3039e7d0..f693aafd1cbb 100644 --- a/tests/ui/crashes/ice-4775.rs +++ b/tests/ui/crashes/ice-4775.rs @@ -1,3 +1,5 @@ +#![allow(clippy::uninlined_format_args)] + pub struct ArrayWrapper([usize; N]); impl ArrayWrapper<{ N }> { diff --git a/tests/ui/crashes/ice-9445.rs b/tests/ui/crashes/ice-9445.rs new file mode 100644 index 000000000000..c67b22f6f8c4 --- /dev/null +++ b/tests/ui/crashes/ice-9445.rs @@ -0,0 +1,3 @@ +const UNINIT: core::mem::MaybeUninit> = core::mem::MaybeUninit::uninit(); + +fn main() {} diff --git a/tests/ui/crashes/ice-9459.rs b/tests/ui/crashes/ice-9459.rs new file mode 100644 index 000000000000..55615124fcf0 --- /dev/null +++ b/tests/ui/crashes/ice-9459.rs @@ -0,0 +1,5 @@ +#![feature(unsized_fn_params)] + +pub fn f0(_f: dyn FnOnce()) {} + +fn main() {} diff --git a/tests/ui/crashes/regressions.rs b/tests/ui/crashes/regressions.rs index 55a8b403407c..b34997d4ee0b 100644 --- a/tests/ui/crashes/regressions.rs +++ b/tests/ui/crashes/regressions.rs @@ -1,4 +1,4 @@ -#![allow(clippy::disallowed_names)] +#![allow(clippy::disallowed_names, clippy::uninlined_format_args)] pub fn foo(bar: *const u8) { println!("{:#p}", bar); diff --git a/tests/ui/default_trait_access.fixed b/tests/ui/default_trait_access.fixed index fce66eb17596..eedd43619392 100644 --- a/tests/ui/default_trait_access.fixed +++ b/tests/ui/default_trait_access.fixed @@ -1,8 +1,8 @@ // run-rustfix // aux-build: proc_macro_with_span.rs - -#![allow(unused_imports, dead_code)] #![deny(clippy::default_trait_access)] +#![allow(dead_code, unused_imports)] +#![allow(clippy::uninlined_format_args)] extern crate proc_macro_with_span; diff --git a/tests/ui/default_trait_access.rs b/tests/ui/default_trait_access.rs index 3e8e898b7bc6..11d4bc5c5f02 100644 --- a/tests/ui/default_trait_access.rs +++ b/tests/ui/default_trait_access.rs @@ -1,8 +1,8 @@ // run-rustfix // aux-build: proc_macro_with_span.rs - -#![allow(unused_imports, dead_code)] #![deny(clippy::default_trait_access)] +#![allow(dead_code, unused_imports)] +#![allow(clippy::uninlined_format_args)] extern crate proc_macro_with_span; diff --git a/tests/ui/default_trait_access.stderr b/tests/ui/default_trait_access.stderr index 3493de37a55b..49b2dde3f1e8 100644 --- a/tests/ui/default_trait_access.stderr +++ b/tests/ui/default_trait_access.stderr @@ -5,7 +5,7 @@ LL | let s1: String = Default::default(); | ^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()` | note: the lint level is defined here - --> $DIR/default_trait_access.rs:5:9 + --> $DIR/default_trait_access.rs:3:9 | LL | #![deny(clippy::default_trait_access)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/doc_link_with_quotes.rs b/tests/ui/doc_link_with_quotes.rs index ab52fb1a4a69..17c04c34e246 100644 --- a/tests/ui/doc_link_with_quotes.rs +++ b/tests/ui/doc_link_with_quotes.rs @@ -4,9 +4,14 @@ fn main() { foo() } -/// Calls ['bar'] +/// Calls ['bar'] uselessly pub fn foo() { bar() } +/// # Examples +/// This demonstrates issue \#8961 +/// ``` +/// let _ = vec!['w', 'a', 't']; +/// ``` pub fn bar() {} diff --git a/tests/ui/doc_link_with_quotes.stderr b/tests/ui/doc_link_with_quotes.stderr index bf6d57d8afe8..ea730e667d65 100644 --- a/tests/ui/doc_link_with_quotes.stderr +++ b/tests/ui/doc_link_with_quotes.stderr @@ -1,8 +1,8 @@ error: possible intra-doc link using quotes instead of backticks - --> $DIR/doc_link_with_quotes.rs:7:1 + --> $DIR/doc_link_with_quotes.rs:7:12 | -LL | /// Calls ['bar'] - | ^^^^^^^^^^^^^^^^^ +LL | /// Calls ['bar'] uselessly + | ^^^^^ | = note: `-D clippy::doc-link-with-quotes` implied by `-D warnings` diff --git a/tests/ui/drop_forget_copy.rs b/tests/ui/drop_forget_copy.rs index 7c7a9ecff67f..a7276dd59f43 100644 --- a/tests/ui/drop_forget_copy.rs +++ b/tests/ui/drop_forget_copy.rs @@ -64,3 +64,23 @@ fn main() { let a5 = a1.clone(); forget(a5); } + +#[allow(unused)] +#[allow(clippy::unit_cmp)] +fn issue9482(x: u8) { + fn println_and(t: T) -> T { + println!("foo"); + t + } + + match x { + 0 => drop(println_and(12)), // Don't lint (copy type), we only care about side-effects + 1 => drop(println_and(String::new())), // Don't lint (no copy type), we only care about side-effects + 2 => { + drop(println_and(13)); // Lint, even if we only care about the side-effect, it's already in a block + }, + 3 if drop(println_and(14)) == () => (), // Lint, idiomatic use is only in body of `Arm` + 4 => drop(2), // Lint, not a fn/method call + _ => (), + } +} diff --git a/tests/ui/drop_forget_copy.stderr b/tests/ui/drop_forget_copy.stderr index 21adb3b3a504..90bef1c3c439 100644 --- a/tests/ui/drop_forget_copy.stderr +++ b/tests/ui/drop_forget_copy.stderr @@ -72,5 +72,41 @@ note: argument has type `SomeStruct` LL | forget(s4); | ^^ -error: aborting due to 6 previous errors +error: calls to `std::mem::drop` with a value that implements `Copy`. Dropping a copy leaves the original intact + --> $DIR/drop_forget_copy.rs:80:13 + | +LL | drop(println_and(13)); // Lint, even if we only care about the side-effect, it's already in a block + | ^^^^^^^^^^^^^^^^^^^^^ + | +note: argument has type `i32` + --> $DIR/drop_forget_copy.rs:80:18 + | +LL | drop(println_and(13)); // Lint, even if we only care about the side-effect, it's already in a block + | ^^^^^^^^^^^^^^^ + +error: calls to `std::mem::drop` with a value that implements `Copy`. Dropping a copy leaves the original intact + --> $DIR/drop_forget_copy.rs:82:14 + | +LL | 3 if drop(println_and(14)) == () => (), // Lint, idiomatic use is only in body of `Arm` + | ^^^^^^^^^^^^^^^^^^^^^ + | +note: argument has type `i32` + --> $DIR/drop_forget_copy.rs:82:19 + | +LL | 3 if drop(println_and(14)) == () => (), // Lint, idiomatic use is only in body of `Arm` + | ^^^^^^^^^^^^^^^ + +error: calls to `std::mem::drop` with a value that implements `Copy`. Dropping a copy leaves the original intact + --> $DIR/drop_forget_copy.rs:83:14 + | +LL | 4 => drop(2), // Lint, not a fn/method call + | ^^^^^^^ + | +note: argument has type `i32` + --> $DIR/drop_forget_copy.rs:83:19 + | +LL | 4 => drop(2), // Lint, not a fn/method call + | ^ + +error: aborting due to 9 previous errors diff --git a/tests/ui/eta.fixed b/tests/ui/eta.fixed index f8d559bf226f..a9cc80aaaf62 100644 --- a/tests/ui/eta.fixed +++ b/tests/ui/eta.fixed @@ -1,14 +1,14 @@ // run-rustfix - -#![allow( - unused, - clippy::no_effect, - clippy::redundant_closure_call, - clippy::needless_pass_by_value, - clippy::option_map_unit_fn, - clippy::needless_borrow -)] #![warn(clippy::redundant_closure, clippy::redundant_closure_for_method_calls)] +#![allow(unused)] +#![allow( + clippy::needless_borrow, + clippy::needless_pass_by_value, + clippy::no_effect, + clippy::option_map_unit_fn, + clippy::redundant_closure_call, + clippy::uninlined_format_args +)] use std::path::{Path, PathBuf}; @@ -303,3 +303,16 @@ fn not_general_enough() { fn f(_: impl FnMut(&Path) -> std::io::Result<()>) {} f(|path| std::fs::remove_file(path)); } + +// https://github.com/rust-lang/rust-clippy/issues/9369 +pub fn mutable_impl_fn_mut(mut f: impl FnMut(), mut f_used_once: impl FnMut()) -> impl FnMut() { + fn takes_fn_mut(_: impl FnMut()) {} + takes_fn_mut(&mut f); + + fn takes_fn_once(_: impl FnOnce()) {} + takes_fn_once(&mut f); + + f(); + + move || takes_fn_mut(&mut f_used_once) +} diff --git a/tests/ui/eta.rs b/tests/ui/eta.rs index f0fb55a1e5f0..cc99906ccd66 100644 --- a/tests/ui/eta.rs +++ b/tests/ui/eta.rs @@ -1,14 +1,14 @@ // run-rustfix - -#![allow( - unused, - clippy::no_effect, - clippy::redundant_closure_call, - clippy::needless_pass_by_value, - clippy::option_map_unit_fn, - clippy::needless_borrow -)] #![warn(clippy::redundant_closure, clippy::redundant_closure_for_method_calls)] +#![allow(unused)] +#![allow( + clippy::needless_borrow, + clippy::needless_pass_by_value, + clippy::no_effect, + clippy::option_map_unit_fn, + clippy::redundant_closure_call, + clippy::uninlined_format_args +)] use std::path::{Path, PathBuf}; @@ -303,3 +303,16 @@ fn not_general_enough() { fn f(_: impl FnMut(&Path) -> std::io::Result<()>) {} f(|path| std::fs::remove_file(path)); } + +// https://github.com/rust-lang/rust-clippy/issues/9369 +pub fn mutable_impl_fn_mut(mut f: impl FnMut(), mut f_used_once: impl FnMut()) -> impl FnMut() { + fn takes_fn_mut(_: impl FnMut()) {} + takes_fn_mut(|| f()); + + fn takes_fn_once(_: impl FnOnce()) {} + takes_fn_once(|| f()); + + f(); + + move || takes_fn_mut(|| f_used_once()) +} diff --git a/tests/ui/eta.stderr b/tests/ui/eta.stderr index bf2e97e744ab..434706b7e258 100644 --- a/tests/ui/eta.stderr +++ b/tests/ui/eta.stderr @@ -116,5 +116,23 @@ error: redundant closure LL | Some(1).map(|n| in_loop(n)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `in_loop` -error: aborting due to 19 previous errors +error: redundant closure + --> $DIR/eta.rs:310:18 + | +LL | takes_fn_mut(|| f()); + | ^^^^^^ help: replace the closure with the function itself: `&mut f` + +error: redundant closure + --> $DIR/eta.rs:313:19 + | +LL | takes_fn_once(|| f()); + | ^^^^^^ help: replace the closure with the function itself: `&mut f` + +error: redundant closure + --> $DIR/eta.rs:317:26 + | +LL | move || takes_fn_mut(|| f_used_once()) + | ^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut f_used_once` + +error: aborting due to 22 previous errors diff --git a/tests/ui/expect_fun_call.fixed b/tests/ui/expect_fun_call.fixed index 53e45d28bded..15172ae345c2 100644 --- a/tests/ui/expect_fun_call.fixed +++ b/tests/ui/expect_fun_call.fixed @@ -1,7 +1,6 @@ // run-rustfix - #![warn(clippy::expect_fun_call)] -#![allow(clippy::to_string_in_format_args)] +#![allow(clippy::to_string_in_format_args, clippy::uninlined_format_args)] /// Checks implementation of the `EXPECT_FUN_CALL` lint @@ -101,4 +100,10 @@ fn main() { let opt_ref = &opt; opt_ref.unwrap_or_else(|| panic!("{:?}", opt_ref)); } + + let format_capture: Option = None; + format_capture.unwrap_or_else(|| panic!("{error_code}")); + + let format_capture_and_value: Option = None; + format_capture_and_value.unwrap_or_else(|| panic!("{error_code}, {}", 1)); } diff --git a/tests/ui/expect_fun_call.rs b/tests/ui/expect_fun_call.rs index 22e530b80349..0f448d004174 100644 --- a/tests/ui/expect_fun_call.rs +++ b/tests/ui/expect_fun_call.rs @@ -1,7 +1,6 @@ // run-rustfix - #![warn(clippy::expect_fun_call)] -#![allow(clippy::to_string_in_format_args)] +#![allow(clippy::to_string_in_format_args, clippy::uninlined_format_args)] /// Checks implementation of the `EXPECT_FUN_CALL` lint @@ -101,4 +100,10 @@ fn main() { let opt_ref = &opt; opt_ref.expect(&format!("{:?}", opt_ref)); } + + let format_capture: Option = None; + format_capture.expect(&format!("{error_code}")); + + let format_capture_and_value: Option = None; + format_capture_and_value.expect(&format!("{error_code}, {}", 1)); } diff --git a/tests/ui/expect_fun_call.stderr b/tests/ui/expect_fun_call.stderr index aca15935fca0..cb55e32aee02 100644 --- a/tests/ui/expect_fun_call.stderr +++ b/tests/ui/expect_fun_call.stderr @@ -1,5 +1,5 @@ error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:35:26 + --> $DIR/expect_fun_call.rs:34:26 | LL | with_none_and_format.expect(&format!("Error {}: fake error", error_code)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("Error {}: fake error", error_code))` @@ -7,76 +7,88 @@ LL | with_none_and_format.expect(&format!("Error {}: fake error", error_code = note: `-D clippy::expect-fun-call` implied by `-D warnings` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:38:26 + --> $DIR/expect_fun_call.rs:37:26 | LL | with_none_and_as_str.expect(format!("Error {}: fake error", error_code).as_str()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("Error {}: fake error", error_code))` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:41:37 + --> $DIR/expect_fun_call.rs:40:37 | LL | with_none_and_format_with_macro.expect(format!("Error {}: fake error", one!()).as_str()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("Error {}: fake error", one!()))` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:51:25 + --> $DIR/expect_fun_call.rs:50:25 | LL | with_err_and_format.expect(&format!("Error {}: fake error", error_code)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| panic!("Error {}: fake error", error_code))` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:54:25 + --> $DIR/expect_fun_call.rs:53:25 | LL | with_err_and_as_str.expect(format!("Error {}: fake error", error_code).as_str()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| panic!("Error {}: fake error", error_code))` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:66:17 + --> $DIR/expect_fun_call.rs:65:17 | LL | Some("foo").expect(format!("{} {}", 1, 2).as_ref()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("{} {}", 1, 2))` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:87:21 + --> $DIR/expect_fun_call.rs:86:21 | LL | Some("foo").expect(&get_string()); | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!("{}", get_string()) })` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:88:21 + --> $DIR/expect_fun_call.rs:87:21 | LL | Some("foo").expect(get_string().as_ref()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!("{}", get_string()) })` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:89:21 + --> $DIR/expect_fun_call.rs:88:21 | LL | Some("foo").expect(get_string().as_str()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!("{}", get_string()) })` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:91:21 + --> $DIR/expect_fun_call.rs:90:21 | LL | Some("foo").expect(get_static_str()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!("{}", get_static_str()) })` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:92:21 + --> $DIR/expect_fun_call.rs:91:21 | LL | Some("foo").expect(get_non_static_str(&0)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!("{}", get_non_static_str(&0).to_string()) })` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:96:16 + --> $DIR/expect_fun_call.rs:95:16 | LL | Some(true).expect(&format!("key {}, {}", 1, 2)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("key {}, {}", 1, 2))` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:102:17 + --> $DIR/expect_fun_call.rs:101:17 | LL | opt_ref.expect(&format!("{:?}", opt_ref)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("{:?}", opt_ref))` -error: aborting due to 13 previous errors +error: use of `expect` followed by a function call + --> $DIR/expect_fun_call.rs:105:20 + | +LL | format_capture.expect(&format!("{error_code}")); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("{error_code}"))` + +error: use of `expect` followed by a function call + --> $DIR/expect_fun_call.rs:108:30 + | +LL | format_capture_and_value.expect(&format!("{error_code}, {}", 1)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("{error_code}, {}", 1))` + +error: aborting due to 15 previous errors diff --git a/tests/ui/explicit_counter_loop.rs b/tests/ui/explicit_counter_loop.rs index aa966761febd..6eddc01e2c47 100644 --- a/tests/ui/explicit_counter_loop.rs +++ b/tests/ui/explicit_counter_loop.rs @@ -1,4 +1,5 @@ #![warn(clippy::explicit_counter_loop)] +#![allow(clippy::uninlined_format_args)] fn main() { let mut vec = vec![1, 2, 3, 4]; diff --git a/tests/ui/explicit_counter_loop.stderr b/tests/ui/explicit_counter_loop.stderr index f9f8407d5775..d3f3c626bbdf 100644 --- a/tests/ui/explicit_counter_loop.stderr +++ b/tests/ui/explicit_counter_loop.stderr @@ -1,5 +1,5 @@ error: the variable `_index` is used as a loop counter - --> $DIR/explicit_counter_loop.rs:6:5 + --> $DIR/explicit_counter_loop.rs:7:5 | LL | for _v in &vec { | ^^^^^^^^^^^^^^ help: consider using: `for (_index, _v) in vec.iter().enumerate()` @@ -7,49 +7,49 @@ LL | for _v in &vec { = note: `-D clippy::explicit-counter-loop` implied by `-D warnings` error: the variable `_index` is used as a loop counter - --> $DIR/explicit_counter_loop.rs:12:5 + --> $DIR/explicit_counter_loop.rs:13:5 | LL | for _v in &vec { | ^^^^^^^^^^^^^^ help: consider using: `for (_index, _v) in vec.iter().enumerate()` error: the variable `_index` is used as a loop counter - --> $DIR/explicit_counter_loop.rs:17:5 + --> $DIR/explicit_counter_loop.rs:18:5 | LL | for _v in &mut vec { | ^^^^^^^^^^^^^^^^^^ help: consider using: `for (_index, _v) in vec.iter_mut().enumerate()` error: the variable `_index` is used as a loop counter - --> $DIR/explicit_counter_loop.rs:22:5 + --> $DIR/explicit_counter_loop.rs:23:5 | LL | for _v in vec { | ^^^^^^^^^^^^^ help: consider using: `for (_index, _v) in vec.into_iter().enumerate()` error: the variable `count` is used as a loop counter - --> $DIR/explicit_counter_loop.rs:61:9 + --> $DIR/explicit_counter_loop.rs:62:9 | LL | for ch in text.chars() { | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `for (count, ch) in text.chars().enumerate()` error: the variable `count` is used as a loop counter - --> $DIR/explicit_counter_loop.rs:72:9 + --> $DIR/explicit_counter_loop.rs:73:9 | LL | for ch in text.chars() { | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `for (count, ch) in text.chars().enumerate()` error: the variable `count` is used as a loop counter - --> $DIR/explicit_counter_loop.rs:130:9 + --> $DIR/explicit_counter_loop.rs:131:9 | LL | for _i in 3..10 { | ^^^^^^^^^^^^^^^ help: consider using: `for (count, _i) in (3..10).enumerate()` error: the variable `idx_usize` is used as a loop counter - --> $DIR/explicit_counter_loop.rs:170:9 + --> $DIR/explicit_counter_loop.rs:171:9 | LL | for _item in slice { | ^^^^^^^^^^^^^^^^^^ help: consider using: `for (idx_usize, _item) in slice.iter().enumerate()` error: the variable `idx_u32` is used as a loop counter - --> $DIR/explicit_counter_loop.rs:182:9 + --> $DIR/explicit_counter_loop.rs:183:9 | LL | for _item in slice { | ^^^^^^^^^^^^^^^^^^ help: consider using: `for (idx_u32, _item) in (0_u32..).zip(slice.iter())` diff --git a/tests/ui/explicit_deref_methods.fixed b/tests/ui/explicit_deref_methods.fixed index 523cae183ee6..6d32bbece1e5 100644 --- a/tests/ui/explicit_deref_methods.fixed +++ b/tests/ui/explicit_deref_methods.fixed @@ -1,13 +1,13 @@ // run-rustfix - -#![allow( - unused_variables, - clippy::clone_double_ref, - clippy::needless_borrow, - clippy::borrow_deref_ref, - clippy::explicit_auto_deref -)] #![warn(clippy::explicit_deref_methods)] +#![allow(unused_variables)] +#![allow( + clippy::borrow_deref_ref, + clippy::clone_double_ref, + clippy::explicit_auto_deref, + clippy::needless_borrow, + clippy::uninlined_format_args +)] use std::ops::{Deref, DerefMut}; diff --git a/tests/ui/explicit_deref_methods.rs b/tests/ui/explicit_deref_methods.rs index 0bbc1ae57cdf..779909e42380 100644 --- a/tests/ui/explicit_deref_methods.rs +++ b/tests/ui/explicit_deref_methods.rs @@ -1,13 +1,13 @@ // run-rustfix - -#![allow( - unused_variables, - clippy::clone_double_ref, - clippy::needless_borrow, - clippy::borrow_deref_ref, - clippy::explicit_auto_deref -)] #![warn(clippy::explicit_deref_methods)] +#![allow(unused_variables)] +#![allow( + clippy::borrow_deref_ref, + clippy::clone_double_ref, + clippy::explicit_auto_deref, + clippy::needless_borrow, + clippy::uninlined_format_args +)] use std::ops::{Deref, DerefMut}; diff --git a/tests/ui/explicit_write.fixed b/tests/ui/explicit_write.fixed index 35283725619a..862c3fea9ee8 100644 --- a/tests/ui/explicit_write.fixed +++ b/tests/ui/explicit_write.fixed @@ -1,6 +1,7 @@ // run-rustfix -#![allow(unused_imports)] #![warn(clippy::explicit_write)] +#![allow(unused_imports)] +#![allow(clippy::uninlined_format_args)] fn stdout() -> String { String::new() diff --git a/tests/ui/explicit_write.rs b/tests/ui/explicit_write.rs index be864a55b663..41d7c2255738 100644 --- a/tests/ui/explicit_write.rs +++ b/tests/ui/explicit_write.rs @@ -1,6 +1,7 @@ // run-rustfix -#![allow(unused_imports)] #![warn(clippy::explicit_write)] +#![allow(unused_imports)] +#![allow(clippy::uninlined_format_args)] fn stdout() -> String { String::new() diff --git a/tests/ui/explicit_write.stderr b/tests/ui/explicit_write.stderr index ff05f4343d77..457e9c627180 100644 --- a/tests/ui/explicit_write.stderr +++ b/tests/ui/explicit_write.stderr @@ -1,5 +1,5 @@ error: use of `write!(stdout(), ...).unwrap()` - --> $DIR/explicit_write.rs:23:9 + --> $DIR/explicit_write.rs:24:9 | LL | write!(std::io::stdout(), "test").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `print!("test")` @@ -7,73 +7,73 @@ LL | write!(std::io::stdout(), "test").unwrap(); = note: `-D clippy::explicit-write` implied by `-D warnings` error: use of `write!(stderr(), ...).unwrap()` - --> $DIR/explicit_write.rs:24:9 + --> $DIR/explicit_write.rs:25:9 | LL | write!(std::io::stderr(), "test").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprint!("test")` error: use of `writeln!(stdout(), ...).unwrap()` - --> $DIR/explicit_write.rs:25:9 + --> $DIR/explicit_write.rs:26:9 | LL | writeln!(std::io::stdout(), "test").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `println!("test")` error: use of `writeln!(stderr(), ...).unwrap()` - --> $DIR/explicit_write.rs:26:9 + --> $DIR/explicit_write.rs:27:9 | LL | writeln!(std::io::stderr(), "test").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("test")` error: use of `stdout().write_fmt(...).unwrap()` - --> $DIR/explicit_write.rs:27:9 + --> $DIR/explicit_write.rs:28:9 | LL | std::io::stdout().write_fmt(format_args!("test")).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `print!("test")` error: use of `stderr().write_fmt(...).unwrap()` - --> $DIR/explicit_write.rs:28:9 + --> $DIR/explicit_write.rs:29:9 | LL | std::io::stderr().write_fmt(format_args!("test")).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprint!("test")` error: use of `writeln!(stdout(), ...).unwrap()` - --> $DIR/explicit_write.rs:31:9 + --> $DIR/explicit_write.rs:32:9 | LL | writeln!(std::io::stdout(), "test/ntest").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `println!("test/ntest")` error: use of `writeln!(stderr(), ...).unwrap()` - --> $DIR/explicit_write.rs:32:9 + --> $DIR/explicit_write.rs:33:9 | LL | writeln!(std::io::stderr(), "test/ntest").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("test/ntest")` error: use of `writeln!(stderr(), ...).unwrap()` - --> $DIR/explicit_write.rs:35:9 + --> $DIR/explicit_write.rs:36:9 | LL | writeln!(std::io::stderr(), "with {}", value).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("with {}", value)` error: use of `writeln!(stderr(), ...).unwrap()` - --> $DIR/explicit_write.rs:36:9 + --> $DIR/explicit_write.rs:37:9 | LL | writeln!(std::io::stderr(), "with {} {}", 2, value).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("with {} {}", 2, value)` error: use of `writeln!(stderr(), ...).unwrap()` - --> $DIR/explicit_write.rs:37:9 + --> $DIR/explicit_write.rs:38:9 | LL | writeln!(std::io::stderr(), "with {value}").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("with {value}")` error: use of `writeln!(stderr(), ...).unwrap()` - --> $DIR/explicit_write.rs:38:9 + --> $DIR/explicit_write.rs:39:9 | LL | writeln!(std::io::stderr(), "macro arg {}", one!()).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("macro arg {}", one!())` error: use of `writeln!(stderr(), ...).unwrap()` - --> $DIR/explicit_write.rs:40:9 + --> $DIR/explicit_write.rs:41:9 | LL | writeln!(std::io::stderr(), "{:w$}", value, w = width).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("{:w$}", value, w = width)` diff --git a/tests/ui/fallible_impl_from.rs b/tests/ui/fallible_impl_from.rs index 5d5af4e46329..fb6e8ec706b1 100644 --- a/tests/ui/fallible_impl_from.rs +++ b/tests/ui/fallible_impl_from.rs @@ -1,4 +1,5 @@ #![deny(clippy::fallible_impl_from)] +#![allow(clippy::uninlined_format_args)] // docs example struct Foo(i32); diff --git a/tests/ui/fallible_impl_from.stderr b/tests/ui/fallible_impl_from.stderr index 28a061af664d..21761484f8c4 100644 --- a/tests/ui/fallible_impl_from.stderr +++ b/tests/ui/fallible_impl_from.stderr @@ -1,5 +1,5 @@ error: consider implementing `TryFrom` instead - --> $DIR/fallible_impl_from.rs:5:1 + --> $DIR/fallible_impl_from.rs:6:1 | LL | / impl From for Foo { LL | | fn from(s: String) -> Self { @@ -10,7 +10,7 @@ LL | | } | = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail note: potential failure(s) - --> $DIR/fallible_impl_from.rs:7:13 + --> $DIR/fallible_impl_from.rs:8:13 | LL | Foo(s.parse().unwrap()) | ^^^^^^^^^^^^^^^^^^ @@ -21,7 +21,7 @@ LL | #![deny(clippy::fallible_impl_from)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: consider implementing `TryFrom` instead - --> $DIR/fallible_impl_from.rs:26:1 + --> $DIR/fallible_impl_from.rs:27:1 | LL | / impl From for Invalid { LL | | fn from(i: usize) -> Invalid { @@ -34,14 +34,14 @@ LL | | } | = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail note: potential failure(s) - --> $DIR/fallible_impl_from.rs:29:13 + --> $DIR/fallible_impl_from.rs:30:13 | LL | panic!(); | ^^^^^^^^ = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) error: consider implementing `TryFrom` instead - --> $DIR/fallible_impl_from.rs:35:1 + --> $DIR/fallible_impl_from.rs:36:1 | LL | / impl From> for Invalid { LL | | fn from(s: Option) -> Invalid { @@ -54,7 +54,7 @@ LL | | } | = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail note: potential failure(s) - --> $DIR/fallible_impl_from.rs:37:17 + --> $DIR/fallible_impl_from.rs:38:17 | LL | let s = s.unwrap(); | ^^^^^^^^^^ @@ -68,7 +68,7 @@ LL | panic!("{:?}", s); = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) error: consider implementing `TryFrom` instead - --> $DIR/fallible_impl_from.rs:53:1 + --> $DIR/fallible_impl_from.rs:54:1 | LL | / impl<'a> From<&'a mut as ProjStrTrait>::ProjString> for Invalid { LL | | fn from(s: &'a mut as ProjStrTrait>::ProjString) -> Invalid { @@ -81,7 +81,7 @@ LL | | } | = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail note: potential failure(s) - --> $DIR/fallible_impl_from.rs:55:12 + --> $DIR/fallible_impl_from.rs:56:12 | LL | if s.parse::().ok().unwrap() != 42 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/floating_point_exp.fixed b/tests/ui/floating_point_exp.fixed index c86a502d15f0..b9e3d89c2b29 100644 --- a/tests/ui/floating_point_exp.fixed +++ b/tests/ui/floating_point_exp.fixed @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::imprecise_flops)] +#![allow(clippy::unnecessary_cast)] fn main() { let x = 2f32; diff --git a/tests/ui/floating_point_exp.rs b/tests/ui/floating_point_exp.rs index e59589f912a2..ef008dd9be05 100644 --- a/tests/ui/floating_point_exp.rs +++ b/tests/ui/floating_point_exp.rs @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::imprecise_flops)] +#![allow(clippy::unnecessary_cast)] fn main() { let x = 2f32; diff --git a/tests/ui/floating_point_exp.stderr b/tests/ui/floating_point_exp.stderr index f84eede19872..b92fae56e421 100644 --- a/tests/ui/floating_point_exp.stderr +++ b/tests/ui/floating_point_exp.stderr @@ -1,5 +1,5 @@ error: (e.pow(x) - 1) can be computed more accurately - --> $DIR/floating_point_exp.rs:6:13 + --> $DIR/floating_point_exp.rs:7:13 | LL | let _ = x.exp() - 1.0; | ^^^^^^^^^^^^^ help: consider using: `x.exp_m1()` @@ -7,25 +7,25 @@ LL | let _ = x.exp() - 1.0; = note: `-D clippy::imprecise-flops` implied by `-D warnings` error: (e.pow(x) - 1) can be computed more accurately - --> $DIR/floating_point_exp.rs:7:13 + --> $DIR/floating_point_exp.rs:8:13 | LL | let _ = x.exp() - 1.0 + 2.0; | ^^^^^^^^^^^^^ help: consider using: `x.exp_m1()` error: (e.pow(x) - 1) can be computed more accurately - --> $DIR/floating_point_exp.rs:8:13 + --> $DIR/floating_point_exp.rs:9:13 | LL | let _ = (x as f32).exp() - 1.0 + 2.0; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x as f32).exp_m1()` error: (e.pow(x) - 1) can be computed more accurately - --> $DIR/floating_point_exp.rs:14:13 + --> $DIR/floating_point_exp.rs:15:13 | LL | let _ = x.exp() - 1.0; | ^^^^^^^^^^^^^ help: consider using: `x.exp_m1()` error: (e.pow(x) - 1) can be computed more accurately - --> $DIR/floating_point_exp.rs:15:13 + --> $DIR/floating_point_exp.rs:16:13 | LL | let _ = x.exp() - 1.0 + 2.0; | ^^^^^^^^^^^^^ help: consider using: `x.exp_m1()` diff --git a/tests/ui/floating_point_log.fixed b/tests/ui/floating_point_log.fixed index 4def9300bb7d..ee5406461600 100644 --- a/tests/ui/floating_point_log.fixed +++ b/tests/ui/floating_point_log.fixed @@ -1,5 +1,5 @@ // run-rustfix -#![allow(dead_code, clippy::double_parens)] +#![allow(dead_code, clippy::double_parens, clippy::unnecessary_cast)] #![warn(clippy::suboptimal_flops, clippy::imprecise_flops)] const TWO: f32 = 2.0; diff --git a/tests/ui/floating_point_log.rs b/tests/ui/floating_point_log.rs index 1e04caa7d2a8..0590670a50bc 100644 --- a/tests/ui/floating_point_log.rs +++ b/tests/ui/floating_point_log.rs @@ -1,5 +1,5 @@ // run-rustfix -#![allow(dead_code, clippy::double_parens)] +#![allow(dead_code, clippy::double_parens, clippy::unnecessary_cast)] #![warn(clippy::suboptimal_flops, clippy::imprecise_flops)] const TWO: f32 = 2.0; diff --git a/tests/ui/floating_point_logbase.fixed b/tests/ui/floating_point_logbase.fixed index 936462f94066..7347bf72cbea 100644 --- a/tests/ui/floating_point_logbase.fixed +++ b/tests/ui/floating_point_logbase.fixed @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::suboptimal_flops)] +#![allow(clippy::unnecessary_cast)] fn main() { let x = 3f32; diff --git a/tests/ui/floating_point_logbase.rs b/tests/ui/floating_point_logbase.rs index 0b56fa8fa41f..ba5b8d406928 100644 --- a/tests/ui/floating_point_logbase.rs +++ b/tests/ui/floating_point_logbase.rs @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::suboptimal_flops)] +#![allow(clippy::unnecessary_cast)] fn main() { let x = 3f32; diff --git a/tests/ui/floating_point_logbase.stderr b/tests/ui/floating_point_logbase.stderr index 384e3554cbbe..9d736b5e1a27 100644 --- a/tests/ui/floating_point_logbase.stderr +++ b/tests/ui/floating_point_logbase.stderr @@ -1,5 +1,5 @@ error: log base can be expressed more clearly - --> $DIR/floating_point_logbase.rs:7:13 + --> $DIR/floating_point_logbase.rs:8:13 | LL | let _ = x.ln() / y.ln(); | ^^^^^^^^^^^^^^^ help: consider using: `x.log(y)` @@ -7,25 +7,25 @@ LL | let _ = x.ln() / y.ln(); = note: `-D clippy::suboptimal-flops` implied by `-D warnings` error: log base can be expressed more clearly - --> $DIR/floating_point_logbase.rs:8:13 + --> $DIR/floating_point_logbase.rs:9:13 | LL | let _ = (x as f32).ln() / y.ln(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x as f32).log(y)` error: log base can be expressed more clearly - --> $DIR/floating_point_logbase.rs:9:13 + --> $DIR/floating_point_logbase.rs:10:13 | LL | let _ = x.log2() / y.log2(); | ^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log(y)` error: log base can be expressed more clearly - --> $DIR/floating_point_logbase.rs:10:13 + --> $DIR/floating_point_logbase.rs:11:13 | LL | let _ = x.log10() / y.log10(); | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log(y)` error: log base can be expressed more clearly - --> $DIR/floating_point_logbase.rs:11:13 + --> $DIR/floating_point_logbase.rs:12:13 | LL | let _ = x.log(5f32) / y.log(5f32); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log(y)` diff --git a/tests/ui/floating_point_mul_add.fixed b/tests/ui/floating_point_mul_add.fixed index 169ec02f82be..d3e536ba3500 100644 --- a/tests/ui/floating_point_mul_add.fixed +++ b/tests/ui/floating_point_mul_add.fixed @@ -19,7 +19,9 @@ fn main() { let d: f64 = 0.0001; let _ = a.mul_add(b, c); + let _ = a.mul_add(b, -c); let _ = a.mul_add(b, c); + let _ = a.mul_add(-b, c); let _ = 2.0f64.mul_add(4.0, a); let _ = 2.0f64.mul_add(4., a); diff --git a/tests/ui/floating_point_mul_add.rs b/tests/ui/floating_point_mul_add.rs index 5338d4fc2b74..5d4a9e35cfc2 100644 --- a/tests/ui/floating_point_mul_add.rs +++ b/tests/ui/floating_point_mul_add.rs @@ -19,7 +19,9 @@ fn main() { let d: f64 = 0.0001; let _ = a * b + c; + let _ = a * b - c; let _ = c + a * b; + let _ = c - a * b; let _ = a + 2.0 * 4.0; let _ = a + 2. * 4.; diff --git a/tests/ui/floating_point_mul_add.stderr b/tests/ui/floating_point_mul_add.stderr index e637bbf90caa..a79ae94e8d43 100644 --- a/tests/ui/floating_point_mul_add.stderr +++ b/tests/ui/floating_point_mul_add.stderr @@ -9,56 +9,68 @@ LL | let _ = a * b + c; error: multiply and add expressions can be calculated more efficiently and accurately --> $DIR/floating_point_mul_add.rs:22:13 | +LL | let _ = a * b - c; + | ^^^^^^^^^ help: consider using: `a.mul_add(b, -c)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_mul_add.rs:23:13 + | LL | let _ = c + a * b; | ^^^^^^^^^ help: consider using: `a.mul_add(b, c)` error: multiply and add expressions can be calculated more efficiently and accurately - --> $DIR/floating_point_mul_add.rs:23:13 + --> $DIR/floating_point_mul_add.rs:24:13 + | +LL | let _ = c - a * b; + | ^^^^^^^^^ help: consider using: `a.mul_add(-b, c)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_mul_add.rs:25:13 | LL | let _ = a + 2.0 * 4.0; | ^^^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(4.0, a)` error: multiply and add expressions can be calculated more efficiently and accurately - --> $DIR/floating_point_mul_add.rs:24:13 + --> $DIR/floating_point_mul_add.rs:26:13 | LL | let _ = a + 2. * 4.; | ^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(4., a)` error: multiply and add expressions can be calculated more efficiently and accurately - --> $DIR/floating_point_mul_add.rs:26:13 + --> $DIR/floating_point_mul_add.rs:28:13 | LL | let _ = (a * b) + c; | ^^^^^^^^^^^ help: consider using: `a.mul_add(b, c)` error: multiply and add expressions can be calculated more efficiently and accurately - --> $DIR/floating_point_mul_add.rs:27:13 + --> $DIR/floating_point_mul_add.rs:29:13 | LL | let _ = c + (a * b); | ^^^^^^^^^^^ help: consider using: `a.mul_add(b, c)` error: multiply and add expressions can be calculated more efficiently and accurately - --> $DIR/floating_point_mul_add.rs:28:13 + --> $DIR/floating_point_mul_add.rs:30:13 | LL | let _ = a * b * c + d; | ^^^^^^^^^^^^^ help: consider using: `(a * b).mul_add(c, d)` error: multiply and add expressions can be calculated more efficiently and accurately - --> $DIR/floating_point_mul_add.rs:30:13 + --> $DIR/floating_point_mul_add.rs:32:13 | LL | let _ = a.mul_add(b, c) * a.mul_add(b, c) + a.mul_add(b, c) + c; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `a.mul_add(b, c).mul_add(a.mul_add(b, c), a.mul_add(b, c))` error: multiply and add expressions can be calculated more efficiently and accurately - --> $DIR/floating_point_mul_add.rs:31:13 + --> $DIR/floating_point_mul_add.rs:33:13 | LL | let _ = 1234.567_f64 * 45.67834_f64 + 0.0004_f64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1234.567_f64.mul_add(45.67834_f64, 0.0004_f64)` error: multiply and add expressions can be calculated more efficiently and accurately - --> $DIR/floating_point_mul_add.rs:33:13 + --> $DIR/floating_point_mul_add.rs:35:13 | LL | let _ = (a * a + b).sqrt(); | ^^^^^^^^^^^ help: consider using: `a.mul_add(a, b)` -error: aborting due to 10 previous errors +error: aborting due to 12 previous errors diff --git a/tests/ui/floating_point_powf.fixed b/tests/ui/floating_point_powf.fixed index e7ef45634dff..f7f93de29577 100644 --- a/tests/ui/floating_point_powf.fixed +++ b/tests/ui/floating_point_powf.fixed @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::suboptimal_flops, clippy::imprecise_flops)] +#![allow(clippy::unnecessary_cast)] fn main() { let x = 3f32; diff --git a/tests/ui/floating_point_powf.rs b/tests/ui/floating_point_powf.rs index d749aa2d48a4..499fc0e15e47 100644 --- a/tests/ui/floating_point_powf.rs +++ b/tests/ui/floating_point_powf.rs @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::suboptimal_flops, clippy::imprecise_flops)] +#![allow(clippy::unnecessary_cast)] fn main() { let x = 3f32; diff --git a/tests/ui/floating_point_powf.stderr b/tests/ui/floating_point_powf.stderr index e9693de8fc90..7c9d50db2f78 100644 --- a/tests/ui/floating_point_powf.stderr +++ b/tests/ui/floating_point_powf.stderr @@ -1,5 +1,5 @@ error: exponent for bases 2 and e can be computed more accurately - --> $DIR/floating_point_powf.rs:6:13 + --> $DIR/floating_point_powf.rs:7:13 | LL | let _ = 2f32.powf(x); | ^^^^^^^^^^^^ help: consider using: `x.exp2()` @@ -7,43 +7,43 @@ LL | let _ = 2f32.powf(x); = note: `-D clippy::suboptimal-flops` implied by `-D warnings` error: exponent for bases 2 and e can be computed more accurately - --> $DIR/floating_point_powf.rs:7:13 + --> $DIR/floating_point_powf.rs:8:13 | LL | let _ = 2f32.powf(3.1); | ^^^^^^^^^^^^^^ help: consider using: `3.1f32.exp2()` error: exponent for bases 2 and e can be computed more accurately - --> $DIR/floating_point_powf.rs:8:13 + --> $DIR/floating_point_powf.rs:9:13 | LL | let _ = 2f32.powf(-3.1); | ^^^^^^^^^^^^^^^ help: consider using: `(-3.1f32).exp2()` error: exponent for bases 2 and e can be computed more accurately - --> $DIR/floating_point_powf.rs:9:13 + --> $DIR/floating_point_powf.rs:10:13 | LL | let _ = std::f32::consts::E.powf(x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.exp()` error: exponent for bases 2 and e can be computed more accurately - --> $DIR/floating_point_powf.rs:10:13 + --> $DIR/floating_point_powf.rs:11:13 | LL | let _ = std::f32::consts::E.powf(3.1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `3.1f32.exp()` error: exponent for bases 2 and e can be computed more accurately - --> $DIR/floating_point_powf.rs:11:13 + --> $DIR/floating_point_powf.rs:12:13 | LL | let _ = std::f32::consts::E.powf(-3.1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(-3.1f32).exp()` error: square-root of a number can be computed more efficiently and accurately - --> $DIR/floating_point_powf.rs:12:13 + --> $DIR/floating_point_powf.rs:13:13 | LL | let _ = x.powf(1.0 / 2.0); | ^^^^^^^^^^^^^^^^^ help: consider using: `x.sqrt()` error: cube-root of a number can be computed more accurately - --> $DIR/floating_point_powf.rs:13:13 + --> $DIR/floating_point_powf.rs:14:13 | LL | let _ = x.powf(1.0 / 3.0); | ^^^^^^^^^^^^^^^^^ help: consider using: `x.cbrt()` @@ -51,139 +51,139 @@ LL | let _ = x.powf(1.0 / 3.0); = note: `-D clippy::imprecise-flops` implied by `-D warnings` error: cube-root of a number can be computed more accurately - --> $DIR/floating_point_powf.rs:14:13 + --> $DIR/floating_point_powf.rs:15:13 | LL | let _ = (x as f32).powf(1.0 / 3.0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x as f32).cbrt()` error: exponentiation with integer powers can be computed more efficiently - --> $DIR/floating_point_powf.rs:15:13 + --> $DIR/floating_point_powf.rs:16:13 | LL | let _ = x.powf(3.0); | ^^^^^^^^^^^ help: consider using: `x.powi(3)` error: exponentiation with integer powers can be computed more efficiently - --> $DIR/floating_point_powf.rs:16:13 + --> $DIR/floating_point_powf.rs:17:13 | LL | let _ = x.powf(-2.0); | ^^^^^^^^^^^^ help: consider using: `x.powi(-2)` error: exponentiation with integer powers can be computed more efficiently - --> $DIR/floating_point_powf.rs:17:13 + --> $DIR/floating_point_powf.rs:18:13 | LL | let _ = x.powf(16_777_215.0); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(16_777_215)` error: exponentiation with integer powers can be computed more efficiently - --> $DIR/floating_point_powf.rs:18:13 + --> $DIR/floating_point_powf.rs:19:13 | LL | let _ = x.powf(-16_777_215.0); | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(-16_777_215)` error: exponentiation with integer powers can be computed more efficiently - --> $DIR/floating_point_powf.rs:19:13 + --> $DIR/floating_point_powf.rs:20:13 | LL | let _ = (x as f32).powf(-16_777_215.0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x as f32).powi(-16_777_215)` error: exponentiation with integer powers can be computed more efficiently - --> $DIR/floating_point_powf.rs:20:13 + --> $DIR/floating_point_powf.rs:21:13 | LL | let _ = (x as f32).powf(3.0); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x as f32).powi(3)` error: cube-root of a number can be computed more accurately - --> $DIR/floating_point_powf.rs:21:13 + --> $DIR/floating_point_powf.rs:22:13 | LL | let _ = (1.5_f32 + 1.0).powf(1.0 / 3.0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(1.5_f32 + 1.0).cbrt()` error: cube-root of a number can be computed more accurately - --> $DIR/floating_point_powf.rs:22:13 + --> $DIR/floating_point_powf.rs:23:13 | LL | let _ = 1.5_f64.powf(1.0 / 3.0); | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1.5_f64.cbrt()` error: square-root of a number can be computed more efficiently and accurately - --> $DIR/floating_point_powf.rs:23:13 + --> $DIR/floating_point_powf.rs:24:13 | LL | let _ = 1.5_f64.powf(1.0 / 2.0); | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1.5_f64.sqrt()` error: exponentiation with integer powers can be computed more efficiently - --> $DIR/floating_point_powf.rs:24:13 + --> $DIR/floating_point_powf.rs:25:13 | LL | let _ = 1.5_f64.powf(3.0); | ^^^^^^^^^^^^^^^^^ help: consider using: `1.5_f64.powi(3)` error: exponent for bases 2 and e can be computed more accurately - --> $DIR/floating_point_powf.rs:33:13 + --> $DIR/floating_point_powf.rs:34:13 | LL | let _ = 2f64.powf(x); | ^^^^^^^^^^^^ help: consider using: `x.exp2()` error: exponent for bases 2 and e can be computed more accurately - --> $DIR/floating_point_powf.rs:34:13 + --> $DIR/floating_point_powf.rs:35:13 | LL | let _ = 2f64.powf(3.1); | ^^^^^^^^^^^^^^ help: consider using: `3.1f64.exp2()` error: exponent for bases 2 and e can be computed more accurately - --> $DIR/floating_point_powf.rs:35:13 + --> $DIR/floating_point_powf.rs:36:13 | LL | let _ = 2f64.powf(-3.1); | ^^^^^^^^^^^^^^^ help: consider using: `(-3.1f64).exp2()` error: exponent for bases 2 and e can be computed more accurately - --> $DIR/floating_point_powf.rs:36:13 + --> $DIR/floating_point_powf.rs:37:13 | LL | let _ = std::f64::consts::E.powf(x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.exp()` error: exponent for bases 2 and e can be computed more accurately - --> $DIR/floating_point_powf.rs:37:13 + --> $DIR/floating_point_powf.rs:38:13 | LL | let _ = std::f64::consts::E.powf(3.1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `3.1f64.exp()` error: exponent for bases 2 and e can be computed more accurately - --> $DIR/floating_point_powf.rs:38:13 + --> $DIR/floating_point_powf.rs:39:13 | LL | let _ = std::f64::consts::E.powf(-3.1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(-3.1f64).exp()` error: square-root of a number can be computed more efficiently and accurately - --> $DIR/floating_point_powf.rs:39:13 + --> $DIR/floating_point_powf.rs:40:13 | LL | let _ = x.powf(1.0 / 2.0); | ^^^^^^^^^^^^^^^^^ help: consider using: `x.sqrt()` error: cube-root of a number can be computed more accurately - --> $DIR/floating_point_powf.rs:40:13 + --> $DIR/floating_point_powf.rs:41:13 | LL | let _ = x.powf(1.0 / 3.0); | ^^^^^^^^^^^^^^^^^ help: consider using: `x.cbrt()` error: exponentiation with integer powers can be computed more efficiently - --> $DIR/floating_point_powf.rs:41:13 + --> $DIR/floating_point_powf.rs:42:13 | LL | let _ = x.powf(3.0); | ^^^^^^^^^^^ help: consider using: `x.powi(3)` error: exponentiation with integer powers can be computed more efficiently - --> $DIR/floating_point_powf.rs:42:13 + --> $DIR/floating_point_powf.rs:43:13 | LL | let _ = x.powf(-2.0); | ^^^^^^^^^^^^ help: consider using: `x.powi(-2)` error: exponentiation with integer powers can be computed more efficiently - --> $DIR/floating_point_powf.rs:43:13 + --> $DIR/floating_point_powf.rs:44:13 | LL | let _ = x.powf(-2_147_483_648.0); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(-2_147_483_648)` error: exponentiation with integer powers can be computed more efficiently - --> $DIR/floating_point_powf.rs:44:13 + --> $DIR/floating_point_powf.rs:45:13 | LL | let _ = x.powf(2_147_483_647.0); | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2_147_483_647)` diff --git a/tests/ui/floating_point_powi.fixed b/tests/ui/floating_point_powi.fixed index 5758db7c6c82..884d05fed71b 100644 --- a/tests/ui/floating_point_powi.fixed +++ b/tests/ui/floating_point_powi.fixed @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::suboptimal_flops)] +#![allow(clippy::unnecessary_cast)] fn main() { let one = 1; @@ -7,7 +8,9 @@ fn main() { let y = 4f32; let _ = x.mul_add(x, y); + let _ = x.mul_add(x, -y); let _ = y.mul_add(y, x); + let _ = y.mul_add(-y, x); let _ = (y as f32).mul_add(y as f32, x); let _ = x.mul_add(x, y).sqrt(); let _ = y.mul_add(y, x).sqrt(); diff --git a/tests/ui/floating_point_powi.rs b/tests/ui/floating_point_powi.rs index 5926bf1b0004..e6a1c895371b 100644 --- a/tests/ui/floating_point_powi.rs +++ b/tests/ui/floating_point_powi.rs @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::suboptimal_flops)] +#![allow(clippy::unnecessary_cast)] fn main() { let one = 1; @@ -7,7 +8,9 @@ fn main() { let y = 4f32; let _ = x.powi(2) + y; + let _ = x.powi(2) - y; let _ = x + y.powi(2); + let _ = x - y.powi(2); let _ = x + (y as f32).powi(2); let _ = (x.powi(2) + y).sqrt(); let _ = (x + y.powi(2)).sqrt(); diff --git a/tests/ui/floating_point_powi.stderr b/tests/ui/floating_point_powi.stderr index a3c74544212b..5df0de1fef22 100644 --- a/tests/ui/floating_point_powi.stderr +++ b/tests/ui/floating_point_powi.stderr @@ -1,5 +1,5 @@ error: multiply and add expressions can be calculated more efficiently and accurately - --> $DIR/floating_point_powi.rs:9:13 + --> $DIR/floating_point_powi.rs:10:13 | LL | let _ = x.powi(2) + y; | ^^^^^^^^^^^^^ help: consider using: `x.mul_add(x, y)` @@ -7,28 +7,40 @@ LL | let _ = x.powi(2) + y; = note: `-D clippy::suboptimal-flops` implied by `-D warnings` error: multiply and add expressions can be calculated more efficiently and accurately - --> $DIR/floating_point_powi.rs:10:13 + --> $DIR/floating_point_powi.rs:11:13 + | +LL | let _ = x.powi(2) - y; + | ^^^^^^^^^^^^^ help: consider using: `x.mul_add(x, -y)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_powi.rs:12:13 | LL | let _ = x + y.powi(2); | ^^^^^^^^^^^^^ help: consider using: `y.mul_add(y, x)` error: multiply and add expressions can be calculated more efficiently and accurately - --> $DIR/floating_point_powi.rs:11:13 + --> $DIR/floating_point_powi.rs:13:13 + | +LL | let _ = x - y.powi(2); + | ^^^^^^^^^^^^^ help: consider using: `y.mul_add(-y, x)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_powi.rs:14:13 | LL | let _ = x + (y as f32).powi(2); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(y as f32).mul_add(y as f32, x)` error: multiply and add expressions can be calculated more efficiently and accurately - --> $DIR/floating_point_powi.rs:12:13 + --> $DIR/floating_point_powi.rs:15:13 | LL | let _ = (x.powi(2) + y).sqrt(); | ^^^^^^^^^^^^^^^ help: consider using: `x.mul_add(x, y)` error: multiply and add expressions can be calculated more efficiently and accurately - --> $DIR/floating_point_powi.rs:13:13 + --> $DIR/floating_point_powi.rs:16:13 | LL | let _ = (x + y.powi(2)).sqrt(); | ^^^^^^^^^^^^^^^ help: consider using: `y.mul_add(y, x)` -error: aborting due to 5 previous errors +error: aborting due to 7 previous errors diff --git a/tests/ui/for_loop_fixable.fixed b/tests/ui/for_loop_fixable.fixed index aa69781d15a6..e9dd38fe40e6 100644 --- a/tests/ui/for_loop_fixable.fixed +++ b/tests/ui/for_loop_fixable.fixed @@ -1,6 +1,6 @@ // run-rustfix - #![allow(dead_code, unused)] +#![allow(clippy::uninlined_format_args)] use std::collections::*; diff --git a/tests/ui/for_loop_fixable.rs b/tests/ui/for_loop_fixable.rs index 7c063d99511d..534fb4dd4ef2 100644 --- a/tests/ui/for_loop_fixable.rs +++ b/tests/ui/for_loop_fixable.rs @@ -1,6 +1,6 @@ // run-rustfix - #![allow(dead_code, unused)] +#![allow(clippy::uninlined_format_args)] use std::collections::*; diff --git a/tests/ui/for_loops_over_fallibles.rs b/tests/ui/for_loops_over_fallibles.rs index 3390111d0a8f..4b2a9297d084 100644 --- a/tests/ui/for_loops_over_fallibles.rs +++ b/tests/ui/for_loops_over_fallibles.rs @@ -1,4 +1,5 @@ #![warn(clippy::for_loops_over_fallibles)] +#![allow(clippy::uninlined_format_args)] fn for_loops_over_fallibles() { let option = Some(1); diff --git a/tests/ui/for_loops_over_fallibles.stderr b/tests/ui/for_loops_over_fallibles.stderr index 68d2735b040e..f09adccabd1a 100644 --- a/tests/ui/for_loops_over_fallibles.stderr +++ b/tests/ui/for_loops_over_fallibles.stderr @@ -1,5 +1,5 @@ error: for loop over `option`, which is an `Option`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:9:14 + --> $DIR/for_loops_over_fallibles.rs:10:14 | LL | for x in option { | ^^^^^^ @@ -8,7 +8,7 @@ LL | for x in option { = note: `-D clippy::for-loops-over-fallibles` implied by `-D warnings` error: for loop over `option`, which is an `Option`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:14:14 + --> $DIR/for_loops_over_fallibles.rs:15:14 | LL | for x in option.iter() { | ^^^^^^ @@ -16,7 +16,7 @@ LL | for x in option.iter() { = help: consider replacing `for x in option.iter()` with `if let Some(x) = option` error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:19:14 + --> $DIR/for_loops_over_fallibles.rs:20:14 | LL | for x in result { | ^^^^^^ @@ -24,7 +24,7 @@ LL | for x in result { = help: consider replacing `for x in result` with `if let Ok(x) = result` error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:24:14 + --> $DIR/for_loops_over_fallibles.rs:25:14 | LL | for x in result.iter_mut() { | ^^^^^^ @@ -32,7 +32,7 @@ LL | for x in result.iter_mut() { = help: consider replacing `for x in result.iter_mut()` with `if let Ok(x) = result` error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:29:14 + --> $DIR/for_loops_over_fallibles.rs:30:14 | LL | for x in result.into_iter() { | ^^^^^^ @@ -40,7 +40,7 @@ LL | for x in result.into_iter() { = help: consider replacing `for x in result.into_iter()` with `if let Ok(x) = result` error: for loop over `option.ok_or("x not found")`, which is a `Result`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:33:14 + --> $DIR/for_loops_over_fallibles.rs:34:14 | LL | for x in option.ok_or("x not found") { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -48,7 +48,7 @@ LL | for x in option.ok_or("x not found") { = help: consider replacing `for x in option.ok_or("x not found")` with `if let Ok(x) = option.ok_or("x not found")` error: you are iterating over `Iterator::next()` which is an Option; this will compile but is probably not what you want - --> $DIR/for_loops_over_fallibles.rs:39:14 + --> $DIR/for_loops_over_fallibles.rs:40:14 | LL | for x in v.iter().next() { | ^^^^^^^^^^^^^^^ @@ -56,7 +56,7 @@ LL | for x in v.iter().next() { = note: `#[deny(clippy::iter_next_loop)]` on by default error: for loop over `v.iter().next().and(Some(0))`, which is an `Option`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:44:14 + --> $DIR/for_loops_over_fallibles.rs:45:14 | LL | for x in v.iter().next().and(Some(0)) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -64,7 +64,7 @@ LL | for x in v.iter().next().and(Some(0)) { = help: consider replacing `for x in v.iter().next().and(Some(0))` with `if let Some(x) = v.iter().next().and(Some(0))` error: for loop over `v.iter().next().ok_or("x not found")`, which is a `Result`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:48:14 + --> $DIR/for_loops_over_fallibles.rs:49:14 | LL | for x in v.iter().next().ok_or("x not found") { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -72,7 +72,7 @@ LL | for x in v.iter().next().ok_or("x not found") { = help: consider replacing `for x in v.iter().next().ok_or("x not found")` with `if let Ok(x) = v.iter().next().ok_or("x not found")` error: this loop never actually loops - --> $DIR/for_loops_over_fallibles.rs:60:5 + --> $DIR/for_loops_over_fallibles.rs:61:5 | LL | / while let Some(x) = option { LL | | println!("{}", x); @@ -83,7 +83,7 @@ LL | | } = note: `#[deny(clippy::never_loop)]` on by default error: this loop never actually loops - --> $DIR/for_loops_over_fallibles.rs:66:5 + --> $DIR/for_loops_over_fallibles.rs:67:5 | LL | / while let Ok(x) = result { LL | | println!("{}", x); diff --git a/tests/ui/format.fixed b/tests/ui/format.fixed index e0c5f692740a..beedf2c1db29 100644 --- a/tests/ui/format.fixed +++ b/tests/ui/format.fixed @@ -1,13 +1,13 @@ // run-rustfix - +#![warn(clippy::useless_format)] #![allow( unused_tuple_struct_fields, clippy::print_literal, clippy::redundant_clone, clippy::to_string_in_format_args, - clippy::needless_borrow + clippy::needless_borrow, + clippy::uninlined_format_args )] -#![warn(clippy::useless_format)] struct Foo(pub String); diff --git a/tests/ui/format.rs b/tests/ui/format.rs index ff83cd64bf09..e805f1818898 100644 --- a/tests/ui/format.rs +++ b/tests/ui/format.rs @@ -1,13 +1,13 @@ // run-rustfix - +#![warn(clippy::useless_format)] #![allow( unused_tuple_struct_fields, clippy::print_literal, clippy::redundant_clone, clippy::to_string_in_format_args, - clippy::needless_borrow + clippy::needless_borrow, + clippy::uninlined_format_args )] -#![warn(clippy::useless_format)] struct Foo(pub String); diff --git a/tests/ui/format_args.fixed b/tests/ui/format_args.fixed index e1c2d4d70be4..24cf0847dd58 100644 --- a/tests/ui/format_args.fixed +++ b/tests/ui/format_args.fixed @@ -1,10 +1,12 @@ // run-rustfix - -#![allow(unused)] -#![allow(clippy::assertions_on_constants)] -#![allow(clippy::eq_op)] -#![allow(clippy::print_literal)] #![warn(clippy::to_string_in_format_args)] +#![allow(unused)] +#![allow( + clippy::assertions_on_constants, + clippy::eq_op, + clippy::print_literal, + clippy::uninlined_format_args +)] use std::io::{stdout, Write}; use std::ops::Deref; diff --git a/tests/ui/format_args.rs b/tests/ui/format_args.rs index b9a4d66c28ad..753babf0afdc 100644 --- a/tests/ui/format_args.rs +++ b/tests/ui/format_args.rs @@ -1,10 +1,12 @@ // run-rustfix - -#![allow(unused)] -#![allow(clippy::assertions_on_constants)] -#![allow(clippy::eq_op)] -#![allow(clippy::print_literal)] #![warn(clippy::to_string_in_format_args)] +#![allow(unused)] +#![allow( + clippy::assertions_on_constants, + clippy::eq_op, + clippy::print_literal, + clippy::uninlined_format_args +)] use std::io::{stdout, Write}; use std::ops::Deref; diff --git a/tests/ui/format_args.stderr b/tests/ui/format_args.stderr index aa6e3659b43b..68b0bb9e089e 100644 --- a/tests/ui/format_args.stderr +++ b/tests/ui/format_args.stderr @@ -1,5 +1,5 @@ error: `to_string` applied to a type that implements `Display` in `format!` args - --> $DIR/format_args.rs:74:72 + --> $DIR/format_args.rs:76:72 | LL | let _ = format!("error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this @@ -7,133 +7,133 @@ LL | let _ = format!("error: something failed at {}", Location::caller().to_ = note: `-D clippy::to-string-in-format-args` implied by `-D warnings` error: `to_string` applied to a type that implements `Display` in `write!` args - --> $DIR/format_args.rs:78:27 + --> $DIR/format_args.rs:80:27 | LL | Location::caller().to_string() | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `writeln!` args - --> $DIR/format_args.rs:83:27 + --> $DIR/format_args.rs:85:27 | LL | Location::caller().to_string() | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `print!` args - --> $DIR/format_args.rs:85:63 + --> $DIR/format_args.rs:87:63 | LL | print!("error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:86:65 + --> $DIR/format_args.rs:88:65 | LL | println!("error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `eprint!` args - --> $DIR/format_args.rs:87:64 + --> $DIR/format_args.rs:89:64 | LL | eprint!("error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `eprintln!` args - --> $DIR/format_args.rs:88:66 + --> $DIR/format_args.rs:90:66 | LL | eprintln!("error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `format_args!` args - --> $DIR/format_args.rs:89:77 + --> $DIR/format_args.rs:91:77 | LL | let _ = format_args!("error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `assert!` args - --> $DIR/format_args.rs:90:70 + --> $DIR/format_args.rs:92:70 | LL | assert!(true, "error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `assert_eq!` args - --> $DIR/format_args.rs:91:73 + --> $DIR/format_args.rs:93:73 | LL | assert_eq!(0, 0, "error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `assert_ne!` args - --> $DIR/format_args.rs:92:73 + --> $DIR/format_args.rs:94:73 | LL | assert_ne!(0, 0, "error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `panic!` args - --> $DIR/format_args.rs:93:63 + --> $DIR/format_args.rs:95:63 | LL | panic!("error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:94:20 + --> $DIR/format_args.rs:96:20 | LL | println!("{}", X(1).to_string()); | ^^^^^^^^^^^^^^^^ help: use this: `*X(1)` error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:95:20 + --> $DIR/format_args.rs:97:20 | LL | println!("{}", Y(&X(1)).to_string()); | ^^^^^^^^^^^^^^^^^^^^ help: use this: `***Y(&X(1))` error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:96:24 + --> $DIR/format_args.rs:98:24 | LL | println!("{}", Z(1).to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:97:20 + --> $DIR/format_args.rs:99:20 | LL | println!("{}", x.to_string()); | ^^^^^^^^^^^^^ help: use this: `**x` error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:98:20 + --> $DIR/format_args.rs:100:20 | LL | println!("{}", x_ref.to_string()); | ^^^^^^^^^^^^^^^^^ help: use this: `***x_ref` error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:100:39 + --> $DIR/format_args.rs:102:39 | LL | println!("{foo}{bar}", foo = "foo".to_string(), bar = "bar"); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:101:52 + --> $DIR/format_args.rs:103:52 | LL | println!("{foo}{bar}", foo = "foo", bar = "bar".to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:102:39 + --> $DIR/format_args.rs:104:39 | LL | println!("{foo}{bar}", bar = "bar".to_string(), foo = "foo"); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:103:52 + --> $DIR/format_args.rs:105:52 | LL | println!("{foo}{bar}", bar = "bar", foo = "foo".to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `format!` args - --> $DIR/format_args.rs:142:38 + --> $DIR/format_args.rs:144:38 | LL | let x = format!("{} {}", a, b.to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:156:24 + --> $DIR/format_args.rs:158:24 | LL | println!("{}", original[..10].to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use this: `&original[..10]` diff --git a/tests/ui/format_args_unfixable.rs b/tests/ui/format_args_unfixable.rs index b24ddf7321e4..eb0ac15bfbf1 100644 --- a/tests/ui/format_args_unfixable.rs +++ b/tests/ui/format_args_unfixable.rs @@ -1,7 +1,5 @@ -#![allow(clippy::assertions_on_constants)] -#![allow(clippy::eq_op)] -#![warn(clippy::format_in_format_args)] -#![warn(clippy::to_string_in_format_args)] +#![warn(clippy::format_in_format_args, clippy::to_string_in_format_args)] +#![allow(clippy::assertions_on_constants, clippy::eq_op, clippy::uninlined_format_args)] use std::io::{stdout, Error, ErrorKind, Write}; use std::ops::Deref; diff --git a/tests/ui/format_args_unfixable.stderr b/tests/ui/format_args_unfixable.stderr index 37a6afb1ba7b..b291d475ad90 100644 --- a/tests/ui/format_args_unfixable.stderr +++ b/tests/ui/format_args_unfixable.stderr @@ -1,5 +1,5 @@ error: `format!` in `println!` args - --> $DIR/format_args_unfixable.rs:27:5 + --> $DIR/format_args_unfixable.rs:25:5 | LL | println!("error: {}", format!("something failed at {}", Location::caller())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | println!("error: {}", format!("something failed at {}", Location::calle = note: `-D clippy::format-in-format-args` implied by `-D warnings` error: `format!` in `println!` args - --> $DIR/format_args_unfixable.rs:28:5 + --> $DIR/format_args_unfixable.rs:26:5 | LL | println!("{}: {}", error, format!("something failed at {}", Location::caller())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -18,7 +18,7 @@ LL | println!("{}: {}", error, format!("something failed at {}", Location::c = help: or consider changing `format!` to `format_args!` error: `format!` in `println!` args - --> $DIR/format_args_unfixable.rs:29:5 + --> $DIR/format_args_unfixable.rs:27:5 | LL | println!("{:?}: {}", error, format!("something failed at {}", Location::caller())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -27,7 +27,7 @@ LL | println!("{:?}: {}", error, format!("something failed at {}", Location: = help: or consider changing `format!` to `format_args!` error: `format!` in `println!` args - --> $DIR/format_args_unfixable.rs:30:5 + --> $DIR/format_args_unfixable.rs:28:5 | LL | println!("{{}}: {}", format!("something failed at {}", Location::caller())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -36,7 +36,7 @@ LL | println!("{{}}: {}", format!("something failed at {}", Location::caller = help: or consider changing `format!` to `format_args!` error: `format!` in `println!` args - --> $DIR/format_args_unfixable.rs:31:5 + --> $DIR/format_args_unfixable.rs:29:5 | LL | println!(r#"error: "{}""#, format!("something failed at {}", Location::caller())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -45,7 +45,7 @@ LL | println!(r#"error: "{}""#, format!("something failed at {}", Location:: = help: or consider changing `format!` to `format_args!` error: `format!` in `println!` args - --> $DIR/format_args_unfixable.rs:32:5 + --> $DIR/format_args_unfixable.rs:30:5 | LL | println!("error: {}", format!(r#"something failed at "{}""#, Location::caller())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -54,7 +54,7 @@ LL | println!("error: {}", format!(r#"something failed at "{}""#, Location:: = help: or consider changing `format!` to `format_args!` error: `format!` in `println!` args - --> $DIR/format_args_unfixable.rs:33:5 + --> $DIR/format_args_unfixable.rs:31:5 | LL | println!("error: {}", format!("something failed at {} {0}", Location::caller())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -63,7 +63,7 @@ LL | println!("error: {}", format!("something failed at {} {0}", Location::c = help: or consider changing `format!` to `format_args!` error: `format!` in `format!` args - --> $DIR/format_args_unfixable.rs:34:13 + --> $DIR/format_args_unfixable.rs:32:13 | LL | let _ = format!("error: {}", format!("something failed at {}", Location::caller())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -72,7 +72,7 @@ LL | let _ = format!("error: {}", format!("something failed at {}", Location = help: or consider changing `format!` to `format_args!` error: `format!` in `write!` args - --> $DIR/format_args_unfixable.rs:35:13 + --> $DIR/format_args_unfixable.rs:33:13 | LL | let _ = write!( | _____________^ @@ -86,7 +86,7 @@ LL | | ); = help: or consider changing `format!` to `format_args!` error: `format!` in `writeln!` args - --> $DIR/format_args_unfixable.rs:40:13 + --> $DIR/format_args_unfixable.rs:38:13 | LL | let _ = writeln!( | _____________^ @@ -100,7 +100,7 @@ LL | | ); = help: or consider changing `format!` to `format_args!` error: `format!` in `print!` args - --> $DIR/format_args_unfixable.rs:45:5 + --> $DIR/format_args_unfixable.rs:43:5 | LL | print!("error: {}", format!("something failed at {}", Location::caller())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -109,7 +109,7 @@ LL | print!("error: {}", format!("something failed at {}", Location::caller( = help: or consider changing `format!` to `format_args!` error: `format!` in `eprint!` args - --> $DIR/format_args_unfixable.rs:46:5 + --> $DIR/format_args_unfixable.rs:44:5 | LL | eprint!("error: {}", format!("something failed at {}", Location::caller())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -118,7 +118,7 @@ LL | eprint!("error: {}", format!("something failed at {}", Location::caller = help: or consider changing `format!` to `format_args!` error: `format!` in `eprintln!` args - --> $DIR/format_args_unfixable.rs:47:5 + --> $DIR/format_args_unfixable.rs:45:5 | LL | eprintln!("error: {}", format!("something failed at {}", Location::caller())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -127,7 +127,7 @@ LL | eprintln!("error: {}", format!("something failed at {}", Location::call = help: or consider changing `format!` to `format_args!` error: `format!` in `format_args!` args - --> $DIR/format_args_unfixable.rs:48:13 + --> $DIR/format_args_unfixable.rs:46:13 | LL | let _ = format_args!("error: {}", format!("something failed at {}", Location::caller())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -136,7 +136,7 @@ LL | let _ = format_args!("error: {}", format!("something failed at {}", Loc = help: or consider changing `format!` to `format_args!` error: `format!` in `assert!` args - --> $DIR/format_args_unfixable.rs:49:5 + --> $DIR/format_args_unfixable.rs:47:5 | LL | assert!(true, "error: {}", format!("something failed at {}", Location::caller())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -145,7 +145,7 @@ LL | assert!(true, "error: {}", format!("something failed at {}", Location:: = help: or consider changing `format!` to `format_args!` error: `format!` in `assert_eq!` args - --> $DIR/format_args_unfixable.rs:50:5 + --> $DIR/format_args_unfixable.rs:48:5 | LL | assert_eq!(0, 0, "error: {}", format!("something failed at {}", Location::caller())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -154,7 +154,7 @@ LL | assert_eq!(0, 0, "error: {}", format!("something failed at {}", Locatio = help: or consider changing `format!` to `format_args!` error: `format!` in `assert_ne!` args - --> $DIR/format_args_unfixable.rs:51:5 + --> $DIR/format_args_unfixable.rs:49:5 | LL | assert_ne!(0, 0, "error: {}", format!("something failed at {}", Location::caller())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -163,7 +163,7 @@ LL | assert_ne!(0, 0, "error: {}", format!("something failed at {}", Locatio = help: or consider changing `format!` to `format_args!` error: `format!` in `panic!` args - --> $DIR/format_args_unfixable.rs:52:5 + --> $DIR/format_args_unfixable.rs:50:5 | LL | panic!("error: {}", format!("something failed at {}", Location::caller())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/functions.rs b/tests/ui/functions.rs index 5521870eaecf..18149bfbc3fe 100644 --- a/tests/ui/functions.rs +++ b/tests/ui/functions.rs @@ -1,6 +1,6 @@ #![warn(clippy::all)] -#![allow(dead_code)] -#![allow(unused_unsafe, clippy::missing_safety_doc)] +#![allow(dead_code, unused_unsafe)] +#![allow(clippy::missing_safety_doc, clippy::uninlined_format_args)] // TOO_MANY_ARGUMENTS fn good(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool) {} diff --git a/tests/ui/identity_op.fixed b/tests/ui/identity_op.fixed index fa564e23cd27..e7b9a78c5dbc 100644 --- a/tests/ui/identity_op.fixed +++ b/tests/ui/identity_op.fixed @@ -1,13 +1,13 @@ // run-rustfix - #![warn(clippy::identity_op)] +#![allow(unused)] #![allow( clippy::eq_op, clippy::no_effect, clippy::unnecessary_operation, clippy::op_ref, clippy::double_parens, - unused + clippy::uninlined_format_args )] use std::fmt::Write as _; diff --git a/tests/ui/identity_op.rs b/tests/ui/identity_op.rs index 3d06d2a73b62..9a435cdbb753 100644 --- a/tests/ui/identity_op.rs +++ b/tests/ui/identity_op.rs @@ -1,13 +1,13 @@ // run-rustfix - #![warn(clippy::identity_op)] +#![allow(unused)] #![allow( clippy::eq_op, clippy::no_effect, clippy::unnecessary_operation, clippy::op_ref, clippy::double_parens, - unused + clippy::uninlined_format_args )] use std::fmt::Write as _; diff --git a/tests/ui/implicit_saturating_add.fixed b/tests/ui/implicit_saturating_add.fixed new file mode 100644 index 000000000000..7d363d59a6f0 --- /dev/null +++ b/tests/ui/implicit_saturating_add.fixed @@ -0,0 +1,106 @@ +// run-rustfix + +#![allow(unused)] +#![warn(clippy::implicit_saturating_add)] + +fn main() { + let mut u_8: u8 = 255; + let mut u_16: u16 = 500; + let mut u_32: u32 = 7000; + let mut u_64: u64 = 7000; + let mut i_8: i8 = 30; + let mut i_16: i16 = 500; + let mut i_32: i32 = 7000; + let mut i_64: i64 = 7000; + + if i_8 < 42 { + i_8 += 1; + } + if i_8 != 42 { + i_8 += 1; + } + + u_8 = u_8.saturating_add(1); + + u_8 = u_8.saturating_add(1); + + if u_8 < 15 { + u_8 += 1; + } + + u_16 = u_16.saturating_add(1); + + u_16 = u_16.saturating_add(1); + + u_16 = u_16.saturating_add(1); + + u_32 = u_32.saturating_add(1); + + u_32 = u_32.saturating_add(1); + + u_32 = u_32.saturating_add(1); + + u_64 = u_64.saturating_add(1); + + u_64 = u_64.saturating_add(1); + + u_64 = u_64.saturating_add(1); + + i_8 = i_8.saturating_add(1); + + i_8 = i_8.saturating_add(1); + + i_8 = i_8.saturating_add(1); + + i_16 = i_16.saturating_add(1); + + i_16 = i_16.saturating_add(1); + + i_16 = i_16.saturating_add(1); + + i_32 = i_32.saturating_add(1); + + i_32 = i_32.saturating_add(1); + + i_32 = i_32.saturating_add(1); + + i_64 = i_64.saturating_add(1); + + i_64 = i_64.saturating_add(1); + + i_64 = i_64.saturating_add(1); + + if i_64 < 42 { + i_64 += 1; + } + + if 42 > i_64 { + i_64 += 1; + } + + let a = 12; + let mut b = 8; + + if a < u8::MAX { + b += 1; + } + + if u8::MAX > a { + b += 1; + } + + if u_32 < u32::MAX { + u_32 += 1; + } else { + println!("don't lint this"); + } + + if u_32 < u32::MAX { + println!("don't lint this"); + u_32 += 1; + } + + if u_32 < 42 { + println!("brace yourself!"); + } else {u_32 = u_32.saturating_add(1); } +} diff --git a/tests/ui/implicit_saturating_add.rs b/tests/ui/implicit_saturating_add.rs new file mode 100644 index 000000000000..31a5916277fa --- /dev/null +++ b/tests/ui/implicit_saturating_add.rs @@ -0,0 +1,154 @@ +// run-rustfix + +#![allow(unused)] +#![warn(clippy::implicit_saturating_add)] + +fn main() { + let mut u_8: u8 = 255; + let mut u_16: u16 = 500; + let mut u_32: u32 = 7000; + let mut u_64: u64 = 7000; + let mut i_8: i8 = 30; + let mut i_16: i16 = 500; + let mut i_32: i32 = 7000; + let mut i_64: i64 = 7000; + + if i_8 < 42 { + i_8 += 1; + } + if i_8 != 42 { + i_8 += 1; + } + + if u_8 != u8::MAX { + u_8 += 1; + } + + if u_8 < u8::MAX { + u_8 += 1; + } + + if u_8 < 15 { + u_8 += 1; + } + + if u_16 != u16::MAX { + u_16 += 1; + } + + if u_16 < u16::MAX { + u_16 += 1; + } + + if u16::MAX > u_16 { + u_16 += 1; + } + + if u_32 != u32::MAX { + u_32 += 1; + } + + if u_32 < u32::MAX { + u_32 += 1; + } + + if u32::MAX > u_32 { + u_32 += 1; + } + + if u_64 != u64::MAX { + u_64 += 1; + } + + if u_64 < u64::MAX { + u_64 += 1; + } + + if u64::MAX > u_64 { + u_64 += 1; + } + + if i_8 != i8::MAX { + i_8 += 1; + } + + if i_8 < i8::MAX { + i_8 += 1; + } + + if i8::MAX > i_8 { + i_8 += 1; + } + + if i_16 != i16::MAX { + i_16 += 1; + } + + if i_16 < i16::MAX { + i_16 += 1; + } + + if i16::MAX > i_16 { + i_16 += 1; + } + + if i_32 != i32::MAX { + i_32 += 1; + } + + if i_32 < i32::MAX { + i_32 += 1; + } + + if i32::MAX > i_32 { + i_32 += 1; + } + + if i_64 != i64::MAX { + i_64 += 1; + } + + if i_64 < i64::MAX { + i_64 += 1; + } + + if i64::MAX > i_64 { + i_64 += 1; + } + + if i_64 < 42 { + i_64 += 1; + } + + if 42 > i_64 { + i_64 += 1; + } + + let a = 12; + let mut b = 8; + + if a < u8::MAX { + b += 1; + } + + if u8::MAX > a { + b += 1; + } + + if u_32 < u32::MAX { + u_32 += 1; + } else { + println!("don't lint this"); + } + + if u_32 < u32::MAX { + println!("don't lint this"); + u_32 += 1; + } + + if u_32 < 42 { + println!("brace yourself!"); + } else if u_32 < u32::MAX { + u_32 += 1; + } +} diff --git a/tests/ui/implicit_saturating_add.stderr b/tests/ui/implicit_saturating_add.stderr new file mode 100644 index 000000000000..42ae1b488853 --- /dev/null +++ b/tests/ui/implicit_saturating_add.stderr @@ -0,0 +1,197 @@ +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:23:5 + | +LL | / if u_8 != u8::MAX { +LL | | u_8 += 1; +LL | | } + | |_____^ help: use instead: `u_8 = u_8.saturating_add(1);` + | + = note: `-D clippy::implicit-saturating-add` implied by `-D warnings` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:27:5 + | +LL | / if u_8 < u8::MAX { +LL | | u_8 += 1; +LL | | } + | |_____^ help: use instead: `u_8 = u_8.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:35:5 + | +LL | / if u_16 != u16::MAX { +LL | | u_16 += 1; +LL | | } + | |_____^ help: use instead: `u_16 = u_16.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:39:5 + | +LL | / if u_16 < u16::MAX { +LL | | u_16 += 1; +LL | | } + | |_____^ help: use instead: `u_16 = u_16.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:43:5 + | +LL | / if u16::MAX > u_16 { +LL | | u_16 += 1; +LL | | } + | |_____^ help: use instead: `u_16 = u_16.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:47:5 + | +LL | / if u_32 != u32::MAX { +LL | | u_32 += 1; +LL | | } + | |_____^ help: use instead: `u_32 = u_32.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:51:5 + | +LL | / if u_32 < u32::MAX { +LL | | u_32 += 1; +LL | | } + | |_____^ help: use instead: `u_32 = u_32.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:55:5 + | +LL | / if u32::MAX > u_32 { +LL | | u_32 += 1; +LL | | } + | |_____^ help: use instead: `u_32 = u_32.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:59:5 + | +LL | / if u_64 != u64::MAX { +LL | | u_64 += 1; +LL | | } + | |_____^ help: use instead: `u_64 = u_64.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:63:5 + | +LL | / if u_64 < u64::MAX { +LL | | u_64 += 1; +LL | | } + | |_____^ help: use instead: `u_64 = u_64.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:67:5 + | +LL | / if u64::MAX > u_64 { +LL | | u_64 += 1; +LL | | } + | |_____^ help: use instead: `u_64 = u_64.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:71:5 + | +LL | / if i_8 != i8::MAX { +LL | | i_8 += 1; +LL | | } + | |_____^ help: use instead: `i_8 = i_8.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:75:5 + | +LL | / if i_8 < i8::MAX { +LL | | i_8 += 1; +LL | | } + | |_____^ help: use instead: `i_8 = i_8.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:79:5 + | +LL | / if i8::MAX > i_8 { +LL | | i_8 += 1; +LL | | } + | |_____^ help: use instead: `i_8 = i_8.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:83:5 + | +LL | / if i_16 != i16::MAX { +LL | | i_16 += 1; +LL | | } + | |_____^ help: use instead: `i_16 = i_16.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:87:5 + | +LL | / if i_16 < i16::MAX { +LL | | i_16 += 1; +LL | | } + | |_____^ help: use instead: `i_16 = i_16.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:91:5 + | +LL | / if i16::MAX > i_16 { +LL | | i_16 += 1; +LL | | } + | |_____^ help: use instead: `i_16 = i_16.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:95:5 + | +LL | / if i_32 != i32::MAX { +LL | | i_32 += 1; +LL | | } + | |_____^ help: use instead: `i_32 = i_32.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:99:5 + | +LL | / if i_32 < i32::MAX { +LL | | i_32 += 1; +LL | | } + | |_____^ help: use instead: `i_32 = i_32.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:103:5 + | +LL | / if i32::MAX > i_32 { +LL | | i_32 += 1; +LL | | } + | |_____^ help: use instead: `i_32 = i_32.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:107:5 + | +LL | / if i_64 != i64::MAX { +LL | | i_64 += 1; +LL | | } + | |_____^ help: use instead: `i_64 = i_64.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:111:5 + | +LL | / if i_64 < i64::MAX { +LL | | i_64 += 1; +LL | | } + | |_____^ help: use instead: `i_64 = i_64.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:115:5 + | +LL | / if i64::MAX > i_64 { +LL | | i_64 += 1; +LL | | } + | |_____^ help: use instead: `i_64 = i_64.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:151:12 + | +LL | } else if u_32 < u32::MAX { + | ____________^ +LL | | u_32 += 1; +LL | | } + | |_____^ help: use instead: `{u_32 = u_32.saturating_add(1); }` + +error: aborting due to 24 previous errors + diff --git a/tests/ui/index_refutable_slice/if_let_slice_binding.rs b/tests/ui/index_refutable_slice/if_let_slice_binding.rs index c2c0c520dc62..0a3374d11b03 100644 --- a/tests/ui/index_refutable_slice/if_let_slice_binding.rs +++ b/tests/ui/index_refutable_slice/if_let_slice_binding.rs @@ -1,4 +1,5 @@ #![deny(clippy::index_refutable_slice)] +#![allow(clippy::uninlined_format_args)] enum SomeEnum { One(T), diff --git a/tests/ui/index_refutable_slice/if_let_slice_binding.stderr b/tests/ui/index_refutable_slice/if_let_slice_binding.stderr index a607df9b8766..0a13ac1354e5 100644 --- a/tests/ui/index_refutable_slice/if_let_slice_binding.stderr +++ b/tests/ui/index_refutable_slice/if_let_slice_binding.stderr @@ -1,5 +1,5 @@ error: this binding can be a slice pattern to avoid indexing - --> $DIR/if_let_slice_binding.rs:13:17 + --> $DIR/if_let_slice_binding.rs:14:17 | LL | if let Some(slice) = slice { | ^^^^^ @@ -19,7 +19,7 @@ LL | println!("{}", slice_0); | ~~~~~~~ error: this binding can be a slice pattern to avoid indexing - --> $DIR/if_let_slice_binding.rs:19:17 + --> $DIR/if_let_slice_binding.rs:20:17 | LL | if let Some(slice) = slice { | ^^^^^ @@ -34,7 +34,7 @@ LL | println!("{}", slice_0); | ~~~~~~~ error: this binding can be a slice pattern to avoid indexing - --> $DIR/if_let_slice_binding.rs:25:17 + --> $DIR/if_let_slice_binding.rs:26:17 | LL | if let Some(slice) = slice { | ^^^^^ @@ -50,7 +50,7 @@ LL ~ println!("{}", slice_0); | error: this binding can be a slice pattern to avoid indexing - --> $DIR/if_let_slice_binding.rs:32:26 + --> $DIR/if_let_slice_binding.rs:33:26 | LL | if let SomeEnum::One(slice) | SomeEnum::Three(slice) = slice_wrapped { | ^^^^^ @@ -65,7 +65,7 @@ LL | println!("{}", slice_0); | ~~~~~~~ error: this binding can be a slice pattern to avoid indexing - --> $DIR/if_let_slice_binding.rs:39:29 + --> $DIR/if_let_slice_binding.rs:40:29 | LL | if let (SomeEnum::Three(a), Some(b)) = (a_wrapped, b_wrapped) { | ^ @@ -80,7 +80,7 @@ LL | println!("{} -> {}", a_2, b[1]); | ~~~ error: this binding can be a slice pattern to avoid indexing - --> $DIR/if_let_slice_binding.rs:39:38 + --> $DIR/if_let_slice_binding.rs:40:38 | LL | if let (SomeEnum::Three(a), Some(b)) = (a_wrapped, b_wrapped) { | ^ @@ -95,7 +95,7 @@ LL | println!("{} -> {}", a[2], b_1); | ~~~ error: this binding can be a slice pattern to avoid indexing - --> $DIR/if_let_slice_binding.rs:46:21 + --> $DIR/if_let_slice_binding.rs:47:21 | LL | if let Some(ref slice) = slice { | ^^^^^ @@ -110,7 +110,7 @@ LL | println!("{:?}", slice_1); | ~~~~~~~ error: this binding can be a slice pattern to avoid indexing - --> $DIR/if_let_slice_binding.rs:54:17 + --> $DIR/if_let_slice_binding.rs:55:17 | LL | if let Some(slice) = &slice { | ^^^^^ @@ -125,7 +125,7 @@ LL | println!("{:?}", slice_0); | ~~~~~~~ error: this binding can be a slice pattern to avoid indexing - --> $DIR/if_let_slice_binding.rs:123:17 + --> $DIR/if_let_slice_binding.rs:124:17 | LL | if let Some(slice) = wrap.inner { | ^^^^^ @@ -140,7 +140,7 @@ LL | println!("This is awesome! {}", slice_0); | ~~~~~~~ error: this binding can be a slice pattern to avoid indexing - --> $DIR/if_let_slice_binding.rs:130:17 + --> $DIR/if_let_slice_binding.rs:131:17 | LL | if let Some(slice) = wrap.inner { | ^^^^^ diff --git a/tests/ui/infinite_iter.rs b/tests/ui/infinite_iter.rs index a1e5fad0c621..622644f675d3 100644 --- a/tests/ui/infinite_iter.rs +++ b/tests/ui/infinite_iter.rs @@ -1,3 +1,5 @@ +#![allow(clippy::uninlined_format_args)] + use std::iter::repeat; fn square_is_lower_64(x: &u32) -> bool { x * x < 64 diff --git a/tests/ui/infinite_iter.stderr b/tests/ui/infinite_iter.stderr index ba277e36339a..b911163f715e 100644 --- a/tests/ui/infinite_iter.stderr +++ b/tests/ui/infinite_iter.stderr @@ -1,29 +1,29 @@ error: infinite iteration detected - --> $DIR/infinite_iter.rs:9:5 + --> $DIR/infinite_iter.rs:11:5 | LL | repeat(0_u8).collect::>(); // infinite iter | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: the lint level is defined here - --> $DIR/infinite_iter.rs:7:8 + --> $DIR/infinite_iter.rs:9:8 | LL | #[deny(clippy::infinite_iter)] | ^^^^^^^^^^^^^^^^^^^^^ error: infinite iteration detected - --> $DIR/infinite_iter.rs:10:5 + --> $DIR/infinite_iter.rs:12:5 | LL | (0..8_u32).take_while(square_is_lower_64).cycle().count(); // infinite iter | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: infinite iteration detected - --> $DIR/infinite_iter.rs:11:5 + --> $DIR/infinite_iter.rs:13:5 | LL | (0..8_u64).chain(0..).max(); // infinite iter | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: infinite iteration detected - --> $DIR/infinite_iter.rs:16:5 + --> $DIR/infinite_iter.rs:18:5 | LL | / (0..8_u32) LL | | .rev() @@ -33,37 +33,37 @@ LL | | .for_each(|x| println!("{}", x)); // infinite iter | |________________________________________^ error: infinite iteration detected - --> $DIR/infinite_iter.rs:22:5 + --> $DIR/infinite_iter.rs:24:5 | LL | (0_usize..).flat_map(|x| 0..x).product::(); // infinite iter | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: infinite iteration detected - --> $DIR/infinite_iter.rs:23:5 + --> $DIR/infinite_iter.rs:25:5 | LL | (0_u64..).filter(|x| x % 2 == 0).last(); // infinite iter | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: possible infinite iteration detected - --> $DIR/infinite_iter.rs:30:5 + --> $DIR/infinite_iter.rs:32:5 | LL | (0..).zip((0..).take_while(square_is_lower_64)).count(); // maybe infinite iter | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: the lint level is defined here - --> $DIR/infinite_iter.rs:28:8 + --> $DIR/infinite_iter.rs:30:8 | LL | #[deny(clippy::maybe_infinite_iter)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: possible infinite iteration detected - --> $DIR/infinite_iter.rs:31:5 + --> $DIR/infinite_iter.rs:33:5 | LL | repeat(42).take_while(|x| *x == 42).chain(0..42).max(); // maybe infinite iter | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: possible infinite iteration detected - --> $DIR/infinite_iter.rs:32:5 + --> $DIR/infinite_iter.rs:34:5 | LL | / (1..) LL | | .scan(0, |state, x| { @@ -74,31 +74,31 @@ LL | | .min(); // maybe infinite iter | |______________^ error: possible infinite iteration detected - --> $DIR/infinite_iter.rs:38:5 + --> $DIR/infinite_iter.rs:40:5 | LL | (0..).find(|x| *x == 24); // maybe infinite iter | ^^^^^^^^^^^^^^^^^^^^^^^^ error: possible infinite iteration detected - --> $DIR/infinite_iter.rs:39:5 + --> $DIR/infinite_iter.rs:41:5 | LL | (0..).position(|x| x == 24); // maybe infinite iter | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: possible infinite iteration detected - --> $DIR/infinite_iter.rs:40:5 + --> $DIR/infinite_iter.rs:42:5 | LL | (0..).any(|x| x == 24); // maybe infinite iter | ^^^^^^^^^^^^^^^^^^^^^^ error: possible infinite iteration detected - --> $DIR/infinite_iter.rs:41:5 + --> $DIR/infinite_iter.rs:43:5 | LL | (0..).all(|x| x == 24); // maybe infinite iter | ^^^^^^^^^^^^^^^^^^^^^^ error: infinite iteration detected - --> $DIR/infinite_iter.rs:63:31 + --> $DIR/infinite_iter.rs:65:31 | LL | let _: HashSet = (0..).collect(); // Infinite iter | ^^^^^^^^^^^^^^^ diff --git a/tests/ui/issue_2356.fixed b/tests/ui/issue_2356.fixed index 942e99fa8787..a73ee0fb2e59 100644 --- a/tests/ui/issue_2356.fixed +++ b/tests/ui/issue_2356.fixed @@ -1,6 +1,7 @@ // run-rustfix #![deny(clippy::while_let_on_iterator)] #![allow(unused_mut)] +#![allow(clippy::uninlined_format_args)] use std::iter::Iterator; diff --git a/tests/ui/issue_2356.rs b/tests/ui/issue_2356.rs index b000234ea596..9dd9069609b1 100644 --- a/tests/ui/issue_2356.rs +++ b/tests/ui/issue_2356.rs @@ -1,6 +1,7 @@ // run-rustfix #![deny(clippy::while_let_on_iterator)] #![allow(unused_mut)] +#![allow(clippy::uninlined_format_args)] use std::iter::Iterator; diff --git a/tests/ui/issue_2356.stderr b/tests/ui/issue_2356.stderr index 4e3ff7522e0b..a24b0b32e470 100644 --- a/tests/ui/issue_2356.stderr +++ b/tests/ui/issue_2356.stderr @@ -1,5 +1,5 @@ error: this loop could be written as a `for` loop - --> $DIR/issue_2356.rs:17:9 + --> $DIR/issue_2356.rs:18:9 | LL | while let Some(e) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for e in it` diff --git a/tests/ui/issue_4266.rs b/tests/ui/issue_4266.rs index d9d48189bd74..8e0620e52b65 100644 --- a/tests/ui/issue_4266.rs +++ b/tests/ui/issue_4266.rs @@ -1,4 +1,5 @@ #![allow(dead_code)] +#![allow(clippy::uninlined_format_args)] async fn sink1<'a>(_: &'a str) {} // lint async fn sink1_elided(_: &str) {} // ok diff --git a/tests/ui/issue_4266.stderr b/tests/ui/issue_4266.stderr index 240f4bcc38fd..fb2a93c9580e 100644 --- a/tests/ui/issue_4266.stderr +++ b/tests/ui/issue_4266.stderr @@ -1,5 +1,5 @@ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/issue_4266.rs:3:1 + --> $DIR/issue_4266.rs:4:1 | LL | async fn sink1<'a>(_: &'a str) {} // lint | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,13 +7,13 @@ LL | async fn sink1<'a>(_: &'a str) {} // lint = note: `-D clippy::needless-lifetimes` implied by `-D warnings` error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/issue_4266.rs:7:1 + --> $DIR/issue_4266.rs:8:1 | LL | async fn one_to_one<'a>(s: &'a str) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: methods called `new` usually take no `self` - --> $DIR/issue_4266.rs:27:22 + --> $DIR/issue_4266.rs:28:22 | LL | pub async fn new(&mut self) -> Self { | ^^^^^^^^^ diff --git a/tests/ui/item_after_statement.rs b/tests/ui/item_after_statement.rs index d439ca1e4e1a..5e92dcab1f5a 100644 --- a/tests/ui/item_after_statement.rs +++ b/tests/ui/item_after_statement.rs @@ -1,4 +1,5 @@ #![warn(clippy::items_after_statements)] +#![allow(clippy::uninlined_format_args)] fn ok() { fn foo() { diff --git a/tests/ui/item_after_statement.stderr b/tests/ui/item_after_statement.stderr index ab4a6374c73c..2523c53ac53a 100644 --- a/tests/ui/item_after_statement.stderr +++ b/tests/ui/item_after_statement.stderr @@ -1,5 +1,5 @@ error: adding items after statements is confusing, since items exist from the start of the scope - --> $DIR/item_after_statement.rs:12:5 + --> $DIR/item_after_statement.rs:13:5 | LL | / fn foo() { LL | | println!("foo"); @@ -9,7 +9,7 @@ LL | | } = note: `-D clippy::items-after-statements` implied by `-D warnings` error: adding items after statements is confusing, since items exist from the start of the scope - --> $DIR/item_after_statement.rs:19:5 + --> $DIR/item_after_statement.rs:20:5 | LL | / fn foo() { LL | | println!("foo"); @@ -17,7 +17,7 @@ LL | | } | |_____^ error: adding items after statements is confusing, since items exist from the start of the scope - --> $DIR/item_after_statement.rs:32:13 + --> $DIR/item_after_statement.rs:33:13 | LL | / fn say_something() { LL | | println!("something"); diff --git a/tests/ui/manual_assert.edition2018.fixed b/tests/ui/manual_assert.edition2018.fixed index 65598f1eaccc..26e3b8f63e70 100644 --- a/tests/ui/manual_assert.edition2018.fixed +++ b/tests/ui/manual_assert.edition2018.fixed @@ -4,7 +4,8 @@ // run-rustfix #![warn(clippy::manual_assert)] -#![allow(clippy::nonminimal_bool)] +#![allow(dead_code, unused_doc_comments)] +#![allow(clippy::nonminimal_bool, clippy::uninlined_format_args)] macro_rules! one { () => { @@ -50,3 +51,14 @@ fn main() { assert!(!(a.is_empty() || !b.is_empty()), "panic5"); assert!(!a.is_empty(), "with expansion {}", one!()); } + +fn issue7730(a: u8) { + // Suggestion should preserve comment + // comment +/* this is a + multiline + comment */ +/// Doc comment +// comment after `panic!` +assert!(!(a > 2), "panic with comment"); +} diff --git a/tests/ui/manual_assert.edition2018.stderr b/tests/ui/manual_assert.edition2018.stderr index a0f31afd6ebf..7718588fdf6f 100644 --- a/tests/ui/manual_assert.edition2018.stderr +++ b/tests/ui/manual_assert.edition2018.stderr @@ -1,68 +1,124 @@ error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:30:5 + --> $DIR/manual_assert.rs:31:5 | LL | / if !a.is_empty() { LL | | panic!("qaqaq{:?}", a); LL | | } - | |_____^ help: try: `assert!(a.is_empty(), "qaqaq{:?}", a);` + | |_____^ | = note: `-D clippy::manual-assert` implied by `-D warnings` +help: try instead + | +LL | assert!(a.is_empty(), "qaqaq{:?}", a); + | error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:33:5 + --> $DIR/manual_assert.rs:34:5 | LL | / if !a.is_empty() { LL | | panic!("qwqwq"); LL | | } - | |_____^ help: try: `assert!(a.is_empty(), "qwqwq");` + | |_____^ + | +help: try instead + | +LL | assert!(a.is_empty(), "qwqwq"); + | error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:50:5 + --> $DIR/manual_assert.rs:51:5 | LL | / if b.is_empty() { LL | | panic!("panic1"); LL | | } - | |_____^ help: try: `assert!(!b.is_empty(), "panic1");` + | |_____^ + | +help: try instead + | +LL | assert!(!b.is_empty(), "panic1"); + | error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:53:5 + --> $DIR/manual_assert.rs:54:5 | LL | / if b.is_empty() && a.is_empty() { LL | | panic!("panic2"); LL | | } - | |_____^ help: try: `assert!(!(b.is_empty() && a.is_empty()), "panic2");` + | |_____^ + | +help: try instead + | +LL | assert!(!(b.is_empty() && a.is_empty()), "panic2"); + | error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:56:5 + --> $DIR/manual_assert.rs:57:5 | LL | / if a.is_empty() && !b.is_empty() { LL | | panic!("panic3"); LL | | } - | |_____^ help: try: `assert!(!(a.is_empty() && !b.is_empty()), "panic3");` + | |_____^ + | +help: try instead + | +LL | assert!(!(a.is_empty() && !b.is_empty()), "panic3"); + | error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:59:5 + --> $DIR/manual_assert.rs:60:5 | LL | / if b.is_empty() || a.is_empty() { LL | | panic!("panic4"); LL | | } - | |_____^ help: try: `assert!(!(b.is_empty() || a.is_empty()), "panic4");` + | |_____^ + | +help: try instead + | +LL | assert!(!(b.is_empty() || a.is_empty()), "panic4"); + | error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:62:5 + --> $DIR/manual_assert.rs:63:5 | LL | / if a.is_empty() || !b.is_empty() { LL | | panic!("panic5"); LL | | } - | |_____^ help: try: `assert!(!(a.is_empty() || !b.is_empty()), "panic5");` + | |_____^ + | +help: try instead + | +LL | assert!(!(a.is_empty() || !b.is_empty()), "panic5"); + | error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:65:5 + --> $DIR/manual_assert.rs:66:5 | LL | / if a.is_empty() { LL | | panic!("with expansion {}", one!()) LL | | } - | |_____^ help: try: `assert!(!a.is_empty(), "with expansion {}", one!());` + | |_____^ + | +help: try instead + | +LL | assert!(!a.is_empty(), "with expansion {}", one!()); + | -error: aborting due to 8 previous errors +error: only a `panic!` in `if`-then statement + --> $DIR/manual_assert.rs:73:5 + | +LL | / if a > 2 { +LL | | // comment +LL | | /* this is a +LL | | multiline +... | +LL | | panic!("panic with comment") // comment after `panic!` +LL | | } + | |_____^ + | +help: try instead + | +LL | assert!(!(a > 2), "panic with comment"); + | + +error: aborting due to 9 previous errors diff --git a/tests/ui/manual_assert.edition2021.fixed b/tests/ui/manual_assert.edition2021.fixed index 65598f1eaccc..26e3b8f63e70 100644 --- a/tests/ui/manual_assert.edition2021.fixed +++ b/tests/ui/manual_assert.edition2021.fixed @@ -4,7 +4,8 @@ // run-rustfix #![warn(clippy::manual_assert)] -#![allow(clippy::nonminimal_bool)] +#![allow(dead_code, unused_doc_comments)] +#![allow(clippy::nonminimal_bool, clippy::uninlined_format_args)] macro_rules! one { () => { @@ -50,3 +51,14 @@ fn main() { assert!(!(a.is_empty() || !b.is_empty()), "panic5"); assert!(!a.is_empty(), "with expansion {}", one!()); } + +fn issue7730(a: u8) { + // Suggestion should preserve comment + // comment +/* this is a + multiline + comment */ +/// Doc comment +// comment after `panic!` +assert!(!(a > 2), "panic with comment"); +} diff --git a/tests/ui/manual_assert.edition2021.stderr b/tests/ui/manual_assert.edition2021.stderr index a0f31afd6ebf..7718588fdf6f 100644 --- a/tests/ui/manual_assert.edition2021.stderr +++ b/tests/ui/manual_assert.edition2021.stderr @@ -1,68 +1,124 @@ error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:30:5 + --> $DIR/manual_assert.rs:31:5 | LL | / if !a.is_empty() { LL | | panic!("qaqaq{:?}", a); LL | | } - | |_____^ help: try: `assert!(a.is_empty(), "qaqaq{:?}", a);` + | |_____^ | = note: `-D clippy::manual-assert` implied by `-D warnings` +help: try instead + | +LL | assert!(a.is_empty(), "qaqaq{:?}", a); + | error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:33:5 + --> $DIR/manual_assert.rs:34:5 | LL | / if !a.is_empty() { LL | | panic!("qwqwq"); LL | | } - | |_____^ help: try: `assert!(a.is_empty(), "qwqwq");` + | |_____^ + | +help: try instead + | +LL | assert!(a.is_empty(), "qwqwq"); + | error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:50:5 + --> $DIR/manual_assert.rs:51:5 | LL | / if b.is_empty() { LL | | panic!("panic1"); LL | | } - | |_____^ help: try: `assert!(!b.is_empty(), "panic1");` + | |_____^ + | +help: try instead + | +LL | assert!(!b.is_empty(), "panic1"); + | error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:53:5 + --> $DIR/manual_assert.rs:54:5 | LL | / if b.is_empty() && a.is_empty() { LL | | panic!("panic2"); LL | | } - | |_____^ help: try: `assert!(!(b.is_empty() && a.is_empty()), "panic2");` + | |_____^ + | +help: try instead + | +LL | assert!(!(b.is_empty() && a.is_empty()), "panic2"); + | error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:56:5 + --> $DIR/manual_assert.rs:57:5 | LL | / if a.is_empty() && !b.is_empty() { LL | | panic!("panic3"); LL | | } - | |_____^ help: try: `assert!(!(a.is_empty() && !b.is_empty()), "panic3");` + | |_____^ + | +help: try instead + | +LL | assert!(!(a.is_empty() && !b.is_empty()), "panic3"); + | error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:59:5 + --> $DIR/manual_assert.rs:60:5 | LL | / if b.is_empty() || a.is_empty() { LL | | panic!("panic4"); LL | | } - | |_____^ help: try: `assert!(!(b.is_empty() || a.is_empty()), "panic4");` + | |_____^ + | +help: try instead + | +LL | assert!(!(b.is_empty() || a.is_empty()), "panic4"); + | error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:62:5 + --> $DIR/manual_assert.rs:63:5 | LL | / if a.is_empty() || !b.is_empty() { LL | | panic!("panic5"); LL | | } - | |_____^ help: try: `assert!(!(a.is_empty() || !b.is_empty()), "panic5");` + | |_____^ + | +help: try instead + | +LL | assert!(!(a.is_empty() || !b.is_empty()), "panic5"); + | error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:65:5 + --> $DIR/manual_assert.rs:66:5 | LL | / if a.is_empty() { LL | | panic!("with expansion {}", one!()) LL | | } - | |_____^ help: try: `assert!(!a.is_empty(), "with expansion {}", one!());` + | |_____^ + | +help: try instead + | +LL | assert!(!a.is_empty(), "with expansion {}", one!()); + | -error: aborting due to 8 previous errors +error: only a `panic!` in `if`-then statement + --> $DIR/manual_assert.rs:73:5 + | +LL | / if a > 2 { +LL | | // comment +LL | | /* this is a +LL | | multiline +... | +LL | | panic!("panic with comment") // comment after `panic!` +LL | | } + | |_____^ + | +help: try instead + | +LL | assert!(!(a > 2), "panic with comment"); + | + +error: aborting due to 9 previous errors diff --git a/tests/ui/manual_assert.fixed b/tests/ui/manual_assert.fixed deleted file mode 100644 index a2393674fe61..000000000000 --- a/tests/ui/manual_assert.fixed +++ /dev/null @@ -1,45 +0,0 @@ -// revisions: edition2018 edition2021 -// [edition2018] edition:2018 -// [edition2021] edition:2021 -// run-rustfix - -#![warn(clippy::manual_assert)] -#![allow(clippy::nonminimal_bool)] - -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); - } - assert!(a.is_empty(), "qaqaq{:?}", a); - 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]; - assert!(!b.is_empty(), "panic1"); - assert!(!(b.is_empty() && a.is_empty()), "panic2"); - assert!(!(a.is_empty() && !b.is_empty()), "panic3"); - assert!(!(b.is_empty() || a.is_empty()), "panic4"); - assert!(!(a.is_empty() || !b.is_empty()), "panic5"); -} diff --git a/tests/ui/manual_assert.rs b/tests/ui/manual_assert.rs index 4d2706dd6211..8c37753071df 100644 --- a/tests/ui/manual_assert.rs +++ b/tests/ui/manual_assert.rs @@ -4,7 +4,8 @@ // run-rustfix #![warn(clippy::manual_assert)] -#![allow(clippy::nonminimal_bool)] +#![allow(dead_code, unused_doc_comments)] +#![allow(clippy::nonminimal_bool, clippy::uninlined_format_args)] macro_rules! one { () => { @@ -66,3 +67,15 @@ fn main() { panic!("with expansion {}", one!()) } } + +fn issue7730(a: u8) { + // Suggestion should preserve comment + if a > 2 { + // comment + /* this is a + multiline + comment */ + /// Doc comment + panic!("panic with comment") // comment after `panic!` + } +} diff --git a/tests/ui/manual_bits.fixed b/tests/ui/manual_bits.fixed index 386360dbdcdb..e7f8cd878ca7 100644 --- a/tests/ui/manual_bits.fixed +++ b/tests/ui/manual_bits.fixed @@ -6,7 +6,8 @@ clippy::useless_conversion, path_statements, unused_must_use, - clippy::unnecessary_operation + clippy::unnecessary_operation, + clippy::unnecessary_cast )] use std::mem::{size_of, size_of_val}; diff --git a/tests/ui/manual_bits.rs b/tests/ui/manual_bits.rs index 62638f047eb0..7b1d15495287 100644 --- a/tests/ui/manual_bits.rs +++ b/tests/ui/manual_bits.rs @@ -6,7 +6,8 @@ clippy::useless_conversion, path_statements, unused_must_use, - clippy::unnecessary_operation + clippy::unnecessary_operation, + clippy::unnecessary_cast )] use std::mem::{size_of, size_of_val}; diff --git a/tests/ui/manual_bits.stderr b/tests/ui/manual_bits.stderr index 69c591a203d3..652fafbc41d8 100644 --- a/tests/ui/manual_bits.stderr +++ b/tests/ui/manual_bits.stderr @@ -1,5 +1,5 @@ error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:15:5 + --> $DIR/manual_bits.rs:16:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^ help: consider using: `i8::BITS as usize` @@ -7,169 +7,169 @@ LL | size_of::() * 8; = note: `-D clippy::manual-bits` implied by `-D warnings` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:16:5 + --> $DIR/manual_bits.rs:17:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i16::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:17:5 + --> $DIR/manual_bits.rs:18:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i32::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:18:5 + --> $DIR/manual_bits.rs:19:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i64::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:19:5 + --> $DIR/manual_bits.rs:20:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `i128::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:20:5 + --> $DIR/manual_bits.rs:21:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `isize::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:22:5 + --> $DIR/manual_bits.rs:23:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^ help: consider using: `u8::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:23:5 + --> $DIR/manual_bits.rs:24:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u16::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:24:5 + --> $DIR/manual_bits.rs:25:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u32::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:25:5 + --> $DIR/manual_bits.rs:26:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u64::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:26:5 + --> $DIR/manual_bits.rs:27:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:27:5 + --> $DIR/manual_bits.rs:28:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `usize::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:29:5 + --> $DIR/manual_bits.rs:30:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^ help: consider using: `i8::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:30:5 + --> $DIR/manual_bits.rs:31:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i16::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:31:5 + --> $DIR/manual_bits.rs:32:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i32::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:32:5 + --> $DIR/manual_bits.rs:33:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i64::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:33:5 + --> $DIR/manual_bits.rs:34:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `i128::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:34:5 + --> $DIR/manual_bits.rs:35:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `isize::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:36:5 + --> $DIR/manual_bits.rs:37:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^ help: consider using: `u8::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:37:5 + --> $DIR/manual_bits.rs:38:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u16::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:38:5 + --> $DIR/manual_bits.rs:39:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u32::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:39:5 + --> $DIR/manual_bits.rs:40:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u64::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:40:5 + --> $DIR/manual_bits.rs:41:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:41:5 + --> $DIR/manual_bits.rs:42:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `usize::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:51:5 + --> $DIR/manual_bits.rs:52:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `Word::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:55:18 + --> $DIR/manual_bits.rs:56:18 | LL | let _: u32 = (size_of::() * 8) as u32; | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:56:18 + --> $DIR/manual_bits.rs:57:18 | LL | let _: u32 = (size_of::() * 8).try_into().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:57:13 + --> $DIR/manual_bits.rs:58:13 | LL | let _ = (size_of::() * 8).pow(5); | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(u128::BITS as usize)` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:58:14 + --> $DIR/manual_bits.rs:59:14 | LL | let _ = &(size_of::() * 8); | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(u128::BITS as usize)` diff --git a/tests/ui/manual_clamp.rs b/tests/ui/manual_clamp.rs new file mode 100644 index 000000000000..54fd888af99f --- /dev/null +++ b/tests/ui/manual_clamp.rs @@ -0,0 +1,304 @@ +#![warn(clippy::manual_clamp)] +#![allow( + unused, + dead_code, + clippy::unnecessary_operation, + clippy::no_effect, + clippy::if_same_then_else +)] + +use std::cmp::{max as cmp_max, min as cmp_min}; + +const CONST_MAX: i32 = 10; +const CONST_MIN: i32 = 4; + +const CONST_F64_MAX: f64 = 10.0; +const CONST_F64_MIN: f64 = 4.0; + +fn main() { + let (input, min, max) = (0, -2, 3); + // Lint + let x0 = if max < input { + max + } else if min > input { + min + } else { + input + }; + + let x1 = if input > max { + max + } else if input < min { + min + } else { + input + }; + + let x2 = if input < min { + min + } else if input > max { + max + } else { + input + }; + + let x3 = if min > input { + min + } else if max < input { + max + } else { + input + }; + + let x4 = input.max(min).min(max); + + let x5 = input.min(max).max(min); + + let x6 = match input { + x if x > max => max, + x if x < min => min, + x => x, + }; + + let x7 = match input { + x if x < min => min, + x if x > max => max, + x => x, + }; + + let x8 = match input { + x if max < x => max, + x if min > x => min, + x => x, + }; + + let mut x9 = input; + if x9 < min { + x9 = min; + } + if x9 > max { + x9 = max; + } + + let x10 = match input { + x if min > x => min, + x if max < x => max, + x => x, + }; + + let mut x11 = input; + let _ = 1; + if x11 > max { + x11 = max; + } + if x11 < min { + x11 = min; + } + + let mut x12 = input; + if min > x12 { + x12 = min; + } + if max < x12 { + x12 = max; + } + + let mut x13 = input; + if max < x13 { + x13 = max; + } + if min > x13 { + x13 = min; + } + + let x14 = if input > CONST_MAX { + CONST_MAX + } else if input < CONST_MIN { + CONST_MIN + } else { + input + }; + { + let (input, min, max) = (0.0f64, -2.0, 3.0); + let x15 = if input > max { + max + } else if input < min { + min + } else { + input + }; + } + { + let input: i32 = cmp_min_max(1); + // These can only be detected if exactly one of the arguments to the inner function is const. + let x16 = cmp_max(cmp_min(input, CONST_MAX), CONST_MIN); + let x17 = cmp_min(cmp_max(input, CONST_MIN), CONST_MAX); + let x18 = cmp_max(CONST_MIN, cmp_min(input, CONST_MAX)); + let x19 = cmp_min(CONST_MAX, cmp_max(input, CONST_MIN)); + let x20 = cmp_max(cmp_min(CONST_MAX, input), CONST_MIN); + let x21 = cmp_min(cmp_max(CONST_MIN, input), CONST_MAX); + let x22 = cmp_max(CONST_MIN, cmp_min(CONST_MAX, input)); + let x23 = cmp_min(CONST_MAX, cmp_max(CONST_MIN, input)); + let input: f64 = cmp_min_max(1) as f64; + let x24 = f64::max(f64::min(input, CONST_F64_MAX), CONST_F64_MIN); + let x25 = f64::min(f64::max(input, CONST_F64_MIN), CONST_F64_MAX); + let x26 = f64::max(CONST_F64_MIN, f64::min(input, CONST_F64_MAX)); + let x27 = f64::min(CONST_F64_MAX, f64::max(input, CONST_F64_MIN)); + let x28 = f64::max(f64::min(CONST_F64_MAX, input), CONST_F64_MIN); + let x29 = f64::min(f64::max(CONST_F64_MIN, input), CONST_F64_MAX); + let x30 = f64::max(CONST_F64_MIN, f64::min(CONST_F64_MAX, input)); + let x31 = f64::min(CONST_F64_MAX, f64::max(CONST_F64_MIN, input)); + } + let mut x32 = input; + if x32 < min { + x32 = min; + } else if x32 > max { + x32 = max; + } + + // It's important this be the last set of statements + let mut x33 = input; + if max < x33 { + x33 = max; + } + if min > x33 { + x33 = min; + } +} + +// This code intentionally nonsense. +fn no_lint() { + let (input, min, max) = (0, -2, 3); + let x0 = if max < input { + max + } else if min > input { + max + } else { + min + }; + + let x1 = if input > max { + max + } else if input > min { + min + } else { + max + }; + + let x2 = if max < min { + min + } else if input > max { + input + } else { + input + }; + + let x3 = if min > input { + input + } else if max < input { + max + } else { + max + }; + + let x6 = match input { + x if x < max => x, + x if x < min => x, + x => x, + }; + + let x7 = match input { + x if x < min => max, + x if x > max => min, + x => x, + }; + + let x8 = match input { + x if max > x => max, + x if min > x => min, + x => x, + }; + + let mut x9 = input; + if x9 > min { + x9 = min; + } + if x9 > max { + x9 = max; + } + + let x10 = match input { + x if min > x => min, + x if max < x => max, + x => min, + }; + + let mut x11 = input; + if x11 > max { + x11 = min; + } + if x11 < min { + x11 = max; + } + + let mut x12 = input; + if min > x12 { + x12 = max * 3; + } + if max < x12 { + x12 = min; + } + + let mut x13 = input; + if max < x13 { + let x13 = max; + } + if min > x13 { + x13 = min; + } + let mut x14 = input; + if x14 < min { + x14 = 3; + } else if x14 > max { + x14 = max; + } + { + let input: i32 = cmp_min_max(1); + // These can only be detected if exactly one of the arguments to the inner function is const. + let x16 = cmp_max(cmp_max(input, CONST_MAX), CONST_MIN); + let x17 = cmp_min(cmp_min(input, CONST_MIN), CONST_MAX); + let x18 = cmp_max(CONST_MIN, cmp_max(input, CONST_MAX)); + let x19 = cmp_min(CONST_MAX, cmp_min(input, CONST_MIN)); + let x20 = cmp_max(cmp_max(CONST_MAX, input), CONST_MIN); + let x21 = cmp_min(cmp_min(CONST_MIN, input), CONST_MAX); + let x22 = cmp_max(CONST_MIN, cmp_max(CONST_MAX, input)); + let x23 = cmp_min(CONST_MAX, cmp_min(CONST_MIN, input)); + let input: f64 = cmp_min_max(1) as f64; + let x24 = f64::max(f64::max(input, CONST_F64_MAX), CONST_F64_MIN); + let x25 = f64::min(f64::min(input, CONST_F64_MIN), CONST_F64_MAX); + let x26 = f64::max(CONST_F64_MIN, f64::max(input, CONST_F64_MAX)); + let x27 = f64::min(CONST_F64_MAX, f64::min(input, CONST_F64_MIN)); + let x28 = f64::max(f64::max(CONST_F64_MAX, input), CONST_F64_MIN); + let x29 = f64::min(f64::min(CONST_F64_MIN, input), CONST_F64_MAX); + let x30 = f64::max(CONST_F64_MIN, f64::max(CONST_F64_MAX, input)); + let x31 = f64::min(CONST_F64_MAX, f64::min(CONST_F64_MIN, input)); + let x32 = f64::min(CONST_F64_MAX, f64::min(CONST_F64_MIN, CONST_F64_MAX)); + } +} + +fn dont_tell_me_what_to_do() { + let (input, min, max) = (0, -2, 3); + let mut x_never = input; + #[allow(clippy::manual_clamp)] + if x_never < min { + x_never = min; + } + if x_never > max { + x_never = max; + } +} + +/// Just to ensure this isn't const evaled +fn cmp_min_max(input: i32) -> i32 { + input * 3 +} diff --git a/tests/ui/manual_clamp.stderr b/tests/ui/manual_clamp.stderr new file mode 100644 index 000000000000..0604f8606c3f --- /dev/null +++ b/tests/ui/manual_clamp.stderr @@ -0,0 +1,375 @@ +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:76:5 + | +LL | / if x9 < min { +LL | | x9 = min; +LL | | } +LL | | if x9 > max { +LL | | x9 = max; +LL | | } + | |_____^ help: replace with clamp: `x9 = x9.clamp(min, max);` + | + = note: clamp will panic if max < min + = note: `-D clippy::manual-clamp` implied by `-D warnings` + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:91:5 + | +LL | / if x11 > max { +LL | | x11 = max; +LL | | } +LL | | if x11 < min { +LL | | x11 = min; +LL | | } + | |_____^ help: replace with clamp: `x11 = x11.clamp(min, max);` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:99:5 + | +LL | / if min > x12 { +LL | | x12 = min; +LL | | } +LL | | if max < x12 { +LL | | x12 = max; +LL | | } + | |_____^ help: replace with clamp: `x12 = x12.clamp(min, max);` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:107:5 + | +LL | / if max < x13 { +LL | | x13 = max; +LL | | } +LL | | if min > x13 { +LL | | x13 = min; +LL | | } + | |_____^ help: replace with clamp: `x13 = x13.clamp(min, max);` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:161:5 + | +LL | / if max < x33 { +LL | | x33 = max; +LL | | } +LL | | if min > x33 { +LL | | x33 = min; +LL | | } + | |_____^ help: replace with clamp: `x33 = x33.clamp(min, max);` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:21:14 + | +LL | let x0 = if max < input { + | ______________^ +LL | | max +LL | | } else if min > input { +LL | | min +LL | | } else { +LL | | input +LL | | }; + | |_____^ help: replace with clamp: `input.clamp(min, max)` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:29:14 + | +LL | let x1 = if input > max { + | ______________^ +LL | | max +LL | | } else if input < min { +LL | | min +LL | | } else { +LL | | input +LL | | }; + | |_____^ help: replace with clamp: `input.clamp(min, max)` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:37:14 + | +LL | let x2 = if input < min { + | ______________^ +LL | | min +LL | | } else if input > max { +LL | | max +LL | | } else { +LL | | input +LL | | }; + | |_____^ help: replace with clamp: `input.clamp(min, max)` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:45:14 + | +LL | let x3 = if min > input { + | ______________^ +LL | | min +LL | | } else if max < input { +LL | | max +LL | | } else { +LL | | input +LL | | }; + | |_____^ help: replace with clamp: `input.clamp(min, max)` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:53:14 + | +LL | let x4 = input.max(min).min(max); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(min, max)` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:55:14 + | +LL | let x5 = input.min(max).max(min); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(min, max)` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:57:14 + | +LL | let x6 = match input { + | ______________^ +LL | | x if x > max => max, +LL | | x if x < min => min, +LL | | x => x, +LL | | }; + | |_____^ help: replace with clamp: `input.clamp(min, max)` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:63:14 + | +LL | let x7 = match input { + | ______________^ +LL | | x if x < min => min, +LL | | x if x > max => max, +LL | | x => x, +LL | | }; + | |_____^ help: replace with clamp: `input.clamp(min, max)` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:69:14 + | +LL | let x8 = match input { + | ______________^ +LL | | x if max < x => max, +LL | | x if min > x => min, +LL | | x => x, +LL | | }; + | |_____^ help: replace with clamp: `input.clamp(min, max)` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:83:15 + | +LL | let x10 = match input { + | _______________^ +LL | | x if min > x => min, +LL | | x if max < x => max, +LL | | x => x, +LL | | }; + | |_____^ help: replace with clamp: `input.clamp(min, max)` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:114:15 + | +LL | let x14 = if input > CONST_MAX { + | _______________^ +LL | | CONST_MAX +LL | | } else if input < CONST_MIN { +LL | | CONST_MIN +LL | | } else { +LL | | input +LL | | }; + | |_____^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:123:19 + | +LL | let x15 = if input > max { + | ___________________^ +LL | | max +LL | | } else if input < min { +LL | | min +LL | | } else { +LL | | input +LL | | }; + | |_________^ help: replace with clamp: `input.clamp(min, max)` + | + = note: clamp will panic if max < min, min.is_nan(), or max.is_nan() + = note: clamp returns NaN if the input is NaN + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:134:19 + | +LL | let x16 = cmp_max(cmp_min(input, CONST_MAX), CONST_MIN); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:135:19 + | +LL | let x17 = cmp_min(cmp_max(input, CONST_MIN), CONST_MAX); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:136:19 + | +LL | let x18 = cmp_max(CONST_MIN, cmp_min(input, CONST_MAX)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:137:19 + | +LL | let x19 = cmp_min(CONST_MAX, cmp_max(input, CONST_MIN)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:138:19 + | +LL | let x20 = cmp_max(cmp_min(CONST_MAX, input), CONST_MIN); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:139:19 + | +LL | let x21 = cmp_min(cmp_max(CONST_MIN, input), CONST_MAX); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:140:19 + | +LL | let x22 = cmp_max(CONST_MIN, cmp_min(CONST_MAX, input)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:141:19 + | +LL | let x23 = cmp_min(CONST_MAX, cmp_max(CONST_MIN, input)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:143:19 + | +LL | let x24 = f64::max(f64::min(input, CONST_F64_MAX), CONST_F64_MIN); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` + | + = note: clamp will panic if max < min, min.is_nan(), or max.is_nan() + = note: clamp returns NaN if the input is NaN + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:144:19 + | +LL | let x25 = f64::min(f64::max(input, CONST_F64_MIN), CONST_F64_MAX); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` + | + = note: clamp will panic if max < min, min.is_nan(), or max.is_nan() + = note: clamp returns NaN if the input is NaN + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:145:19 + | +LL | let x26 = f64::max(CONST_F64_MIN, f64::min(input, CONST_F64_MAX)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` + | + = note: clamp will panic if max < min, min.is_nan(), or max.is_nan() + = note: clamp returns NaN if the input is NaN + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:146:19 + | +LL | let x27 = f64::min(CONST_F64_MAX, f64::max(input, CONST_F64_MIN)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` + | + = note: clamp will panic if max < min, min.is_nan(), or max.is_nan() + = note: clamp returns NaN if the input is NaN + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:147:19 + | +LL | let x28 = f64::max(f64::min(CONST_F64_MAX, input), CONST_F64_MIN); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` + | + = note: clamp will panic if max < min, min.is_nan(), or max.is_nan() + = note: clamp returns NaN if the input is NaN + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:148:19 + | +LL | let x29 = f64::min(f64::max(CONST_F64_MIN, input), CONST_F64_MAX); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` + | + = note: clamp will panic if max < min, min.is_nan(), or max.is_nan() + = note: clamp returns NaN if the input is NaN + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:149:19 + | +LL | let x30 = f64::max(CONST_F64_MIN, f64::min(CONST_F64_MAX, input)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` + | + = note: clamp will panic if max < min, min.is_nan(), or max.is_nan() + = note: clamp returns NaN if the input is NaN + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:150:19 + | +LL | let x31 = f64::min(CONST_F64_MAX, f64::max(CONST_F64_MIN, input)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` + | + = note: clamp will panic if max < min, min.is_nan(), or max.is_nan() + = note: clamp returns NaN if the input is NaN + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:153:5 + | +LL | / if x32 < min { +LL | | x32 = min; +LL | | } else if x32 > max { +LL | | x32 = max; +LL | | } + | |_____^ help: replace with clamp: `x32 = x32.clamp(min, max);` + | + = note: clamp will panic if max < min + +error: aborting due to 34 previous errors + diff --git a/tests/ui/manual_find_fixable.fixed b/tests/ui/manual_find_fixable.fixed index 36d1644c22bd..2bce6e624c90 100644 --- a/tests/ui/manual_find_fixable.fixed +++ b/tests/ui/manual_find_fixable.fixed @@ -1,7 +1,7 @@ // run-rustfix - -#![allow(unused, clippy::needless_return)] #![warn(clippy::manual_find)] +#![allow(unused)] +#![allow(clippy::needless_return, clippy::uninlined_format_args)] use std::collections::HashMap; diff --git a/tests/ui/manual_find_fixable.rs b/tests/ui/manual_find_fixable.rs index ed277ddaa722..f5c6de37a257 100644 --- a/tests/ui/manual_find_fixable.rs +++ b/tests/ui/manual_find_fixable.rs @@ -1,7 +1,7 @@ // run-rustfix - -#![allow(unused, clippy::needless_return)] #![warn(clippy::manual_find)] +#![allow(unused)] +#![allow(clippy::needless_return, clippy::uninlined_format_args)] use std::collections::HashMap; diff --git a/tests/ui/manual_flatten.rs b/tests/ui/manual_flatten.rs index d922593bc6f9..96cd87c0e19a 100644 --- a/tests/ui/manual_flatten.rs +++ b/tests/ui/manual_flatten.rs @@ -1,5 +1,5 @@ #![warn(clippy::manual_flatten)] -#![allow(clippy::useless_vec)] +#![allow(clippy::useless_vec, clippy::uninlined_format_args)] fn main() { // Test for loop over implicitly adjusted `Iterator` with `if let` expression diff --git a/tests/ui/map_unwrap_or.rs b/tests/ui/map_unwrap_or.rs index 87e16f5d09bd..5429fb4e454e 100644 --- a/tests/ui/map_unwrap_or.rs +++ b/tests/ui/map_unwrap_or.rs @@ -1,6 +1,6 @@ // aux-build:option_helpers.rs - #![warn(clippy::map_unwrap_or)] +#![allow(clippy::uninlined_format_args)] #[macro_use] extern crate option_helpers; diff --git a/tests/ui/match_ref_pats.fixed b/tests/ui/match_ref_pats.fixed index 1b6c2d924121..cf37fc6dc90a 100644 --- a/tests/ui/match_ref_pats.fixed +++ b/tests/ui/match_ref_pats.fixed @@ -1,6 +1,7 @@ // run-rustfix #![warn(clippy::match_ref_pats)] -#![allow(dead_code, unused_variables, clippy::equatable_if_let, clippy::enum_variant_names)] +#![allow(dead_code, unused_variables)] +#![allow(clippy::enum_variant_names, clippy::equatable_if_let, clippy::uninlined_format_args)] fn ref_pats() { { diff --git a/tests/ui/match_ref_pats.rs b/tests/ui/match_ref_pats.rs index 68dfac4e2e97..3220b97d1b51 100644 --- a/tests/ui/match_ref_pats.rs +++ b/tests/ui/match_ref_pats.rs @@ -1,6 +1,7 @@ // run-rustfix #![warn(clippy::match_ref_pats)] -#![allow(dead_code, unused_variables, clippy::equatable_if_let, clippy::enum_variant_names)] +#![allow(dead_code, unused_variables)] +#![allow(clippy::enum_variant_names, clippy::equatable_if_let, clippy::uninlined_format_args)] fn ref_pats() { { diff --git a/tests/ui/match_ref_pats.stderr b/tests/ui/match_ref_pats.stderr index 353f7399d9c2..7d9646c842ee 100644 --- a/tests/ui/match_ref_pats.stderr +++ b/tests/ui/match_ref_pats.stderr @@ -1,5 +1,5 @@ error: you don't need to add `&` to all patterns - --> $DIR/match_ref_pats.rs:8:9 + --> $DIR/match_ref_pats.rs:9:9 | LL | / match v { LL | | &Some(v) => println!("{:?}", v), @@ -16,7 +16,7 @@ LL ~ None => println!("none"), | error: you don't need to add `&` to both the expression and the patterns - --> $DIR/match_ref_pats.rs:25:5 + --> $DIR/match_ref_pats.rs:26:5 | LL | / match &w { LL | | &Some(v) => println!("{:?}", v), @@ -32,7 +32,7 @@ LL ~ None => println!("none"), | error: redundant pattern matching, consider using `is_none()` - --> $DIR/match_ref_pats.rs:37:12 + --> $DIR/match_ref_pats.rs:38:12 | LL | if let &None = a { | -------^^^^^---- help: try this: `if a.is_none()` @@ -40,13 +40,13 @@ LL | if let &None = a { = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` error: redundant pattern matching, consider using `is_none()` - --> $DIR/match_ref_pats.rs:42:12 + --> $DIR/match_ref_pats.rs:43:12 | LL | if let &None = &b { | -------^^^^^----- help: try this: `if b.is_none()` error: you don't need to add `&` to all patterns - --> $DIR/match_ref_pats.rs:102:9 + --> $DIR/match_ref_pats.rs:103:9 | LL | / match foobar_variant!(0) { LL | | &FooBar::Foo => println!("Foo"), diff --git a/tests/ui/match_result_ok.fixed b/tests/ui/match_result_ok.fixed index d4760a97567e..8b91b9854a04 100644 --- a/tests/ui/match_result_ok.fixed +++ b/tests/ui/match_result_ok.fixed @@ -1,8 +1,7 @@ // run-rustfix - #![warn(clippy::match_result_ok)] -#![allow(clippy::boxed_local)] #![allow(dead_code)] +#![allow(clippy::boxed_local, clippy::uninlined_format_args)] // Checking `if` cases diff --git a/tests/ui/match_result_ok.rs b/tests/ui/match_result_ok.rs index 0b818723d989..bc2c4b50e272 100644 --- a/tests/ui/match_result_ok.rs +++ b/tests/ui/match_result_ok.rs @@ -1,8 +1,7 @@ // run-rustfix - #![warn(clippy::match_result_ok)] -#![allow(clippy::boxed_local)] #![allow(dead_code)] +#![allow(clippy::boxed_local, clippy::uninlined_format_args)] // Checking `if` cases diff --git a/tests/ui/match_result_ok.stderr b/tests/ui/match_result_ok.stderr index cc3bc8c76ff3..98a95705ca59 100644 --- a/tests/ui/match_result_ok.stderr +++ b/tests/ui/match_result_ok.stderr @@ -1,5 +1,5 @@ error: matching on `Some` with `ok()` is redundant - --> $DIR/match_result_ok.rs:10:5 + --> $DIR/match_result_ok.rs:9:5 | LL | if let Some(y) = x.parse().ok() { y } else { 0 } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -11,7 +11,7 @@ LL | if let Ok(y) = x.parse() { y } else { 0 } | ~~~~~~~~~~~~~~~~~~~~~~~~ error: matching on `Some` with `ok()` is redundant - --> $DIR/match_result_ok.rs:20:9 + --> $DIR/match_result_ok.rs:19:9 | LL | if let Some(y) = x . parse() . ok () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -22,7 +22,7 @@ LL | if let Ok(y) = x . parse() { | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: matching on `Some` with `ok()` is redundant - --> $DIR/match_result_ok.rs:46:5 + --> $DIR/match_result_ok.rs:45:5 | LL | while let Some(a) = wat.next().ok() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/match_same_arms2.rs b/tests/ui/match_same_arms2.rs index 61793e80c98d..82b2c433d99e 100644 --- a/tests/ui/match_same_arms2.rs +++ b/tests/ui/match_same_arms2.rs @@ -1,5 +1,9 @@ #![warn(clippy::match_same_arms)] -#![allow(clippy::disallowed_names, clippy::diverging_sub_expression)] +#![allow( + clippy::disallowed_names, + clippy::diverging_sub_expression, + clippy::uninlined_format_args +)] fn bar(_: T) {} fn foo() -> bool { diff --git a/tests/ui/match_same_arms2.stderr b/tests/ui/match_same_arms2.stderr index b260155d2189..06cd4300054d 100644 --- a/tests/ui/match_same_arms2.stderr +++ b/tests/ui/match_same_arms2.stderr @@ -1,5 +1,5 @@ error: this match arm has an identical body to the `_` wildcard arm - --> $DIR/match_same_arms2.rs:11:9 + --> $DIR/match_same_arms2.rs:15:9 | LL | / 42 => { LL | | foo(); @@ -12,7 +12,7 @@ LL | | }, | = help: or try changing either arm body note: `_` wildcard arm here - --> $DIR/match_same_arms2.rs:20:9 + --> $DIR/match_same_arms2.rs:24:9 | LL | / _ => { LL | | //~ ERROR match arms have same body @@ -25,7 +25,7 @@ LL | | }, = note: `-D clippy::match-same-arms` implied by `-D warnings` error: this match arm has an identical body to another arm - --> $DIR/match_same_arms2.rs:34:9 + --> $DIR/match_same_arms2.rs:38:9 | LL | 51 => foo(), //~ ERROR match arms have same body | --^^^^^^^^^ @@ -34,13 +34,13 @@ LL | 51 => foo(), //~ ERROR match arms have same body | = help: or try changing either arm body note: other arm here - --> $DIR/match_same_arms2.rs:33:9 + --> $DIR/match_same_arms2.rs:37:9 | LL | 42 => foo(), | ^^^^^^^^^^^ error: this match arm has an identical body to another arm - --> $DIR/match_same_arms2.rs:40:9 + --> $DIR/match_same_arms2.rs:44:9 | LL | None => 24, //~ ERROR match arms have same body | ----^^^^^^ @@ -49,13 +49,13 @@ LL | None => 24, //~ ERROR match arms have same body | = help: or try changing either arm body note: other arm here - --> $DIR/match_same_arms2.rs:39:9 + --> $DIR/match_same_arms2.rs:43:9 | LL | Some(_) => 24, | ^^^^^^^^^^^^^ error: this match arm has an identical body to another arm - --> $DIR/match_same_arms2.rs:62:9 + --> $DIR/match_same_arms2.rs:66:9 | LL | (None, Some(a)) => bar(a), //~ ERROR match arms have same body | ---------------^^^^^^^^^^ @@ -64,13 +64,13 @@ LL | (None, Some(a)) => bar(a), //~ ERROR match arms have same body | = help: or try changing either arm body note: other arm here - --> $DIR/match_same_arms2.rs:61:9 + --> $DIR/match_same_arms2.rs:65:9 | LL | (Some(a), None) => bar(a), | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: this match arm has an identical body to another arm - --> $DIR/match_same_arms2.rs:67:9 + --> $DIR/match_same_arms2.rs:71:9 | LL | (Some(a), ..) => bar(a), | -------------^^^^^^^^^^ @@ -79,13 +79,13 @@ LL | (Some(a), ..) => bar(a), | = help: or try changing either arm body note: other arm here - --> $DIR/match_same_arms2.rs:68:9 + --> $DIR/match_same_arms2.rs:72:9 | LL | (.., Some(a)) => bar(a), //~ ERROR match arms have same body | ^^^^^^^^^^^^^^^^^^^^^^^ error: this match arm has an identical body to another arm - --> $DIR/match_same_arms2.rs:101:9 + --> $DIR/match_same_arms2.rs:105:9 | LL | (Ok(x), Some(_)) => println!("ok {}", x), | ----------------^^^^^^^^^^^^^^^^^^^^^^^^ @@ -94,13 +94,13 @@ LL | (Ok(x), Some(_)) => println!("ok {}", x), | = help: or try changing either arm body note: other arm here - --> $DIR/match_same_arms2.rs:102:9 + --> $DIR/match_same_arms2.rs:106:9 | LL | (Ok(_), Some(x)) => println!("ok {}", x), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this match arm has an identical body to another arm - --> $DIR/match_same_arms2.rs:117:9 + --> $DIR/match_same_arms2.rs:121:9 | LL | Ok(_) => println!("ok"), | -----^^^^^^^^^^^^^^^^^^ @@ -109,13 +109,13 @@ LL | Ok(_) => println!("ok"), | = help: or try changing either arm body note: other arm here - --> $DIR/match_same_arms2.rs:116:9 + --> $DIR/match_same_arms2.rs:120:9 | LL | Ok(3) => println!("ok"), | ^^^^^^^^^^^^^^^^^^^^^^^ error: this match arm has an identical body to another arm - --> $DIR/match_same_arms2.rs:144:9 + --> $DIR/match_same_arms2.rs:148:9 | LL | 1 => { | ^ help: try merging the arm patterns: `1 | 0` @@ -127,7 +127,7 @@ LL | | }, | = help: or try changing either arm body note: other arm here - --> $DIR/match_same_arms2.rs:141:9 + --> $DIR/match_same_arms2.rs:145:9 | LL | / 0 => { LL | | empty!(0); @@ -135,7 +135,7 @@ LL | | }, | |_________^ error: match expression looks like `matches!` macro - --> $DIR/match_same_arms2.rs:162:16 + --> $DIR/match_same_arms2.rs:166:16 | LL | let _ans = match x { | ________________^ @@ -148,7 +148,7 @@ LL | | }; = note: `-D clippy::match-like-matches-macro` implied by `-D warnings` error: this match arm has an identical body to another arm - --> $DIR/match_same_arms2.rs:194:9 + --> $DIR/match_same_arms2.rs:198:9 | LL | Foo::X(0) => 1, | ---------^^^^^ @@ -157,13 +157,13 @@ LL | Foo::X(0) => 1, | = help: or try changing either arm body note: other arm here - --> $DIR/match_same_arms2.rs:196:9 + --> $DIR/match_same_arms2.rs:200:9 | LL | Foo::Z(_) => 1, | ^^^^^^^^^^^^^^ error: this match arm has an identical body to another arm - --> $DIR/match_same_arms2.rs:204:9 + --> $DIR/match_same_arms2.rs:208:9 | LL | Foo::Z(_) => 1, | ---------^^^^^ @@ -172,13 +172,13 @@ LL | Foo::Z(_) => 1, | = help: or try changing either arm body note: other arm here - --> $DIR/match_same_arms2.rs:202:9 + --> $DIR/match_same_arms2.rs:206:9 | LL | Foo::X(0) => 1, | ^^^^^^^^^^^^^^ error: this match arm has an identical body to another arm - --> $DIR/match_same_arms2.rs:227:9 + --> $DIR/match_same_arms2.rs:231:9 | LL | Some(Bar { y: 0, x: 5, .. }) => 1, | ----------------------------^^^^^ @@ -187,7 +187,7 @@ LL | Some(Bar { y: 0, x: 5, .. }) => 1, | = help: or try changing either arm body note: other arm here - --> $DIR/match_same_arms2.rs:224:9 + --> $DIR/match_same_arms2.rs:228:9 | LL | Some(Bar { x: 0, y: 5, .. }) => 1, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/match_single_binding.fixed b/tests/ui/match_single_binding.fixed index de46e6cff55b..951f552eb32b 100644 --- a/tests/ui/match_single_binding.fixed +++ b/tests/ui/match_single_binding.fixed @@ -1,7 +1,7 @@ // run-rustfix - #![warn(clippy::match_single_binding)] -#![allow(unused_variables, clippy::toplevel_ref_arg)] +#![allow(unused_variables)] +#![allow(clippy::toplevel_ref_arg, clippy::uninlined_format_args)] struct Point { x: i32, diff --git a/tests/ui/match_single_binding.rs b/tests/ui/match_single_binding.rs index eea64fcb292b..19c0fee8fd68 100644 --- a/tests/ui/match_single_binding.rs +++ b/tests/ui/match_single_binding.rs @@ -1,7 +1,7 @@ // run-rustfix - #![warn(clippy::match_single_binding)] -#![allow(unused_variables, clippy::toplevel_ref_arg)] +#![allow(unused_variables)] +#![allow(clippy::toplevel_ref_arg, clippy::uninlined_format_args)] struct Point { x: i32, diff --git a/tests/ui/match_single_binding2.fixed b/tests/ui/match_single_binding2.fixed index a91fcc2125d4..6a7db67e311a 100644 --- a/tests/ui/match_single_binding2.fixed +++ b/tests/ui/match_single_binding2.fixed @@ -1,7 +1,7 @@ // run-rustfix - #![warn(clippy::match_single_binding)] #![allow(unused_variables)] +#![allow(clippy::uninlined_format_args)] fn main() { // Lint (additional curly braces needed, see #6572) diff --git a/tests/ui/match_single_binding2.rs b/tests/ui/match_single_binding2.rs index 476386ebabe2..5a4bb8441fff 100644 --- a/tests/ui/match_single_binding2.rs +++ b/tests/ui/match_single_binding2.rs @@ -1,7 +1,7 @@ // run-rustfix - #![warn(clippy::match_single_binding)] #![allow(unused_variables)] +#![allow(clippy::uninlined_format_args)] fn main() { // Lint (additional curly braces needed, see #6572) diff --git a/tests/ui/min_max.rs b/tests/ui/min_max.rs index b2bc97f4744a..24e52afd6917 100644 --- a/tests/ui/min_max.rs +++ b/tests/ui/min_max.rs @@ -1,4 +1,5 @@ #![warn(clippy::all)] +#![allow(clippy::manual_clamp)] use std::cmp::max as my_max; use std::cmp::min as my_min; diff --git a/tests/ui/min_max.stderr b/tests/ui/min_max.stderr index c70b77eabbd8..069d9068657e 100644 --- a/tests/ui/min_max.stderr +++ b/tests/ui/min_max.stderr @@ -1,5 +1,5 @@ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:23:5 + --> $DIR/min_max.rs:24:5 | LL | min(1, max(3, x)); | ^^^^^^^^^^^^^^^^^ @@ -7,73 +7,73 @@ LL | min(1, max(3, x)); = note: `-D clippy::min-max` implied by `-D warnings` error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:24:5 + --> $DIR/min_max.rs:25:5 | LL | min(max(3, x), 1); | ^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:25:5 + --> $DIR/min_max.rs:26:5 | LL | max(min(x, 1), 3); | ^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:26:5 + --> $DIR/min_max.rs:27:5 | LL | max(3, min(x, 1)); | ^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:28:5 + --> $DIR/min_max.rs:29:5 | LL | my_max(3, my_min(x, 1)); | ^^^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:38:5 + --> $DIR/min_max.rs:39:5 | LL | min("Apple", max("Zoo", s)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:39:5 + --> $DIR/min_max.rs:40:5 | LL | max(min(s, "Apple"), "Zoo"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:44:5 + --> $DIR/min_max.rs:45:5 | LL | x.min(1).max(3); | ^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:45:5 + --> $DIR/min_max.rs:46:5 | LL | x.max(3).min(1); | ^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:46:5 + --> $DIR/min_max.rs:47:5 | LL | f.max(3f32).min(1f32); | ^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:52:5 + --> $DIR/min_max.rs:53:5 | LL | max(x.min(1), 3); | ^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:55:5 + --> $DIR/min_max.rs:56:5 | LL | s.max("Zoo").min("Apple"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:56:5 + --> $DIR/min_max.rs:57:5 | LL | s.min("Apple").max("Zoo"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/min_rust_version_attr.rs b/tests/ui/min_rust_version_attr.rs index 44e407bd1ab2..c4c6391bb4c1 100644 --- a/tests/ui/min_rust_version_attr.rs +++ b/tests/ui/min_rust_version_attr.rs @@ -160,6 +160,17 @@ fn manual_rem_euclid() { let _: i32 = ((x % 4) + 4) % 4; } +fn manual_clamp() { + let (input, min, max) = (0, -1, 2); + let _ = if input < min { + min + } else if input > max { + max + } else { + input + }; +} + fn main() { filter_map_next(); checked_conversion(); @@ -180,6 +191,7 @@ fn main() { err_expect(); cast_abs_to_unsigned(); manual_rem_euclid(); + manual_clamp(); } mod just_under_msrv { diff --git a/tests/ui/min_rust_version_attr.stderr b/tests/ui/min_rust_version_attr.stderr index 6e749d2741c4..d1cffc26a831 100644 --- a/tests/ui/min_rust_version_attr.stderr +++ b/tests/ui/min_rust_version_attr.stderr @@ -1,21 +1,3 @@ -error: stripping a prefix manually - --> $DIR/min_rust_version_attr.rs:204:24 - | -LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); - | ^^^^^^^^^^^^^^^^^^^^ - | -note: the prefix was tested here - --> $DIR/min_rust_version_attr.rs:203:9 - | -LL | if s.starts_with("hello, ") { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: `-D clippy::manual-strip` implied by `-D warnings` -help: try using the `strip_prefix` method - | -LL ~ if let Some() = s.strip_prefix("hello, ") { -LL ~ assert_eq!(.to_uppercase(), "WORLD!"); - | - error: stripping a prefix manually --> $DIR/min_rust_version_attr.rs:216:24 | @@ -25,6 +7,24 @@ LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); note: the prefix was tested here --> $DIR/min_rust_version_attr.rs:215:9 | +LL | if s.starts_with("hello, ") { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: `-D clippy::manual-strip` implied by `-D warnings` +help: try using the `strip_prefix` method + | +LL ~ if let Some() = s.strip_prefix("hello, ") { +LL ~ assert_eq!(.to_uppercase(), "WORLD!"); + | + +error: stripping a prefix manually + --> $DIR/min_rust_version_attr.rs:228:24 + | +LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); + | ^^^^^^^^^^^^^^^^^^^^ + | +note: the prefix was tested here + --> $DIR/min_rust_version_attr.rs:227:9 + | LL | if s.starts_with("hello, ") { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using the `strip_prefix` method diff --git a/tests/ui/mut_mut.rs b/tests/ui/mut_mut.rs index be854d941833..ac8fd9d8fb09 100644 --- a/tests/ui/mut_mut.rs +++ b/tests/ui/mut_mut.rs @@ -1,7 +1,7 @@ // aux-build:macro_rules.rs - -#![allow(unused, clippy::no_effect, clippy::unnecessary_operation)] #![warn(clippy::mut_mut)] +#![allow(unused)] +#![allow(clippy::no_effect, clippy::uninlined_format_args, clippy::unnecessary_operation)] #[macro_use] extern crate macro_rules; diff --git a/tests/ui/needless_borrow.fixed b/tests/ui/needless_borrow.fixed index 8cf93bd24817..aa2687159ef4 100644 --- a/tests/ui/needless_borrow.fixed +++ b/tests/ui/needless_borrow.fixed @@ -1,9 +1,9 @@ // run-rustfix - #![feature(custom_inner_attributes, lint_reasons)] #[warn(clippy::all, clippy::needless_borrow)] -#[allow(unused_variables, clippy::unnecessary_mut_passed)] +#[allow(unused_variables)] +#[allow(clippy::uninlined_format_args, clippy::unnecessary_mut_passed)] fn main() { let a = 5; let ref_a = &a; @@ -298,3 +298,32 @@ mod meets_msrv { let _ = std::process::Command::new("ls").args(["-a", "-l"]).status().unwrap(); } } + +#[allow(unused)] +fn issue9383() { + // Should not lint because unions need explicit deref when accessing field + use std::mem::ManuallyDrop; + + union Coral { + crab: ManuallyDrop>, + } + + union Ocean { + coral: ManuallyDrop, + } + + let mut ocean = Ocean { + coral: ManuallyDrop::new(Coral { + crab: ManuallyDrop::new(vec![1, 2, 3]), + }), + }; + + unsafe { + ManuallyDrop::drop(&mut (&mut ocean.coral).crab); + + (*ocean.coral).crab = ManuallyDrop::new(vec![4, 5, 6]); + ManuallyDrop::drop(&mut (*ocean.coral).crab); + + ManuallyDrop::drop(&mut ocean.coral); + } +} diff --git a/tests/ui/needless_borrow.rs b/tests/ui/needless_borrow.rs index fd9b2a11df96..d41251e8f6aa 100644 --- a/tests/ui/needless_borrow.rs +++ b/tests/ui/needless_borrow.rs @@ -1,9 +1,9 @@ // run-rustfix - #![feature(custom_inner_attributes, lint_reasons)] #[warn(clippy::all, clippy::needless_borrow)] -#[allow(unused_variables, clippy::unnecessary_mut_passed)] +#[allow(unused_variables)] +#[allow(clippy::uninlined_format_args, clippy::unnecessary_mut_passed)] fn main() { let a = 5; let ref_a = &a; @@ -298,3 +298,32 @@ mod meets_msrv { let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap(); } } + +#[allow(unused)] +fn issue9383() { + // Should not lint because unions need explicit deref when accessing field + use std::mem::ManuallyDrop; + + union Coral { + crab: ManuallyDrop>, + } + + union Ocean { + coral: ManuallyDrop, + } + + let mut ocean = Ocean { + coral: ManuallyDrop::new(Coral { + crab: ManuallyDrop::new(vec![1, 2, 3]), + }), + }; + + unsafe { + ManuallyDrop::drop(&mut (&mut ocean.coral).crab); + + (*ocean.coral).crab = ManuallyDrop::new(vec![4, 5, 6]); + ManuallyDrop::drop(&mut (*ocean.coral).crab); + + ManuallyDrop::drop(&mut ocean.coral); + } +} diff --git a/tests/ui/needless_borrowed_ref.fixed b/tests/ui/needless_borrowed_ref.fixed index a0937a2c5f62..bcb4eb2dd48a 100644 --- a/tests/ui/needless_borrowed_ref.fixed +++ b/tests/ui/needless_borrowed_ref.fixed @@ -1,17 +1,38 @@ // run-rustfix -#[warn(clippy::needless_borrowed_reference)] -#[allow(unused_variables)] -fn main() { +#![warn(clippy::needless_borrowed_reference)] +#![allow(unused, clippy::needless_borrow)] + +fn main() {} + +fn should_lint(array: [u8; 4], slice: &[u8], slice_of_refs: &[&u8], vec: Vec) { let mut v = Vec::::new(); let _ = v.iter_mut().filter(|a| a.is_empty()); - // ^ should be linted let var = 3; let thingy = Some(&var); - if let Some(&ref v) = thingy { - // ^ should be linted - } + if let Some(v) = thingy {} + + if let &[a, ref b] = slice_of_refs {} + + let [a, ..] = &array; + let [a, b, ..] = &array; + + if let [a, b] = slice {} + if let [a, b] = &vec[..] {} + + if let [a, b, ..] = slice {} + if let [a, .., b] = slice {} + if let [.., a, b] = slice {} +} + +fn should_not_lint(array: [u8; 4], slice: &[u8], slice_of_refs: &[&u8], vec: Vec) { + if let [ref a] = slice {} + if let &[ref a, b] = slice {} + if let &[ref a, .., b] = slice {} + + // must not be removed as variables must be bound consistently across | patterns + if let (&[ref a], _) | ([], ref a) = (slice_of_refs, &1u8) {} let mut var2 = 5; let thingy2 = Some(&mut var2); @@ -28,17 +49,15 @@ fn main() { } } -#[allow(dead_code)] enum Animal { Cat(u64), Dog(u64), } -#[allow(unused_variables)] -#[allow(dead_code)] fn foo(a: &Animal, b: &Animal) { match (a, b) { - (&Animal::Cat(v), &ref k) | (&ref k, &Animal::Cat(v)) => (), // lifetime mismatch error if there is no '&ref' + // lifetime mismatch error if there is no '&ref' before `feature(nll)` stabilization in 1.63 + (&Animal::Cat(v), &ref k) | (&ref k, &Animal::Cat(v)) => (), // ^ and ^ should **not** be linted (&Animal::Dog(ref a), &Animal::Dog(_)) => (), // ^ should **not** be linted } diff --git a/tests/ui/needless_borrowed_ref.rs b/tests/ui/needless_borrowed_ref.rs index 500ac448f0d5..f6de1a6d83d1 100644 --- a/tests/ui/needless_borrowed_ref.rs +++ b/tests/ui/needless_borrowed_ref.rs @@ -1,17 +1,38 @@ // run-rustfix -#[warn(clippy::needless_borrowed_reference)] -#[allow(unused_variables)] -fn main() { +#![warn(clippy::needless_borrowed_reference)] +#![allow(unused, clippy::needless_borrow)] + +fn main() {} + +fn should_lint(array: [u8; 4], slice: &[u8], slice_of_refs: &[&u8], vec: Vec) { let mut v = Vec::::new(); let _ = v.iter_mut().filter(|&ref a| a.is_empty()); - // ^ should be linted let var = 3; let thingy = Some(&var); - if let Some(&ref v) = thingy { - // ^ should be linted - } + if let Some(&ref v) = thingy {} + + if let &[&ref a, ref b] = slice_of_refs {} + + let &[ref a, ..] = &array; + let &[ref a, ref b, ..] = &array; + + if let &[ref a, ref b] = slice {} + if let &[ref a, ref b] = &vec[..] {} + + if let &[ref a, ref b, ..] = slice {} + if let &[ref a, .., ref b] = slice {} + if let &[.., ref a, ref b] = slice {} +} + +fn should_not_lint(array: [u8; 4], slice: &[u8], slice_of_refs: &[&u8], vec: Vec) { + if let [ref a] = slice {} + if let &[ref a, b] = slice {} + if let &[ref a, .., b] = slice {} + + // must not be removed as variables must be bound consistently across | patterns + if let (&[ref a], _) | ([], ref a) = (slice_of_refs, &1u8) {} let mut var2 = 5; let thingy2 = Some(&mut var2); @@ -28,17 +49,15 @@ fn main() { } } -#[allow(dead_code)] enum Animal { Cat(u64), Dog(u64), } -#[allow(unused_variables)] -#[allow(dead_code)] fn foo(a: &Animal, b: &Animal) { match (a, b) { - (&Animal::Cat(v), &ref k) | (&ref k, &Animal::Cat(v)) => (), // lifetime mismatch error if there is no '&ref' + // lifetime mismatch error if there is no '&ref' before `feature(nll)` stabilization in 1.63 + (&Animal::Cat(v), &ref k) | (&ref k, &Animal::Cat(v)) => (), // ^ and ^ should **not** be linted (&Animal::Dog(ref a), &Animal::Dog(_)) => (), // ^ should **not** be linted } diff --git a/tests/ui/needless_borrowed_ref.stderr b/tests/ui/needless_borrowed_ref.stderr index 0a5cfb3db0b1..7453542e673f 100644 --- a/tests/ui/needless_borrowed_ref.stderr +++ b/tests/ui/needless_borrowed_ref.stderr @@ -1,10 +1,123 @@ -error: this pattern takes a reference on something that is being de-referenced - --> $DIR/needless_borrowed_ref.rs:7:34 +error: this pattern takes a reference on something that is being dereferenced + --> $DIR/needless_borrowed_ref.rs:10:34 | LL | let _ = v.iter_mut().filter(|&ref a| a.is_empty()); - | ^^^^^^ help: try removing the `&ref` part and just keep: `a` + | ^^^^^^ | = note: `-D clippy::needless-borrowed-reference` implied by `-D warnings` +help: try removing the `&ref` part + | +LL - let _ = v.iter_mut().filter(|&ref a| a.is_empty()); +LL + let _ = v.iter_mut().filter(|a| a.is_empty()); + | -error: aborting due to previous error +error: this pattern takes a reference on something that is being dereferenced + --> $DIR/needless_borrowed_ref.rs:14:17 + | +LL | if let Some(&ref v) = thingy {} + | ^^^^^^ + | +help: try removing the `&ref` part + | +LL - if let Some(&ref v) = thingy {} +LL + if let Some(v) = thingy {} + | + +error: this pattern takes a reference on something that is being dereferenced + --> $DIR/needless_borrowed_ref.rs:16:14 + | +LL | if let &[&ref a, ref b] = slice_of_refs {} + | ^^^^^^ + | +help: try removing the `&ref` part + | +LL - if let &[&ref a, ref b] = slice_of_refs {} +LL + if let &[a, ref b] = slice_of_refs {} + | + +error: dereferencing a slice pattern where every element takes a reference + --> $DIR/needless_borrowed_ref.rs:18:9 + | +LL | let &[ref a, ..] = &array; + | ^^^^^^^^^^^^ + | +help: try removing the `&` and `ref` parts + | +LL - let &[ref a, ..] = &array; +LL + let [a, ..] = &array; + | + +error: dereferencing a slice pattern where every element takes a reference + --> $DIR/needless_borrowed_ref.rs:19:9 + | +LL | let &[ref a, ref b, ..] = &array; + | ^^^^^^^^^^^^^^^^^^^ + | +help: try removing the `&` and `ref` parts + | +LL - let &[ref a, ref b, ..] = &array; +LL + let [a, b, ..] = &array; + | + +error: dereferencing a slice pattern where every element takes a reference + --> $DIR/needless_borrowed_ref.rs:21:12 + | +LL | if let &[ref a, ref b] = slice {} + | ^^^^^^^^^^^^^^^ + | +help: try removing the `&` and `ref` parts + | +LL - if let &[ref a, ref b] = slice {} +LL + if let [a, b] = slice {} + | + +error: dereferencing a slice pattern where every element takes a reference + --> $DIR/needless_borrowed_ref.rs:22:12 + | +LL | if let &[ref a, ref b] = &vec[..] {} + | ^^^^^^^^^^^^^^^ + | +help: try removing the `&` and `ref` parts + | +LL - if let &[ref a, ref b] = &vec[..] {} +LL + if let [a, b] = &vec[..] {} + | + +error: dereferencing a slice pattern where every element takes a reference + --> $DIR/needless_borrowed_ref.rs:24:12 + | +LL | if let &[ref a, ref b, ..] = slice {} + | ^^^^^^^^^^^^^^^^^^^ + | +help: try removing the `&` and `ref` parts + | +LL - if let &[ref a, ref b, ..] = slice {} +LL + if let [a, b, ..] = slice {} + | + +error: dereferencing a slice pattern where every element takes a reference + --> $DIR/needless_borrowed_ref.rs:25:12 + | +LL | if let &[ref a, .., ref b] = slice {} + | ^^^^^^^^^^^^^^^^^^^ + | +help: try removing the `&` and `ref` parts + | +LL - if let &[ref a, .., ref b] = slice {} +LL + if let [a, .., b] = slice {} + | + +error: dereferencing a slice pattern where every element takes a reference + --> $DIR/needless_borrowed_ref.rs:26:12 + | +LL | if let &[.., ref a, ref b] = slice {} + | ^^^^^^^^^^^^^^^^^^^ + | +help: try removing the `&` and `ref` parts + | +LL - if let &[.., ref a, ref b] = slice {} +LL + if let [.., a, b] = slice {} + | + +error: aborting due to 10 previous errors diff --git a/tests/ui/needless_collect_indirect.rs b/tests/ui/needless_collect_indirect.rs index 12a9ace1ee68..6d213b46c20c 100644 --- a/tests/ui/needless_collect_indirect.rs +++ b/tests/ui/needless_collect_indirect.rs @@ -1,3 +1,5 @@ +#![allow(clippy::uninlined_format_args)] + use std::collections::{BinaryHeap, HashMap, HashSet, LinkedList, VecDeque}; fn main() { diff --git a/tests/ui/needless_collect_indirect.stderr b/tests/ui/needless_collect_indirect.stderr index 9f0880cc6069..99e1b91d8fea 100644 --- a/tests/ui/needless_collect_indirect.stderr +++ b/tests/ui/needless_collect_indirect.stderr @@ -1,5 +1,5 @@ error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:5:39 + --> $DIR/needless_collect_indirect.rs:7:39 | LL | let indirect_iter = sample.iter().collect::>(); | ^^^^^^^ @@ -14,7 +14,7 @@ LL ~ sample.iter().map(|x| (x, x + 1)).collect::>(); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:7:38 + --> $DIR/needless_collect_indirect.rs:9:38 | LL | let indirect_len = sample.iter().collect::>(); | ^^^^^^^ @@ -28,7 +28,7 @@ LL ~ sample.iter().count(); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:9:40 + --> $DIR/needless_collect_indirect.rs:11:40 | LL | let indirect_empty = sample.iter().collect::>(); | ^^^^^^^ @@ -42,7 +42,7 @@ LL ~ sample.iter().next().is_none(); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:11:43 + --> $DIR/needless_collect_indirect.rs:13:43 | LL | let indirect_contains = sample.iter().collect::>(); | ^^^^^^^ @@ -56,7 +56,7 @@ LL ~ sample.iter().any(|x| x == &5); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:23:48 + --> $DIR/needless_collect_indirect.rs:25:48 | LL | let non_copy_contains = sample.into_iter().collect::>(); | ^^^^^^^ @@ -70,7 +70,7 @@ LL ~ sample.into_iter().any(|x| x == a); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:52:51 + --> $DIR/needless_collect_indirect.rs:54:51 | LL | let buffer: Vec<&str> = string.split('/').collect(); | ^^^^^^^ @@ -84,7 +84,7 @@ LL ~ string.split('/').count() | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:57:55 + --> $DIR/needless_collect_indirect.rs:59:55 | LL | let indirect_len: VecDeque<_> = sample.iter().collect(); | ^^^^^^^ @@ -98,7 +98,7 @@ LL ~ sample.iter().count() | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:62:57 + --> $DIR/needless_collect_indirect.rs:64:57 | LL | let indirect_len: LinkedList<_> = sample.iter().collect(); | ^^^^^^^ @@ -112,7 +112,7 @@ LL ~ sample.iter().count() | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:67:57 + --> $DIR/needless_collect_indirect.rs:69:57 | LL | let indirect_len: BinaryHeap<_> = sample.iter().collect(); | ^^^^^^^ @@ -126,7 +126,7 @@ LL ~ sample.iter().count() | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:127:59 + --> $DIR/needless_collect_indirect.rs:129:59 | LL | let y: Vec = vec.iter().map(|k| k * k).collect(); | ^^^^^^^ @@ -143,7 +143,7 @@ LL ~ vec.iter().map(|k| k * k).any(|x| x == i); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:152:59 + --> $DIR/needless_collect_indirect.rs:154:59 | LL | let y: Vec = vec.iter().map(|k| k * k).collect(); | ^^^^^^^ @@ -160,7 +160,7 @@ LL ~ vec.iter().map(|k| k * k).any(|x| x == n); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:181:63 + --> $DIR/needless_collect_indirect.rs:183:63 | LL | let y: Vec = vec.iter().map(|k| k * k).collect(); | ^^^^^^^ @@ -177,7 +177,7 @@ LL ~ vec.iter().map(|k| k * k).any(|x| x == n); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:217:59 + --> $DIR/needless_collect_indirect.rs:219:59 | LL | let y: Vec = vec.iter().map(|k| k * k).collect(); | ^^^^^^^ @@ -195,7 +195,7 @@ LL ~ vec.iter().map(|k| k * k).any(|x| x == n); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:242:26 + --> $DIR/needless_collect_indirect.rs:244:26 | LL | let w = v.iter().collect::>(); | ^^^^^^^ @@ -211,7 +211,7 @@ LL ~ for _ in 0..v.iter().count() { | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:264:30 + --> $DIR/needless_collect_indirect.rs:266:30 | LL | let mut w = v.iter().collect::>(); | ^^^^^^^ @@ -227,7 +227,7 @@ LL ~ while 1 == v.iter().count() { | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:286:30 + --> $DIR/needless_collect_indirect.rs:288:30 | LL | let mut w = v.iter().collect::>(); | ^^^^^^^ diff --git a/tests/ui/needless_continue.rs b/tests/ui/needless_continue.rs index f105d3d659ac..c891c9de3aec 100644 --- a/tests/ui/needless_continue.rs +++ b/tests/ui/needless_continue.rs @@ -1,4 +1,5 @@ #![warn(clippy::needless_continue)] +#![allow(clippy::uninlined_format_args)] macro_rules! zero { ($x:expr) => { diff --git a/tests/ui/needless_continue.stderr b/tests/ui/needless_continue.stderr index 005ba010f34f..d99989b54fc2 100644 --- a/tests/ui/needless_continue.stderr +++ b/tests/ui/needless_continue.stderr @@ -1,5 +1,5 @@ error: this `else` block is redundant - --> $DIR/needless_continue.rs:29:16 + --> $DIR/needless_continue.rs:30:16 | LL | } else { | ________________^ @@ -35,7 +35,7 @@ LL | | } = note: `-D clippy::needless-continue` implied by `-D warnings` error: there is no need for an explicit `else` block for this `if` expression - --> $DIR/needless_continue.rs:44:9 + --> $DIR/needless_continue.rs:45:9 | LL | / if (zero!(i % 2) || nonzero!(i % 5)) && i % 3 != 0 { LL | | continue; @@ -55,7 +55,7 @@ LL | | } } error: this `continue` expression is redundant - --> $DIR/needless_continue.rs:57:9 + --> $DIR/needless_continue.rs:58:9 | LL | continue; // should lint here | ^^^^^^^^^ @@ -63,7 +63,7 @@ LL | continue; // should lint here = help: consider dropping the `continue` expression error: this `continue` expression is redundant - --> $DIR/needless_continue.rs:64:9 + --> $DIR/needless_continue.rs:65:9 | LL | continue; // should lint here | ^^^^^^^^^ @@ -71,7 +71,7 @@ LL | continue; // should lint here = help: consider dropping the `continue` expression error: this `continue` expression is redundant - --> $DIR/needless_continue.rs:71:9 + --> $DIR/needless_continue.rs:72:9 | LL | continue // should lint here | ^^^^^^^^ @@ -79,7 +79,7 @@ LL | continue // should lint here = help: consider dropping the `continue` expression error: this `continue` expression is redundant - --> $DIR/needless_continue.rs:79:9 + --> $DIR/needless_continue.rs:80:9 | LL | continue // should lint here | ^^^^^^^^ @@ -87,7 +87,7 @@ LL | continue // should lint here = help: consider dropping the `continue` expression error: this `else` block is redundant - --> $DIR/needless_continue.rs:129:24 + --> $DIR/needless_continue.rs:130:24 | LL | } else { | ________________________^ @@ -110,7 +110,7 @@ LL | | } } error: there is no need for an explicit `else` block for this `if` expression - --> $DIR/needless_continue.rs:135:17 + --> $DIR/needless_continue.rs:136:17 | LL | / if condition() { LL | | continue; // should lint here diff --git a/tests/ui/needless_for_each_fixable.fixed b/tests/ui/needless_for_each_fixable.fixed index c1685f7b6d7a..09e671b88e1a 100644 --- a/tests/ui/needless_for_each_fixable.fixed +++ b/tests/ui/needless_for_each_fixable.fixed @@ -1,10 +1,11 @@ // run-rustfix #![warn(clippy::needless_for_each)] +#![allow(unused)] #![allow( - unused, - clippy::needless_return, + clippy::let_unit_value, clippy::match_single_binding, - clippy::let_unit_value + clippy::needless_return, + clippy::uninlined_format_args )] use std::collections::HashMap; diff --git a/tests/ui/needless_for_each_fixable.rs b/tests/ui/needless_for_each_fixable.rs index ad17b0956fa9..abb4045b9197 100644 --- a/tests/ui/needless_for_each_fixable.rs +++ b/tests/ui/needless_for_each_fixable.rs @@ -1,10 +1,11 @@ // run-rustfix #![warn(clippy::needless_for_each)] +#![allow(unused)] #![allow( - unused, - clippy::needless_return, + clippy::let_unit_value, clippy::match_single_binding, - clippy::let_unit_value + clippy::needless_return, + clippy::uninlined_format_args )] use std::collections::HashMap; diff --git a/tests/ui/needless_for_each_fixable.stderr b/tests/ui/needless_for_each_fixable.stderr index 08e995851d7a..aebb762cc228 100644 --- a/tests/ui/needless_for_each_fixable.stderr +++ b/tests/ui/needless_for_each_fixable.stderr @@ -1,5 +1,5 @@ error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:15:5 + --> $DIR/needless_for_each_fixable.rs:16:5 | LL | / v.iter().for_each(|elem| { LL | | acc += elem; @@ -15,7 +15,7 @@ LL + } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:18:5 + --> $DIR/needless_for_each_fixable.rs:19:5 | LL | / v.into_iter().for_each(|elem| { LL | | acc += elem; @@ -30,7 +30,7 @@ LL + } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:22:5 + --> $DIR/needless_for_each_fixable.rs:23:5 | LL | / [1, 2, 3].iter().for_each(|elem| { LL | | acc += elem; @@ -45,7 +45,7 @@ LL + } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:27:5 + --> $DIR/needless_for_each_fixable.rs:28:5 | LL | / hash_map.iter().for_each(|(k, v)| { LL | | acc += k + v; @@ -60,7 +60,7 @@ LL + } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:30:5 + --> $DIR/needless_for_each_fixable.rs:31:5 | LL | / hash_map.iter_mut().for_each(|(k, v)| { LL | | acc += *k + *v; @@ -75,7 +75,7 @@ LL + } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:33:5 + --> $DIR/needless_for_each_fixable.rs:34:5 | LL | / hash_map.keys().for_each(|k| { LL | | acc += k; @@ -90,7 +90,7 @@ LL + } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:36:5 + --> $DIR/needless_for_each_fixable.rs:37:5 | LL | / hash_map.values().for_each(|v| { LL | | acc += v; @@ -105,7 +105,7 @@ LL + } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:43:5 + --> $DIR/needless_for_each_fixable.rs:44:5 | LL | / my_vec().iter().for_each(|elem| { LL | | acc += elem; diff --git a/tests/ui/needless_for_each_unfixable.rs b/tests/ui/needless_for_each_unfixable.rs index d765d7dab65c..282c72881d51 100644 --- a/tests/ui/needless_for_each_unfixable.rs +++ b/tests/ui/needless_for_each_unfixable.rs @@ -1,5 +1,5 @@ #![warn(clippy::needless_for_each)] -#![allow(clippy::needless_return)] +#![allow(clippy::needless_return, clippy::uninlined_format_args)] fn main() { let v: Vec = Vec::new(); diff --git a/tests/ui/needless_late_init.fixed b/tests/ui/needless_late_init.fixed index fee8e3030b80..17f2227ba91c 100644 --- a/tests/ui/needless_late_init.fixed +++ b/tests/ui/needless_late_init.fixed @@ -1,12 +1,13 @@ // run-rustfix #![feature(let_chains)] +#![allow(unused)] #![allow( - unused, clippy::assign_op_pattern, clippy::blocks_in_if_conditions, clippy::let_and_return, clippy::let_unit_value, - clippy::nonminimal_bool + clippy::nonminimal_bool, + clippy::uninlined_format_args )] use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; diff --git a/tests/ui/needless_late_init.rs b/tests/ui/needless_late_init.rs index 402d9f9ef7f8..d84457a29875 100644 --- a/tests/ui/needless_late_init.rs +++ b/tests/ui/needless_late_init.rs @@ -1,12 +1,13 @@ // run-rustfix #![feature(let_chains)] +#![allow(unused)] #![allow( - unused, clippy::assign_op_pattern, clippy::blocks_in_if_conditions, clippy::let_and_return, clippy::let_unit_value, - clippy::nonminimal_bool + clippy::nonminimal_bool, + clippy::uninlined_format_args )] use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; diff --git a/tests/ui/needless_late_init.stderr b/tests/ui/needless_late_init.stderr index 313cdbbeba18..0a256fb4a131 100644 --- a/tests/ui/needless_late_init.stderr +++ b/tests/ui/needless_late_init.stderr @@ -1,5 +1,5 @@ error: unneeded late initialization - --> $DIR/needless_late_init.rs:23:5 + --> $DIR/needless_late_init.rs:24:5 | LL | let a; | ^^^^^^ created here @@ -13,7 +13,7 @@ LL | let a = "zero"; | ~~~~~ error: unneeded late initialization - --> $DIR/needless_late_init.rs:26:5 + --> $DIR/needless_late_init.rs:27:5 | LL | let b; | ^^^^^^ created here @@ -27,7 +27,7 @@ LL | let b = 1; | ~~~~~ error: unneeded late initialization - --> $DIR/needless_late_init.rs:27:5 + --> $DIR/needless_late_init.rs:28:5 | LL | let c; | ^^^^^^ created here @@ -41,7 +41,7 @@ LL | let c = 2; | ~~~~~ error: unneeded late initialization - --> $DIR/needless_late_init.rs:31:5 + --> $DIR/needless_late_init.rs:32:5 | LL | let d: usize; | ^^^^^^^^^^^^^ created here @@ -54,7 +54,7 @@ LL | let d: usize = 1; | ~~~~~~~~~~~~ error: unneeded late initialization - --> $DIR/needless_late_init.rs:34:5 + --> $DIR/needless_late_init.rs:35:5 | LL | let e; | ^^^^^^ created here @@ -67,7 +67,7 @@ LL | let e = format!("{}", d); | ~~~~~ error: unneeded late initialization - --> $DIR/needless_late_init.rs:39:5 + --> $DIR/needless_late_init.rs:40:5 | LL | let a; | ^^^^^^ @@ -88,7 +88,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:48:5 + --> $DIR/needless_late_init.rs:49:5 | LL | let b; | ^^^^^^ @@ -109,7 +109,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:55:5 + --> $DIR/needless_late_init.rs:56:5 | LL | let d; | ^^^^^^ @@ -130,7 +130,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:63:5 + --> $DIR/needless_late_init.rs:64:5 | LL | let e; | ^^^^^^ @@ -151,7 +151,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:70:5 + --> $DIR/needless_late_init.rs:71:5 | LL | let f; | ^^^^^^ @@ -167,7 +167,7 @@ LL + 1 => "three", | error: unneeded late initialization - --> $DIR/needless_late_init.rs:76:5 + --> $DIR/needless_late_init.rs:77:5 | LL | let g: usize; | ^^^^^^^^^^^^^ @@ -187,7 +187,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:84:5 + --> $DIR/needless_late_init.rs:85:5 | LL | let x; | ^^^^^^ created here @@ -201,7 +201,7 @@ LL | let x = 1; | ~~~~~ error: unneeded late initialization - --> $DIR/needless_late_init.rs:88:5 + --> $DIR/needless_late_init.rs:89:5 | LL | let x; | ^^^^^^ created here @@ -215,7 +215,7 @@ LL | let x = SignificantDrop; | ~~~~~ error: unneeded late initialization - --> $DIR/needless_late_init.rs:92:5 + --> $DIR/needless_late_init.rs:93:5 | LL | let x; | ^^^^^^ created here @@ -229,7 +229,7 @@ LL | let x = SignificantDrop; | ~~~~~ error: unneeded late initialization - --> $DIR/needless_late_init.rs:111:5 + --> $DIR/needless_late_init.rs:112:5 | LL | let a; | ^^^^^^ @@ -250,7 +250,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:128:5 + --> $DIR/needless_late_init.rs:129:5 | LL | let a; | ^^^^^^ diff --git a/tests/ui/needless_pass_by_value.rs b/tests/ui/needless_pass_by_value.rs index 5a35b100afe0..d79ad86b1948 100644 --- a/tests/ui/needless_pass_by_value.rs +++ b/tests/ui/needless_pass_by_value.rs @@ -1,10 +1,11 @@ #![warn(clippy::needless_pass_by_value)] +#![allow(dead_code)] #![allow( - dead_code, - clippy::single_match, - clippy::redundant_pattern_matching, clippy::option_option, - clippy::redundant_clone + clippy::redundant_clone, + clippy::redundant_pattern_matching, + clippy::single_match, + clippy::uninlined_format_args )] use std::borrow::Borrow; diff --git a/tests/ui/needless_pass_by_value.stderr b/tests/ui/needless_pass_by_value.stderr index 38f33c53f128..0e660a77dc0c 100644 --- a/tests/ui/needless_pass_by_value.stderr +++ b/tests/ui/needless_pass_by_value.stderr @@ -1,5 +1,5 @@ error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:17:23 + --> $DIR/needless_pass_by_value.rs:18:23 | LL | fn foo(v: Vec, w: Vec, mut x: Vec, y: Vec) -> Vec { | ^^^^^^ help: consider changing the type to: `&[T]` @@ -7,55 +7,55 @@ LL | fn foo(v: Vec, w: Vec, mut x: Vec, y: Vec) -> Vec $DIR/needless_pass_by_value.rs:31:11 + --> $DIR/needless_pass_by_value.rs:32:11 | LL | fn bar(x: String, y: Wrapper) { | ^^^^^^ help: consider changing the type to: `&str` error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:31:22 + --> $DIR/needless_pass_by_value.rs:32:22 | LL | fn bar(x: String, y: Wrapper) { | ^^^^^^^ help: consider taking a reference instead: `&Wrapper` error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:37:71 + --> $DIR/needless_pass_by_value.rs:38:71 | LL | fn test_borrow_trait, U: AsRef, V>(t: T, u: U, v: V) { | ^ help: consider taking a reference instead: `&V` error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:49:18 + --> $DIR/needless_pass_by_value.rs:50:18 | LL | fn test_match(x: Option>, y: Option>) { | ^^^^^^^^^^^^^^^^^^^^^^ help: consider taking a reference instead: `&Option>` error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:62:24 + --> $DIR/needless_pass_by_value.rs:63:24 | LL | fn test_destructure(x: Wrapper, y: Wrapper, z: Wrapper) { | ^^^^^^^ help: consider taking a reference instead: `&Wrapper` error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:62:36 + --> $DIR/needless_pass_by_value.rs:63:36 | LL | fn test_destructure(x: Wrapper, y: Wrapper, z: Wrapper) { | ^^^^^^^ help: consider taking a reference instead: `&Wrapper` error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:78:49 + --> $DIR/needless_pass_by_value.rs:79:49 | LL | fn test_blanket_ref(_foo: T, _serializable: S) {} | ^ help: consider taking a reference instead: `&T` error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:80:18 + --> $DIR/needless_pass_by_value.rs:81:18 | LL | fn issue_2114(s: String, t: String, u: Vec, v: Vec) { | ^^^^^^ help: consider taking a reference instead: `&String` error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:80:29 + --> $DIR/needless_pass_by_value.rs:81:29 | LL | fn issue_2114(s: String, t: String, u: Vec, v: Vec) { | ^^^^^^ @@ -70,13 +70,13 @@ LL | let _ = t.to_string(); | ~~~~~~~~~~~~~ error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:80:40 + --> $DIR/needless_pass_by_value.rs:81:40 | LL | fn issue_2114(s: String, t: String, u: Vec, v: Vec) { | ^^^^^^^^ help: consider taking a reference instead: `&Vec` error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:80:53 + --> $DIR/needless_pass_by_value.rs:81:53 | LL | fn issue_2114(s: String, t: String, u: Vec, v: Vec) { | ^^^^^^^^ @@ -91,85 +91,85 @@ LL | let _ = v.to_owned(); | ~~~~~~~~~~~~ error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:93:12 + --> $DIR/needless_pass_by_value.rs:94:12 | LL | s: String, | ^^^^^^ help: consider changing the type to: `&str` error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:94:12 + --> $DIR/needless_pass_by_value.rs:95:12 | LL | t: String, | ^^^^^^ help: consider taking a reference instead: `&String` error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:103:23 + --> $DIR/needless_pass_by_value.rs:104:23 | LL | fn baz(&self, _u: U, _s: Self) {} | ^ help: consider taking a reference instead: `&U` error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:103:30 + --> $DIR/needless_pass_by_value.rs:104:30 | LL | fn baz(&self, _u: U, _s: Self) {} | ^^^^ help: consider taking a reference instead: `&Self` error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:125:24 + --> $DIR/needless_pass_by_value.rs:126:24 | LL | fn bar_copy(x: u32, y: CopyWrapper) { | ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper` | help: consider marking this type as `Copy` - --> $DIR/needless_pass_by_value.rs:123:1 + --> $DIR/needless_pass_by_value.rs:124:1 | LL | struct CopyWrapper(u32); | ^^^^^^^^^^^^^^^^^^ error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:131:29 + --> $DIR/needless_pass_by_value.rs:132:29 | LL | fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) { | ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper` | help: consider marking this type as `Copy` - --> $DIR/needless_pass_by_value.rs:123:1 + --> $DIR/needless_pass_by_value.rs:124:1 | LL | struct CopyWrapper(u32); | ^^^^^^^^^^^^^^^^^^ error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:131:45 + --> $DIR/needless_pass_by_value.rs:132:45 | LL | fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) { | ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper` | help: consider marking this type as `Copy` - --> $DIR/needless_pass_by_value.rs:123:1 + --> $DIR/needless_pass_by_value.rs:124:1 | LL | struct CopyWrapper(u32); | ^^^^^^^^^^^^^^^^^^ error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:131:61 + --> $DIR/needless_pass_by_value.rs:132:61 | LL | fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) { | ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper` | help: consider marking this type as `Copy` - --> $DIR/needless_pass_by_value.rs:123:1 + --> $DIR/needless_pass_by_value.rs:124:1 | LL | struct CopyWrapper(u32); | ^^^^^^^^^^^^^^^^^^ error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:143:40 + --> $DIR/needless_pass_by_value.rs:144:40 | LL | fn some_fun<'b, S: Bar<'b, ()>>(_item: S) {} | ^ help: consider taking a reference instead: `&S` error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:148:20 + --> $DIR/needless_pass_by_value.rs:149:20 | LL | fn more_fun(_item: impl Club<'static, i32>) {} | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider taking a reference instead: `&impl Club<'static, i32>` diff --git a/tests/ui/needless_range_loop.rs b/tests/ui/needless_range_loop.rs index 3fce34367ae5..921801138a9b 100644 --- a/tests/ui/needless_range_loop.rs +++ b/tests/ui/needless_range_loop.rs @@ -1,4 +1,5 @@ #![warn(clippy::needless_range_loop)] +#![allow(clippy::uninlined_format_args)] static STATIC: [usize; 4] = [0, 1, 8, 16]; const CONST: [usize; 4] = [0, 1, 8, 16]; diff --git a/tests/ui/needless_range_loop.stderr b/tests/ui/needless_range_loop.stderr index a86cc69dfc5d..b31544ec334a 100644 --- a/tests/ui/needless_range_loop.stderr +++ b/tests/ui/needless_range_loop.stderr @@ -1,5 +1,5 @@ error: the loop variable `i` is only used to index `vec` - --> $DIR/needless_range_loop.rs:10:14 + --> $DIR/needless_range_loop.rs:11:14 | LL | for i in 0..vec.len() { | ^^^^^^^^^^^^ @@ -11,7 +11,7 @@ LL | for in &vec { | ~~~~~~ ~~~~ error: the loop variable `i` is only used to index `vec` - --> $DIR/needless_range_loop.rs:19:14 + --> $DIR/needless_range_loop.rs:20:14 | LL | for i in 0..vec.len() { | ^^^^^^^^^^^^ @@ -22,7 +22,7 @@ LL | for in &vec { | ~~~~~~ ~~~~ error: the loop variable `j` is only used to index `STATIC` - --> $DIR/needless_range_loop.rs:24:14 + --> $DIR/needless_range_loop.rs:25:14 | LL | for j in 0..4 { | ^^^^ @@ -33,7 +33,7 @@ LL | for in &STATIC { | ~~~~~~ ~~~~~~~ error: the loop variable `j` is only used to index `CONST` - --> $DIR/needless_range_loop.rs:28:14 + --> $DIR/needless_range_loop.rs:29:14 | LL | for j in 0..4 { | ^^^^ @@ -44,7 +44,7 @@ LL | for in &CONST { | ~~~~~~ ~~~~~~ error: the loop variable `i` is used to index `vec` - --> $DIR/needless_range_loop.rs:32:14 + --> $DIR/needless_range_loop.rs:33:14 | LL | for i in 0..vec.len() { | ^^^^^^^^^^^^ @@ -55,7 +55,7 @@ LL | for (i, ) in vec.iter().enumerate() { | ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~ error: the loop variable `i` is only used to index `vec2` - --> $DIR/needless_range_loop.rs:40:14 + --> $DIR/needless_range_loop.rs:41:14 | LL | for i in 0..vec.len() { | ^^^^^^^^^^^^ @@ -66,7 +66,7 @@ LL | for in vec2.iter().take(vec.len()) { | ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: the loop variable `i` is only used to index `vec` - --> $DIR/needless_range_loop.rs:44:14 + --> $DIR/needless_range_loop.rs:45:14 | LL | for i in 5..vec.len() { | ^^^^^^^^^^^^ @@ -77,7 +77,7 @@ LL | for in vec.iter().skip(5) { | ~~~~~~ ~~~~~~~~~~~~~~~~~~ error: the loop variable `i` is only used to index `vec` - --> $DIR/needless_range_loop.rs:48:14 + --> $DIR/needless_range_loop.rs:49:14 | LL | for i in 0..MAX_LEN { | ^^^^^^^^^^ @@ -88,7 +88,7 @@ LL | for in vec.iter().take(MAX_LEN) { | ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~ error: the loop variable `i` is only used to index `vec` - --> $DIR/needless_range_loop.rs:52:14 + --> $DIR/needless_range_loop.rs:53:14 | LL | for i in 0..=MAX_LEN { | ^^^^^^^^^^^ @@ -99,7 +99,7 @@ LL | for in vec.iter().take(MAX_LEN + 1) { | ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: the loop variable `i` is only used to index `vec` - --> $DIR/needless_range_loop.rs:56:14 + --> $DIR/needless_range_loop.rs:57:14 | LL | for i in 5..10 { | ^^^^^ @@ -110,7 +110,7 @@ LL | for in vec.iter().take(10).skip(5) { | ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: the loop variable `i` is only used to index `vec` - --> $DIR/needless_range_loop.rs:60:14 + --> $DIR/needless_range_loop.rs:61:14 | LL | for i in 5..=10 { | ^^^^^^ @@ -121,7 +121,7 @@ LL | for in vec.iter().take(10 + 1).skip(5) { | ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: the loop variable `i` is used to index `vec` - --> $DIR/needless_range_loop.rs:64:14 + --> $DIR/needless_range_loop.rs:65:14 | LL | for i in 5..vec.len() { | ^^^^^^^^^^^^ @@ -132,7 +132,7 @@ LL | for (i, ) in vec.iter().enumerate().skip(5) { | ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: the loop variable `i` is used to index `vec` - --> $DIR/needless_range_loop.rs:68:14 + --> $DIR/needless_range_loop.rs:69:14 | LL | for i in 5..10 { | ^^^^^ @@ -143,7 +143,7 @@ LL | for (i, ) in vec.iter().enumerate().take(10).skip(5) { | ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: the loop variable `i` is used to index `vec` - --> $DIR/needless_range_loop.rs:73:14 + --> $DIR/needless_range_loop.rs:74:14 | LL | for i in 0..vec.len() { | ^^^^^^^^^^^^ diff --git a/tests/ui/needless_return.fixed b/tests/ui/needless_return.fixed index 695883e8dff7..d2163b14fcad 100644 --- a/tests/ui/needless_return.fixed +++ b/tests/ui/needless_return.fixed @@ -232,4 +232,41 @@ fn issue_9361() -> i32 { return 1 + 2; } +fn issue8336(x: i32) -> bool { + if x > 0 { + println!("something"); + true + } else { + false + } +} + +fn issue8156(x: u8) -> u64 { + match x { + 80 => { + 10 + }, + _ => { + 100 + }, + } +} + +// Ideally the compiler should throw `unused_braces` in this case +fn issue9192() -> i32 { + { + 0 + } +} + +fn issue9503(x: usize) -> isize { + unsafe { + if x > 12 { + *(x as *const isize) + } else { + !*(x as *const isize) + } + } +} + fn main() {} diff --git a/tests/ui/needless_return.rs b/tests/ui/needless_return.rs index 63d9fe9ecdf8..114414b5fac7 100644 --- a/tests/ui/needless_return.rs +++ b/tests/ui/needless_return.rs @@ -232,4 +232,41 @@ fn issue_9361() -> i32 { return 1 + 2; } +fn issue8336(x: i32) -> bool { + if x > 0 { + println!("something"); + return true; + } else { + return false; + }; +} + +fn issue8156(x: u8) -> u64 { + match x { + 80 => { + return 10; + }, + _ => { + return 100; + }, + }; +} + +// Ideally the compiler should throw `unused_braces` in this case +fn issue9192() -> i32 { + { + return 0; + }; +} + +fn issue9503(x: usize) -> isize { + unsafe { + if x > 12 { + return *(x as *const isize); + } else { + return !*(x as *const isize); + }; + }; +} + fn main() {} diff --git a/tests/ui/needless_return.stderr b/tests/ui/needless_return.stderr index cadee6e00dff..047fb6c2311a 100644 --- a/tests/ui/needless_return.stderr +++ b/tests/ui/needless_return.stderr @@ -2,225 +2,354 @@ error: unneeded `return` statement --> $DIR/needless_return.rs:26:5 | LL | return true; - | ^^^^^^^^^^^^ help: remove `return`: `true` + | ^^^^^^^^^^^ | = note: `-D clippy::needless-return` implied by `-D warnings` + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:30:5 | LL | return true; - | ^^^^^^^^^^^^ help: remove `return`: `true` + | ^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:35:9 | LL | return true; - | ^^^^^^^^^^^^ help: remove `return`: `true` + | ^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:37:9 | LL | return false; - | ^^^^^^^^^^^^^ help: remove `return`: `false` + | ^^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:43:17 | LL | true => return false, - | ^^^^^^^^^^^^ help: remove `return`: `false` + | ^^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:45:13 | LL | return true; - | ^^^^^^^^^^^^ help: remove `return`: `true` + | ^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:52:9 | LL | return true; - | ^^^^^^^^^^^^ help: remove `return`: `true` + | ^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:54:16 | LL | let _ = || return true; - | ^^^^^^^^^^^ help: remove `return`: `true` + | ^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:58:5 | LL | return the_answer!(); - | ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `the_answer!()` + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:62:5 | LL | return; - | ^^^^^^^ help: remove `return` + | ^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:67:9 | LL | return; - | ^^^^^^^ help: remove `return` + | ^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:69:9 | LL | return; - | ^^^^^^^ help: remove `return` + | ^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:76:14 | LL | _ => return, - | ^^^^^^ help: replace `return` with a unit value: `()` + | ^^^^^^ + | + = help: replace `return` with a unit value error: unneeded `return` statement --> $DIR/needless_return.rs:85:13 | LL | return; - | ^^^^^^^ help: remove `return` + | ^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:87:14 | LL | _ => return, - | ^^^^^^ help: replace `return` with a unit value: `()` + | ^^^^^^ + | + = help: replace `return` with a unit value error: unneeded `return` statement --> $DIR/needless_return.rs:100:9 | LL | return String::from("test"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::from("test")` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:102:9 | LL | return String::new(); - | ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()` + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:124:32 | LL | bar.unwrap_or_else(|_| return) - | ^^^^^^ help: replace `return` with an empty block: `{}` + | ^^^^^^ + | + = help: replace `return` with an empty block error: unneeded `return` statement --> $DIR/needless_return.rs:129:13 | LL | return; - | ^^^^^^^ help: remove `return` + | ^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:131:20 | LL | let _ = || return; - | ^^^^^^ help: replace `return` with an empty block: `{}` + | ^^^^^^ + | + = help: replace `return` with an empty block error: unneeded `return` statement --> $DIR/needless_return.rs:137:32 | LL | res.unwrap_or_else(|_| return Foo) - | ^^^^^^^^^^ help: remove `return`: `Foo` + | ^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:146:5 | LL | return true; - | ^^^^^^^^^^^^ help: remove `return`: `true` + | ^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:150:5 | LL | return true; - | ^^^^^^^^^^^^ help: remove `return`: `true` + | ^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:155:9 | LL | return true; - | ^^^^^^^^^^^^ help: remove `return`: `true` + | ^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:157:9 | LL | return false; - | ^^^^^^^^^^^^^ help: remove `return`: `false` + | ^^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:163:17 | LL | true => return false, - | ^^^^^^^^^^^^ help: remove `return`: `false` + | ^^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:165:13 | LL | return true; - | ^^^^^^^^^^^^ help: remove `return`: `true` + | ^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:172:9 | LL | return true; - | ^^^^^^^^^^^^ help: remove `return`: `true` + | ^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:174:16 | LL | let _ = || return true; - | ^^^^^^^^^^^ help: remove `return`: `true` + | ^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:178:5 | LL | return the_answer!(); - | ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `the_answer!()` + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:182:5 | LL | return; - | ^^^^^^^ help: remove `return` + | ^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:187:9 | LL | return; - | ^^^^^^^ help: remove `return` + | ^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:189:9 | LL | return; - | ^^^^^^^ help: remove `return` + | ^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:196:14 | LL | _ => return, - | ^^^^^^ help: replace `return` with a unit value: `()` + | ^^^^^^ + | + = help: replace `return` with a unit value error: unneeded `return` statement --> $DIR/needless_return.rs:209:9 | LL | return String::from("test"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::from("test")` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:211:9 | LL | return String::new(); - | ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()` + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:227:5 | LL | return format!("Hello {}", "world!"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `format!("Hello {}", "world!")` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: remove `return` -error: aborting due to 37 previous errors +error: unneeded `return` statement + --> $DIR/needless_return.rs:238:9 + | +LL | return true; + | ^^^^^^^^^^^ + | + = help: remove `return` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:240:9 + | +LL | return false; + | ^^^^^^^^^^^^ + | + = help: remove `return` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:247:13 + | +LL | return 10; + | ^^^^^^^^^ + | + = help: remove `return` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:250:13 + | +LL | return 100; + | ^^^^^^^^^^ + | + = help: remove `return` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:258:9 + | +LL | return 0; + | ^^^^^^^^ + | + = help: remove `return` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:265:13 + | +LL | return *(x as *const isize); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: remove `return` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:267:13 + | +LL | return !*(x as *const isize); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: remove `return` + +error: aborting due to 44 previous errors diff --git a/tests/ui/never_loop.rs b/tests/ui/never_loop.rs index 0a21589dd0d4..3dbef19890e9 100644 --- a/tests/ui/never_loop.rs +++ b/tests/ui/never_loop.rs @@ -203,6 +203,32 @@ pub fn test17() { }; } +// Issue #9356: `continue` in else branch of let..else +pub fn test18() { + let x = Some(0); + let y = 0; + // might loop + let _ = loop { + let Some(x) = x else { + if y > 0 { + continue; + } else { + return; + } + }; + + break x; + }; + // never loops + let _ = loop { + let Some(x) = x else { + return; + }; + + break x; + }; +} + fn main() { test1(); test2(); diff --git a/tests/ui/never_loop.stderr b/tests/ui/never_loop.stderr index f49b23924efe..3033f019244a 100644 --- a/tests/ui/never_loop.stderr +++ b/tests/ui/never_loop.stderr @@ -101,5 +101,18 @@ LL | | break 'label; LL | | } | |_________^ -error: aborting due to 9 previous errors +error: this loop never actually loops + --> $DIR/never_loop.rs:223:13 + | +LL | let _ = loop { + | _____________^ +LL | | let Some(x) = x else { +LL | | return; +LL | | }; +LL | | +LL | | break x; +LL | | }; + | |_____^ + +error: aborting due to 10 previous errors diff --git a/tests/ui/no_effect.rs b/tests/ui/no_effect.rs index fdefb11ae17a..f08eb092e6b2 100644 --- a/tests/ui/no_effect.rs +++ b/tests/ui/no_effect.rs @@ -1,9 +1,7 @@ #![feature(box_syntax, fn_traits, unboxed_closures)] #![warn(clippy::no_effect_underscore_binding)] -#![allow(dead_code)] -#![allow(path_statements)] -#![allow(clippy::deref_addrof)] -#![allow(clippy::redundant_field_names)] +#![allow(dead_code, path_statements)] +#![allow(clippy::deref_addrof, clippy::redundant_field_names, clippy::uninlined_format_args)] struct Unit; struct Tuple(i32); diff --git a/tests/ui/no_effect.stderr b/tests/ui/no_effect.stderr index 328d2555ceb8..6a1e636f9a61 100644 --- a/tests/ui/no_effect.stderr +++ b/tests/ui/no_effect.stderr @@ -1,5 +1,5 @@ error: statement with no effect - --> $DIR/no_effect.rs:94:5 + --> $DIR/no_effect.rs:92:5 | LL | 0; | ^^ @@ -7,157 +7,157 @@ LL | 0; = note: `-D clippy::no-effect` implied by `-D warnings` error: statement with no effect - --> $DIR/no_effect.rs:95:5 + --> $DIR/no_effect.rs:93:5 | LL | s2; | ^^^ error: statement with no effect - --> $DIR/no_effect.rs:96:5 + --> $DIR/no_effect.rs:94:5 | LL | Unit; | ^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:97:5 + --> $DIR/no_effect.rs:95:5 | LL | Tuple(0); | ^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:98:5 + --> $DIR/no_effect.rs:96:5 | LL | Struct { field: 0 }; | ^^^^^^^^^^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:99:5 + --> $DIR/no_effect.rs:97:5 | LL | Struct { ..s }; | ^^^^^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:100:5 + --> $DIR/no_effect.rs:98:5 | LL | Union { a: 0 }; | ^^^^^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:101:5 + --> $DIR/no_effect.rs:99:5 | LL | Enum::Tuple(0); | ^^^^^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:102:5 + --> $DIR/no_effect.rs:100:5 | LL | Enum::Struct { field: 0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:103:5 + --> $DIR/no_effect.rs:101:5 | LL | 5 + 6; | ^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:104:5 + --> $DIR/no_effect.rs:102:5 | LL | *&42; | ^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:105:5 + --> $DIR/no_effect.rs:103:5 | LL | &6; | ^^^ error: statement with no effect - --> $DIR/no_effect.rs:106:5 + --> $DIR/no_effect.rs:104:5 | LL | (5, 6, 7); | ^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:107:5 + --> $DIR/no_effect.rs:105:5 | LL | box 42; | ^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:108:5 + --> $DIR/no_effect.rs:106:5 | LL | ..; | ^^^ error: statement with no effect - --> $DIR/no_effect.rs:109:5 + --> $DIR/no_effect.rs:107:5 | LL | 5..; | ^^^^ error: statement with no effect - --> $DIR/no_effect.rs:110:5 + --> $DIR/no_effect.rs:108:5 | LL | ..5; | ^^^^ error: statement with no effect - --> $DIR/no_effect.rs:111:5 + --> $DIR/no_effect.rs:109:5 | LL | 5..6; | ^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:112:5 + --> $DIR/no_effect.rs:110:5 | LL | 5..=6; | ^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:113:5 + --> $DIR/no_effect.rs:111:5 | LL | [42, 55]; | ^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:114:5 + --> $DIR/no_effect.rs:112:5 | LL | [42, 55][1]; | ^^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:115:5 + --> $DIR/no_effect.rs:113:5 | LL | (42, 55).1; | ^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:116:5 + --> $DIR/no_effect.rs:114:5 | LL | [42; 55]; | ^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:117:5 + --> $DIR/no_effect.rs:115:5 | LL | [42; 55][13]; | ^^^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:119:5 + --> $DIR/no_effect.rs:117:5 | LL | || x += 5; | ^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:121:5 + --> $DIR/no_effect.rs:119:5 | LL | FooString { s: s }; | ^^^^^^^^^^^^^^^^^^^ error: binding to `_` prefixed variable with no side-effect - --> $DIR/no_effect.rs:122:5 + --> $DIR/no_effect.rs:120:5 | LL | let _unused = 1; | ^^^^^^^^^^^^^^^^ @@ -165,19 +165,19 @@ LL | let _unused = 1; = note: `-D clippy::no-effect-underscore-binding` implied by `-D warnings` error: binding to `_` prefixed variable with no side-effect - --> $DIR/no_effect.rs:123:5 + --> $DIR/no_effect.rs:121:5 | LL | let _penguin = || println!("Some helpful closure"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: binding to `_` prefixed variable with no side-effect - --> $DIR/no_effect.rs:124:5 + --> $DIR/no_effect.rs:122:5 | LL | let _duck = Struct { field: 0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: binding to `_` prefixed variable with no side-effect - --> $DIR/no_effect.rs:125:5 + --> $DIR/no_effect.rs:123:5 | LL | let _cat = [2, 4, 6, 8][2]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/option_map_unit_fn_fixable.fixed b/tests/ui/option_map_unit_fn_fixable.fixed index 1290bd8efebd..00264dcceaa8 100644 --- a/tests/ui/option_map_unit_fn_fixable.fixed +++ b/tests/ui/option_map_unit_fn_fixable.fixed @@ -1,8 +1,7 @@ // run-rustfix - #![warn(clippy::option_map_unit_fn)] #![allow(unused)] -#![allow(clippy::unnecessary_wraps)] +#![allow(clippy::uninlined_format_args, clippy::unnecessary_wraps)] fn do_nothing(_: T) {} diff --git a/tests/ui/option_map_unit_fn_fixable.rs b/tests/ui/option_map_unit_fn_fixable.rs index f3e5b62c65b7..f3363ebce54e 100644 --- a/tests/ui/option_map_unit_fn_fixable.rs +++ b/tests/ui/option_map_unit_fn_fixable.rs @@ -1,8 +1,7 @@ // run-rustfix - #![warn(clippy::option_map_unit_fn)] #![allow(unused)] -#![allow(clippy::unnecessary_wraps)] +#![allow(clippy::uninlined_format_args, clippy::unnecessary_wraps)] fn do_nothing(_: T) {} diff --git a/tests/ui/option_map_unit_fn_fixable.stderr b/tests/ui/option_map_unit_fn_fixable.stderr index ab2a294a060f..0305387b9f8a 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 `()` - --> $DIR/option_map_unit_fn_fixable.rs:39:5 + --> $DIR/option_map_unit_fn_fixable.rs:38:5 | LL | x.field.map(do_nothing); | ^^^^^^^^^^^^^^^^^^^^^^^- @@ -9,7 +9,7 @@ LL | x.field.map(do_nothing); = note: `-D clippy::option-map-unit-fn` implied by `-D warnings` error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` - --> $DIR/option_map_unit_fn_fixable.rs:41:5 + --> $DIR/option_map_unit_fn_fixable.rs:40:5 | LL | x.field.map(do_nothing); | ^^^^^^^^^^^^^^^^^^^^^^^- @@ -17,7 +17,7 @@ LL | x.field.map(do_nothing); | help: try this: `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 `()` - --> $DIR/option_map_unit_fn_fixable.rs:43:5 + --> $DIR/option_map_unit_fn_fixable.rs:42:5 | LL | x.field.map(diverge); | ^^^^^^^^^^^^^^^^^^^^- @@ -25,7 +25,7 @@ LL | x.field.map(diverge); | help: try this: `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 `()` - --> $DIR/option_map_unit_fn_fixable.rs:49:5 + --> $DIR/option_map_unit_fn_fixable.rs:48:5 | LL | x.field.map(|value| x.do_option_nothing(value + captured)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -33,7 +33,7 @@ LL | x.field.map(|value| x.do_option_nothing(value + captured)); | help: try this: `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 `()` - --> $DIR/option_map_unit_fn_fixable.rs:51:5 + --> $DIR/option_map_unit_fn_fixable.rs:50:5 | LL | x.field.map(|value| { x.do_option_plus_one(value + captured); }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -41,7 +41,7 @@ LL | x.field.map(|value| { x.do_option_plus_one(value + captured); }); | help: try this: `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 `()` - --> $DIR/option_map_unit_fn_fixable.rs:54:5 + --> $DIR/option_map_unit_fn_fixable.rs:53:5 | LL | x.field.map(|value| do_nothing(value + captured)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -49,7 +49,7 @@ LL | x.field.map(|value| do_nothing(value + captured)); | help: try this: `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 `()` - --> $DIR/option_map_unit_fn_fixable.rs:56:5 + --> $DIR/option_map_unit_fn_fixable.rs:55:5 | LL | x.field.map(|value| { do_nothing(value + captured) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -57,7 +57,7 @@ LL | x.field.map(|value| { do_nothing(value + captured) }); | help: try this: `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 `()` - --> $DIR/option_map_unit_fn_fixable.rs:58:5 + --> $DIR/option_map_unit_fn_fixable.rs:57:5 | LL | x.field.map(|value| { do_nothing(value + captured); }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -65,7 +65,7 @@ LL | x.field.map(|value| { do_nothing(value + captured); }); | help: try this: `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 `()` - --> $DIR/option_map_unit_fn_fixable.rs:60:5 + --> $DIR/option_map_unit_fn_fixable.rs:59:5 | LL | x.field.map(|value| { { do_nothing(value + captured); } }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -73,7 +73,7 @@ LL | x.field.map(|value| { { do_nothing(value + captured); } }); | help: try this: `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 `()` - --> $DIR/option_map_unit_fn_fixable.rs:63:5 + --> $DIR/option_map_unit_fn_fixable.rs:62:5 | LL | x.field.map(|value| diverge(value + captured)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -81,7 +81,7 @@ LL | x.field.map(|value| diverge(value + captured)); | help: try this: `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 `()` - --> $DIR/option_map_unit_fn_fixable.rs:65:5 + --> $DIR/option_map_unit_fn_fixable.rs:64:5 | LL | x.field.map(|value| { diverge(value + captured) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -89,7 +89,7 @@ LL | x.field.map(|value| { diverge(value + captured) }); | help: try this: `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 `()` - --> $DIR/option_map_unit_fn_fixable.rs:67:5 + --> $DIR/option_map_unit_fn_fixable.rs:66:5 | LL | x.field.map(|value| { diverge(value + captured); }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -97,7 +97,7 @@ LL | x.field.map(|value| { diverge(value + captured); }); | help: try this: `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 `()` - --> $DIR/option_map_unit_fn_fixable.rs:69:5 + --> $DIR/option_map_unit_fn_fixable.rs:68:5 | LL | x.field.map(|value| { { diverge(value + captured); } }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -105,7 +105,7 @@ LL | x.field.map(|value| { { diverge(value + captured); } }); | help: try this: `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 `()` - --> $DIR/option_map_unit_fn_fixable.rs:74:5 + --> $DIR/option_map_unit_fn_fixable.rs:73:5 | LL | x.field.map(|value| { let y = plus_one(value + captured); }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -113,7 +113,7 @@ LL | x.field.map(|value| { let y = plus_one(value + captured); }); | help: try this: `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 `()` - --> $DIR/option_map_unit_fn_fixable.rs:76:5 + --> $DIR/option_map_unit_fn_fixable.rs:75:5 | LL | x.field.map(|value| { plus_one(value + captured); }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -121,7 +121,7 @@ LL | x.field.map(|value| { plus_one(value + captured); }); | help: try this: `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 `()` - --> $DIR/option_map_unit_fn_fixable.rs:78:5 + --> $DIR/option_map_unit_fn_fixable.rs:77:5 | LL | x.field.map(|value| { { plus_one(value + captured); } }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -129,7 +129,7 @@ LL | x.field.map(|value| { { plus_one(value + captured); } }); | help: try this: `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 `()` - --> $DIR/option_map_unit_fn_fixable.rs:81:5 + --> $DIR/option_map_unit_fn_fixable.rs:80:5 | LL | x.field.map(|ref value| { do_nothing(value + captured) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -137,7 +137,7 @@ LL | x.field.map(|ref value| { do_nothing(value + captured) }); | help: try this: `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 `()` - --> $DIR/option_map_unit_fn_fixable.rs:83:5 + --> $DIR/option_map_unit_fn_fixable.rs:82:5 | LL | option().map(do_nothing); | ^^^^^^^^^^^^^^^^^^^^^^^^- @@ -145,7 +145,7 @@ LL | option().map(do_nothing); | help: try this: `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 `()` - --> $DIR/option_map_unit_fn_fixable.rs:85:5 + --> $DIR/option_map_unit_fn_fixable.rs:84:5 | LL | option().map(|value| println!("{:?}", value)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- diff --git a/tests/ui/option_take_on_temporary.fixed b/tests/ui/option_take_on_temporary.fixed deleted file mode 100644 index 29691e81666f..000000000000 --- a/tests/ui/option_take_on_temporary.fixed +++ /dev/null @@ -1,15 +0,0 @@ -// run-rustfix - -fn main() { - println!("Testing non erroneous option_take_on_temporary"); - let mut option = Some(1); - let _ = Box::new(move || option.take().unwrap()); - - println!("Testing non erroneous option_take_on_temporary"); - let x = Some(3); - x.as_ref(); - - println!("Testing erroneous option_take_on_temporary"); - let x = Some(3); - x.as_ref(); -} diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 5991188ab637..896430780ea8 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -1,8 +1,7 @@ // run-rustfix - #![warn(clippy::or_fun_call)] #![allow(dead_code)] -#![allow(clippy::unnecessary_wraps, clippy::borrow_as_ptr)] +#![allow(clippy::borrow_as_ptr, clippy::uninlined_format_args, clippy::unnecessary_wraps)] use std::collections::BTreeMap; use std::collections::HashMap; diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index c353b41e4495..2473163d4fd2 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -1,8 +1,7 @@ // run-rustfix - #![warn(clippy::or_fun_call)] #![allow(dead_code)] -#![allow(clippy::unnecessary_wraps, clippy::borrow_as_ptr)] +#![allow(clippy::borrow_as_ptr, clippy::uninlined_format_args, clippy::unnecessary_wraps)] use std::collections::BTreeMap; use std::collections::HashMap; diff --git a/tests/ui/or_fun_call.stderr b/tests/ui/or_fun_call.stderr index e3dab4cb1477..113ba150c619 100644 --- a/tests/ui/or_fun_call.stderr +++ b/tests/ui/or_fun_call.stderr @@ -1,5 +1,5 @@ error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:49:22 + --> $DIR/or_fun_call.rs:48:22 | LL | with_constructor.unwrap_or(make()); | ^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(make)` @@ -7,151 +7,151 @@ LL | with_constructor.unwrap_or(make()); = note: `-D clippy::or-fun-call` implied by `-D warnings` error: use of `unwrap_or` followed by a call to `new` - --> $DIR/or_fun_call.rs:52:14 + --> $DIR/or_fun_call.rs:51:14 | LL | with_new.unwrap_or(Vec::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:55:21 + --> $DIR/or_fun_call.rs:54:21 | LL | with_const_args.unwrap_or(Vec::with_capacity(12)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| Vec::with_capacity(12))` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:58:14 + --> $DIR/or_fun_call.rs:57:14 | LL | with_err.unwrap_or(make()); | ^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| make())` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:61:19 + --> $DIR/or_fun_call.rs:60:19 | LL | with_err_args.unwrap_or(Vec::with_capacity(12)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| Vec::with_capacity(12))` error: use of `unwrap_or` followed by a call to `default` - --> $DIR/or_fun_call.rs:64:24 + --> $DIR/or_fun_call.rs:63:24 | LL | with_default_trait.unwrap_or(Default::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()` error: use of `unwrap_or` followed by a call to `default` - --> $DIR/or_fun_call.rs:67:23 + --> $DIR/or_fun_call.rs:66:23 | LL | with_default_type.unwrap_or(u64::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:70:18 + --> $DIR/or_fun_call.rs:69:18 | LL | self_default.unwrap_or(::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(::default)` error: use of `unwrap_or` followed by a call to `default` - --> $DIR/or_fun_call.rs:73:18 + --> $DIR/or_fun_call.rs:72:18 | LL | real_default.unwrap_or(::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()` error: use of `unwrap_or` followed by a call to `new` - --> $DIR/or_fun_call.rs:76:14 + --> $DIR/or_fun_call.rs:75:14 | LL | with_vec.unwrap_or(vec![]); | ^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:79:21 + --> $DIR/or_fun_call.rs:78:21 | LL | without_default.unwrap_or(Foo::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(Foo::new)` error: use of `or_insert` followed by a call to `new` - --> $DIR/or_fun_call.rs:82:19 + --> $DIR/or_fun_call.rs:81:19 | LL | map.entry(42).or_insert(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_default()` error: use of `or_insert` followed by a call to `new` - --> $DIR/or_fun_call.rs:85:23 + --> $DIR/or_fun_call.rs:84:23 | LL | map_vec.entry(42).or_insert(vec![]); | ^^^^^^^^^^^^^^^^^ help: try this: `or_default()` error: use of `or_insert` followed by a call to `new` - --> $DIR/or_fun_call.rs:88:21 + --> $DIR/or_fun_call.rs:87:21 | LL | btree.entry(42).or_insert(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_default()` error: use of `or_insert` followed by a call to `new` - --> $DIR/or_fun_call.rs:91:25 + --> $DIR/or_fun_call.rs:90:25 | LL | btree_vec.entry(42).or_insert(vec![]); | ^^^^^^^^^^^^^^^^^ help: try this: `or_default()` error: use of `unwrap_or` followed by a call to `new` - --> $DIR/or_fun_call.rs:94:21 + --> $DIR/or_fun_call.rs:93:21 | LL | let _ = stringy.unwrap_or(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:102:21 + --> $DIR/or_fun_call.rs:101:21 | LL | let _ = Some(1).unwrap_or(map[&1]); | ^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| map[&1])` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:104:21 + --> $DIR/or_fun_call.rs:103:21 | LL | let _ = Some(1).unwrap_or(map[&1]); | ^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| map[&1])` error: use of `or` followed by a function call - --> $DIR/or_fun_call.rs:128:35 + --> $DIR/or_fun_call.rs:127:35 | LL | let _ = Some("a".to_string()).or(Some("b".to_string())); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some("b".to_string()))` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:167:14 + --> $DIR/or_fun_call.rs:166:14 | LL | None.unwrap_or(ptr_to_ref(s)); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| ptr_to_ref(s))` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:173:14 + --> $DIR/or_fun_call.rs:172:14 | LL | None.unwrap_or(unsafe { ptr_to_ref(s) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:175:14 + --> $DIR/or_fun_call.rs:174:14 | LL | None.unwrap_or( unsafe { ptr_to_ref(s) } ); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })` error: use of `unwrap_or` followed by a call to `new` - --> $DIR/or_fun_call.rs:189:14 + --> $DIR/or_fun_call.rs:188:14 | LL | .unwrap_or(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()` error: use of `unwrap_or` followed by a call to `new` - --> $DIR/or_fun_call.rs:202:14 + --> $DIR/or_fun_call.rs:201:14 | LL | .unwrap_or(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()` error: use of `unwrap_or` followed by a call to `new` - --> $DIR/or_fun_call.rs:214:14 + --> $DIR/or_fun_call.rs:213:14 | LL | .unwrap_or(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()` error: use of `unwrap_or` followed by a call to `new` - --> $DIR/or_fun_call.rs:225:10 + --> $DIR/or_fun_call.rs:224:10 | LL | .unwrap_or(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()` diff --git a/tests/ui/panic_in_result_fn_assertions.rs b/tests/ui/panic_in_result_fn_assertions.rs index ffdf8288adc7..08ab4d8681ed 100644 --- a/tests/ui/panic_in_result_fn_assertions.rs +++ b/tests/ui/panic_in_result_fn_assertions.rs @@ -1,5 +1,5 @@ #![warn(clippy::panic_in_result_fn)] -#![allow(clippy::unnecessary_wraps)] +#![allow(clippy::uninlined_format_args, clippy::unnecessary_wraps)] struct A; diff --git a/tests/ui/panic_in_result_fn_debug_assertions.rs b/tests/ui/panic_in_result_fn_debug_assertions.rs index c4fcd7e70944..df89d8c50246 100644 --- a/tests/ui/panic_in_result_fn_debug_assertions.rs +++ b/tests/ui/panic_in_result_fn_debug_assertions.rs @@ -1,5 +1,5 @@ #![warn(clippy::panic_in_result_fn)] -#![allow(clippy::unnecessary_wraps)] +#![allow(clippy::uninlined_format_args, clippy::unnecessary_wraps)] // debug_assert should never trigger the `panic_in_result_fn` lint diff --git a/tests/ui/patterns.fixed b/tests/ui/patterns.fixed index f22388154499..cd69014326eb 100644 --- a/tests/ui/patterns.fixed +++ b/tests/ui/patterns.fixed @@ -1,6 +1,7 @@ // run-rustfix -#![allow(unused)] #![warn(clippy::all)] +#![allow(unused)] +#![allow(clippy::uninlined_format_args)] fn main() { let v = Some(true); diff --git a/tests/ui/patterns.rs b/tests/ui/patterns.rs index 5848ecd38d98..9128da420c0d 100644 --- a/tests/ui/patterns.rs +++ b/tests/ui/patterns.rs @@ -1,6 +1,7 @@ // run-rustfix -#![allow(unused)] #![warn(clippy::all)] +#![allow(unused)] +#![allow(clippy::uninlined_format_args)] fn main() { let v = Some(true); diff --git a/tests/ui/patterns.stderr b/tests/ui/patterns.stderr index af067580688b..2c46b4eb593e 100644 --- a/tests/ui/patterns.stderr +++ b/tests/ui/patterns.stderr @@ -1,5 +1,5 @@ error: the `y @ _` pattern can be written as just `y` - --> $DIR/patterns.rs:10:9 + --> $DIR/patterns.rs:11:9 | LL | y @ _ => (), | ^^^^^ help: try: `y` @@ -7,13 +7,13 @@ LL | y @ _ => (), = note: `-D clippy::redundant-pattern` implied by `-D warnings` error: the `x @ _` pattern can be written as just `x` - --> $DIR/patterns.rs:25:9 + --> $DIR/patterns.rs:26:9 | LL | ref mut x @ _ => { | ^^^^^^^^^^^^^ help: try: `ref mut x` error: the `x @ _` pattern can be written as just `x` - --> $DIR/patterns.rs:33:9 + --> $DIR/patterns.rs:34:9 | LL | ref x @ _ => println!("vec: {:?}", x), | ^^^^^^^^^ help: try: `ref x` diff --git a/tests/ui/print_literal.rs b/tests/ui/print_literal.rs index 3f6639c14585..86f908f66b8f 100644 --- a/tests/ui/print_literal.rs +++ b/tests/ui/print_literal.rs @@ -1,4 +1,5 @@ #![warn(clippy::print_literal)] +#![allow(clippy::uninlined_format_args)] fn main() { // these should be fine diff --git a/tests/ui/print_literal.stderr b/tests/ui/print_literal.stderr index 23e6dbc3e341..6404dacdafa5 100644 --- a/tests/ui/print_literal.stderr +++ b/tests/ui/print_literal.stderr @@ -1,5 +1,5 @@ error: literal with an empty format string - --> $DIR/print_literal.rs:26:24 + --> $DIR/print_literal.rs:27:24 | LL | print!("Hello {}", "world"); | ^^^^^^^ @@ -12,7 +12,7 @@ LL + print!("Hello world"); | error: literal with an empty format string - --> $DIR/print_literal.rs:27:36 + --> $DIR/print_literal.rs:28:36 | LL | println!("Hello {} {}", world, "world"); | ^^^^^^^ @@ -24,7 +24,7 @@ LL + println!("Hello {} world", world); | error: literal with an empty format string - --> $DIR/print_literal.rs:28:26 + --> $DIR/print_literal.rs:29:26 | LL | println!("Hello {}", "world"); | ^^^^^^^ @@ -36,7 +36,7 @@ LL + println!("Hello world"); | error: literal with an empty format string - --> $DIR/print_literal.rs:29:26 + --> $DIR/print_literal.rs:30:26 | LL | println!("{} {:.4}", "a literal", 5); | ^^^^^^^^^^^ @@ -48,7 +48,7 @@ LL + println!("a literal {:.4}", 5); | error: literal with an empty format string - --> $DIR/print_literal.rs:34:25 + --> $DIR/print_literal.rs:35:25 | LL | println!("{0} {1}", "hello", "world"); | ^^^^^^^ @@ -60,7 +60,7 @@ LL + println!("hello {1}", "world"); | error: literal with an empty format string - --> $DIR/print_literal.rs:34:34 + --> $DIR/print_literal.rs:35:34 | LL | println!("{0} {1}", "hello", "world"); | ^^^^^^^ @@ -72,7 +72,7 @@ LL + println!("{0} world", "hello"); | error: literal with an empty format string - --> $DIR/print_literal.rs:35:34 + --> $DIR/print_literal.rs:36:34 | LL | println!("{1} {0}", "hello", "world"); | ^^^^^^^ @@ -84,7 +84,7 @@ LL + println!("world {0}", "hello"); | error: literal with an empty format string - --> $DIR/print_literal.rs:35:25 + --> $DIR/print_literal.rs:36:25 | LL | println!("{1} {0}", "hello", "world"); | ^^^^^^^ @@ -96,7 +96,7 @@ LL + println!("{1} hello", "world"); | error: literal with an empty format string - --> $DIR/print_literal.rs:38:35 + --> $DIR/print_literal.rs:39:35 | LL | println!("{foo} {bar}", foo = "hello", bar = "world"); | ^^^^^^^ @@ -108,7 +108,7 @@ LL + println!("hello {bar}", bar = "world"); | error: literal with an empty format string - --> $DIR/print_literal.rs:38:50 + --> $DIR/print_literal.rs:39:50 | LL | println!("{foo} {bar}", foo = "hello", bar = "world"); | ^^^^^^^ @@ -120,7 +120,7 @@ LL + println!("{foo} world", foo = "hello"); | error: literal with an empty format string - --> $DIR/print_literal.rs:39:50 + --> $DIR/print_literal.rs:40:50 | LL | println!("{bar} {foo}", foo = "hello", bar = "world"); | ^^^^^^^ @@ -132,7 +132,7 @@ LL + println!("world {foo}", foo = "hello"); | error: literal with an empty format string - --> $DIR/print_literal.rs:39:35 + --> $DIR/print_literal.rs:40:35 | LL | println!("{bar} {foo}", foo = "hello", bar = "world"); | ^^^^^^^ diff --git a/tests/ui/ptr_offset_with_cast.fixed b/tests/ui/ptr_offset_with_cast.fixed index 718e391e8bf6..c57e2990fb95 100644 --- a/tests/ui/ptr_offset_with_cast.fixed +++ b/tests/ui/ptr_offset_with_cast.fixed @@ -1,4 +1,5 @@ // run-rustfix +#![allow(clippy::unnecessary_cast)] fn main() { let vec = vec![b'a', b'b', b'c']; diff --git a/tests/ui/ptr_offset_with_cast.rs b/tests/ui/ptr_offset_with_cast.rs index f613742c741e..3de7997acddd 100644 --- a/tests/ui/ptr_offset_with_cast.rs +++ b/tests/ui/ptr_offset_with_cast.rs @@ -1,4 +1,5 @@ // run-rustfix +#![allow(clippy::unnecessary_cast)] fn main() { let vec = vec![b'a', b'b', b'c']; diff --git a/tests/ui/ptr_offset_with_cast.stderr b/tests/ui/ptr_offset_with_cast.stderr index fd45224ca067..3ba40593d644 100644 --- a/tests/ui/ptr_offset_with_cast.stderr +++ b/tests/ui/ptr_offset_with_cast.stderr @@ -1,5 +1,5 @@ error: use of `offset` with a `usize` casted to an `isize` - --> $DIR/ptr_offset_with_cast.rs:12:17 + --> $DIR/ptr_offset_with_cast.rs:13:17 | LL | let _ = ptr.offset(offset_usize as isize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr.add(offset_usize)` @@ -7,7 +7,7 @@ LL | let _ = ptr.offset(offset_usize as isize); = note: `-D clippy::ptr-offset-with-cast` implied by `-D warnings` error: use of `wrapping_offset` with a `usize` casted to an `isize` - --> $DIR/ptr_offset_with_cast.rs:16:17 + --> $DIR/ptr_offset_with_cast.rs:17:17 | LL | let _ = ptr.wrapping_offset(offset_usize as isize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr.wrapping_add(offset_usize)` diff --git a/tests/ui/question_mark.fixed b/tests/ui/question_mark.fixed index 57f23bd1916f..993389232cc2 100644 --- a/tests/ui/question_mark.fixed +++ b/tests/ui/question_mark.fixed @@ -223,3 +223,12 @@ fn pattern() -> Result<(), PatternedError> { } fn main() {} + +// should not lint, `?` operator not available in const context +const fn issue9175(option: Option<()>) -> Option<()> { + if option.is_none() { + return None; + } + //stuff + Some(()) +} diff --git a/tests/ui/question_mark.rs b/tests/ui/question_mark.rs index 436f027c215d..9ae0d88829af 100644 --- a/tests/ui/question_mark.rs +++ b/tests/ui/question_mark.rs @@ -259,3 +259,12 @@ fn pattern() -> Result<(), PatternedError> { } fn main() {} + +// should not lint, `?` operator not available in const context +const fn issue9175(option: Option<()>) -> Option<()> { + if option.is_none() { + return None; + } + //stuff + Some(()) +} diff --git a/tests/ui/recursive_format_impl.rs b/tests/ui/recursive_format_impl.rs index cb6ba36b14c8..b92490b4c523 100644 --- a/tests/ui/recursive_format_impl.rs +++ b/tests/ui/recursive_format_impl.rs @@ -1,9 +1,10 @@ #![warn(clippy::recursive_format_impl)] #![allow( + clippy::borrow_deref_ref, + clippy::deref_addrof, clippy::inherent_to_string_shadow_display, clippy::to_string_in_format_args, - clippy::deref_addrof, - clippy::borrow_deref_ref + clippy::uninlined_format_args )] use std::fmt; diff --git a/tests/ui/recursive_format_impl.stderr b/tests/ui/recursive_format_impl.stderr index 84ce69df5669..8a58b9a3b178 100644 --- a/tests/ui/recursive_format_impl.stderr +++ b/tests/ui/recursive_format_impl.stderr @@ -1,5 +1,5 @@ error: using `self.to_string` in `fmt::Display` implementation will cause infinite recursion - --> $DIR/recursive_format_impl.rs:30:25 + --> $DIR/recursive_format_impl.rs:31:25 | LL | write!(f, "{}", self.to_string()) | ^^^^^^^^^^^^^^^^ @@ -7,7 +7,7 @@ LL | write!(f, "{}", self.to_string()) = note: `-D clippy::recursive-format-impl` implied by `-D warnings` error: using `self` as `Display` in `impl Display` will cause infinite recursion - --> $DIR/recursive_format_impl.rs:74:9 + --> $DIR/recursive_format_impl.rs:75:9 | LL | write!(f, "{}", self) | ^^^^^^^^^^^^^^^^^^^^^ @@ -15,7 +15,7 @@ LL | write!(f, "{}", self) = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) error: using `self` as `Display` in `impl Display` will cause infinite recursion - --> $DIR/recursive_format_impl.rs:83:9 + --> $DIR/recursive_format_impl.rs:84:9 | LL | write!(f, "{}", &self) | ^^^^^^^^^^^^^^^^^^^^^^ @@ -23,7 +23,7 @@ LL | write!(f, "{}", &self) = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) error: using `self` as `Debug` in `impl Debug` will cause infinite recursion - --> $DIR/recursive_format_impl.rs:89:9 + --> $DIR/recursive_format_impl.rs:90:9 | LL | write!(f, "{:?}", &self) | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -31,7 +31,7 @@ LL | write!(f, "{:?}", &self) = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) error: using `self` as `Display` in `impl Display` will cause infinite recursion - --> $DIR/recursive_format_impl.rs:98:9 + --> $DIR/recursive_format_impl.rs:99:9 | LL | write!(f, "{}", &&&self) | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -39,7 +39,7 @@ LL | write!(f, "{}", &&&self) = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) error: using `self` as `Display` in `impl Display` will cause infinite recursion - --> $DIR/recursive_format_impl.rs:172:9 + --> $DIR/recursive_format_impl.rs:173:9 | LL | write!(f, "{}", &*self) | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -47,7 +47,7 @@ LL | write!(f, "{}", &*self) = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) error: using `self` as `Debug` in `impl Debug` will cause infinite recursion - --> $DIR/recursive_format_impl.rs:178:9 + --> $DIR/recursive_format_impl.rs:179:9 | LL | write!(f, "{:?}", &*self) | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -55,7 +55,7 @@ LL | write!(f, "{:?}", &*self) = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) error: using `self` as `Display` in `impl Display` will cause infinite recursion - --> $DIR/recursive_format_impl.rs:194:9 + --> $DIR/recursive_format_impl.rs:195:9 | LL | write!(f, "{}", *self) | ^^^^^^^^^^^^^^^^^^^^^^ @@ -63,7 +63,7 @@ LL | write!(f, "{}", *self) = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) error: using `self` as `Display` in `impl Display` will cause infinite recursion - --> $DIR/recursive_format_impl.rs:210:9 + --> $DIR/recursive_format_impl.rs:211:9 | LL | write!(f, "{}", **&&*self) | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -71,7 +71,7 @@ LL | write!(f, "{}", **&&*self) = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) error: using `self` as `Display` in `impl Display` will cause infinite recursion - --> $DIR/recursive_format_impl.rs:226:9 + --> $DIR/recursive_format_impl.rs:227:9 | LL | write!(f, "{}", &&**&&*self) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/redundant_clone.fixed b/tests/ui/redundant_clone.fixed index da52c0acf93b..00b427450935 100644 --- a/tests/ui/redundant_clone.fixed +++ b/tests/ui/redundant_clone.fixed @@ -1,8 +1,8 @@ // run-rustfix // rustfix-only-machine-applicable - #![feature(lint_reasons)] -#![allow(clippy::implicit_clone, clippy::drop_non_drop)] +#![allow(clippy::drop_non_drop, clippy::implicit_clone, clippy::uninlined_format_args)] + use std::ffi::OsString; use std::path::Path; diff --git a/tests/ui/redundant_clone.rs b/tests/ui/redundant_clone.rs index 5867d019dbb7..f899127db8d0 100644 --- a/tests/ui/redundant_clone.rs +++ b/tests/ui/redundant_clone.rs @@ -1,8 +1,8 @@ // run-rustfix // rustfix-only-machine-applicable - #![feature(lint_reasons)] -#![allow(clippy::implicit_clone, clippy::drop_non_drop)] +#![allow(clippy::drop_non_drop, clippy::implicit_clone, clippy::uninlined_format_args)] + use std::ffi::OsString; use std::path::Path; diff --git a/tests/ui/redundant_pattern_matching_ipaddr.fixed b/tests/ui/redundant_pattern_matching_ipaddr.fixed index acc8de5f41ee..21bae909555c 100644 --- a/tests/ui/redundant_pattern_matching_ipaddr.fixed +++ b/tests/ui/redundant_pattern_matching_ipaddr.fixed @@ -1,8 +1,11 @@ // run-rustfix - -#![warn(clippy::all)] -#![warn(clippy::redundant_pattern_matching)] -#![allow(unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro)] +#![warn(clippy::all, clippy::redundant_pattern_matching)] +#![allow(unused_must_use)] +#![allow( + clippy::match_like_matches_macro, + clippy::needless_bool, + clippy::uninlined_format_args +)] use std::net::{ IpAddr::{self, V4, V6}, diff --git a/tests/ui/redundant_pattern_matching_ipaddr.rs b/tests/ui/redundant_pattern_matching_ipaddr.rs index 678d91ce93ac..4dd9171677ec 100644 --- a/tests/ui/redundant_pattern_matching_ipaddr.rs +++ b/tests/ui/redundant_pattern_matching_ipaddr.rs @@ -1,8 +1,11 @@ // run-rustfix - -#![warn(clippy::all)] -#![warn(clippy::redundant_pattern_matching)] -#![allow(unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro)] +#![warn(clippy::all, clippy::redundant_pattern_matching)] +#![allow(unused_must_use)] +#![allow( + clippy::match_like_matches_macro, + clippy::needless_bool, + clippy::uninlined_format_args +)] use std::net::{ IpAddr::{self, V4, V6}, diff --git a/tests/ui/redundant_pattern_matching_ipaddr.stderr b/tests/ui/redundant_pattern_matching_ipaddr.stderr index caf458cd862e..536b589de54c 100644 --- a/tests/ui/redundant_pattern_matching_ipaddr.stderr +++ b/tests/ui/redundant_pattern_matching_ipaddr.stderr @@ -1,5 +1,5 @@ error: redundant pattern matching, consider using `is_ipv4()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:14:12 + --> $DIR/redundant_pattern_matching_ipaddr.rs:17:12 | LL | if let V4(_) = &ipaddr {} | -------^^^^^---------- help: try this: `if ipaddr.is_ipv4()` @@ -7,31 +7,31 @@ LL | if let V4(_) = &ipaddr {} = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` error: redundant pattern matching, consider using `is_ipv4()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:16:12 + --> $DIR/redundant_pattern_matching_ipaddr.rs:19:12 | LL | if let V4(_) = V4(Ipv4Addr::LOCALHOST) {} | -------^^^^^-------------------------- help: try this: `if V4(Ipv4Addr::LOCALHOST).is_ipv4()` error: redundant pattern matching, consider using `is_ipv6()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:18:12 + --> $DIR/redundant_pattern_matching_ipaddr.rs:21:12 | LL | if let V6(_) = V6(Ipv6Addr::LOCALHOST) {} | -------^^^^^-------------------------- help: try this: `if V6(Ipv6Addr::LOCALHOST).is_ipv6()` error: redundant pattern matching, consider using `is_ipv4()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:20:15 + --> $DIR/redundant_pattern_matching_ipaddr.rs:23:15 | LL | while let V4(_) = V4(Ipv4Addr::LOCALHOST) {} | ----------^^^^^-------------------------- help: try this: `while V4(Ipv4Addr::LOCALHOST).is_ipv4()` error: redundant pattern matching, consider using `is_ipv6()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:22:15 + --> $DIR/redundant_pattern_matching_ipaddr.rs:25:15 | LL | while let V6(_) = V6(Ipv6Addr::LOCALHOST) {} | ----------^^^^^-------------------------- help: try this: `while V6(Ipv6Addr::LOCALHOST).is_ipv6()` error: redundant pattern matching, consider using `is_ipv4()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:32:5 + --> $DIR/redundant_pattern_matching_ipaddr.rs:35:5 | LL | / match V4(Ipv4Addr::LOCALHOST) { LL | | V4(_) => true, @@ -40,7 +40,7 @@ LL | | }; | |_____^ help: try this: `V4(Ipv4Addr::LOCALHOST).is_ipv4()` error: redundant pattern matching, consider using `is_ipv6()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:37:5 + --> $DIR/redundant_pattern_matching_ipaddr.rs:40:5 | LL | / match V4(Ipv4Addr::LOCALHOST) { LL | | V4(_) => false, @@ -49,7 +49,7 @@ LL | | }; | |_____^ help: try this: `V4(Ipv4Addr::LOCALHOST).is_ipv6()` error: redundant pattern matching, consider using `is_ipv6()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:42:5 + --> $DIR/redundant_pattern_matching_ipaddr.rs:45:5 | LL | / match V6(Ipv6Addr::LOCALHOST) { LL | | V4(_) => false, @@ -58,7 +58,7 @@ LL | | }; | |_____^ help: try this: `V6(Ipv6Addr::LOCALHOST).is_ipv6()` error: redundant pattern matching, consider using `is_ipv4()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:47:5 + --> $DIR/redundant_pattern_matching_ipaddr.rs:50:5 | LL | / match V6(Ipv6Addr::LOCALHOST) { LL | | V4(_) => true, @@ -67,49 +67,49 @@ LL | | }; | |_____^ help: try this: `V6(Ipv6Addr::LOCALHOST).is_ipv4()` error: redundant pattern matching, consider using `is_ipv4()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:52:20 + --> $DIR/redundant_pattern_matching_ipaddr.rs:55:20 | LL | let _ = if let V4(_) = V4(Ipv4Addr::LOCALHOST) { | -------^^^^^-------------------------- help: try this: `if V4(Ipv4Addr::LOCALHOST).is_ipv4()` error: redundant pattern matching, consider using `is_ipv4()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:60:20 + --> $DIR/redundant_pattern_matching_ipaddr.rs:63:20 | LL | let _ = if let V4(_) = gen_ipaddr() { | -------^^^^^--------------- help: try this: `if gen_ipaddr().is_ipv4()` error: redundant pattern matching, consider using `is_ipv6()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:62:19 + --> $DIR/redundant_pattern_matching_ipaddr.rs:65:19 | LL | } else if let V6(_) = gen_ipaddr() { | -------^^^^^--------------- help: try this: `if gen_ipaddr().is_ipv6()` error: redundant pattern matching, consider using `is_ipv4()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:74:12 + --> $DIR/redundant_pattern_matching_ipaddr.rs:77:12 | LL | if let V4(_) = V4(Ipv4Addr::LOCALHOST) {} | -------^^^^^-------------------------- help: try this: `if V4(Ipv4Addr::LOCALHOST).is_ipv4()` error: redundant pattern matching, consider using `is_ipv6()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:76:12 + --> $DIR/redundant_pattern_matching_ipaddr.rs:79:12 | LL | if let V6(_) = V6(Ipv6Addr::LOCALHOST) {} | -------^^^^^-------------------------- help: try this: `if V6(Ipv6Addr::LOCALHOST).is_ipv6()` error: redundant pattern matching, consider using `is_ipv4()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:78:15 + --> $DIR/redundant_pattern_matching_ipaddr.rs:81:15 | LL | while let V4(_) = V4(Ipv4Addr::LOCALHOST) {} | ----------^^^^^-------------------------- help: try this: `while V4(Ipv4Addr::LOCALHOST).is_ipv4()` error: redundant pattern matching, consider using `is_ipv6()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:80:15 + --> $DIR/redundant_pattern_matching_ipaddr.rs:83:15 | LL | while let V6(_) = V6(Ipv6Addr::LOCALHOST) {} | ----------^^^^^-------------------------- help: try this: `while V6(Ipv6Addr::LOCALHOST).is_ipv6()` error: redundant pattern matching, consider using `is_ipv4()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:82:5 + --> $DIR/redundant_pattern_matching_ipaddr.rs:85:5 | LL | / match V4(Ipv4Addr::LOCALHOST) { LL | | V4(_) => true, @@ -118,7 +118,7 @@ LL | | }; | |_____^ help: try this: `V4(Ipv4Addr::LOCALHOST).is_ipv4()` error: redundant pattern matching, consider using `is_ipv6()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:87:5 + --> $DIR/redundant_pattern_matching_ipaddr.rs:90:5 | LL | / match V6(Ipv6Addr::LOCALHOST) { LL | | V4(_) => false, diff --git a/tests/ui/redundant_pattern_matching_result.fixed b/tests/ui/redundant_pattern_matching_result.fixed index 83c783385efe..b88c5d0bec82 100644 --- a/tests/ui/redundant_pattern_matching_result.fixed +++ b/tests/ui/redundant_pattern_matching_result.fixed @@ -1,14 +1,13 @@ // run-rustfix - #![warn(clippy::all)] #![warn(clippy::redundant_pattern_matching)] +#![allow(deprecated, unused_must_use)] #![allow( - unused_must_use, - clippy::needless_bool, + clippy::if_same_then_else, clippy::match_like_matches_macro, - clippy::unnecessary_wraps, - deprecated, - clippy::if_same_then_else + clippy::needless_bool, + clippy::uninlined_format_args, + clippy::unnecessary_wraps )] fn main() { diff --git a/tests/ui/redundant_pattern_matching_result.rs b/tests/ui/redundant_pattern_matching_result.rs index e06d4485ae4f..5949cb2271c6 100644 --- a/tests/ui/redundant_pattern_matching_result.rs +++ b/tests/ui/redundant_pattern_matching_result.rs @@ -1,14 +1,13 @@ // run-rustfix - #![warn(clippy::all)] #![warn(clippy::redundant_pattern_matching)] +#![allow(deprecated, unused_must_use)] #![allow( - unused_must_use, - clippy::needless_bool, + clippy::if_same_then_else, clippy::match_like_matches_macro, - clippy::unnecessary_wraps, - deprecated, - clippy::if_same_then_else + clippy::needless_bool, + clippy::uninlined_format_args, + clippy::unnecessary_wraps )] fn main() { diff --git a/tests/ui/redundant_pattern_matching_result.stderr b/tests/ui/redundant_pattern_matching_result.stderr index d674d061e4dd..e6afe9eb78ea 100644 --- a/tests/ui/redundant_pattern_matching_result.stderr +++ b/tests/ui/redundant_pattern_matching_result.stderr @@ -1,5 +1,5 @@ error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:16:12 + --> $DIR/redundant_pattern_matching_result.rs:15:12 | LL | if let Ok(_) = &result {} | -------^^^^^---------- help: try this: `if result.is_ok()` @@ -7,31 +7,31 @@ LL | if let Ok(_) = &result {} = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:18:12 + --> $DIR/redundant_pattern_matching_result.rs:17:12 | LL | if let Ok(_) = Ok::(42) {} | -------^^^^^--------------------- help: try this: `if Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:20:12 + --> $DIR/redundant_pattern_matching_result.rs:19:12 | LL | if let Err(_) = Err::(42) {} | -------^^^^^^---------------------- help: try this: `if Err::(42).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:22:15 + --> $DIR/redundant_pattern_matching_result.rs:21:15 | LL | while let Ok(_) = Ok::(10) {} | ----------^^^^^--------------------- help: try this: `while Ok::(10).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:24:15 + --> $DIR/redundant_pattern_matching_result.rs:23:15 | LL | while let Err(_) = Ok::(10) {} | ----------^^^^^^--------------------- help: try this: `while Ok::(10).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:34:5 + --> $DIR/redundant_pattern_matching_result.rs:33:5 | LL | / match Ok::(42) { LL | | Ok(_) => true, @@ -40,7 +40,7 @@ LL | | }; | |_____^ help: try this: `Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:39:5 + --> $DIR/redundant_pattern_matching_result.rs:38:5 | LL | / match Ok::(42) { LL | | Ok(_) => false, @@ -49,7 +49,7 @@ LL | | }; | |_____^ help: try this: `Ok::(42).is_err()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:44:5 + --> $DIR/redundant_pattern_matching_result.rs:43:5 | LL | / match Err::(42) { LL | | Ok(_) => false, @@ -58,7 +58,7 @@ LL | | }; | |_____^ help: try this: `Err::(42).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:49:5 + --> $DIR/redundant_pattern_matching_result.rs:48:5 | LL | / match Err::(42) { LL | | Ok(_) => true, @@ -67,73 +67,73 @@ LL | | }; | |_____^ help: try this: `Err::(42).is_ok()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:54:20 + --> $DIR/redundant_pattern_matching_result.rs:53:20 | LL | let _ = if let Ok(_) = Ok::(4) { true } else { false }; | -------^^^^^--------------------- help: try this: `if Ok::(4).is_ok()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:60:20 + --> $DIR/redundant_pattern_matching_result.rs:59:20 | LL | let _ = if let Ok(_) = gen_res() { | -------^^^^^------------ help: try this: `if gen_res().is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:62:19 + --> $DIR/redundant_pattern_matching_result.rs:61:19 | LL | } else if let Err(_) = gen_res() { | -------^^^^^^------------ help: try this: `if gen_res().is_err()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_result.rs:85:19 + --> $DIR/redundant_pattern_matching_result.rs:84:19 | LL | while let Some(_) = r#try!(result_opt()) {} | ----------^^^^^^^----------------------- help: try this: `while (r#try!(result_opt())).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_result.rs:86:16 + --> $DIR/redundant_pattern_matching_result.rs:85:16 | LL | if let Some(_) = r#try!(result_opt()) {} | -------^^^^^^^----------------------- help: try this: `if (r#try!(result_opt())).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_result.rs:92:12 + --> $DIR/redundant_pattern_matching_result.rs:91:12 | LL | if let Some(_) = m!() {} | -------^^^^^^^------- help: try this: `if m!().is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_result.rs:93:15 + --> $DIR/redundant_pattern_matching_result.rs:92:15 | LL | while let Some(_) = m!() {} | ----------^^^^^^^------- help: try this: `while m!().is_some()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:111:12 + --> $DIR/redundant_pattern_matching_result.rs:110:12 | LL | if let Ok(_) = Ok::(42) {} | -------^^^^^--------------------- help: try this: `if Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:113:12 + --> $DIR/redundant_pattern_matching_result.rs:112:12 | LL | if let Err(_) = Err::(42) {} | -------^^^^^^---------------------- help: try this: `if Err::(42).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:115:15 + --> $DIR/redundant_pattern_matching_result.rs:114:15 | LL | while let Ok(_) = Ok::(10) {} | ----------^^^^^--------------------- help: try this: `while Ok::(10).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:117:15 + --> $DIR/redundant_pattern_matching_result.rs:116:15 | LL | while let Err(_) = Ok::(10) {} | ----------^^^^^^--------------------- help: try this: `while Ok::(10).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:119:5 + --> $DIR/redundant_pattern_matching_result.rs:118:5 | LL | / match Ok::(42) { LL | | Ok(_) => true, @@ -142,7 +142,7 @@ LL | | }; | |_____^ help: try this: `Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:124:5 + --> $DIR/redundant_pattern_matching_result.rs:123:5 | LL | / match Err::(42) { LL | | Ok(_) => false, diff --git a/tests/ui/result_map_unit_fn_fixable.fixed b/tests/ui/result_map_unit_fn_fixable.fixed index 14c331f67e73..d8b56237e983 100644 --- a/tests/ui/result_map_unit_fn_fixable.fixed +++ b/tests/ui/result_map_unit_fn_fixable.fixed @@ -1,7 +1,7 @@ // run-rustfix - #![warn(clippy::result_map_unit_fn)] #![allow(unused)] +#![allow(clippy::uninlined_format_args)] fn do_nothing(_: T) {} diff --git a/tests/ui/result_map_unit_fn_fixable.rs b/tests/ui/result_map_unit_fn_fixable.rs index 8b0fca9ece1a..44f50d21109c 100644 --- a/tests/ui/result_map_unit_fn_fixable.rs +++ b/tests/ui/result_map_unit_fn_fixable.rs @@ -1,7 +1,7 @@ // run-rustfix - #![warn(clippy::result_map_unit_fn)] #![allow(unused)] +#![allow(clippy::uninlined_format_args)] fn do_nothing(_: T) {} diff --git a/tests/ui/reversed_empty_ranges_fixable.fixed b/tests/ui/reversed_empty_ranges_fixable.fixed index 79e482eec303..c67edb36c67a 100644 --- a/tests/ui/reversed_empty_ranges_fixable.fixed +++ b/tests/ui/reversed_empty_ranges_fixable.fixed @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::reversed_empty_ranges)] +#![allow(clippy::uninlined_format_args)] const ANSWER: i32 = 42; diff --git a/tests/ui/reversed_empty_ranges_fixable.rs b/tests/ui/reversed_empty_ranges_fixable.rs index b2e8bf33771a..0a4fef5bfe87 100644 --- a/tests/ui/reversed_empty_ranges_fixable.rs +++ b/tests/ui/reversed_empty_ranges_fixable.rs @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::reversed_empty_ranges)] +#![allow(clippy::uninlined_format_args)] const ANSWER: i32 = 42; diff --git a/tests/ui/reversed_empty_ranges_fixable.stderr b/tests/ui/reversed_empty_ranges_fixable.stderr index 2d1bfe62c923..c2495ea95f97 100644 --- a/tests/ui/reversed_empty_ranges_fixable.stderr +++ b/tests/ui/reversed_empty_ranges_fixable.stderr @@ -1,5 +1,5 @@ error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_fixable.rs:9:5 + --> $DIR/reversed_empty_ranges_fixable.rs:10:5 | LL | (42..=21).for_each(|x| println!("{}", x)); | ^^^^^^^^^ @@ -11,7 +11,7 @@ LL | (21..=42).rev().for_each(|x| println!("{}", x)); | ~~~~~~~~~~~~~~~ error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_fixable.rs:10:13 + --> $DIR/reversed_empty_ranges_fixable.rs:11:13 | LL | let _ = (ANSWER..21).filter(|x| x % 2 == 0).take(10).collect::>(); | ^^^^^^^^^^^^ @@ -22,7 +22,7 @@ LL | let _ = (21..ANSWER).rev().filter(|x| x % 2 == 0).take(10).collect:: $DIR/reversed_empty_ranges_fixable.rs:12:14 + --> $DIR/reversed_empty_ranges_fixable.rs:13:14 | LL | for _ in -21..=-42 {} | ^^^^^^^^^ @@ -33,7 +33,7 @@ LL | for _ in (-42..=-21).rev() {} | ~~~~~~~~~~~~~~~~~ error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_fixable.rs:13:14 + --> $DIR/reversed_empty_ranges_fixable.rs:14:14 | LL | for _ in 42u32..21u32 {} | ^^^^^^^^^^^^ diff --git a/tests/ui/reversed_empty_ranges_loops_fixable.fixed b/tests/ui/reversed_empty_ranges_loops_fixable.fixed index f1503ed6d12f..78401e463d50 100644 --- a/tests/ui/reversed_empty_ranges_loops_fixable.fixed +++ b/tests/ui/reversed_empty_ranges_loops_fixable.fixed @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::reversed_empty_ranges)] +#![allow(clippy::uninlined_format_args)] fn main() { const MAX_LEN: usize = 42; diff --git a/tests/ui/reversed_empty_ranges_loops_fixable.rs b/tests/ui/reversed_empty_ranges_loops_fixable.rs index a733788dc22c..f9e0f7fcd6db 100644 --- a/tests/ui/reversed_empty_ranges_loops_fixable.rs +++ b/tests/ui/reversed_empty_ranges_loops_fixable.rs @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::reversed_empty_ranges)] +#![allow(clippy::uninlined_format_args)] fn main() { const MAX_LEN: usize = 42; diff --git a/tests/ui/reversed_empty_ranges_loops_fixable.stderr b/tests/ui/reversed_empty_ranges_loops_fixable.stderr index a135da488ffd..dfc52e64c751 100644 --- a/tests/ui/reversed_empty_ranges_loops_fixable.stderr +++ b/tests/ui/reversed_empty_ranges_loops_fixable.stderr @@ -1,5 +1,5 @@ error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_loops_fixable.rs:7:14 + --> $DIR/reversed_empty_ranges_loops_fixable.rs:8:14 | LL | for i in 10..0 { | ^^^^^ @@ -11,7 +11,7 @@ LL | for i in (0..10).rev() { | ~~~~~~~~~~~~~ error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_loops_fixable.rs:11:14 + --> $DIR/reversed_empty_ranges_loops_fixable.rs:12:14 | LL | for i in 10..=0 { | ^^^^^^ @@ -22,7 +22,7 @@ LL | for i in (0..=10).rev() { | ~~~~~~~~~~~~~~ error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_loops_fixable.rs:15:14 + --> $DIR/reversed_empty_ranges_loops_fixable.rs:16:14 | LL | for i in MAX_LEN..0 { | ^^^^^^^^^^ @@ -33,7 +33,7 @@ LL | for i in (0..MAX_LEN).rev() { | ~~~~~~~~~~~~~~~~~~ error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_loops_fixable.rs:34:14 + --> $DIR/reversed_empty_ranges_loops_fixable.rs:35:14 | LL | for i in (10..0).map(|x| x * 2) { | ^^^^^^^ @@ -44,7 +44,7 @@ LL | for i in (0..10).rev().map(|x| x * 2) { | ~~~~~~~~~~~~~ error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_loops_fixable.rs:39:14 + --> $DIR/reversed_empty_ranges_loops_fixable.rs:40:14 | LL | for i in 10..5 + 4 { | ^^^^^^^^^ @@ -55,7 +55,7 @@ LL | for i in (5 + 4..10).rev() { | ~~~~~~~~~~~~~~~~~ error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_loops_fixable.rs:43:14 + --> $DIR/reversed_empty_ranges_loops_fixable.rs:44:14 | LL | for i in (5 + 2)..(3 - 1) { | ^^^^^^^^^^^^^^^^ diff --git a/tests/ui/reversed_empty_ranges_loops_unfixable.rs b/tests/ui/reversed_empty_ranges_loops_unfixable.rs index c4c572244168..50264ef68cc0 100644 --- a/tests/ui/reversed_empty_ranges_loops_unfixable.rs +++ b/tests/ui/reversed_empty_ranges_loops_unfixable.rs @@ -1,4 +1,5 @@ #![warn(clippy::reversed_empty_ranges)] +#![allow(clippy::uninlined_format_args)] fn main() { for i in 5..5 { diff --git a/tests/ui/reversed_empty_ranges_loops_unfixable.stderr b/tests/ui/reversed_empty_ranges_loops_unfixable.stderr index 30095d20cfd4..4490ff35f5a6 100644 --- a/tests/ui/reversed_empty_ranges_loops_unfixable.stderr +++ b/tests/ui/reversed_empty_ranges_loops_unfixable.stderr @@ -1,5 +1,5 @@ error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_loops_unfixable.rs:4:14 + --> $DIR/reversed_empty_ranges_loops_unfixable.rs:5:14 | LL | for i in 5..5 { | ^^^^ @@ -7,7 +7,7 @@ LL | for i in 5..5 { = note: `-D clippy::reversed-empty-ranges` implied by `-D warnings` error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_loops_unfixable.rs:8:14 + --> $DIR/reversed_empty_ranges_loops_unfixable.rs:9:14 | LL | for i in (5 + 2)..(8 - 1) { | ^^^^^^^^^^^^^^^^ diff --git a/tests/ui/same_functions_in_if_condition.rs b/tests/ui/same_functions_in_if_condition.rs index a48829caac01..e6198a1bc9a0 100644 --- a/tests/ui/same_functions_in_if_condition.rs +++ b/tests/ui/same_functions_in_if_condition.rs @@ -1,8 +1,14 @@ #![feature(adt_const_params)] -#![allow(incomplete_features)] #![warn(clippy::same_functions_in_if_condition)] -#![allow(clippy::ifs_same_cond)] // This warning is different from `ifs_same_cond`. -#![allow(clippy::if_same_then_else, clippy::comparison_chain)] // all empty blocks +// ifs_same_cond warning is different from `ifs_same_cond`. +// clippy::if_same_then_else, clippy::comparison_chain -- all empty blocks +#![allow(incomplete_features)] +#![allow( + clippy::comparison_chain, + clippy::if_same_then_else, + clippy::ifs_same_cond, + clippy::uninlined_format_args +)] fn function() -> bool { true diff --git a/tests/ui/same_functions_in_if_condition.stderr b/tests/ui/same_functions_in_if_condition.stderr index 3901546cbd65..f352ade150ee 100644 --- a/tests/ui/same_functions_in_if_condition.stderr +++ b/tests/ui/same_functions_in_if_condition.stderr @@ -1,72 +1,72 @@ error: this `if` has the same function call as a previous `if` - --> $DIR/same_functions_in_if_condition.rs:31:15 + --> $DIR/same_functions_in_if_condition.rs:37:15 | LL | } else if function() { | ^^^^^^^^^^ | note: same as this - --> $DIR/same_functions_in_if_condition.rs:30:8 + --> $DIR/same_functions_in_if_condition.rs:36:8 | LL | if function() { | ^^^^^^^^^^ = note: `-D clippy::same-functions-in-if-condition` implied by `-D warnings` error: this `if` has the same function call as a previous `if` - --> $DIR/same_functions_in_if_condition.rs:36:15 + --> $DIR/same_functions_in_if_condition.rs:42:15 | LL | } else if fn_arg(a) { | ^^^^^^^^^ | note: same as this - --> $DIR/same_functions_in_if_condition.rs:35:8 + --> $DIR/same_functions_in_if_condition.rs:41:8 | LL | if fn_arg(a) { | ^^^^^^^^^ error: this `if` has the same function call as a previous `if` - --> $DIR/same_functions_in_if_condition.rs:41:15 + --> $DIR/same_functions_in_if_condition.rs:47:15 | LL | } else if obj.method() { | ^^^^^^^^^^^^ | note: same as this - --> $DIR/same_functions_in_if_condition.rs:40:8 + --> $DIR/same_functions_in_if_condition.rs:46:8 | LL | if obj.method() { | ^^^^^^^^^^^^ error: this `if` has the same function call as a previous `if` - --> $DIR/same_functions_in_if_condition.rs:46:15 + --> $DIR/same_functions_in_if_condition.rs:52:15 | LL | } else if obj.method_arg(a) { | ^^^^^^^^^^^^^^^^^ | note: same as this - --> $DIR/same_functions_in_if_condition.rs:45:8 + --> $DIR/same_functions_in_if_condition.rs:51:8 | LL | if obj.method_arg(a) { | ^^^^^^^^^^^^^^^^^ error: this `if` has the same function call as a previous `if` - --> $DIR/same_functions_in_if_condition.rs:53:15 + --> $DIR/same_functions_in_if_condition.rs:59:15 | LL | } else if v.pop().is_none() { | ^^^^^^^^^^^^^^^^^ | note: same as this - --> $DIR/same_functions_in_if_condition.rs:51:8 + --> $DIR/same_functions_in_if_condition.rs:57:8 | LL | if v.pop().is_none() { | ^^^^^^^^^^^^^^^^^ error: this `if` has the same function call as a previous `if` - --> $DIR/same_functions_in_if_condition.rs:58:15 + --> $DIR/same_functions_in_if_condition.rs:64:15 | LL | } else if v.len() == 42 { | ^^^^^^^^^^^^^ | note: same as this - --> $DIR/same_functions_in_if_condition.rs:56:8 + --> $DIR/same_functions_in_if_condition.rs:62:8 | LL | if v.len() == 42 { | ^^^^^^^^^^^^^ diff --git a/tests/ui/semicolon_if_nothing_returned.rs b/tests/ui/semicolon_if_nothing_returned.rs index c4dfbd9210e0..4ab7dbab59cf 100644 --- a/tests/ui/semicolon_if_nothing_returned.rs +++ b/tests/ui/semicolon_if_nothing_returned.rs @@ -1,5 +1,5 @@ #![warn(clippy::semicolon_if_nothing_returned)] -#![allow(clippy::redundant_closure)] +#![allow(clippy::redundant_closure, clippy::uninlined_format_args)] fn get_unit() {} diff --git a/tests/ui/should_impl_trait/method_list_1.stderr b/tests/ui/should_impl_trait/method_list_1.stderr index d2f41e3f934a..161dd66b086e 100644 --- a/tests/ui/should_impl_trait/method_list_1.stderr +++ b/tests/ui/should_impl_trait/method_list_1.stderr @@ -99,6 +99,16 @@ LL | | } | = help: consider implementing the trait `std::cmp::Ord` or choosing a less ambiguous method name +error: method `default` can be confused for the standard trait method `std::default::Default::default` + --> $DIR/method_list_1.rs:65:5 + | +LL | / pub fn default() -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::default::Default` or choosing a less ambiguous method name + error: method `deref` can be confused for the standard trait method `std::ops::Deref::deref` --> $DIR/method_list_1.rs:69:5 | @@ -139,5 +149,5 @@ LL | | } | = help: consider implementing the trait `std::ops::Drop` or choosing a less ambiguous method name -error: aborting due to 14 previous errors +error: aborting due to 15 previous errors diff --git a/tests/ui/significant_drop_in_scrutinee.rs b/tests/ui/significant_drop_in_scrutinee.rs index 84ecf1ea53ed..c65df9ece38c 100644 --- a/tests/ui/significant_drop_in_scrutinee.rs +++ b/tests/ui/significant_drop_in_scrutinee.rs @@ -1,11 +1,8 @@ // FIXME: Ideally these suggestions would be fixed via rustfix. Blocked by rust-lang/rust#53934 // // run-rustfix - #![warn(clippy::significant_drop_in_scrutinee)] -#![allow(clippy::single_match)] -#![allow(clippy::match_single_binding)] -#![allow(unused_assignments)] -#![allow(dead_code)] +#![allow(dead_code, unused_assignments)] +#![allow(clippy::match_single_binding, clippy::single_match, clippy::uninlined_format_args)] use std::num::ParseIntError; use std::ops::Deref; diff --git a/tests/ui/significant_drop_in_scrutinee.stderr b/tests/ui/significant_drop_in_scrutinee.stderr index f1ed808ba087..75063a8c987e 100644 --- a/tests/ui/significant_drop_in_scrutinee.stderr +++ b/tests/ui/significant_drop_in_scrutinee.stderr @@ -1,5 +1,5 @@ error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:59:11 + --> $DIR/significant_drop_in_scrutinee.rs:56:11 | LL | match mutex.lock().unwrap().foo() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -19,7 +19,7 @@ LL ~ match value { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:145:11 + --> $DIR/significant_drop_in_scrutinee.rs:142:11 | LL | match s.lock_m().get_the_value() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -38,7 +38,7 @@ LL ~ match value { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:166:11 + --> $DIR/significant_drop_in_scrutinee.rs:163:11 | LL | match s.lock_m_m().get_the_value() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -57,7 +57,7 @@ LL ~ match value { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:214:11 + --> $DIR/significant_drop_in_scrutinee.rs:211:11 | LL | match counter.temp_increment().len() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -73,7 +73,7 @@ LL ~ match value { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:237:16 + --> $DIR/significant_drop_in_scrutinee.rs:234:16 | LL | match (mutex1.lock().unwrap().s.len(), true) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -92,7 +92,7 @@ LL ~ match (value, true) { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:246:22 + --> $DIR/significant_drop_in_scrutinee.rs:243:22 | LL | match (true, mutex1.lock().unwrap().s.len(), true) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -111,7 +111,7 @@ LL ~ match (true, value, true) { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:256:16 + --> $DIR/significant_drop_in_scrutinee.rs:253:16 | LL | match (mutex1.lock().unwrap().s.len(), true, mutex2.lock().unwrap().s.len()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -132,7 +132,7 @@ LL ~ match (value, true, mutex2.lock().unwrap().s.len()) { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:256:54 + --> $DIR/significant_drop_in_scrutinee.rs:253:54 | LL | match (mutex1.lock().unwrap().s.len(), true, mutex2.lock().unwrap().s.len()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -153,7 +153,7 @@ LL ~ match (mutex1.lock().unwrap().s.len(), true, value) { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:267:15 + --> $DIR/significant_drop_in_scrutinee.rs:264:15 | LL | match mutex3.lock().unwrap().s.as_str() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -169,7 +169,7 @@ LL | }; = note: this might lead to deadlocks or other unexpected behavior error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:277:22 + --> $DIR/significant_drop_in_scrutinee.rs:274:22 | LL | match (true, mutex3.lock().unwrap().s.as_str()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -185,7 +185,7 @@ LL | }; = note: this might lead to deadlocks or other unexpected behavior error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:296:11 + --> $DIR/significant_drop_in_scrutinee.rs:293:11 | LL | match mutex.lock().unwrap().s.len() > 1 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -204,7 +204,7 @@ LL ~ match value { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:303:11 + --> $DIR/significant_drop_in_scrutinee.rs:300:11 | LL | match 1 < mutex.lock().unwrap().s.len() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -223,7 +223,7 @@ LL ~ match value { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:321:11 + --> $DIR/significant_drop_in_scrutinee.rs:318:11 | LL | match mutex1.lock().unwrap().s.len() < mutex2.lock().unwrap().s.len() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -244,7 +244,7 @@ LL ~ match value { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:332:11 + --> $DIR/significant_drop_in_scrutinee.rs:329:11 | LL | match mutex1.lock().unwrap().s.len() >= mutex2.lock().unwrap().s.len() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -265,7 +265,7 @@ LL ~ match value { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:367:11 + --> $DIR/significant_drop_in_scrutinee.rs:364:11 | LL | match get_mutex_guard().s.len() > 1 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -284,7 +284,7 @@ LL ~ match value { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:384:11 + --> $DIR/significant_drop_in_scrutinee.rs:381:11 | LL | match match i { | ___________^ @@ -316,7 +316,7 @@ LL ~ match value | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:410:11 + --> $DIR/significant_drop_in_scrutinee.rs:407:11 | LL | match if i > 1 { | ___________^ @@ -349,7 +349,7 @@ LL ~ match value | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:464:11 + --> $DIR/significant_drop_in_scrutinee.rs:461:11 | LL | match s.lock().deref().deref() { | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -367,7 +367,7 @@ LL ~ match value { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:492:11 + --> $DIR/significant_drop_in_scrutinee.rs:489:11 | LL | match s.lock().deref().deref() { | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -380,7 +380,7 @@ LL | }; = note: this might lead to deadlocks or other unexpected behavior error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:511:11 + --> $DIR/significant_drop_in_scrutinee.rs:508:11 | LL | match mutex.lock().unwrap().i = i { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -399,7 +399,7 @@ LL ~ match () { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:517:11 + --> $DIR/significant_drop_in_scrutinee.rs:514:11 | LL | match i = mutex.lock().unwrap().i { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -418,7 +418,7 @@ LL ~ match () { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:523:11 + --> $DIR/significant_drop_in_scrutinee.rs:520:11 | LL | match mutex.lock().unwrap().i += 1 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -437,7 +437,7 @@ LL ~ match () { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:529:11 + --> $DIR/significant_drop_in_scrutinee.rs:526:11 | LL | match i += mutex.lock().unwrap().i { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -456,7 +456,7 @@ LL ~ match () { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:592:11 + --> $DIR/significant_drop_in_scrutinee.rs:589:11 | LL | match rwlock.read().unwrap().to_number() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -467,7 +467,7 @@ LL | }; = note: this might lead to deadlocks or other unexpected behavior error: temporary with significant `Drop` in `for` loop condition will live until the end of the `for` expression - --> $DIR/significant_drop_in_scrutinee.rs:602:14 + --> $DIR/significant_drop_in_scrutinee.rs:599:14 | LL | for s in rwlock.read().unwrap().iter() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -478,7 +478,7 @@ LL | } = note: this might lead to deadlocks or other unexpected behavior error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:617:11 + --> $DIR/significant_drop_in_scrutinee.rs:614:11 | LL | match mutex.lock().unwrap().foo() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/single_match.rs b/tests/ui/single_match.rs index dd148edf5292..d0c9b7b5663e 100644 --- a/tests/ui/single_match.rs +++ b/tests/ui/single_match.rs @@ -1,4 +1,5 @@ #![warn(clippy::single_match)] +#![allow(clippy::uninlined_format_args)] fn dummy() {} diff --git a/tests/ui/single_match.stderr b/tests/ui/single_match.stderr index 4d2b9ec5f903..7cecc1b73950 100644 --- a/tests/ui/single_match.stderr +++ b/tests/ui/single_match.stderr @@ -1,5 +1,5 @@ error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:8:5 + --> $DIR/single_match.rs:9:5 | LL | / match x { LL | | Some(y) => { @@ -18,7 +18,7 @@ LL ~ }; | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:16:5 + --> $DIR/single_match.rs:17:5 | LL | / match x { LL | | // Note the missing block braces. @@ -30,7 +30,7 @@ LL | | } | |_____^ help: try this: `if let Some(y) = x { println!("{:?}", y) }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:25:5 + --> $DIR/single_match.rs:26:5 | LL | / match z { LL | | (2..=3, 7..=9) => dummy(), @@ -39,7 +39,7 @@ LL | | }; | |_____^ help: try this: `if let (2..=3, 7..=9) = z { dummy() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:54:5 + --> $DIR/single_match.rs:55:5 | LL | / match x { LL | | Some(y) => dummy(), @@ -48,7 +48,7 @@ LL | | }; | |_____^ help: try this: `if let Some(y) = x { dummy() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:59:5 + --> $DIR/single_match.rs:60:5 | LL | / match y { LL | | Ok(y) => dummy(), @@ -57,7 +57,7 @@ LL | | }; | |_____^ help: try this: `if let Ok(y) = y { dummy() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:66:5 + --> $DIR/single_match.rs:67:5 | LL | / match c { LL | | Cow::Borrowed(..) => dummy(), @@ -66,7 +66,7 @@ LL | | }; | |_____^ help: try this: `if let Cow::Borrowed(..) = c { dummy() }` error: you seem to be trying to use `match` for an equality check. Consider using `if` - --> $DIR/single_match.rs:87:5 + --> $DIR/single_match.rs:88:5 | LL | / match x { LL | | "test" => println!(), @@ -75,7 +75,7 @@ LL | | } | |_____^ help: try this: `if x == "test" { println!() }` error: you seem to be trying to use `match` for an equality check. Consider using `if` - --> $DIR/single_match.rs:100:5 + --> $DIR/single_match.rs:101:5 | LL | / match x { LL | | Foo::A => println!(), @@ -84,7 +84,7 @@ LL | | } | |_____^ help: try this: `if x == Foo::A { println!() }` error: you seem to be trying to use `match` for an equality check. Consider using `if` - --> $DIR/single_match.rs:106:5 + --> $DIR/single_match.rs:107:5 | LL | / match x { LL | | FOO_C => println!(), @@ -93,7 +93,7 @@ LL | | } | |_____^ help: try this: `if x == FOO_C { println!() }` error: you seem to be trying to use `match` for an equality check. Consider using `if` - --> $DIR/single_match.rs:111:5 + --> $DIR/single_match.rs:112:5 | LL | / match &&x { LL | | Foo::A => println!(), @@ -102,7 +102,7 @@ LL | | } | |_____^ help: try this: `if x == Foo::A { println!() }` error: you seem to be trying to use `match` for an equality check. Consider using `if` - --> $DIR/single_match.rs:117:5 + --> $DIR/single_match.rs:118:5 | LL | / match &x { LL | | Foo::A => println!(), @@ -111,7 +111,7 @@ LL | | } | |_____^ help: try this: `if x == &Foo::A { println!() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:134:5 + --> $DIR/single_match.rs:135:5 | LL | / match x { LL | | Bar::A => println!(), @@ -120,7 +120,7 @@ LL | | } | |_____^ help: try this: `if let Bar::A = x { println!() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:142:5 + --> $DIR/single_match.rs:143:5 | LL | / match x { LL | | None => println!(), @@ -129,7 +129,7 @@ LL | | }; | |_____^ help: try this: `if let None = x { println!() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:164:5 + --> $DIR/single_match.rs:165:5 | LL | / match x { LL | | (Some(_), _) => {}, @@ -138,7 +138,7 @@ LL | | } | |_____^ help: try this: `if let (Some(_), _) = x {}` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:170:5 + --> $DIR/single_match.rs:171:5 | LL | / match x { LL | | (Some(E::V), _) => todo!(), @@ -147,7 +147,7 @@ LL | | } | |_____^ help: try this: `if let (Some(E::V), _) = x { todo!() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:176:5 + --> $DIR/single_match.rs:177:5 | LL | / match (Some(42), Some(E::V), Some(42)) { LL | | (.., Some(E::V), _) => {}, diff --git a/tests/ui/single_match_else.rs b/tests/ui/single_match_else.rs index 70d6febb71f9..5d03f77e9326 100644 --- a/tests/ui/single_match_else.rs +++ b/tests/ui/single_match_else.rs @@ -1,8 +1,6 @@ // aux-build: proc_macro_with_span.rs - #![warn(clippy::single_match_else)] -#![allow(clippy::needless_return)] -#![allow(clippy::no_effect)] +#![allow(clippy::needless_return, clippy::no_effect, clippy::uninlined_format_args)] extern crate proc_macro_with_span; use proc_macro_with_span::with_span; diff --git a/tests/ui/single_match_else.stderr b/tests/ui/single_match_else.stderr index 38fd9c6a6782..62876a55dc61 100644 --- a/tests/ui/single_match_else.stderr +++ b/tests/ui/single_match_else.stderr @@ -1,5 +1,5 @@ error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match_else.rs:19:13 + --> $DIR/single_match_else.rs:17:13 | LL | let _ = match ExprNode::Butterflies { | _____________^ @@ -21,7 +21,7 @@ LL ~ }; | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match_else.rs:84:5 + --> $DIR/single_match_else.rs:82:5 | LL | / match Some(1) { LL | | Some(a) => println!("${:?}", a), @@ -41,7 +41,7 @@ LL + } | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match_else.rs:93:5 + --> $DIR/single_match_else.rs:91:5 | LL | / match Some(1) { LL | | Some(a) => println!("${:?}", a), @@ -61,7 +61,7 @@ LL + } | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match_else.rs:103:5 + --> $DIR/single_match_else.rs:101:5 | LL | / match Result::::Ok(1) { LL | | Ok(a) => println!("${:?}", a), @@ -81,7 +81,7 @@ LL + } | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match_else.rs:112:5 + --> $DIR/single_match_else.rs:110:5 | LL | / match Cow::from("moo") { LL | | Cow::Owned(a) => println!("${:?}", a), diff --git a/tests/ui/std_instead_of_core.rs b/tests/ui/std_instead_of_core.rs index 6b27475de4c8..75b114ba0aed 100644 --- a/tests/ui/std_instead_of_core.rs +++ b/tests/ui/std_instead_of_core.rs @@ -24,6 +24,12 @@ fn std_instead_of_core() { let cell_absolute = ::std::cell::Cell::new(8u32); let _ = std::env!("PATH"); + + // do not lint until `error_in_core` is stable + use std::error::Error; + + // lint items re-exported from private modules, `core::iter::traits::iterator::Iterator` + use std::iter::Iterator; } #[warn(clippy::std_instead_of_alloc)] diff --git a/tests/ui/std_instead_of_core.stderr b/tests/ui/std_instead_of_core.stderr index 8138ccb82a00..d2102497350b 100644 --- a/tests/ui/std_instead_of_core.stderr +++ b/tests/ui/std_instead_of_core.stderr @@ -63,9 +63,17 @@ LL | let cell_absolute = ::std::cell::Cell::new(8u32); | = help: consider importing the item from `core` -error: used import from `std` instead of `alloc` +error: used import from `std` instead of `core` --> $DIR/std_instead_of_core.rs:32:9 | +LL | use std::iter::Iterator; + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider importing the item from `core` + +error: used import from `std` instead of `alloc` + --> $DIR/std_instead_of_core.rs:38:9 + | LL | use std::vec; | ^^^^^^^^ | @@ -73,7 +81,7 @@ LL | use std::vec; = note: `-D clippy::std-instead-of-alloc` implied by `-D warnings` error: used import from `std` instead of `alloc` - --> $DIR/std_instead_of_core.rs:33:9 + --> $DIR/std_instead_of_core.rs:39:9 | LL | use std::vec::Vec; | ^^^^^^^^^^^^^ @@ -81,7 +89,7 @@ LL | use std::vec::Vec; = help: consider importing the item from `alloc` error: used import from `alloc` instead of `core` - --> $DIR/std_instead_of_core.rs:38:9 + --> $DIR/std_instead_of_core.rs:44:9 | LL | use alloc::slice::from_ref; | ^^^^^^^^^^^^^^^^^^^^^^ @@ -89,5 +97,5 @@ LL | use alloc::slice::from_ref; = help: consider importing the item from `core` = note: `-D clippy::alloc-instead-of-core` implied by `-D warnings` -error: aborting due to 11 previous errors +error: aborting due to 12 previous errors diff --git a/tests/ui/toplevel_ref_arg.fixed b/tests/ui/toplevel_ref_arg.fixed index b129d95c5602..09fb66ca37e0 100644 --- a/tests/ui/toplevel_ref_arg.fixed +++ b/tests/ui/toplevel_ref_arg.fixed @@ -1,7 +1,7 @@ // run-rustfix // aux-build:macro_rules.rs - #![warn(clippy::toplevel_ref_arg)] +#![allow(clippy::uninlined_format_args)] #[macro_use] extern crate macro_rules; diff --git a/tests/ui/toplevel_ref_arg.rs b/tests/ui/toplevel_ref_arg.rs index 73eb4ff7306f..9d1f2f810983 100644 --- a/tests/ui/toplevel_ref_arg.rs +++ b/tests/ui/toplevel_ref_arg.rs @@ -1,7 +1,7 @@ // run-rustfix // aux-build:macro_rules.rs - #![warn(clippy::toplevel_ref_arg)] +#![allow(clippy::uninlined_format_args)] #[macro_use] extern crate macro_rules; diff --git a/tests/ui/trivially_copy_pass_by_ref.rs b/tests/ui/trivially_copy_pass_by_ref.rs index c0c64ebcabfb..af4f3b18443b 100644 --- a/tests/ui/trivially_copy_pass_by_ref.rs +++ b/tests/ui/trivially_copy_pass_by_ref.rs @@ -1,8 +1,11 @@ // normalize-stderr-test "\(\d+ byte\)" -> "(N byte)" // normalize-stderr-test "\(limit: \d+ byte\)" -> "(limit: N byte)" - #![deny(clippy::trivially_copy_pass_by_ref)] -#![allow(clippy::disallowed_names, clippy::redundant_field_names)] +#![allow( + clippy::disallowed_names, + clippy::redundant_field_names, + clippy::uninlined_format_args +)] #[derive(Copy, Clone)] struct Foo(u32); diff --git a/tests/ui/trivially_copy_pass_by_ref.stderr b/tests/ui/trivially_copy_pass_by_ref.stderr index 66ecb3d8e77a..6a8eca965534 100644 --- a/tests/ui/trivially_copy_pass_by_ref.stderr +++ b/tests/ui/trivially_copy_pass_by_ref.stderr @@ -1,113 +1,113 @@ error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:47:11 + --> $DIR/trivially_copy_pass_by_ref.rs:50:11 | LL | fn bad(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `u32` | note: the lint level is defined here - --> $DIR/trivially_copy_pass_by_ref.rs:4:9 + --> $DIR/trivially_copy_pass_by_ref.rs:3:9 | LL | #![deny(clippy::trivially_copy_pass_by_ref)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:47:20 + --> $DIR/trivially_copy_pass_by_ref.rs:50:20 | LL | fn bad(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Foo` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:47:29 + --> $DIR/trivially_copy_pass_by_ref.rs:50:29 | LL | fn bad(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Baz` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:54:12 + --> $DIR/trivially_copy_pass_by_ref.rs:57:12 | LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {} | ^^^^^ help: consider passing by value instead: `self` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:54:22 + --> $DIR/trivially_copy_pass_by_ref.rs:57:22 | LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `u32` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:54:31 + --> $DIR/trivially_copy_pass_by_ref.rs:57:31 | LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Foo` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:54:40 + --> $DIR/trivially_copy_pass_by_ref.rs:57:40 | LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Baz` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:56:16 + --> $DIR/trivially_copy_pass_by_ref.rs:59:16 | LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `u32` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:56:25 + --> $DIR/trivially_copy_pass_by_ref.rs:59:25 | LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Foo` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:56:34 + --> $DIR/trivially_copy_pass_by_ref.rs:59:34 | LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Baz` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:58:35 + --> $DIR/trivially_copy_pass_by_ref.rs:61:35 | LL | fn bad_issue7518(self, other: &Self) {} | ^^^^^ help: consider passing by value instead: `Self` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:70:16 + --> $DIR/trivially_copy_pass_by_ref.rs:73:16 | LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `u32` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:70:25 + --> $DIR/trivially_copy_pass_by_ref.rs:73:25 | LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Foo` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:70:34 + --> $DIR/trivially_copy_pass_by_ref.rs:73:34 | LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Baz` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:74:34 + --> $DIR/trivially_copy_pass_by_ref.rs:77:34 | LL | fn trait_method(&self, _foo: &Foo); | ^^^^ help: consider passing by value instead: `Foo` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:106:21 + --> $DIR/trivially_copy_pass_by_ref.rs:109:21 | LL | fn foo_never(x: &i32) { | ^^^^ help: consider passing by value instead: `i32` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:111:15 + --> $DIR/trivially_copy_pass_by_ref.rs:114:15 | LL | fn foo(x: &i32) { | ^^^^ help: consider passing by value instead: `i32` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:138:37 + --> $DIR/trivially_copy_pass_by_ref.rs:141:37 | LL | fn _unrelated_lifetimes<'a, 'b>(_x: &'a u32, y: &'b u32) -> &'b u32 { | ^^^^^^^ help: consider passing by value instead: `u32` diff --git a/tests/ui/uninit_vec.rs b/tests/ui/uninit_vec.rs index dc150cf28f2c..194e4fc157ef 100644 --- a/tests/ui/uninit_vec.rs +++ b/tests/ui/uninit_vec.rs @@ -91,4 +91,10 @@ fn main() { vec1.set_len(200); vec2.set_len(200); } + + // set_len(0) should not be detected + let mut vec: Vec = Vec::with_capacity(1000); + unsafe { + vec.set_len(0); + } } diff --git a/tests/ui/uninlined_format_args.fixed b/tests/ui/uninlined_format_args.fixed new file mode 100644 index 000000000000..dcf10ed60a25 --- /dev/null +++ b/tests/ui/uninlined_format_args.fixed @@ -0,0 +1,164 @@ +// aux-build:proc_macro_with_span.rs +// run-rustfix +#![feature(custom_inner_attributes)] +#![warn(clippy::uninlined_format_args)] +#![allow(named_arguments_used_positionally, unused_imports, unused_macros, unused_variables)] +#![allow(clippy::eq_op, clippy::format_in_format_args, clippy::print_literal)] + +extern crate proc_macro_with_span; +use proc_macro_with_span::with_span; + +macro_rules! no_param_str { + () => { + "{}" + }; +} + +macro_rules! my_println { + ($($args:tt),*) => {{ + println!($($args),*) + }}; +} + +macro_rules! my_println_args { + ($($args:tt),*) => {{ + println!("foo: {}", format_args!($($args),*)) + }}; +} + +fn tester(fn_arg: i32) { + let local_i32 = 1; + let local_f64 = 2.0; + let local_opt: Option = Some(3); + let width = 4; + let prec = 5; + let val = 6; + + // make sure this file hasn't been corrupted with tabs converted to spaces + // let _ = ' '; // <- this is a single tab character + let _: &[u8; 3] = b" "; // <- + + println!("val='{local_i32}'"); + println!("val='{local_i32}'"); // 3 spaces + println!("val='{local_i32}'"); // tab + println!("val='{local_i32}'"); // space+tab + println!("val='{local_i32}'"); // tab+space + println!( + "val='{local_i32}'" + ); + println!("{local_i32}"); + println!("{fn_arg}"); + println!("{local_i32:?}"); + println!("{local_i32:#?}"); + println!("{local_i32:4}"); + println!("{local_i32:04}"); + println!("{local_i32:<3}"); + println!("{local_i32:#010x}"); + println!("{local_f64:.1}"); + println!("Hello {} is {local_f64:.local_i32$}", "x"); + println!("Hello {local_i32} is {local_f64:.*}", 5); + println!("Hello {local_i32} is {local_f64:.*}", 5); + println!("{local_i32} {local_f64}"); + println!("{local_i32}, {}", local_opt.unwrap()); + println!("{val}"); + println!("{val}"); + println!("{} {1}", local_i32, 42); + println!("val='{local_i32}'"); + println!("val='{local_i32}'"); + println!("val='{local_i32}'"); + println!("val='{fn_arg}'"); + println!("{local_i32}"); + println!("{local_i32:?}"); + println!("{local_i32:#?}"); + println!("{local_i32:04}"); + println!("{local_i32:<3}"); + println!("{local_i32:#010x}"); + println!("{local_f64:.1}"); + println!("{local_i32} {local_i32}"); + println!("{local_f64} {local_i32} {local_i32} {local_f64}"); + println!("{local_i32} {local_f64}"); + println!("{local_f64} {local_i32}"); + println!("{local_f64} {local_i32} {local_f64} {local_i32}"); + println!("{1} {0}", "str", local_i32); + println!("{local_i32}"); + println!("{local_i32:width$}"); + println!("{local_i32:width$}"); + println!("{local_i32:.prec$}"); + println!("{local_i32:.prec$}"); + println!("{val:val$}"); + println!("{val:val$}"); + println!("{val:val$.val$}"); + println!("{val:val$.val$}"); + println!("{val:val$.val$}"); + println!("{val:val$.val$}"); + println!("{val:val$.val$}"); + println!("{val:val$.val$}"); + println!("{val:val$.val$}"); + println!("{val:val$.val$}"); + println!("{width:width$}"); + println!("{local_i32:width$}"); + println!("{width:width$}"); + println!("{local_i32:width$}"); + println!("{prec:.prec$}"); + println!("{local_i32:.prec$}"); + println!("{prec:.prec$}"); + println!("{local_i32:.prec$}"); + println!("{width:width$.prec$}"); + println!("{width:width$.prec$}"); + println!("{local_f64:width$.prec$}"); + println!("{local_f64:width$.prec$} {local_f64} {width} {prec}"); + println!( + "{local_i32:width$.prec$} {local_i32:prec$.width$} {width:local_i32$.prec$} {width:prec$.local_i32$} {prec:local_i32$.width$} {prec:width$.local_i32$}", + ); + println!( + "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$} {3}", + local_i32, + width, + prec, + 1 + 2 + ); + println!("Width = {local_i32}, value with width = {local_f64:local_i32$}"); + println!("{local_i32:width$.prec$}"); + println!("{width:width$.prec$}"); + println!("{}", format!("{local_i32}")); + my_println!("{}", local_i32); + my_println_args!("{}", local_i32); + + // these should NOT be modified by the lint + println!(concat!("nope ", "{}"), local_i32); + println!("val='{local_i32}'"); + println!("val='{local_i32 }'"); + println!("val='{local_i32 }'"); // with tab + println!("val='{local_i32\n}'"); + println!("{}", usize::MAX); + println!("{}", local_opt.unwrap()); + println!( + "val='{local_i32 + }'" + ); + println!(no_param_str!(), local_i32); + + println!( + "{val}", + ); + println!("{val}"); + + println!(with_span!("{0} {1}" "{1} {0}"), local_i32, local_f64); + println!("{}", with_span!(span val)); +} + +fn main() { + tester(42); +} + +fn _under_msrv() { + #![clippy::msrv = "1.57"] + let local_i32 = 1; + println!("don't expand='{}'", local_i32); +} + +fn _meets_msrv() { + #![clippy::msrv = "1.58"] + let local_i32 = 1; + println!("expand='{local_i32}'"); +} diff --git a/tests/ui/uninlined_format_args.rs b/tests/ui/uninlined_format_args.rs new file mode 100644 index 000000000000..924191f4324c --- /dev/null +++ b/tests/ui/uninlined_format_args.rs @@ -0,0 +1,169 @@ +// aux-build:proc_macro_with_span.rs +// run-rustfix +#![feature(custom_inner_attributes)] +#![warn(clippy::uninlined_format_args)] +#![allow(named_arguments_used_positionally, unused_imports, unused_macros, unused_variables)] +#![allow(clippy::eq_op, clippy::format_in_format_args, clippy::print_literal)] + +extern crate proc_macro_with_span; +use proc_macro_with_span::with_span; + +macro_rules! no_param_str { + () => { + "{}" + }; +} + +macro_rules! my_println { + ($($args:tt),*) => {{ + println!($($args),*) + }}; +} + +macro_rules! my_println_args { + ($($args:tt),*) => {{ + println!("foo: {}", format_args!($($args),*)) + }}; +} + +fn tester(fn_arg: i32) { + let local_i32 = 1; + let local_f64 = 2.0; + let local_opt: Option = Some(3); + let width = 4; + let prec = 5; + let val = 6; + + // make sure this file hasn't been corrupted with tabs converted to spaces + // let _ = ' '; // <- this is a single tab character + let _: &[u8; 3] = b" "; // <- + + println!("val='{}'", local_i32); + println!("val='{ }'", local_i32); // 3 spaces + println!("val='{ }'", local_i32); // tab + println!("val='{ }'", local_i32); // space+tab + println!("val='{ }'", local_i32); // tab+space + println!( + "val='{ + }'", + local_i32 + ); + println!("{}", local_i32); + println!("{}", fn_arg); + println!("{:?}", local_i32); + println!("{:#?}", local_i32); + println!("{:4}", local_i32); + println!("{:04}", local_i32); + println!("{:<3}", local_i32); + println!("{:#010x}", local_i32); + println!("{:.1}", local_f64); + println!("Hello {} is {:.*}", "x", local_i32, local_f64); + println!("Hello {} is {:.*}", local_i32, 5, local_f64); + println!("Hello {} is {2:.*}", local_i32, 5, local_f64); + println!("{} {}", local_i32, local_f64); + println!("{}, {}", local_i32, local_opt.unwrap()); + println!("{}", val); + println!("{}", v = val); + println!("{} {1}", local_i32, 42); + println!("val='{\t }'", local_i32); + println!("val='{\n }'", local_i32); + println!("val='{local_i32}'", local_i32 = local_i32); + println!("val='{local_i32}'", local_i32 = fn_arg); + println!("{0}", local_i32); + println!("{0:?}", local_i32); + println!("{0:#?}", local_i32); + println!("{0:04}", local_i32); + println!("{0:<3}", local_i32); + println!("{0:#010x}", local_i32); + println!("{0:.1}", local_f64); + println!("{0} {0}", local_i32); + println!("{1} {} {0} {}", local_i32, local_f64); + println!("{0} {1}", local_i32, local_f64); + println!("{1} {0}", local_i32, local_f64); + println!("{1} {0} {1} {0}", local_i32, local_f64); + println!("{1} {0}", "str", local_i32); + println!("{v}", v = local_i32); + println!("{local_i32:0$}", width); + println!("{local_i32:w$}", w = width); + println!("{local_i32:.0$}", prec); + println!("{local_i32:.p$}", p = prec); + println!("{:0$}", v = val); + println!("{0:0$}", v = val); + println!("{:0$.0$}", v = val); + println!("{0:0$.0$}", v = val); + println!("{0:0$.v$}", v = val); + println!("{0:v$.0$}", v = val); + println!("{v:0$.0$}", v = val); + println!("{v:v$.0$}", v = val); + println!("{v:0$.v$}", v = val); + println!("{v:v$.v$}", v = val); + println!("{:0$}", width); + println!("{:1$}", local_i32, width); + println!("{:w$}", w = width); + println!("{:w$}", local_i32, w = width); + println!("{:.0$}", prec); + println!("{:.1$}", local_i32, prec); + println!("{:.p$}", p = prec); + println!("{:.p$}", local_i32, p = prec); + println!("{:0$.1$}", width, prec); + println!("{:0$.w$}", width, w = prec); + println!("{:1$.2$}", local_f64, width, prec); + println!("{:1$.2$} {0} {1} {2}", local_f64, width, prec); + println!( + "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}", + local_i32, width, prec, + ); + println!( + "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$} {3}", + local_i32, + width, + prec, + 1 + 2 + ); + println!("Width = {}, value with width = {:0$}", local_i32, local_f64); + println!("{:w$.p$}", local_i32, w = width, p = prec); + println!("{:w$.p$}", w = width, p = prec); + println!("{}", format!("{}", local_i32)); + my_println!("{}", local_i32); + my_println_args!("{}", local_i32); + + // these should NOT be modified by the lint + println!(concat!("nope ", "{}"), local_i32); + println!("val='{local_i32}'"); + println!("val='{local_i32 }'"); + println!("val='{local_i32 }'"); // with tab + println!("val='{local_i32\n}'"); + println!("{}", usize::MAX); + println!("{}", local_opt.unwrap()); + println!( + "val='{local_i32 + }'" + ); + println!(no_param_str!(), local_i32); + + println!( + "{}", + // comment with a comma , in it + val, + ); + println!("{}", /* comment with a comma , in it */ val); + + println!(with_span!("{0} {1}" "{1} {0}"), local_i32, local_f64); + println!("{}", with_span!(span val)); +} + +fn main() { + tester(42); +} + +fn _under_msrv() { + #![clippy::msrv = "1.57"] + let local_i32 = 1; + println!("don't expand='{}'", local_i32); +} + +fn _meets_msrv() { + #![clippy::msrv = "1.58"] + let local_i32 = 1; + println!("expand='{}'", local_i32); +} diff --git a/tests/ui/uninlined_format_args.stderr b/tests/ui/uninlined_format_args.stderr new file mode 100644 index 000000000000..1b4dada28dac --- /dev/null +++ b/tests/ui/uninlined_format_args.stderr @@ -0,0 +1,894 @@ +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:41:5 + | +LL | println!("val='{}'", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::uninlined-format-args` implied by `-D warnings` +help: change this to + | +LL - println!("val='{}'", local_i32); +LL + println!("val='{local_i32}'"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:42:5 + | +LL | println!("val='{ }'", local_i32); // 3 spaces + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("val='{ }'", local_i32); // 3 spaces +LL + println!("val='{local_i32}'"); // 3 spaces + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:43:5 + | +LL | println!("val='{ }'", local_i32); // tab + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("val='{ }'", local_i32); // tab +LL + println!("val='{local_i32}'"); // tab + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:44:5 + | +LL | println!("val='{ }'", local_i32); // space+tab + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("val='{ }'", local_i32); // space+tab +LL + println!("val='{local_i32}'"); // space+tab + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:45:5 + | +LL | println!("val='{ }'", local_i32); // tab+space + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("val='{ }'", local_i32); // tab+space +LL + println!("val='{local_i32}'"); // tab+space + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:46:5 + | +LL | / println!( +LL | | "val='{ +LL | | }'", +LL | | local_i32 +LL | | ); + | |_____^ + | +help: change this to + | +LL - "val='{ +LL + "val='{local_i32}'" + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:51:5 + | +LL | println!("{}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{}", local_i32); +LL + println!("{local_i32}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:52:5 + | +LL | println!("{}", fn_arg); + | ^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{}", fn_arg); +LL + println!("{fn_arg}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:53:5 + | +LL | println!("{:?}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:?}", local_i32); +LL + println!("{local_i32:?}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:54:5 + | +LL | println!("{:#?}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:#?}", local_i32); +LL + println!("{local_i32:#?}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:55:5 + | +LL | println!("{:4}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:4}", local_i32); +LL + println!("{local_i32:4}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:56:5 + | +LL | println!("{:04}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:04}", local_i32); +LL + println!("{local_i32:04}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:57:5 + | +LL | println!("{:<3}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:<3}", local_i32); +LL + println!("{local_i32:<3}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:58:5 + | +LL | println!("{:#010x}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:#010x}", local_i32); +LL + println!("{local_i32:#010x}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:59:5 + | +LL | println!("{:.1}", local_f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:.1}", local_f64); +LL + println!("{local_f64:.1}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:60:5 + | +LL | println!("Hello {} is {:.*}", "x", local_i32, local_f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("Hello {} is {:.*}", "x", local_i32, local_f64); +LL + println!("Hello {} is {local_f64:.local_i32$}", "x"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:61:5 + | +LL | println!("Hello {} is {:.*}", local_i32, 5, local_f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("Hello {} is {:.*}", local_i32, 5, local_f64); +LL + println!("Hello {local_i32} is {local_f64:.*}", 5); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:62:5 + | +LL | println!("Hello {} is {2:.*}", local_i32, 5, local_f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("Hello {} is {2:.*}", local_i32, 5, local_f64); +LL + println!("Hello {local_i32} is {local_f64:.*}", 5); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:63:5 + | +LL | println!("{} {}", local_i32, local_f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{} {}", local_i32, local_f64); +LL + println!("{local_i32} {local_f64}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:64:5 + | +LL | println!("{}, {}", local_i32, local_opt.unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{}, {}", local_i32, local_opt.unwrap()); +LL + println!("{local_i32}, {}", local_opt.unwrap()); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:65:5 + | +LL | println!("{}", val); + | ^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{}", val); +LL + println!("{val}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:66:5 + | +LL | println!("{}", v = val); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{}", v = val); +LL + println!("{val}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:68:5 + | +LL | println!("val='{/t }'", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("val='{/t }'", local_i32); +LL + println!("val='{local_i32}'"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:69:5 + | +LL | println!("val='{/n }'", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("val='{/n }'", local_i32); +LL + println!("val='{local_i32}'"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:70:5 + | +LL | println!("val='{local_i32}'", local_i32 = local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("val='{local_i32}'", local_i32 = local_i32); +LL + println!("val='{local_i32}'"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:71:5 + | +LL | println!("val='{local_i32}'", local_i32 = fn_arg); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("val='{local_i32}'", local_i32 = fn_arg); +LL + println!("val='{fn_arg}'"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:72:5 + | +LL | println!("{0}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{0}", local_i32); +LL + println!("{local_i32}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:73:5 + | +LL | println!("{0:?}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{0:?}", local_i32); +LL + println!("{local_i32:?}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:74:5 + | +LL | println!("{0:#?}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{0:#?}", local_i32); +LL + println!("{local_i32:#?}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:75:5 + | +LL | println!("{0:04}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{0:04}", local_i32); +LL + println!("{local_i32:04}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:76:5 + | +LL | println!("{0:<3}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{0:<3}", local_i32); +LL + println!("{local_i32:<3}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:77:5 + | +LL | println!("{0:#010x}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{0:#010x}", local_i32); +LL + println!("{local_i32:#010x}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:78:5 + | +LL | println!("{0:.1}", local_f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{0:.1}", local_f64); +LL + println!("{local_f64:.1}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:79:5 + | +LL | println!("{0} {0}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{0} {0}", local_i32); +LL + println!("{local_i32} {local_i32}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:80:5 + | +LL | println!("{1} {} {0} {}", local_i32, local_f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{1} {} {0} {}", local_i32, local_f64); +LL + println!("{local_f64} {local_i32} {local_i32} {local_f64}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:81:5 + | +LL | println!("{0} {1}", local_i32, local_f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{0} {1}", local_i32, local_f64); +LL + println!("{local_i32} {local_f64}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:82:5 + | +LL | println!("{1} {0}", local_i32, local_f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{1} {0}", local_i32, local_f64); +LL + println!("{local_f64} {local_i32}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:83:5 + | +LL | println!("{1} {0} {1} {0}", local_i32, local_f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{1} {0} {1} {0}", local_i32, local_f64); +LL + println!("{local_f64} {local_i32} {local_f64} {local_i32}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:85:5 + | +LL | println!("{v}", v = local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{v}", v = local_i32); +LL + println!("{local_i32}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:86:5 + | +LL | println!("{local_i32:0$}", width); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{local_i32:0$}", width); +LL + println!("{local_i32:width$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:87:5 + | +LL | println!("{local_i32:w$}", w = width); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{local_i32:w$}", w = width); +LL + println!("{local_i32:width$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:88:5 + | +LL | println!("{local_i32:.0$}", prec); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{local_i32:.0$}", prec); +LL + println!("{local_i32:.prec$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:89:5 + | +LL | println!("{local_i32:.p$}", p = prec); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{local_i32:.p$}", p = prec); +LL + println!("{local_i32:.prec$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:90:5 + | +LL | println!("{:0$}", v = val); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:0$}", v = val); +LL + println!("{val:val$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:91:5 + | +LL | println!("{0:0$}", v = val); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{0:0$}", v = val); +LL + println!("{val:val$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:92:5 + | +LL | println!("{:0$.0$}", v = val); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:0$.0$}", v = val); +LL + println!("{val:val$.val$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:93:5 + | +LL | println!("{0:0$.0$}", v = val); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{0:0$.0$}", v = val); +LL + println!("{val:val$.val$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:94:5 + | +LL | println!("{0:0$.v$}", v = val); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{0:0$.v$}", v = val); +LL + println!("{val:val$.val$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:95:5 + | +LL | println!("{0:v$.0$}", v = val); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{0:v$.0$}", v = val); +LL + println!("{val:val$.val$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:96:5 + | +LL | println!("{v:0$.0$}", v = val); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{v:0$.0$}", v = val); +LL + println!("{val:val$.val$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:97:5 + | +LL | println!("{v:v$.0$}", v = val); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{v:v$.0$}", v = val); +LL + println!("{val:val$.val$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:98:5 + | +LL | println!("{v:0$.v$}", v = val); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{v:0$.v$}", v = val); +LL + println!("{val:val$.val$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:99:5 + | +LL | println!("{v:v$.v$}", v = val); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{v:v$.v$}", v = val); +LL + println!("{val:val$.val$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:100:5 + | +LL | println!("{:0$}", width); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:0$}", width); +LL + println!("{width:width$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:101:5 + | +LL | println!("{:1$}", local_i32, width); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:1$}", local_i32, width); +LL + println!("{local_i32:width$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:102:5 + | +LL | println!("{:w$}", w = width); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:w$}", w = width); +LL + println!("{width:width$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:103:5 + | +LL | println!("{:w$}", local_i32, w = width); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:w$}", local_i32, w = width); +LL + println!("{local_i32:width$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:104:5 + | +LL | println!("{:.0$}", prec); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:.0$}", prec); +LL + println!("{prec:.prec$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:105:5 + | +LL | println!("{:.1$}", local_i32, prec); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:.1$}", local_i32, prec); +LL + println!("{local_i32:.prec$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:106:5 + | +LL | println!("{:.p$}", p = prec); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:.p$}", p = prec); +LL + println!("{prec:.prec$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:107:5 + | +LL | println!("{:.p$}", local_i32, p = prec); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:.p$}", local_i32, p = prec); +LL + println!("{local_i32:.prec$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:108:5 + | +LL | println!("{:0$.1$}", width, prec); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:0$.1$}", width, prec); +LL + println!("{width:width$.prec$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:109:5 + | +LL | println!("{:0$.w$}", width, w = prec); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:0$.w$}", width, w = prec); +LL + println!("{width:width$.prec$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:110:5 + | +LL | println!("{:1$.2$}", local_f64, width, prec); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:1$.2$}", local_f64, width, prec); +LL + println!("{local_f64:width$.prec$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:111:5 + | +LL | println!("{:1$.2$} {0} {1} {2}", local_f64, width, prec); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:1$.2$} {0} {1} {2}", local_f64, width, prec); +LL + println!("{local_f64:width$.prec$} {local_f64} {width} {prec}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:112:5 + | +LL | / println!( +LL | | "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}", +LL | | local_i32, width, prec, +LL | | ); + | |_____^ + | +help: change this to + | +LL ~ "{local_i32:width$.prec$} {local_i32:prec$.width$} {width:local_i32$.prec$} {width:prec$.local_i32$} {prec:local_i32$.width$} {prec:width$.local_i32$}", width, prec, +LL ~ "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}", width, prec, +LL ~ "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}", width, prec, +LL ~ "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}", width, prec, +LL ~ "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}", width, prec, +LL ~ "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}", + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:123:5 + | +LL | println!("Width = {}, value with width = {:0$}", local_i32, local_f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("Width = {}, value with width = {:0$}", local_i32, local_f64); +LL + println!("Width = {local_i32}, value with width = {local_f64:local_i32$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:124:5 + | +LL | println!("{:w$.p$}", local_i32, w = width, p = prec); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:w$.p$}", local_i32, w = width, p = prec); +LL + println!("{local_i32:width$.prec$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:125:5 + | +LL | println!("{:w$.p$}", w = width, p = prec); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:w$.p$}", w = width, p = prec); +LL + println!("{width:width$.prec$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:126:20 + | +LL | println!("{}", format!("{}", local_i32)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{}", format!("{}", local_i32)); +LL + println!("{}", format!("{local_i32}")); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:144:5 + | +LL | / println!( +LL | | "{}", +LL | | // comment with a comma , in it +LL | | val, +LL | | ); + | |_____^ + | +help: change this to + | +LL - "{}", +LL + "{val}", + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:149:5 + | +LL | println!("{}", /* comment with a comma , in it */ val); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{}", /* comment with a comma , in it */ val); +LL + println!("{val}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:168:5 + | +LL | println!("expand='{}'", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("expand='{}'", local_i32); +LL + println!("expand='{local_i32}'"); + | + +error: aborting due to 73 previous errors + diff --git a/tests/ui/unit_arg.rs b/tests/ui/unit_arg.rs index 7bf3adc07ac5..07e70873a813 100644 --- a/tests/ui/unit_arg.rs +++ b/tests/ui/unit_arg.rs @@ -1,17 +1,16 @@ // aux-build: proc_macro_with_span.rs - #![warn(clippy::unit_arg)] +#![allow(unused_must_use, unused_variables)] #![allow( - clippy::no_effect, - unused_must_use, - unused_variables, - clippy::unused_unit, - clippy::unnecessary_wraps, - clippy::or_fun_call, - clippy::needless_question_mark, - clippy::self_named_constructors, clippy::let_unit_value, - clippy::never_loop + clippy::needless_question_mark, + clippy::never_loop, + clippy::no_effect, + clippy::or_fun_call, + clippy::self_named_constructors, + clippy::uninlined_format_args, + clippy::unnecessary_wraps, + clippy::unused_unit )] extern crate proc_macro_with_span; diff --git a/tests/ui/unit_arg.stderr b/tests/ui/unit_arg.stderr index 1de9d44bb0d6..74d4d2f4052f 100644 --- a/tests/ui/unit_arg.stderr +++ b/tests/ui/unit_arg.stderr @@ -1,5 +1,5 @@ error: passing a unit value to a function - --> $DIR/unit_arg.rs:63:5 + --> $DIR/unit_arg.rs:62:5 | LL | / foo({ LL | | 1; @@ -20,7 +20,7 @@ LL ~ foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:66:5 + --> $DIR/unit_arg.rs:65:5 | LL | foo(foo(1)); | ^^^^^^^^^^^ @@ -32,7 +32,7 @@ LL ~ foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:67:5 + --> $DIR/unit_arg.rs:66:5 | LL | / foo({ LL | | foo(1); @@ -54,7 +54,7 @@ LL ~ foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:72:5 + --> $DIR/unit_arg.rs:71:5 | LL | / b.bar({ LL | | 1; @@ -74,7 +74,7 @@ LL ~ b.bar(()); | error: passing unit values to a function - --> $DIR/unit_arg.rs:75:5 + --> $DIR/unit_arg.rs:74:5 | LL | taking_multiple_units(foo(0), foo(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -87,7 +87,7 @@ LL ~ taking_multiple_units((), ()); | error: passing unit values to a function - --> $DIR/unit_arg.rs:76:5 + --> $DIR/unit_arg.rs:75:5 | LL | / taking_multiple_units(foo(0), { LL | | foo(1); @@ -110,7 +110,7 @@ LL ~ taking_multiple_units((), ()); | error: passing unit values to a function - --> $DIR/unit_arg.rs:80:5 + --> $DIR/unit_arg.rs:79:5 | LL | / taking_multiple_units( LL | | { @@ -146,7 +146,7 @@ LL ~ ); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:91:13 + --> $DIR/unit_arg.rs:90:13 | LL | None.or(Some(foo(2))); | ^^^^^^^^^^^^ @@ -160,7 +160,7 @@ LL ~ }); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:94:5 + --> $DIR/unit_arg.rs:93:5 | LL | foo(foo(())); | ^^^^^^^^^^^^ @@ -172,7 +172,7 @@ LL ~ foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:131:5 + --> $DIR/unit_arg.rs:130:5 | LL | Some(foo(1)) | ^^^^^^^^^^^^ diff --git a/tests/ui/unit_arg_empty_blocks.fixed b/tests/ui/unit_arg_empty_blocks.fixed index 9400e93cac83..5787471a32ca 100644 --- a/tests/ui/unit_arg_empty_blocks.fixed +++ b/tests/ui/unit_arg_empty_blocks.fixed @@ -1,6 +1,7 @@ // run-rustfix #![warn(clippy::unit_arg)] -#![allow(clippy::no_effect, unused_must_use, unused_variables)] +#![allow(unused_must_use, unused_variables)] +#![allow(clippy::no_effect, clippy::uninlined_format_args)] use std::fmt::Debug; diff --git a/tests/ui/unit_arg_empty_blocks.rs b/tests/ui/unit_arg_empty_blocks.rs index 5f52b6c5315f..6a42c2ccf42b 100644 --- a/tests/ui/unit_arg_empty_blocks.rs +++ b/tests/ui/unit_arg_empty_blocks.rs @@ -1,6 +1,7 @@ // run-rustfix #![warn(clippy::unit_arg)] -#![allow(clippy::no_effect, unused_must_use, unused_variables)] +#![allow(unused_must_use, unused_variables)] +#![allow(clippy::no_effect, clippy::uninlined_format_args)] use std::fmt::Debug; diff --git a/tests/ui/unit_arg_empty_blocks.stderr b/tests/ui/unit_arg_empty_blocks.stderr index d35e931697d2..c697dfb1efa2 100644 --- a/tests/ui/unit_arg_empty_blocks.stderr +++ b/tests/ui/unit_arg_empty_blocks.stderr @@ -1,5 +1,5 @@ error: passing a unit value to a function - --> $DIR/unit_arg_empty_blocks.rs:16:5 + --> $DIR/unit_arg_empty_blocks.rs:17:5 | LL | foo({}); | ^^^^--^ @@ -9,7 +9,7 @@ LL | foo({}); = note: `-D clippy::unit-arg` implied by `-D warnings` error: passing a unit value to a function - --> $DIR/unit_arg_empty_blocks.rs:17:5 + --> $DIR/unit_arg_empty_blocks.rs:18:5 | LL | foo3({}, 2, 2); | ^^^^^--^^^^^^^ @@ -17,7 +17,7 @@ LL | foo3({}, 2, 2); | help: use a unit literal instead: `()` error: passing unit values to a function - --> $DIR/unit_arg_empty_blocks.rs:18:5 + --> $DIR/unit_arg_empty_blocks.rs:19:5 | LL | taking_two_units({}, foo(0)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -29,7 +29,7 @@ LL ~ taking_two_units((), ()); | error: passing unit values to a function - --> $DIR/unit_arg_empty_blocks.rs:19:5 + --> $DIR/unit_arg_empty_blocks.rs:20:5 | LL | taking_three_units({}, foo(0), foo(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/unnecessary_cast.fixed b/tests/ui/unnecessary_cast.fixed index ee9f157342d4..94dc96427263 100644 --- a/tests/ui/unnecessary_cast.fixed +++ b/tests/ui/unnecessary_cast.fixed @@ -97,4 +97,18 @@ mod fixable { let _ = -(1 + 1) as i64; } + + fn issue_9563() { + let _: f64 = (-8.0_f64).exp(); + #[allow(clippy::precedence)] + let _: f64 = -8.0_f64.exp(); // should suggest `-8.0_f64.exp()` here not to change code behavior + } + + fn issue_9562_non_literal() { + fn foo() -> f32 { + 0. + } + + let _num = foo(); + } } diff --git a/tests/ui/unnecessary_cast.rs b/tests/ui/unnecessary_cast.rs index 5b70412424c0..e5150256f69a 100644 --- a/tests/ui/unnecessary_cast.rs +++ b/tests/ui/unnecessary_cast.rs @@ -97,4 +97,18 @@ mod fixable { let _ = -(1 + 1) as i64; } + + fn issue_9563() { + let _: f64 = (-8.0 as f64).exp(); + #[allow(clippy::precedence)] + let _: f64 = -(8.0 as f64).exp(); // should suggest `-8.0_f64.exp()` here not to change code behavior + } + + fn issue_9562_non_literal() { + fn foo() -> f32 { + 0. + } + + let _num = foo() as f32; + } } diff --git a/tests/ui/unnecessary_cast.stderr b/tests/ui/unnecessary_cast.stderr index f7829ff3b0ef..e5c3dd5e53f8 100644 --- a/tests/ui/unnecessary_cast.stderr +++ b/tests/ui/unnecessary_cast.stderr @@ -162,5 +162,23 @@ error: casting integer literal to `i64` is unnecessary LL | let _: i64 = -(1) as i64; | ^^^^^^^^^^^ help: try: `-1_i64` -error: aborting due to 27 previous errors +error: casting float literal to `f64` is unnecessary + --> $DIR/unnecessary_cast.rs:102:22 + | +LL | let _: f64 = (-8.0 as f64).exp(); + | ^^^^^^^^^^^^^ help: try: `(-8.0_f64)` + +error: casting float literal to `f64` is unnecessary + --> $DIR/unnecessary_cast.rs:104:23 + | +LL | let _: f64 = -(8.0 as f64).exp(); // should suggest `-8.0_f64.exp()` here not to change code behavior + | ^^^^^^^^^^^^ help: try: `8.0_f64` + +error: casting to the same type is unnecessary (`f32` -> `f32`) + --> $DIR/unnecessary_cast.rs:112:20 + | +LL | let _num = foo() as f32; + | ^^^^^^^^^^^^ help: try: `foo()` + +error: aborting due to 30 previous errors diff --git a/tests/ui/unnecessary_clone.rs b/tests/ui/unnecessary_clone.rs index 6770a7fac90f..8b1629b19a76 100644 --- a/tests/ui/unnecessary_clone.rs +++ b/tests/ui/unnecessary_clone.rs @@ -1,7 +1,7 @@ // does not test any rustfixable lints - #![warn(clippy::clone_on_ref_ptr)] -#![allow(unused, clippy::redundant_clone, clippy::unnecessary_wraps)] +#![allow(unused)] +#![allow(clippy::redundant_clone, clippy::uninlined_format_args, clippy::unnecessary_wraps)] use std::cell::RefCell; use std::rc::{self, Rc}; diff --git a/tests/ui/unnecessary_join.fixed b/tests/ui/unnecessary_join.fixed index 7e12c6ae4be9..347953960257 100644 --- a/tests/ui/unnecessary_join.fixed +++ b/tests/ui/unnecessary_join.fixed @@ -1,6 +1,6 @@ // run-rustfix - #![warn(clippy::unnecessary_join)] +#![allow(clippy::uninlined_format_args)] fn main() { // should be linted diff --git a/tests/ui/unnecessary_join.rs b/tests/ui/unnecessary_join.rs index 0a21656a7558..344918cd2a2e 100644 --- a/tests/ui/unnecessary_join.rs +++ b/tests/ui/unnecessary_join.rs @@ -1,6 +1,6 @@ // run-rustfix - #![warn(clippy::unnecessary_join)] +#![allow(clippy::uninlined_format_args)] fn main() { // should be linted diff --git a/tests/ui/unnecessary_lazy_eval.fixed b/tests/ui/unnecessary_lazy_eval.fixed index eed817968832..ce4a82e02174 100644 --- a/tests/ui/unnecessary_lazy_eval.fixed +++ b/tests/ui/unnecessary_lazy_eval.fixed @@ -1,9 +1,13 @@ // run-rustfix +// aux-build: proc_macro_with_span.rs #![warn(clippy::unnecessary_lazy_evaluations)] #![allow(clippy::redundant_closure)] #![allow(clippy::bind_instead_of_map)] #![allow(clippy::map_identity)] +extern crate proc_macro_with_span; +use proc_macro_with_span::with_span; + struct Deep(Option); #[derive(Copy, Clone)] @@ -21,6 +25,14 @@ fn some_call() -> T { T::default() } +struct Issue9427(i32); + +impl Drop for Issue9427 { + fn drop(&mut self) { + println!("{}", self.0); + } +} + fn main() { let astronomers_pi = 10; let ext_arr: [usize; 1] = [2]; @@ -73,6 +85,9 @@ fn main() { let _ = deep.0.or_else(|| some_call()); let _ = opt.ok_or_else(|| ext_arr[0]); + // Should not lint - bool + let _ = (0 == 1).then(|| Issue9427(0)); // Issue9427 has a significant drop + // should not lint, bind_instead_of_map takes priority let _ = Some(10).and_then(|idx| Some(ext_arr[idx])); let _ = Some(10).and_then(|idx| Some(idx)); @@ -130,3 +145,9 @@ fn main() { let _: Result = res.and_then(|x| Err(x)); let _: Result = res.or_else(|err| Ok(err)); } + +#[allow(unused)] +fn issue9485() { + // should not lint, is in proc macro + with_span!(span Some(42).unwrap_or_else(|| 2);); +} diff --git a/tests/ui/unnecessary_lazy_eval.rs b/tests/ui/unnecessary_lazy_eval.rs index 1588db79b38a..59cdf6628546 100644 --- a/tests/ui/unnecessary_lazy_eval.rs +++ b/tests/ui/unnecessary_lazy_eval.rs @@ -1,9 +1,13 @@ // run-rustfix +// aux-build: proc_macro_with_span.rs #![warn(clippy::unnecessary_lazy_evaluations)] #![allow(clippy::redundant_closure)] #![allow(clippy::bind_instead_of_map)] #![allow(clippy::map_identity)] +extern crate proc_macro_with_span; +use proc_macro_with_span::with_span; + struct Deep(Option); #[derive(Copy, Clone)] @@ -21,6 +25,14 @@ fn some_call() -> T { T::default() } +struct Issue9427(i32); + +impl Drop for Issue9427 { + fn drop(&mut self) { + println!("{}", self.0); + } +} + fn main() { let astronomers_pi = 10; let ext_arr: [usize; 1] = [2]; @@ -73,6 +85,9 @@ fn main() { let _ = deep.0.or_else(|| some_call()); let _ = opt.ok_or_else(|| ext_arr[0]); + // Should not lint - bool + let _ = (0 == 1).then(|| Issue9427(0)); // Issue9427 has a significant drop + // should not lint, bind_instead_of_map takes priority let _ = Some(10).and_then(|idx| Some(ext_arr[idx])); let _ = Some(10).and_then(|idx| Some(idx)); @@ -130,3 +145,9 @@ fn main() { let _: Result = res.and_then(|x| Err(x)); let _: Result = res.or_else(|err| Ok(err)); } + +#[allow(unused)] +fn issue9485() { + // should not lint, is in proc macro + with_span!(span Some(42).unwrap_or_else(|| 2);); +} diff --git a/tests/ui/unnecessary_lazy_eval.stderr b/tests/ui/unnecessary_lazy_eval.stderr index 83dc7fd832c3..8a9ece4aa7e5 100644 --- a/tests/ui/unnecessary_lazy_eval.stderr +++ b/tests/ui/unnecessary_lazy_eval.stderr @@ -1,5 +1,5 @@ error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:36:13 + --> $DIR/unnecessary_lazy_eval.rs:48:13 | LL | let _ = opt.unwrap_or_else(|| 2); | ^^^^-------------------- @@ -9,7 +9,7 @@ LL | let _ = opt.unwrap_or_else(|| 2); = note: `-D clippy::unnecessary-lazy-evaluations` implied by `-D warnings` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:37:13 + --> $DIR/unnecessary_lazy_eval.rs:49:13 | LL | let _ = opt.unwrap_or_else(|| astronomers_pi); | ^^^^--------------------------------- @@ -17,7 +17,7 @@ LL | let _ = opt.unwrap_or_else(|| astronomers_pi); | help: use `unwrap_or(..)` instead: `unwrap_or(astronomers_pi)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:38:13 + --> $DIR/unnecessary_lazy_eval.rs:50:13 | LL | let _ = opt.unwrap_or_else(|| ext_str.some_field); | ^^^^------------------------------------- @@ -25,7 +25,7 @@ LL | let _ = opt.unwrap_or_else(|| ext_str.some_field); | help: use `unwrap_or(..)` instead: `unwrap_or(ext_str.some_field)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:40:13 + --> $DIR/unnecessary_lazy_eval.rs:52:13 | LL | let _ = opt.and_then(|_| ext_opt); | ^^^^--------------------- @@ -33,7 +33,7 @@ LL | let _ = opt.and_then(|_| ext_opt); | help: use `and(..)` instead: `and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:41:13 + --> $DIR/unnecessary_lazy_eval.rs:53:13 | LL | let _ = opt.or_else(|| ext_opt); | ^^^^------------------- @@ -41,7 +41,7 @@ LL | let _ = opt.or_else(|| ext_opt); | help: use `or(..)` instead: `or(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:42:13 + --> $DIR/unnecessary_lazy_eval.rs:54:13 | LL | let _ = opt.or_else(|| None); | ^^^^---------------- @@ -49,7 +49,7 @@ LL | let _ = opt.or_else(|| None); | help: use `or(..)` instead: `or(None)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:43:13 + --> $DIR/unnecessary_lazy_eval.rs:55:13 | LL | let _ = opt.get_or_insert_with(|| 2); | ^^^^------------------------ @@ -57,7 +57,7 @@ LL | let _ = opt.get_or_insert_with(|| 2); | help: use `get_or_insert(..)` instead: `get_or_insert(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:44:13 + --> $DIR/unnecessary_lazy_eval.rs:56:13 | LL | let _ = opt.ok_or_else(|| 2); | ^^^^---------------- @@ -65,7 +65,7 @@ LL | let _ = opt.ok_or_else(|| 2); | help: use `ok_or(..)` instead: `ok_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:45:13 + --> $DIR/unnecessary_lazy_eval.rs:57:13 | LL | let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2))); | ^^^^^^^^^^^^^^^^^------------------------------- @@ -73,7 +73,7 @@ LL | let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2))); | help: use `unwrap_or(..)` instead: `unwrap_or(Some((1, 2)))` error: unnecessary closure used with `bool::then` - --> $DIR/unnecessary_lazy_eval.rs:46:13 + --> $DIR/unnecessary_lazy_eval.rs:58:13 | LL | let _ = cond.then(|| astronomers_pi); | ^^^^^----------------------- @@ -81,7 +81,7 @@ LL | let _ = cond.then(|| astronomers_pi); | help: use `then_some(..)` instead: `then_some(astronomers_pi)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:49:13 + --> $DIR/unnecessary_lazy_eval.rs:61:13 | LL | let _ = Some(10).unwrap_or_else(|| 2); | ^^^^^^^^^-------------------- @@ -89,7 +89,7 @@ LL | let _ = Some(10).unwrap_or_else(|| 2); | help: use `unwrap_or(..)` instead: `unwrap_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:50:13 + --> $DIR/unnecessary_lazy_eval.rs:62:13 | LL | let _ = Some(10).and_then(|_| ext_opt); | ^^^^^^^^^--------------------- @@ -97,7 +97,7 @@ LL | let _ = Some(10).and_then(|_| ext_opt); | help: use `and(..)` instead: `and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:51:28 + --> $DIR/unnecessary_lazy_eval.rs:63:28 | LL | let _: Option = None.or_else(|| ext_opt); | ^^^^^------------------- @@ -105,7 +105,7 @@ LL | let _: Option = None.or_else(|| ext_opt); | help: use `or(..)` instead: `or(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:52:13 + --> $DIR/unnecessary_lazy_eval.rs:64:13 | LL | let _ = None.get_or_insert_with(|| 2); | ^^^^^------------------------ @@ -113,7 +113,7 @@ LL | let _ = None.get_or_insert_with(|| 2); | help: use `get_or_insert(..)` instead: `get_or_insert(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:53:35 + --> $DIR/unnecessary_lazy_eval.rs:65:35 | LL | let _: Result = None.ok_or_else(|| 2); | ^^^^^---------------- @@ -121,7 +121,7 @@ LL | let _: Result = None.ok_or_else(|| 2); | help: use `ok_or(..)` instead: `ok_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:54:28 + --> $DIR/unnecessary_lazy_eval.rs:66:28 | LL | let _: Option = None.or_else(|| None); | ^^^^^---------------- @@ -129,7 +129,7 @@ LL | let _: Option = None.or_else(|| None); | help: use `or(..)` instead: `or(None)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:57:13 + --> $DIR/unnecessary_lazy_eval.rs:69:13 | LL | let _ = deep.0.unwrap_or_else(|| 2); | ^^^^^^^-------------------- @@ -137,7 +137,7 @@ LL | let _ = deep.0.unwrap_or_else(|| 2); | help: use `unwrap_or(..)` instead: `unwrap_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:58:13 + --> $DIR/unnecessary_lazy_eval.rs:70:13 | LL | let _ = deep.0.and_then(|_| ext_opt); | ^^^^^^^--------------------- @@ -145,7 +145,7 @@ LL | let _ = deep.0.and_then(|_| ext_opt); | help: use `and(..)` instead: `and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:59:13 + --> $DIR/unnecessary_lazy_eval.rs:71:13 | LL | let _ = deep.0.or_else(|| None); | ^^^^^^^---------------- @@ -153,7 +153,7 @@ LL | let _ = deep.0.or_else(|| None); | help: use `or(..)` instead: `or(None)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:60:13 + --> $DIR/unnecessary_lazy_eval.rs:72:13 | LL | let _ = deep.0.get_or_insert_with(|| 2); | ^^^^^^^------------------------ @@ -161,7 +161,7 @@ LL | let _ = deep.0.get_or_insert_with(|| 2); | help: use `get_or_insert(..)` instead: `get_or_insert(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:61:13 + --> $DIR/unnecessary_lazy_eval.rs:73:13 | LL | let _ = deep.0.ok_or_else(|| 2); | ^^^^^^^---------------- @@ -169,7 +169,7 @@ LL | let _ = deep.0.ok_or_else(|| 2); | help: use `ok_or(..)` instead: `ok_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:81:28 + --> $DIR/unnecessary_lazy_eval.rs:96:28 | LL | let _: Option = None.or_else(|| Some(3)); | ^^^^^------------------- @@ -177,7 +177,7 @@ LL | let _: Option = None.or_else(|| Some(3)); | help: use `or(..)` instead: `or(Some(3))` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:82:13 + --> $DIR/unnecessary_lazy_eval.rs:97:13 | LL | let _ = deep.0.or_else(|| Some(3)); | ^^^^^^^------------------- @@ -185,7 +185,7 @@ LL | let _ = deep.0.or_else(|| Some(3)); | help: use `or(..)` instead: `or(Some(3))` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:83:13 + --> $DIR/unnecessary_lazy_eval.rs:98:13 | LL | let _ = opt.or_else(|| Some(3)); | ^^^^------------------- @@ -193,7 +193,7 @@ LL | let _ = opt.or_else(|| Some(3)); | help: use `or(..)` instead: `or(Some(3))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:89:13 + --> $DIR/unnecessary_lazy_eval.rs:104:13 | LL | let _ = res2.unwrap_or_else(|_| 2); | ^^^^^--------------------- @@ -201,7 +201,7 @@ LL | let _ = res2.unwrap_or_else(|_| 2); | help: use `unwrap_or(..)` instead: `unwrap_or(2)` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:90:13 + --> $DIR/unnecessary_lazy_eval.rs:105:13 | LL | let _ = res2.unwrap_or_else(|_| astronomers_pi); | ^^^^^---------------------------------- @@ -209,7 +209,7 @@ LL | let _ = res2.unwrap_or_else(|_| astronomers_pi); | help: use `unwrap_or(..)` instead: `unwrap_or(astronomers_pi)` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:91:13 + --> $DIR/unnecessary_lazy_eval.rs:106:13 | LL | let _ = res2.unwrap_or_else(|_| ext_str.some_field); | ^^^^^-------------------------------------- @@ -217,7 +217,7 @@ LL | let _ = res2.unwrap_or_else(|_| ext_str.some_field); | help: use `unwrap_or(..)` instead: `unwrap_or(ext_str.some_field)` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:113:35 + --> $DIR/unnecessary_lazy_eval.rs:128:35 | LL | let _: Result = res.and_then(|_| Err(2)); | ^^^^-------------------- @@ -225,7 +225,7 @@ LL | let _: Result = res.and_then(|_| Err(2)); | help: use `and(..)` instead: `and(Err(2))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:114:35 + --> $DIR/unnecessary_lazy_eval.rs:129:35 | LL | let _: Result = res.and_then(|_| Err(astronomers_pi)); | ^^^^--------------------------------- @@ -233,7 +233,7 @@ LL | let _: Result = res.and_then(|_| Err(astronomers_pi)); | help: use `and(..)` instead: `and(Err(astronomers_pi))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:115:35 + --> $DIR/unnecessary_lazy_eval.rs:130:35 | LL | let _: Result = res.and_then(|_| Err(ext_str.some_field)); | ^^^^------------------------------------- @@ -241,7 +241,7 @@ LL | let _: Result = res.and_then(|_| Err(ext_str.some_field)) | help: use `and(..)` instead: `and(Err(ext_str.some_field))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:117:35 + --> $DIR/unnecessary_lazy_eval.rs:132:35 | LL | let _: Result = res.or_else(|_| Ok(2)); | ^^^^------------------ @@ -249,7 +249,7 @@ LL | let _: Result = res.or_else(|_| Ok(2)); | help: use `or(..)` instead: `or(Ok(2))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:118:35 + --> $DIR/unnecessary_lazy_eval.rs:133:35 | LL | let _: Result = res.or_else(|_| Ok(astronomers_pi)); | ^^^^------------------------------- @@ -257,7 +257,7 @@ LL | let _: Result = res.or_else(|_| Ok(astronomers_pi)); | help: use `or(..)` instead: `or(Ok(astronomers_pi))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:119:35 + --> $DIR/unnecessary_lazy_eval.rs:134:35 | LL | let _: Result = res.or_else(|_| Ok(ext_str.some_field)); | ^^^^----------------------------------- @@ -265,7 +265,7 @@ LL | let _: Result = res.or_else(|_| Ok(ext_str.some_field)); | help: use `or(..)` instead: `or(Ok(ext_str.some_field))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:120:35 + --> $DIR/unnecessary_lazy_eval.rs:135:35 | LL | let _: Result = res. | ___________________________________^ diff --git a/tests/ui/upper_case_acronyms.rs b/tests/ui/upper_case_acronyms.rs index 48bb9e54b122..9b7c2f28e1cf 100644 --- a/tests/ui/upper_case_acronyms.rs +++ b/tests/ui/upper_case_acronyms.rs @@ -38,4 +38,13 @@ enum ParseErrorPrivate { Parse(T, String), } +// do lint here +struct JSON; + +// do lint here +enum YAML { + Num(u32), + Str(String), +} + fn main() {} diff --git a/tests/ui/upper_case_acronyms.stderr b/tests/ui/upper_case_acronyms.stderr index 250b196a99eb..74082ec16dd4 100644 --- a/tests/ui/upper_case_acronyms.stderr +++ b/tests/ui/upper_case_acronyms.stderr @@ -54,5 +54,17 @@ error: name `WASD` contains a capitalized acronym LL | WASD(u8), | ^^^^ help: consider making the acronym lowercase, except the initial letter: `Wasd` -error: aborting due to 9 previous errors +error: name `JSON` contains a capitalized acronym + --> $DIR/upper_case_acronyms.rs:42:8 + | +LL | struct JSON; + | ^^^^ help: consider making the acronym lowercase, except the initial letter: `Json` + +error: name `YAML` contains a capitalized acronym + --> $DIR/upper_case_acronyms.rs:45:6 + | +LL | enum YAML { + | ^^^^ help: consider making the acronym lowercase, except the initial letter: `Yaml` + +error: aborting due to 11 previous errors diff --git a/tests/ui/used_underscore_binding.rs b/tests/ui/used_underscore_binding.rs index 322083511ac1..8c29e15b1459 100644 --- a/tests/ui/used_underscore_binding.rs +++ b/tests/ui/used_underscore_binding.rs @@ -1,9 +1,8 @@ // aux-build:proc_macro_derive.rs - #![feature(rustc_private)] #![warn(clippy::all)] -#![allow(clippy::disallowed_names, clippy::eq_op)] #![warn(clippy::used_underscore_binding)] +#![allow(clippy::disallowed_names, clippy::eq_op, clippy::uninlined_format_args)] #[macro_use] extern crate proc_macro_derive; diff --git a/tests/ui/used_underscore_binding.stderr b/tests/ui/used_underscore_binding.stderr index 61a9161d212d..875fafe438a1 100644 --- a/tests/ui/used_underscore_binding.stderr +++ b/tests/ui/used_underscore_binding.stderr @@ -1,5 +1,5 @@ error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used - --> $DIR/used_underscore_binding.rs:25:5 + --> $DIR/used_underscore_binding.rs:24:5 | LL | _foo + 1 | ^^^^ @@ -7,31 +7,31 @@ LL | _foo + 1 = note: `-D clippy::used-underscore-binding` implied by `-D warnings` error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used - --> $DIR/used_underscore_binding.rs:30:20 + --> $DIR/used_underscore_binding.rs:29:20 | LL | println!("{}", _foo); | ^^^^ error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used - --> $DIR/used_underscore_binding.rs:31:16 + --> $DIR/used_underscore_binding.rs:30:16 | LL | assert_eq!(_foo, _foo); | ^^^^ error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used - --> $DIR/used_underscore_binding.rs:31:22 + --> $DIR/used_underscore_binding.rs:30:22 | LL | assert_eq!(_foo, _foo); | ^^^^ error: used binding `_underscore_field` which is prefixed with an underscore. A leading underscore signals that a binding will not be used - --> $DIR/used_underscore_binding.rs:44:5 + --> $DIR/used_underscore_binding.rs:43:5 | LL | s._underscore_field += 1; | ^^^^^^^^^^^^^^^^^^^ error: used binding `_i` which is prefixed with an underscore. A leading underscore signals that a binding will not be used - --> $DIR/used_underscore_binding.rs:105:16 + --> $DIR/used_underscore_binding.rs:104:16 | LL | uses_i(_i); | ^^ diff --git a/tests/ui/useless_asref.fixed b/tests/ui/useless_asref.fixed index 90cb8945e77f..38e4b9201e6d 100644 --- a/tests/ui/useless_asref.fixed +++ b/tests/ui/useless_asref.fixed @@ -1,7 +1,6 @@ // run-rustfix - #![deny(clippy::useless_asref)] -#![allow(clippy::explicit_auto_deref)] +#![allow(clippy::explicit_auto_deref, clippy::uninlined_format_args)] use std::fmt::Debug; diff --git a/tests/ui/useless_asref.rs b/tests/ui/useless_asref.rs index cb9f8ae5909a..f1e83f9d396c 100644 --- a/tests/ui/useless_asref.rs +++ b/tests/ui/useless_asref.rs @@ -1,7 +1,6 @@ // run-rustfix - #![deny(clippy::useless_asref)] -#![allow(clippy::explicit_auto_deref)] +#![allow(clippy::explicit_auto_deref, clippy::uninlined_format_args)] use std::fmt::Debug; diff --git a/tests/ui/useless_asref.stderr b/tests/ui/useless_asref.stderr index b21c67bb3645..67ce8b64e0e3 100644 --- a/tests/ui/useless_asref.stderr +++ b/tests/ui/useless_asref.stderr @@ -1,71 +1,71 @@ error: this call to `as_ref` does nothing - --> $DIR/useless_asref.rs:44:18 + --> $DIR/useless_asref.rs:43:18 | LL | foo_rstr(rstr.as_ref()); | ^^^^^^^^^^^^^ help: try this: `rstr` | note: the lint level is defined here - --> $DIR/useless_asref.rs:3:9 + --> $DIR/useless_asref.rs:2:9 | LL | #![deny(clippy::useless_asref)] | ^^^^^^^^^^^^^^^^^^^^^ error: this call to `as_ref` does nothing - --> $DIR/useless_asref.rs:46:20 + --> $DIR/useless_asref.rs:45:20 | LL | foo_rslice(rslice.as_ref()); | ^^^^^^^^^^^^^^^ help: try this: `rslice` error: this call to `as_mut` does nothing - --> $DIR/useless_asref.rs:50:21 + --> $DIR/useless_asref.rs:49:21 | LL | foo_mrslice(mrslice.as_mut()); | ^^^^^^^^^^^^^^^^ help: try this: `mrslice` error: this call to `as_ref` does nothing - --> $DIR/useless_asref.rs:52:20 + --> $DIR/useless_asref.rs:51:20 | LL | foo_rslice(mrslice.as_ref()); | ^^^^^^^^^^^^^^^^ help: try this: `mrslice` error: this call to `as_ref` does nothing - --> $DIR/useless_asref.rs:59:20 + --> $DIR/useless_asref.rs:58:20 | LL | foo_rslice(rrrrrslice.as_ref()); | ^^^^^^^^^^^^^^^^^^^ help: try this: `rrrrrslice` error: this call to `as_ref` does nothing - --> $DIR/useless_asref.rs:61:18 + --> $DIR/useless_asref.rs:60:18 | LL | foo_rstr(rrrrrstr.as_ref()); | ^^^^^^^^^^^^^^^^^ help: try this: `rrrrrstr` error: this call to `as_mut` does nothing - --> $DIR/useless_asref.rs:66:21 + --> $DIR/useless_asref.rs:65:21 | LL | foo_mrslice(mrrrrrslice.as_mut()); | ^^^^^^^^^^^^^^^^^^^^ help: try this: `mrrrrrslice` error: this call to `as_ref` does nothing - --> $DIR/useless_asref.rs:68:20 + --> $DIR/useless_asref.rs:67:20 | LL | foo_rslice(mrrrrrslice.as_ref()); | ^^^^^^^^^^^^^^^^^^^^ help: try this: `mrrrrrslice` error: this call to `as_ref` does nothing - --> $DIR/useless_asref.rs:72:16 + --> $DIR/useless_asref.rs:71:16 | LL | foo_rrrrmr((&&&&MoreRef).as_ref()); | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `(&&&&MoreRef)` error: this call to `as_mut` does nothing - --> $DIR/useless_asref.rs:122:13 + --> $DIR/useless_asref.rs:121:13 | LL | foo_mrt(mrt.as_mut()); | ^^^^^^^^^^^^ help: try this: `mrt` error: this call to `as_ref` does nothing - --> $DIR/useless_asref.rs:124:12 + --> $DIR/useless_asref.rs:123:12 | LL | foo_rt(mrt.as_ref()); | ^^^^^^^^^^^^ help: try this: `mrt` diff --git a/tests/ui/vec.fixed b/tests/ui/vec.fixed index 318f9c2dceb6..2518d8049150 100644 --- a/tests/ui/vec.fixed +++ b/tests/ui/vec.fixed @@ -1,6 +1,6 @@ // run-rustfix -#![allow(clippy::nonstandard_macro_braces)] #![warn(clippy::useless_vec)] +#![allow(clippy::nonstandard_macro_braces, clippy::uninlined_format_args)] #[derive(Debug)] struct NonCopy; diff --git a/tests/ui/vec.rs b/tests/ui/vec.rs index d7673ce3e643..e1492e2f3aef 100644 --- a/tests/ui/vec.rs +++ b/tests/ui/vec.rs @@ -1,6 +1,6 @@ // run-rustfix -#![allow(clippy::nonstandard_macro_braces)] #![warn(clippy::useless_vec)] +#![allow(clippy::nonstandard_macro_braces, clippy::uninlined_format_args)] #[derive(Debug)] struct NonCopy; diff --git a/tests/ui/while_let_loop.rs b/tests/ui/while_let_loop.rs index c42e2a79a9bf..5b8075731cb7 100644 --- a/tests/ui/while_let_loop.rs +++ b/tests/ui/while_let_loop.rs @@ -1,4 +1,5 @@ #![warn(clippy::while_let_loop)] +#![allow(clippy::uninlined_format_args)] fn main() { let y = Some(true); diff --git a/tests/ui/while_let_loop.stderr b/tests/ui/while_let_loop.stderr index 13dd0ee224c1..04808c0b3ada 100644 --- a/tests/ui/while_let_loop.stderr +++ b/tests/ui/while_let_loop.stderr @@ -1,5 +1,5 @@ error: this loop could be written as a `while let` loop - --> $DIR/while_let_loop.rs:5:5 + --> $DIR/while_let_loop.rs:6:5 | LL | / loop { LL | | if let Some(_x) = y { @@ -13,7 +13,7 @@ LL | | } = note: `-D clippy::while-let-loop` implied by `-D warnings` error: this loop could be written as a `while let` loop - --> $DIR/while_let_loop.rs:22:5 + --> $DIR/while_let_loop.rs:23:5 | LL | / loop { LL | | match y { @@ -24,7 +24,7 @@ LL | | } | |_____^ help: try: `while let Some(_x) = y { .. }` error: this loop could be written as a `while let` loop - --> $DIR/while_let_loop.rs:29:5 + --> $DIR/while_let_loop.rs:30:5 | LL | / loop { LL | | let x = match y { @@ -36,7 +36,7 @@ LL | | } | |_____^ help: try: `while let Some(x) = y { .. }` error: this loop could be written as a `while let` loop - --> $DIR/while_let_loop.rs:38:5 + --> $DIR/while_let_loop.rs:39:5 | LL | / loop { LL | | let x = match y { @@ -48,7 +48,7 @@ LL | | } | |_____^ help: try: `while let Some(x) = y { .. }` error: this loop could be written as a `while let` loop - --> $DIR/while_let_loop.rs:68:5 + --> $DIR/while_let_loop.rs:69:5 | LL | / loop { LL | | let (e, l) = match "".split_whitespace().next() { diff --git a/tests/ui/while_let_on_iterator.fixed b/tests/ui/while_let_on_iterator.fixed index c57c46736342..5afa0a89f82c 100644 --- a/tests/ui/while_let_on_iterator.fixed +++ b/tests/ui/while_let_on_iterator.fixed @@ -1,14 +1,12 @@ // run-rustfix - #![warn(clippy::while_let_on_iterator)] +#![allow(dead_code, unreachable_code, unused_mut)] #![allow( - clippy::never_loop, - unreachable_code, - unused_mut, - dead_code, clippy::equatable_if_let, clippy::manual_find, - clippy::redundant_closure_call + clippy::never_loop, + clippy::redundant_closure_call, + clippy::uninlined_format_args )] fn base() { diff --git a/tests/ui/while_let_on_iterator.rs b/tests/ui/while_let_on_iterator.rs index 8b9a2dbcce3a..3de586c9d8fd 100644 --- a/tests/ui/while_let_on_iterator.rs +++ b/tests/ui/while_let_on_iterator.rs @@ -1,14 +1,12 @@ // run-rustfix - #![warn(clippy::while_let_on_iterator)] +#![allow(dead_code, unreachable_code, unused_mut)] #![allow( - clippy::never_loop, - unreachable_code, - unused_mut, - dead_code, clippy::equatable_if_let, clippy::manual_find, - clippy::redundant_closure_call + clippy::never_loop, + clippy::redundant_closure_call, + clippy::uninlined_format_args )] fn base() { diff --git a/tests/ui/while_let_on_iterator.stderr b/tests/ui/while_let_on_iterator.stderr index 3236765e1db0..4d98666190d6 100644 --- a/tests/ui/while_let_on_iterator.stderr +++ b/tests/ui/while_let_on_iterator.stderr @@ -1,5 +1,5 @@ error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:16:5 + --> $DIR/while_let_on_iterator.rs:14:5 | LL | while let Option::Some(x) = iter.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in iter` @@ -7,151 +7,151 @@ LL | while let Option::Some(x) = iter.next() { = note: `-D clippy::while-let-on-iterator` implied by `-D warnings` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:21:5 + --> $DIR/while_let_on_iterator.rs:19:5 | LL | while let Some(x) = iter.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in iter` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:26:5 + --> $DIR/while_let_on_iterator.rs:24:5 | LL | while let Some(_) = iter.next() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in iter` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:102:9 + --> $DIR/while_let_on_iterator.rs:100:9 | LL | while let Some([..]) = it.next() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for [..] in it` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:109:9 + --> $DIR/while_let_on_iterator.rs:107:9 | LL | while let Some([_x]) = it.next() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for [_x] in it` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:122:9 + --> $DIR/while_let_on_iterator.rs:120:9 | LL | while let Some(x @ [_]) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x @ [_] in it` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:142:9 + --> $DIR/while_let_on_iterator.rs:140:9 | LL | while let Some(_) = y.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in y` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:199:9 + --> $DIR/while_let_on_iterator.rs:197:9 | LL | while let Some(m) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it.by_ref()` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:210:5 + --> $DIR/while_let_on_iterator.rs:208:5 | LL | while let Some(n) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for n in it` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:212:9 + --> $DIR/while_let_on_iterator.rs:210:9 | LL | while let Some(m) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:221:9 + --> $DIR/while_let_on_iterator.rs:219:9 | LL | while let Some(m) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:230:9 + --> $DIR/while_let_on_iterator.rs:228:9 | LL | while let Some(m) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it.by_ref()` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:247:9 + --> $DIR/while_let_on_iterator.rs:245:9 | LL | while let Some(m) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it.by_ref()` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:262:13 + --> $DIR/while_let_on_iterator.rs:260:13 | LL | while let Some(i) = self.0.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for i in self.0.by_ref()` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:294:13 + --> $DIR/while_let_on_iterator.rs:292:13 | LL | while let Some(i) = self.0.0.0.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for i in self.0.0.0.by_ref()` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:323:5 + --> $DIR/while_let_on_iterator.rs:321:5 | LL | while let Some(n) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for n in it.by_ref()` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:335:9 + --> $DIR/while_let_on_iterator.rs:333:9 | LL | while let Some(x) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it.by_ref()` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:349:5 + --> $DIR/while_let_on_iterator.rs:347:5 | LL | while let Some(x) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it.by_ref()` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:360:5 + --> $DIR/while_let_on_iterator.rs:358:5 | LL | while let Some(x) = it.0.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it.0.by_ref()` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:395:5 + --> $DIR/while_let_on_iterator.rs:393:5 | LL | while let Some(x) = s.x.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in s.x.by_ref()` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:402:5 + --> $DIR/while_let_on_iterator.rs:400:5 | LL | while let Some(x) = x[0].next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in x[0].by_ref()` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:410:9 + --> $DIR/while_let_on_iterator.rs:408:9 | LL | while let Some(x) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:420:9 + --> $DIR/while_let_on_iterator.rs:418:9 | LL | while let Some(x) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:430:9 + --> $DIR/while_let_on_iterator.rs:428:9 | LL | while let Some(x) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it.by_ref()` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:440:9 + --> $DIR/while_let_on_iterator.rs:438:9 | LL | while let Some(x) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:450:5 + --> $DIR/while_let_on_iterator.rs:448:5 | LL | while let Some(..) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in it` diff --git a/tests/ui/wildcard_enum_match_arm.fixed b/tests/ui/wildcard_enum_match_arm.fixed index 3ee4ab48ac84..23607497841e 100644 --- a/tests/ui/wildcard_enum_match_arm.fixed +++ b/tests/ui/wildcard_enum_match_arm.fixed @@ -1,15 +1,13 @@ // run-rustfix // aux-build:non-exhaustive-enum.rs - #![deny(clippy::wildcard_enum_match_arm)] +#![allow(dead_code, unreachable_code, unused_variables)] #![allow( - unreachable_code, - unused_variables, - dead_code, + clippy::diverging_sub_expression, clippy::single_match, - clippy::wildcard_in_or_patterns, + clippy::uninlined_format_args, clippy::unnested_or_patterns, - clippy::diverging_sub_expression + clippy::wildcard_in_or_patterns )] extern crate non_exhaustive_enum; diff --git a/tests/ui/wildcard_enum_match_arm.rs b/tests/ui/wildcard_enum_match_arm.rs index 468865504533..decd86165f3a 100644 --- a/tests/ui/wildcard_enum_match_arm.rs +++ b/tests/ui/wildcard_enum_match_arm.rs @@ -1,15 +1,13 @@ // run-rustfix // aux-build:non-exhaustive-enum.rs - #![deny(clippy::wildcard_enum_match_arm)] +#![allow(dead_code, unreachable_code, unused_variables)] #![allow( - unreachable_code, - unused_variables, - dead_code, + clippy::diverging_sub_expression, clippy::single_match, - clippy::wildcard_in_or_patterns, + clippy::uninlined_format_args, clippy::unnested_or_patterns, - clippy::diverging_sub_expression + clippy::wildcard_in_or_patterns )] extern crate non_exhaustive_enum; diff --git a/tests/ui/wildcard_enum_match_arm.stderr b/tests/ui/wildcard_enum_match_arm.stderr index d63f20903531..efecc9576cc7 100644 --- a/tests/ui/wildcard_enum_match_arm.stderr +++ b/tests/ui/wildcard_enum_match_arm.stderr @@ -1,41 +1,41 @@ error: wildcard match will also match any future added variants - --> $DIR/wildcard_enum_match_arm.rs:42:9 + --> $DIR/wildcard_enum_match_arm.rs:40:9 | LL | _ => eprintln!("Not red"), | ^ help: try this: `Color::Green | Color::Blue | Color::Rgb(..) | Color::Cyan` | note: the lint level is defined here - --> $DIR/wildcard_enum_match_arm.rs:4:9 + --> $DIR/wildcard_enum_match_arm.rs:3:9 | LL | #![deny(clippy::wildcard_enum_match_arm)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: wildcard match will also match any future added variants - --> $DIR/wildcard_enum_match_arm.rs:46:9 + --> $DIR/wildcard_enum_match_arm.rs:44:9 | LL | _not_red => eprintln!("Not red"), | ^^^^^^^^ help: try this: `_not_red @ Color::Green | _not_red @ Color::Blue | _not_red @ Color::Rgb(..) | _not_red @ Color::Cyan` error: wildcard match will also match any future added variants - --> $DIR/wildcard_enum_match_arm.rs:50:9 + --> $DIR/wildcard_enum_match_arm.rs:48:9 | LL | not_red => format!("{:?}", not_red), | ^^^^^^^ help: try this: `not_red @ Color::Green | not_red @ Color::Blue | not_red @ Color::Rgb(..) | not_red @ Color::Cyan` error: wildcard match will also match any future added variants - --> $DIR/wildcard_enum_match_arm.rs:66:9 + --> $DIR/wildcard_enum_match_arm.rs:64:9 | LL | _ => "No red", | ^ help: try this: `Color::Red | Color::Green | Color::Blue | Color::Rgb(..) | Color::Cyan` error: wildcard matches known variants and will also match future added variants - --> $DIR/wildcard_enum_match_arm.rs:83:9 + --> $DIR/wildcard_enum_match_arm.rs:81:9 | LL | _ => {}, | ^ help: try this: `ErrorKind::PermissionDenied | _` error: wildcard matches known variants and will also match future added variants - --> $DIR/wildcard_enum_match_arm.rs:101:13 + --> $DIR/wildcard_enum_match_arm.rs:99:13 | LL | _ => (), | ^ help: try this: `Enum::B | _` diff --git a/tests/ui/write_literal.rs b/tests/ui/write_literal.rs index 5892818aa9a6..218385ea1296 100644 --- a/tests/ui/write_literal.rs +++ b/tests/ui/write_literal.rs @@ -1,5 +1,5 @@ -#![allow(unused_must_use)] #![warn(clippy::write_literal)] +#![allow(clippy::uninlined_format_args, unused_must_use)] use std::io::Write; diff --git a/tests/versioncheck.rs b/tests/versioncheck.rs index 38498ebdcf2c..9e07769a8e4f 100644 --- a/tests/versioncheck.rs +++ b/tests/versioncheck.rs @@ -8,12 +8,12 @@ use std::fs; #[test] fn check_that_clippy_lints_and_clippy_utils_have_the_same_version_as_clippy() { fn read_version(path: &str) -> String { - let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("error reading `{}`: {:?}", path, e)); + let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("error reading `{path}`: {e:?}")); contents .lines() .filter_map(|l| l.split_once('=')) .find_map(|(k, v)| (k.trim() == "version").then(|| v.trim())) - .unwrap_or_else(|| panic!("error finding version in `{}`", path)) + .unwrap_or_else(|| panic!("error finding version in `{path}`")) .to_string() } @@ -83,7 +83,7 @@ fn check_that_clippy_has_the_same_major_version_as_rustc() { // we don't want our tests failing suddenly }, _ => { - panic!("Failed to parse rustc version: {:?}", vsplit); + panic!("Failed to parse rustc version: {vsplit:?}"); }, }; } From 13dbc33d8f129453212116b90cbd96b0a013d23a Mon Sep 17 00:00:00 2001 From: ouz-a Date: Tue, 4 Oct 2022 21:39:43 +0300 Subject: [PATCH 103/309] Remove `mir::CastKind::Misc` --- clippy_utils/src/qualify_min_const_fn.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index f7ce71917726..7af27ebb9883 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -129,7 +129,12 @@ fn check_rvalue<'tcx>( | Rvalue::Use(operand) | Rvalue::Cast( CastKind::PointerFromExposedAddress - | CastKind::Misc + | CastKind::IntToInt + | CastKind::FloatToInt + | CastKind::IntToFloat + | CastKind::FloatToFloat + | CastKind::FnPtrToPtr + | CastKind::PtrToPtr | CastKind::Pointer(PointerCast::MutToConstPointer | PointerCast::ArrayToPointer), operand, _, From 187b76965a58dcb64b4ca6cd2513c517eda5ad67 Mon Sep 17 00:00:00 2001 From: ouz-a Date: Tue, 4 Oct 2022 21:39:43 +0300 Subject: [PATCH 104/309] Remove `mir::CastKind::Misc` --- src/base.rs | 7 ++++++- src/constant.rs | 11 ++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/base.rs b/src/base.rs index 11540d800816..4303d63fe213 100644 --- a/src/base.rs +++ b/src/base.rs @@ -633,7 +633,12 @@ fn codegen_stmt<'tcx>( lval.write_cvalue(fx, operand.cast_pointer_to(to_layout)); } Rvalue::Cast( - CastKind::Misc + CastKind::IntToInt + | CastKind::FloatToFloat + | CastKind::FloatToInt + | CastKind::IntToFloat + | CastKind::FnPtrToPtr + | CastKind::PtrToPtr | CastKind::PointerExposeAddress | CastKind::PointerFromExposedAddress, ref operand, diff --git a/src/constant.rs b/src/constant.rs index e12805b093cc..c5f44bb84796 100644 --- a/src/constant.rs +++ b/src/constant.rs @@ -490,7 +490,16 @@ pub(crate) fn mir_operand_get_const_val<'tcx>( match &stmt.kind { StatementKind::Assign(local_and_rvalue) if &local_and_rvalue.0 == place => { match &local_and_rvalue.1 { - Rvalue::Cast(CastKind::Misc, operand, ty) => { + Rvalue::Cast( + CastKind::IntToInt + | CastKind::FloatToFloat + | CastKind::FloatToInt + | CastKind::IntToFloat + | CastKind::FnPtrToPtr + | CastKind::PtrToPtr, + operand, + ty, + ) => { if computed_const_val.is_some() { return None; // local assigned twice } From d3c041a08696b722da3276846e0064dd03742940 Mon Sep 17 00:00:00 2001 From: Andre Bogus Date: Mon, 3 Oct 2022 17:29:38 +0200 Subject: [PATCH 105/309] extend `box-default` lint, add suggestion --- clippy_lints/src/box_default.rs | 94 ++++++++++++++++++++++++++++----- clippy_utils/src/lib.rs | 26 ++++++++- src/docs/box_default.txt | 6 --- tests/ui/box_default.fixed | 43 +++++++++++++++ tests/ui/box_default.rs | 14 ++++- tests/ui/box_default.stderr | 77 +++++++++++++++++---------- 6 files changed, 212 insertions(+), 48 deletions(-) create mode 100644 tests/ui/box_default.fixed diff --git a/clippy_lints/src/box_default.rs b/clippy_lints/src/box_default.rs index 792183ac4081..f35a79dcc739 100644 --- a/clippy_lints/src/box_default.rs +++ b/clippy_lints/src/box_default.rs @@ -1,5 +1,12 @@ -use clippy_utils::{diagnostics::span_lint_and_help, is_default_equivalent, path_def_id}; -use rustc_hir::{Expr, ExprKind, QPath}; +use clippy_utils::{ + diagnostics::span_lint_and_sugg, get_parent_node, is_default_equivalent, macros::macro_backtrace, match_path, + path_def_id, paths, ty::expr_sig, +}; +use rustc_errors::Applicability; +use rustc_hir::{ + intravisit::{walk_ty, Visitor}, + Block, Expr, ExprKind, Local, Node, QPath, TyKind, +}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -15,12 +22,6 @@ declare_clippy_lint! { /// Second, `Box::default()` can be faster /// [in certain cases](https://nnethercote.github.io/perf-book/standard-library-types.html#box). /// - /// ### Known problems - /// The lint may miss some cases (e.g. Box::new(String::from(""))). - /// On the other hand, it will trigger on cases where the `default` - /// code comes from a macro that does something different based on - /// e.g. target operating system. - /// /// ### Example /// ```rust /// let x: Box = Box::new(Default::default()); @@ -41,21 +42,88 @@ impl LateLintPass<'_> for BoxDefault { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { if let ExprKind::Call(box_new, [arg]) = expr.kind && let ExprKind::Path(QPath::TypeRelative(ty, seg)) = box_new.kind - && let ExprKind::Call(..) = arg.kind + && let ExprKind::Call(arg_path, ..) = arg.kind && !in_external_macro(cx.sess(), expr.span) - && expr.span.eq_ctxt(arg.span) + && (expr.span.eq_ctxt(arg.span) || is_vec_expn(cx, arg)) && seg.ident.name == sym::new && path_def_id(cx, ty) == cx.tcx.lang_items().owned_box() && is_default_equivalent(cx, arg) { - span_lint_and_help( + let arg_ty = cx.typeck_results().expr_ty(arg); + span_lint_and_sugg( cx, BOX_DEFAULT, expr.span, "`Box::new(_)` of default value", - None, - "use `Box::default()` instead", + "try", + if is_plain_default(arg_path) || given_type(cx, expr) { + "Box::default()".into() + } else { + format!("Box::<{arg_ty}>::default()") + }, + Applicability::MachineApplicable ); } } } + +fn is_plain_default(arg_path: &Expr<'_>) -> bool { + // we need to match the actual path so we don't match e.g. "u8::default" + if let ExprKind::Path(QPath::Resolved(None, path)) = &arg_path.kind { + // avoid generic parameters + match_path(path, &paths::DEFAULT_TRAIT_METHOD) && path.segments.iter().all(|seg| seg.args.is_none()) + } else { + false + } +} + +fn is_vec_expn(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + macro_backtrace(expr.span) + .next() + .map_or(false, |call| cx.tcx.is_diagnostic_item(sym::vec_macro, call.def_id)) +} + +#[derive(Default)] +struct InferVisitor(bool); + +impl<'tcx> Visitor<'tcx> for InferVisitor { + fn visit_ty(&mut self, t: &rustc_hir::Ty<'_>) { + self.0 |= matches!(t.kind, TyKind::Infer); + if !self.0 { + walk_ty(self, t); + } + } +} + +fn given_type(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + match get_parent_node(cx.tcx, expr.hir_id) { + Some(Node::Local(Local { ty: Some(ty), .. })) => { + let mut v = InferVisitor::default(); + v.visit_ty(ty); + !v.0 + }, + Some( + Node::Expr(Expr { + kind: ExprKind::Call(path, args), + .. + }) | Node::Block(Block { + expr: + Some(Expr { + kind: ExprKind::Call(path, args), + .. + }), + .. + }), + ) => { + if let Some(index) = args.iter().position(|arg| arg.hir_id == expr.hir_id) && + let Some(sig) = expr_sig(cx, path) && + let Some(input) = sig.input(index) + { + input.no_bound_vars().is_some() + } else { + false + } + }, + _ => false, + } +} diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 42374fdd7baf..e6492d76260e 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -815,13 +815,37 @@ pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { false } }, - ExprKind::Call(repl_func, _) => is_default_equivalent_call(cx, repl_func), + ExprKind::Call(repl_func, []) => is_default_equivalent_call(cx, repl_func), + ExprKind::Call(from_func, [ref 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::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])), _ => false, } } +fn is_default_equivalent_from(cx: &LateContext<'_>, from_func: &Expr<'_>, arg: &Expr<'_>) -> bool { + if let ExprKind::Path(QPath::TypeRelative(ty, seg)) = from_func.kind && + seg.ident.name == sym::from + { + match arg.kind { + ExprKind::Lit(hir::Lit { + node: LitKind::Str(ref sym, _), + .. + }) => return sym.is_empty() && is_path_diagnostic_item(cx, ty, sym::String), + ExprKind::Array([]) => return is_path_diagnostic_item(cx, ty, sym::Vec), + ExprKind::Repeat(_, ArrayLen::Body(len)) => { + if let ExprKind::Lit(ref const_lit) = cx.tcx.hir().body(len.body).value.kind && + let LitKind::Int(v, _) = const_lit.node + { + return v == 0 && is_path_diagnostic_item(cx, ty, sym::Vec); + } + } + _ => (), + } + } + false +} + /// Checks if the top level expression can be moved into a closure as is. /// Currently checks for: /// * Break/Continue outside the given loop HIR ids. diff --git a/src/docs/box_default.txt b/src/docs/box_default.txt index ffac894d0c50..1c670c773337 100644 --- a/src/docs/box_default.txt +++ b/src/docs/box_default.txt @@ -7,12 +7,6 @@ First, it's more complex, involving two calls instead of one. Second, `Box::default()` can be faster [in certain cases](https://nnethercote.github.io/perf-book/standard-library-types.html#box). -### Known problems -The lint may miss some cases (e.g. Box::new(String::from(""))). -On the other hand, it will trigger on cases where the `default` -code comes from a macro that does something different based on -e.g. target operating system. - ### Example ``` let x: Box = Box::new(Default::default()); diff --git a/tests/ui/box_default.fixed b/tests/ui/box_default.fixed new file mode 100644 index 000000000000..7fbb272ce5a3 --- /dev/null +++ b/tests/ui/box_default.fixed @@ -0,0 +1,43 @@ +// run-rustfix +#![warn(clippy::box_default)] + +#[derive(Default)] +struct ImplementsDefault; + +struct OwnDefault; + +impl OwnDefault { + fn default() -> Self { + Self + } +} + +macro_rules! outer { + ($e: expr) => { + $e + }; +} + +fn main() { + let _string: Box = Box::default(); + let _byte = Box::::default(); + let _vec = Box::>::default(); + let _impl = Box::::default(); + let _impl2 = Box::::default(); + let _impl3: Box = Box::default(); + let _own = Box::new(OwnDefault::default()); // should not lint + let _in_macro = outer!(Box::::default()); + let _string_default = outer!(Box::::default()); + let _vec2: Box> = Box::default(); + let _vec3: Box> = Box::default(); + let _vec4: Box<_> = Box::>::default(); + let _more = ret_ty_fn(); + call_ty_fn(Box::default()); +} + +fn ret_ty_fn() -> Box { + Box::::default() +} + +#[allow(clippy::boxed_local)] +fn call_ty_fn(_b: Box) {} diff --git a/tests/ui/box_default.rs b/tests/ui/box_default.rs index dc522705bc62..64c4f3887af7 100644 --- a/tests/ui/box_default.rs +++ b/tests/ui/box_default.rs @@ -1,3 +1,4 @@ +// run-rustfix #![warn(clippy::box_default)] #[derive(Default)] @@ -26,6 +27,17 @@ fn main() { let _impl3: Box = Box::new(Default::default()); let _own = Box::new(OwnDefault::default()); // should not lint let _in_macro = outer!(Box::new(String::new())); - // false negative: default is from different expansion + let _string_default = outer!(Box::new(String::from(""))); let _vec2: Box> = Box::new(vec![]); + let _vec3: Box> = Box::new(Vec::from([])); + let _vec4: Box<_> = Box::new(Vec::from([false; 0])); + let _more = ret_ty_fn(); + call_ty_fn(Box::new(u8::default())); } + +fn ret_ty_fn() -> Box { + Box::new(bool::default()) +} + +#[allow(clippy::boxed_local)] +fn call_ty_fn(_b: Box) {} diff --git a/tests/ui/box_default.stderr b/tests/ui/box_default.stderr index b2030e95acb1..313255fc950e 100644 --- a/tests/ui/box_default.stderr +++ b/tests/ui/box_default.stderr @@ -1,59 +1,82 @@ error: `Box::new(_)` of default value - --> $DIR/box_default.rs:21:32 + --> $DIR/box_default.rs:22:32 | LL | let _string: Box = Box::new(Default::default()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::default()` | - = help: use `Box::default()` instead = note: `-D clippy::box-default` implied by `-D warnings` error: `Box::new(_)` of default value - --> $DIR/box_default.rs:22:17 + --> $DIR/box_default.rs:23:17 | LL | let _byte = Box::new(u8::default()); - | ^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use `Box::default()` instead + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` error: `Box::new(_)` of default value - --> $DIR/box_default.rs:23:16 + --> $DIR/box_default.rs:24:16 | LL | let _vec = Box::new(Vec::::new()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use `Box::default()` instead + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::>::default()` error: `Box::new(_)` of default value - --> $DIR/box_default.rs:24:17 + --> $DIR/box_default.rs:25:17 | LL | let _impl = Box::new(ImplementsDefault::default()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use `Box::default()` instead + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` error: `Box::new(_)` of default value - --> $DIR/box_default.rs:25:18 + --> $DIR/box_default.rs:26:18 | LL | let _impl2 = Box::new(::default()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use `Box::default()` instead + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` error: `Box::new(_)` of default value - --> $DIR/box_default.rs:26:42 + --> $DIR/box_default.rs:27:42 | LL | let _impl3: Box = Box::new(Default::default()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use `Box::default()` instead + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::default()` error: `Box::new(_)` of default value - --> $DIR/box_default.rs:28:28 + --> $DIR/box_default.rs:29:28 | LL | let _in_macro = outer!(Box::new(String::new())); - | ^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` + +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:30:34 | - = help: use `Box::default()` instead +LL | let _string_default = outer!(Box::new(String::from(""))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` -error: aborting due to 7 previous errors +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:31:46 + | +LL | let _vec2: Box> = Box::new(vec![]); + | ^^^^^^^^^^^^^^^^ help: try: `Box::default()` + +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:32:33 + | +LL | let _vec3: Box> = Box::new(Vec::from([])); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::default()` + +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:33:25 + | +LL | let _vec4: Box<_> = Box::new(Vec::from([false; 0])); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::>::default()` + +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:35:16 + | +LL | call_ty_fn(Box::new(u8::default())); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::default()` + +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:39:5 + | +LL | Box::new(bool::default()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` + +error: aborting due to 13 previous errors From 09a554db25840a35bbfc13d531e288801b10fe6a Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 6 Oct 2022 17:41:53 +0200 Subject: [PATCH 106/309] Merge commit '8f1ebdd18bdecc621f16baaf779898cc08cc2766' into clippyup --- clippy_lints/src/attrs.rs | 3 +- clippy_lints/src/format_args.rs | 17 +++------ clippy_utils/src/macros.rs | 2 +- tests/ui/uninlined_format_args.fixed | 11 ++++-- tests/ui/uninlined_format_args.stderr | 53 +-------------------------- tests/ui/unsafe_removed_from_name.rs | 3 ++ 6 files changed, 20 insertions(+), 69 deletions(-) diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 5f45c69d7f98..0bd1f8b784e8 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -357,7 +357,8 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { "wildcard_imports" | "enum_glob_use" | "redundant_pub_crate" - | "macro_use_imports", + | "macro_use_imports" + | "unsafe_removed_from_name", ) }) { diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index cefebc2a98a2..99bef62f8143 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -8,7 +8,7 @@ use if_chain::if_chain; use itertools::Itertools; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, HirId, QPath}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty::adjustment::{Adjust, Adjustment}; use rustc_middle::ty::Ty; use rustc_semver::RustcVersion; @@ -173,17 +173,10 @@ fn check_uninlined_args(cx: &LateContext<'_>, args: &FormatArgsExpn<'_>, call_si return; } - // FIXME: Properly ignore a rare case where the format string is wrapped in a macro. - // Example: `format!(indoc!("{}"), foo);` - // If inlined, they will cause a compilation error: - // > to avoid ambiguity, `format_args!` cannot capture variables - // > when the format string is expanded from a macro - // @Alexendoo explanation: - // > indoc! is a proc macro that is producing a string literal with its span - // > set to its input it's not marked as from expansion, and since it's compatible - // > tokenization wise clippy_utils::is_from_proc_macro wouldn't catch it either - // This might be a relatively expensive test, so do it only we are ready to replace. - // See more examples in tests/ui/uninlined_format_args.rs + // Temporarily ignore multiline spans: https://github.com/rust-lang/rust/pull/102729#discussion_r988704308 + if fixes.iter().any(|(span, _)| cx.sess().source_map().is_multiline(*span)) { + return; + } span_lint_and_then( cx, diff --git a/clippy_utils/src/macros.rs b/clippy_utils/src/macros.rs index dd0ce1da6575..5a63c290a315 100644 --- a/clippy_utils/src/macros.rs +++ b/clippy_utils/src/macros.rs @@ -414,7 +414,7 @@ impl FormatString { struct FormatArgsValues<'tcx> { /// Values passed after the format string and implicit captures. `[1, z + 2, x]` for - /// `format!("{x} {} {y}", 1, z + 2)`. + /// `format!("{x} {} {}", 1, z + 2)`. value_args: Vec<&'tcx Expr<'tcx>>, /// Maps an `rt::v1::Argument::position` or an `rt::v1::Count::Param` to its index in /// `value_args` diff --git a/tests/ui/uninlined_format_args.fixed b/tests/ui/uninlined_format_args.fixed index dcf10ed60a25..3ca7a4019025 100644 --- a/tests/ui/uninlined_format_args.fixed +++ b/tests/ui/uninlined_format_args.fixed @@ -44,7 +44,9 @@ fn tester(fn_arg: i32) { println!("val='{local_i32}'"); // space+tab println!("val='{local_i32}'"); // tab+space println!( - "val='{local_i32}'" + "val='{ + }'", + local_i32 ); println!("{local_i32}"); println!("{fn_arg}"); @@ -108,7 +110,8 @@ fn tester(fn_arg: i32) { println!("{local_f64:width$.prec$}"); println!("{local_f64:width$.prec$} {local_f64} {width} {prec}"); println!( - "{local_i32:width$.prec$} {local_i32:prec$.width$} {width:local_i32$.prec$} {width:prec$.local_i32$} {prec:local_i32$.width$} {prec:width$.local_i32$}", + "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}", + local_i32, width, prec, ); println!( "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$} {3}", @@ -139,7 +142,9 @@ fn tester(fn_arg: i32) { println!(no_param_str!(), local_i32); println!( - "{val}", + "{}", + // comment with a comma , in it + val, ); println!("{val}"); diff --git a/tests/ui/uninlined_format_args.stderr b/tests/ui/uninlined_format_args.stderr index 1b4dada28dac..d1a774926342 100644 --- a/tests/ui/uninlined_format_args.stderr +++ b/tests/ui/uninlined_format_args.stderr @@ -59,22 +59,6 @@ LL - println!("val='{ }'", local_i32); // tab+space LL + println!("val='{local_i32}'"); // tab+space | -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:46:5 - | -LL | / println!( -LL | | "val='{ -LL | | }'", -LL | | local_i32 -LL | | ); - | |_____^ - | -help: change this to - | -LL - "val='{ -LL + "val='{local_i32}'" - | - error: variables can be used directly in the `format!` string --> $DIR/uninlined_format_args.rs:51:5 | @@ -783,25 +767,6 @@ LL - println!("{:1$.2$} {0} {1} {2}", local_f64, width, prec); LL + println!("{local_f64:width$.prec$} {local_f64} {width} {prec}"); | -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:112:5 - | -LL | / println!( -LL | | "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}", -LL | | local_i32, width, prec, -LL | | ); - | |_____^ - | -help: change this to - | -LL ~ "{local_i32:width$.prec$} {local_i32:prec$.width$} {width:local_i32$.prec$} {width:prec$.local_i32$} {prec:local_i32$.width$} {prec:width$.local_i32$}", width, prec, -LL ~ "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}", width, prec, -LL ~ "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}", width, prec, -LL ~ "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}", width, prec, -LL ~ "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}", width, prec, -LL ~ "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}", - | - error: variables can be used directly in the `format!` string --> $DIR/uninlined_format_args.rs:123:5 | @@ -850,22 +815,6 @@ LL - println!("{}", format!("{}", local_i32)); LL + println!("{}", format!("{local_i32}")); | -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:144:5 - | -LL | / println!( -LL | | "{}", -LL | | // comment with a comma , in it -LL | | val, -LL | | ); - | |_____^ - | -help: change this to - | -LL - "{}", -LL + "{val}", - | - error: variables can be used directly in the `format!` string --> $DIR/uninlined_format_args.rs:149:5 | @@ -890,5 +839,5 @@ LL - println!("expand='{}'", local_i32); LL + println!("expand='{local_i32}'"); | -error: aborting due to 73 previous errors +error: aborting due to 70 previous errors diff --git a/tests/ui/unsafe_removed_from_name.rs b/tests/ui/unsafe_removed_from_name.rs index cde4e96d668c..d29888ac62f6 100644 --- a/tests/ui/unsafe_removed_from_name.rs +++ b/tests/ui/unsafe_removed_from_name.rs @@ -24,4 +24,7 @@ use mod_with_some_unsafe_things::Unsafe as LieAboutModSafety; use mod_with_some_unsafe_things::Safe as IPromiseItsSafeThisTime; use mod_with_some_unsafe_things::Unsafe as SuperUnsafeModThing; +#[allow(clippy::unsafe_removed_from_name)] +use mod_with_some_unsafe_things::Unsafe as SuperSafeThing; + fn main() {} From 493684888f0f68b43d46d70b89c2f2a75091601f Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Thu, 6 Oct 2022 12:12:55 -0400 Subject: [PATCH 107/309] Change uninlined_format_args into a style lint --- clippy_lints/src/format_args.rs | 2 +- clippy_lints/src/lib.register_all.rs | 1 + clippy_lints/src/lib.register_pedantic.rs | 1 - clippy_lints/src/lib.register_style.rs | 1 + 4 files changed, 3 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index 45ed21e066ad..f8da7f222788 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -111,7 +111,7 @@ declare_clippy_lint! { /// nothing will be suggested, e.g. `println!("{0}={1}", var, 1+2)`. #[clippy::version = "1.65.0"] pub UNINLINED_FORMAT_ARGS, - pedantic, + style, "using non-inlined variables in `format!` calls" } diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 5d26e4b33601..d09ee6443702 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -71,6 +71,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(format::USELESS_FORMAT), LintId::of(format_args::FORMAT_IN_FORMAT_ARGS), LintId::of(format_args::TO_STRING_IN_FORMAT_ARGS), + LintId::of(format_args::UNINLINED_FORMAT_ARGS), LintId::of(format_impl::PRINT_IN_FORMAT_IMPL), LintId::of(format_impl::RECURSIVE_FORMAT_IMPL), LintId::of(formatting::POSSIBLE_MISSING_COMMA), diff --git a/clippy_lints/src/lib.register_pedantic.rs b/clippy_lints/src/lib.register_pedantic.rs index dd3e2b7d29c1..1b9d575f3e4c 100644 --- a/clippy_lints/src/lib.register_pedantic.rs +++ b/clippy_lints/src/lib.register_pedantic.rs @@ -29,7 +29,6 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS), LintId::of(excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS), LintId::of(excessive_bools::STRUCT_EXCESSIVE_BOOLS), - LintId::of(format_args::UNINLINED_FORMAT_ARGS), LintId::of(functions::MUST_USE_CANDIDATE), LintId::of(functions::TOO_MANY_LINES), LintId::of(if_not_else::IF_NOT_ELSE), diff --git a/clippy_lints/src/lib.register_style.rs b/clippy_lints/src/lib.register_style.rs index 8e1390167dc8..81fefb84e0da 100644 --- a/clippy_lints/src/lib.register_style.rs +++ b/clippy_lints/src/lib.register_style.rs @@ -25,6 +25,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(enum_variants::MODULE_INCEPTION), LintId::of(eta_reduction::REDUNDANT_CLOSURE), LintId::of(float_literal::EXCESSIVE_PRECISION), + LintId::of(format_args::UNINLINED_FORMAT_ARGS), LintId::of(from_over_into::FROM_OVER_INTO), LintId::of(from_str_radix_10::FROM_STR_RADIX_10), LintId::of(functions::DOUBLE_MUST_USE), From 23b16998c35674e74511bfe70a7324ca12119976 Mon Sep 17 00:00:00 2001 From: Evan Typanski Date: Thu, 6 Oct 2022 15:14:55 -0400 Subject: [PATCH 108/309] Add curlies to scrutinees with side effects --- .../src/matches/match_single_binding.rs | 31 +++++++++++++------ tests/ui/match_single_binding.fixed | 9 ++++++ tests/ui/match_single_binding.rs | 8 +++++ tests/ui/match_single_binding.stderr | 19 +++++++++++- 4 files changed, 57 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/matches/match_single_binding.rs b/clippy_lints/src/matches/match_single_binding.rs index 68682cedf1de..b64c45887483 100644 --- a/clippy_lints/src/matches/match_single_binding.rs +++ b/clippy_lints/src/matches/match_single_binding.rs @@ -58,6 +58,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e &snippet_body, &mut applicability, Some(span), + false, ); span_lint_and_sugg( @@ -90,6 +91,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e &snippet_body, &mut applicability, None, + false, ); (expr.span, sugg) }, @@ -107,10 +109,14 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e }, PatKind::Wild => { if ex.can_have_side_effects() { - let indent = " ".repeat(indent_of(cx, expr.span).unwrap_or(0)); - let sugg = format!( - "{};\n{indent}{snippet_body}", - snippet_with_applicability(cx, ex.span, "..", &mut applicability) + let sugg = sugg_with_curlies( + cx, + (ex, expr), + (bind_names, matched_vars), + &snippet_body, + &mut applicability, + None, + true, ); span_lint_and_sugg( @@ -169,6 +175,7 @@ fn sugg_with_curlies<'a>( snippet_body: &str, applicability: &mut Applicability, assignment: Option, + needs_var_binding: bool, ) -> String { let mut indent = " ".repeat(indent_of(cx, ex.span).unwrap_or(0)); @@ -200,9 +207,15 @@ fn sugg_with_curlies<'a>( s }); - format!( - "{cbrace_start}let {} = {};\n{indent}{assignment_str}{snippet_body}{cbrace_end}", - snippet_with_applicability(cx, bind_names, "..", applicability), - snippet_with_applicability(cx, matched_vars, "..", applicability) - ) + let scrutinee = if needs_var_binding { + snippet_with_applicability(cx, matched_vars, "..", applicability).to_string() + } else { + format!( + "let {} = {}", + snippet_with_applicability(cx, bind_names, "..", applicability), + snippet_with_applicability(cx, matched_vars, "..", applicability) + ) + }; + + format!("{cbrace_start}{scrutinee};\n{indent}{assignment_str}{snippet_body}{cbrace_end}") } diff --git a/tests/ui/match_single_binding.fixed b/tests/ui/match_single_binding.fixed index 951f552eb32b..a6e315e4773a 100644 --- a/tests/ui/match_single_binding.fixed +++ b/tests/ui/match_single_binding.fixed @@ -124,3 +124,12 @@ fn issue_8723() { let _ = val; } + +#[allow(dead_code)] +fn issue_9575() { + fn side_effects() {} + let _ = || { + side_effects(); + println!("Needs curlies"); + }; +} diff --git a/tests/ui/match_single_binding.rs b/tests/ui/match_single_binding.rs index 19c0fee8fd68..cecbd703e566 100644 --- a/tests/ui/match_single_binding.rs +++ b/tests/ui/match_single_binding.rs @@ -140,3 +140,11 @@ fn issue_8723() { let _ = val; } + +#[allow(dead_code)] +fn issue_9575() { + fn side_effects() {} + let _ = || match side_effects() { + _ => println!("Needs curlies"), + }; +} diff --git a/tests/ui/match_single_binding.stderr b/tests/ui/match_single_binding.stderr index 5d4e7314b213..2b9ec7ee7026 100644 --- a/tests/ui/match_single_binding.stderr +++ b/tests/ui/match_single_binding.stderr @@ -196,5 +196,22 @@ LL + suf LL ~ }; | -error: aborting due to 13 previous errors +error: this match could be replaced by its scrutinee and body + --> $DIR/match_single_binding.rs:147:16 + | +LL | let _ = || match side_effects() { + | ________________^ +LL | | _ => println!("Needs curlies"), +LL | | }; + | |_____^ + | +help: consider using the scrutinee and body instead + | +LL ~ let _ = || { +LL + side_effects(); +LL + println!("Needs curlies"); +LL ~ }; + | + +error: aborting due to 14 previous errors From 39164acf6e62a3ff55d6c210acc1095d9ccc95bb Mon Sep 17 00:00:00 2001 From: Evan Typanski Date: Thu, 6 Oct 2022 15:36:28 -0400 Subject: [PATCH 109/309] Fix flipped variable that made it through --- clippy_lints/src/matches/match_single_binding.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/matches/match_single_binding.rs b/clippy_lints/src/matches/match_single_binding.rs index b64c45887483..1bf8d4e96ad4 100644 --- a/clippy_lints/src/matches/match_single_binding.rs +++ b/clippy_lints/src/matches/match_single_binding.rs @@ -58,7 +58,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e &snippet_body, &mut applicability, Some(span), - false, + true, ); span_lint_and_sugg( @@ -91,7 +91,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e &snippet_body, &mut applicability, None, - false, + true, ); (expr.span, sugg) }, @@ -116,7 +116,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e &snippet_body, &mut applicability, None, - true, + false, ); span_lint_and_sugg( @@ -208,13 +208,13 @@ fn sugg_with_curlies<'a>( }); let scrutinee = if needs_var_binding { - snippet_with_applicability(cx, matched_vars, "..", applicability).to_string() - } else { format!( "let {} = {}", snippet_with_applicability(cx, bind_names, "..", applicability), snippet_with_applicability(cx, matched_vars, "..", applicability) ) + } else { + snippet_with_applicability(cx, matched_vars, "..", applicability).to_string() }; format!("{cbrace_start}{scrutinee};\n{indent}{assignment_str}{snippet_body}{cbrace_end}") From 2618f7ae04bb458f53fc8fe4c40b54a9377ce0bb Mon Sep 17 00:00:00 2001 From: Thom Chiovoloni Date: Thu, 6 Oct 2022 20:35:58 -0700 Subject: [PATCH 110/309] Don't needless link to `libresolv` on Darwin in libstd --- library/std/src/sys/unix/mod.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/library/std/src/sys/unix/mod.rs b/library/std/src/sys/unix/mod.rs index c84e292eac15..c84e9e0d43b6 100644 --- a/library/std/src/sys/unix/mod.rs +++ b/library/std/src/sys/unix/mod.rs @@ -352,16 +352,12 @@ cfg_if::cfg_if! { extern "C" {} } else if #[cfg(target_os = "macos")] { #[link(name = "System")] - // res_init and friends require -lresolv on macOS/iOS. - // See #41582 and https://blog.achernya.com/2013/03/os-x-has-silly-libsystem.html - #[link(name = "resolv")] extern "C" {} } else if #[cfg(any(target_os = "ios", target_os = "watchos"))] { #[link(name = "System")] #[link(name = "objc")] #[link(name = "Security", kind = "framework")] #[link(name = "Foundation", kind = "framework")] - #[link(name = "resolv")] extern "C" {} } else if #[cfg(target_os = "fuchsia")] { #[link(name = "zircon")] From 037f698147eeb97699c06a270e8774d2f7a6fbbc Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Thu, 25 Aug 2022 00:14:21 +0000 Subject: [PATCH 111/309] `needless_borrow` uses `used_exactly_once` --- clippy_lints/src/dereference.rs | 99 ++++- clippy_lints/src/lib.rs | 1 - clippy_lints/src/redundant_clone.rs | 454 ++------------------ clippy_utils/src/lib.rs | 3 + clippy_utils/src/mir/maybe_storage_live.rs | 52 +++ clippy_utils/src/mir/mod.rs | 165 +++++++ clippy_utils/src/mir/possible_borrower.rs | 241 +++++++++++ clippy_utils/src/mir/possible_origin.rs | 59 +++ clippy_utils/src/mir/transitive_relation.rs | 29 ++ clippy_utils/src/paths.rs | 6 - tests/ui/needless_borrow.fixed | 62 ++- tests/ui/needless_borrow.rs | 60 ++- tests/ui/needless_borrow.stderr | 96 +++-- tests/ui/unnecessary_to_owned.fixed | 2 +- tests/ui/unnecessary_to_owned.rs | 2 +- 15 files changed, 855 insertions(+), 476 deletions(-) create mode 100644 clippy_utils/src/mir/maybe_storage_live.rs create mode 100644 clippy_utils/src/mir/mod.rs create mode 100644 clippy_utils/src/mir/possible_borrower.rs create mode 100644 clippy_utils/src/mir/possible_origin.rs create mode 100644 clippy_utils/src/mir/transitive_relation.rs diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 3cd8f236e7a5..45ee2fce4e47 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; +use clippy_utils::mir::{enclosing_mir, expr_local, local_assignments, used_exactly_once, PossibleBorrowerMap}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; use clippy_utils::ty::{expr_sig, is_copy, peel_mid_ty_refs, ty_sig, variant_of_res}; @@ -11,13 +12,16 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_ty, Visitor}; use rustc_hir::{ - self as hir, def_id::DefId, BindingAnnotation, Body, BodyId, BorrowKind, Closure, Expr, ExprKind, FnRetTy, - GenericArg, HirId, ImplItem, ImplItemKind, Item, ItemKind, Local, MatchSource, Mutability, Node, Pat, PatKind, - Path, QPath, TraitItem, TraitItemKind, TyKind, UnOp, + self as hir, + def_id::{DefId, LocalDefId}, + BindingAnnotation, Body, BodyId, BorrowKind, Closure, Expr, ExprKind, FnRetTy, GenericArg, HirId, ImplItem, + ImplItemKind, Item, ItemKind, Local, MatchSource, Mutability, Node, Pat, PatKind, Path, QPath, TraitItem, + TraitItemKind, TyKind, UnOp, }; use rustc_index::bit_set::BitSet; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::mir::{Rvalue, StatementKind}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; use rustc_middle::ty::{ self, Binder, BoundVariableKind, EarlyBinder, FnSig, GenericArgKind, List, ParamTy, PredicateKind, @@ -141,7 +145,7 @@ declare_clippy_lint! { "dereferencing when the compiler would automatically dereference" } -impl_lint_pass!(Dereferencing => [ +impl_lint_pass!(Dereferencing<'_> => [ EXPLICIT_DEREF_METHODS, NEEDLESS_BORROW, REF_BINDING_TO_REFERENCE, @@ -149,7 +153,7 @@ impl_lint_pass!(Dereferencing => [ ]); #[derive(Default)] -pub struct Dereferencing { +pub struct Dereferencing<'tcx> { state: Option<(State, StateData)>, // While parsing a `deref` method call in ufcs form, the path to the function is itself an @@ -170,11 +174,16 @@ pub struct Dereferencing { /// e.g. `m!(x) | Foo::Bar(ref x)` ref_locals: FxIndexMap>, + /// Stack of (body owner, `PossibleBorrowerMap`) pairs. Used by + /// `needless_borrow_impl_arg_position` to determine when a borrowed expression can instead + /// be moved. + possible_borrowers: Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, + // `IntoIterator` for arrays requires Rust 1.53. msrv: Option, } -impl Dereferencing { +impl<'tcx> Dereferencing<'tcx> { #[must_use] pub fn new(msrv: Option) -> Self { Self { @@ -244,7 +253,7 @@ struct RefPat { hir_id: HirId, } -impl<'tcx> LateLintPass<'tcx> for Dereferencing { +impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { #[expect(clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // Skip path expressions from deref calls. e.g. `Deref::deref(e)` @@ -278,7 +287,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing { match (self.state.take(), kind) { (None, kind) => { let expr_ty = typeck.expr_ty(expr); - let (position, adjustments) = walk_parents(cx, expr, self.msrv); + let (position, adjustments) = walk_parents(cx, &mut self.possible_borrowers, expr, self.msrv); match kind { RefOp::Deref => { if let Position::FieldAccess { @@ -550,6 +559,12 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing { } fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) { + if self.possible_borrowers.last().map_or(false, |&(local_def_id, _)| { + local_def_id == cx.tcx.hir().body_owner_def_id(body.id()) + }) { + self.possible_borrowers.pop(); + } + if Some(body.id()) == self.current_body { for pat in self.ref_locals.drain(..).filter_map(|(_, x)| x) { let replacements = pat.replacements; @@ -682,6 +697,7 @@ impl Position { #[expect(clippy::too_many_lines)] fn walk_parents<'tcx>( cx: &LateContext<'tcx>, + possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, e: &'tcx Expr<'_>, msrv: Option, ) -> (Position, &'tcx [Adjustment<'tcx>]) { @@ -796,7 +812,16 @@ fn walk_parents<'tcx>( Some(hir_ty) => binding_ty_auto_deref_stability(cx, hir_ty, precedence, ty.bound_vars()), None => { if let ty::Param(param_ty) = ty.skip_binder().kind() { - needless_borrow_impl_arg_position(cx, parent, i, *param_ty, e, precedence, msrv) + needless_borrow_impl_arg_position( + cx, + possible_borrowers, + parent, + i, + *param_ty, + e, + precedence, + msrv, + ) } else { ty_auto_deref_stability(cx, cx.tcx.erase_late_bound_regions(ty), precedence) .position_for_arg() @@ -844,7 +869,16 @@ fn walk_parents<'tcx>( args.iter().position(|arg| arg.hir_id == child_id).map(|i| { let ty = cx.tcx.fn_sig(id).skip_binder().inputs()[i + 1]; if let ty::Param(param_ty) = ty.kind() { - needless_borrow_impl_arg_position(cx, parent, i + 1, *param_ty, e, precedence, msrv) + needless_borrow_impl_arg_position( + cx, + possible_borrowers, + parent, + i + 1, + *param_ty, + e, + precedence, + msrv, + ) } else { ty_auto_deref_stability( cx, @@ -1018,8 +1052,10 @@ fn ty_contains_infer(ty: &hir::Ty<'_>) -> bool { // If the conditions are met, returns `Some(Position::ImplArg(..))`; otherwise, returns `None`. // The "is copyable" condition is to avoid the case where removing the `&` means `e` would have to // be moved, but it cannot be. +#[expect(clippy::too_many_arguments)] fn needless_borrow_impl_arg_position<'tcx>( cx: &LateContext<'tcx>, + possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, parent: &Expr<'tcx>, arg_index: usize, param_ty: ParamTy, @@ -1082,10 +1118,13 @@ fn needless_borrow_impl_arg_position<'tcx>( // elements are modified each time `check_referent` is called. let mut substs_with_referent_ty = substs_with_expr_ty.to_vec(); - let mut check_referent = |referent| { + let mut check_reference_and_referent = |reference, referent| { let referent_ty = cx.typeck_results().expr_ty(referent); - if !is_copy(cx, referent_ty) { + if !is_copy(cx, referent_ty) + && (referent_ty.has_significant_drop(cx.tcx, cx.param_env) + || !referent_used_exactly_once(cx, possible_borrowers, reference)) + { return false; } @@ -1127,7 +1166,7 @@ fn needless_borrow_impl_arg_position<'tcx>( let mut needless_borrow = false; while let ExprKind::AddrOf(_, _, referent) = expr.kind { - if !check_referent(referent) { + if !check_reference_and_referent(expr, referent) { break; } expr = referent; @@ -1155,6 +1194,36 @@ fn has_ref_mut_self_method(cx: &LateContext<'_>, trait_def_id: DefId) -> bool { }) } +fn referent_used_exactly_once<'a, 'tcx>( + cx: &'a LateContext<'tcx>, + possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, + reference: &Expr<'tcx>, +) -> bool { + let mir = enclosing_mir(cx.tcx, reference.hir_id); + if let Some(local) = expr_local(cx.tcx, reference) + && let [location] = *local_assignments(mir, local).as_slice() + && let StatementKind::Assign(box (_, Rvalue::Ref(_, _, place))) = + mir.basic_blocks[location.block].statements[location.statement_index].kind + && !place.has_deref() + { + let body_owner_local_def_id = cx.tcx.hir().enclosing_body_owner(reference.hir_id); + if possible_borrowers + .last() + .map_or(true, |&(local_def_id, _)| local_def_id != body_owner_local_def_id) + { + possible_borrowers.push((body_owner_local_def_id, PossibleBorrowerMap::new(cx, mir))); + } + let possible_borrower = &mut possible_borrowers.last_mut().unwrap().1; + // If `only_borrowers` were used here, the `copyable_iterator::warn` test would fail. The reason is + // that `PossibleBorrowerVisitor::visit_terminator` considers `place.local` a possible borrower of + // itself. See the comment in that method for an explanation as to why. + possible_borrower.bounded_borrowers(&[local], &[local, place.local], place.local, location) + && used_exactly_once(mir, place.local).unwrap_or(false) + } else { + false + } +} + // Iteratively replaces `param_ty` with `new_ty` in `substs`, and similarly for each resulting // projected type that is a type parameter. Returns `false` if replacing the types would have an // effect on the function signature beyond substituting `new_ty` for `param_ty`. @@ -1439,8 +1508,8 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data } } -impl Dereferencing { - fn check_local_usage<'tcx>(&mut self, cx: &LateContext<'tcx>, e: &Expr<'tcx>, local: HirId) { +impl<'tcx> Dereferencing<'tcx> { + fn check_local_usage(&mut self, cx: &LateContext<'tcx>, e: &Expr<'tcx>, local: HirId) { if let Some(outer_pat) = self.ref_locals.get_mut(&local) { if let Some(pat) = outer_pat { // Check for auto-deref diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 2dcefd78763b..d9dfb4fc4e0e 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -38,7 +38,6 @@ extern crate rustc_infer; extern crate rustc_lexer; extern crate rustc_lint; extern crate rustc_middle; -extern crate rustc_mir_dataflow; extern crate rustc_parse; extern crate rustc_session; extern crate rustc_span; diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index 9fd86331ec75..aedbe08e3e46 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -1,25 +1,18 @@ use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; +use clippy_utils::mir::{visit_local_usage, LocalUsage, PossibleBorrowerMap}; use clippy_utils::source::snippet_opt; use clippy_utils::ty::{has_drop, is_copy, is_type_diagnostic_item, walk_ptrs_ty_depth}; use clippy_utils::{fn_has_unsatisfiable_preds, match_def_path, paths}; use if_chain::if_chain; -use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::{def_id, Body, FnDecl, HirId}; -use rustc_index::bit_set::{BitSet, HybridBitSet}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::mir::{ - self, traversal, - visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor as _}, - Mutability, -}; -use rustc_middle::ty::{self, visit::TypeVisitor, Ty}; -use rustc_mir_dataflow::{Analysis, AnalysisDomain, CallReturnPlaces, GenKill, GenKillAnalysis, ResultsCursor}; +use rustc_middle::mir; +use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::{BytePos, Span}; use rustc_span::sym; -use std::ops::ControlFlow; macro_rules! unwrap_or_continue { ($x:expr) => { @@ -89,21 +82,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { let mir = cx.tcx.optimized_mir(def_id.to_def_id()); - let possible_origin = { - let mut vis = PossibleOriginVisitor::new(mir); - vis.visit_body(mir); - vis.into_map(cx) - }; - let maybe_storage_live_result = MaybeStorageLive - .into_engine(cx.tcx, mir) - .pass_name("redundant_clone") - .iterate_to_fixpoint() - .into_results_cursor(mir); - let mut possible_borrower = { - let mut vis = PossibleBorrowerVisitor::new(cx, mir, possible_origin); - vis.visit_body(mir); - vis.into_map(cx, maybe_storage_live_result) - }; + let mut possible_borrower = PossibleBorrowerMap::new(cx, mir); for (bb, bbdata) in mir.basic_blocks.iter_enumerated() { let terminator = bbdata.terminator(); @@ -374,403 +353,40 @@ struct CloneUsage { /// Whether the clone value is mutated. clone_consumed_or_mutated: bool, } + fn visit_clone_usage(cloned: mir::Local, clone: mir::Local, mir: &mir::Body<'_>, bb: mir::BasicBlock) -> CloneUsage { - struct V { - cloned: mir::Local, - clone: mir::Local, - result: CloneUsage, - } - impl<'tcx> mir::visit::Visitor<'tcx> for V { - fn visit_basic_block_data(&mut self, block: mir::BasicBlock, data: &mir::BasicBlockData<'tcx>) { - let statements = &data.statements; - for (statement_index, statement) in statements.iter().enumerate() { - self.visit_statement(statement, mir::Location { block, statement_index }); - } - - self.visit_terminator( - data.terminator(), - mir::Location { - block, - statement_index: statements.len(), - }, - ); - } - - fn visit_place(&mut self, place: &mir::Place<'tcx>, ctx: PlaceContext, loc: mir::Location) { - let local = place.local; - - if local == self.cloned - && !matches!( - ctx, - PlaceContext::MutatingUse(MutatingUseContext::Drop) | PlaceContext::NonUse(_) - ) - { - self.result.cloned_used = true; - self.result.cloned_consume_or_mutate_loc = self.result.cloned_consume_or_mutate_loc.or_else(|| { - matches!( - ctx, - PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) - | PlaceContext::MutatingUse(MutatingUseContext::Borrow) - ) - .then(|| loc) - }); - } else if local == self.clone { - match ctx { - PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) - | PlaceContext::MutatingUse(MutatingUseContext::Borrow) => { - self.result.clone_consumed_or_mutated = true; - }, - _ => {}, - } - } - } - } - - let init = CloneUsage { - cloned_used: false, - cloned_consume_or_mutate_loc: None, - // Consider non-temporary clones consumed. - // TODO: Actually check for mutation of non-temporaries. - clone_consumed_or_mutated: mir.local_kind(clone) != mir::LocalKind::Temp, - }; - traversal::ReversePostorder::new(mir, bb) - .skip(1) - .fold(init, |usage, (tbb, tdata)| { - // Short-circuit - if (usage.cloned_used && usage.clone_consumed_or_mutated) || - // Give up on loops - tdata.terminator().successors().any(|s| s == bb) - { - return CloneUsage { - cloned_used: true, - clone_consumed_or_mutated: true, - ..usage - }; - } - - let mut v = V { - cloned, - clone, - result: usage, - }; - v.visit_basic_block_data(tbb, tdata); - v.result - }) -} - -/// Determines liveness of each local purely based on `StorageLive`/`Dead`. -#[derive(Copy, Clone)] -struct MaybeStorageLive; - -impl<'tcx> AnalysisDomain<'tcx> for MaybeStorageLive { - type Domain = BitSet; - const NAME: &'static str = "maybe_storage_live"; - - fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { - // bottom = dead - BitSet::new_empty(body.local_decls.len()) - } - - fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domain) { - for arg in body.args_iter() { - state.insert(arg); - } - } -} - -impl<'tcx> GenKillAnalysis<'tcx> for MaybeStorageLive { - type Idx = mir::Local; - - fn statement_effect(&self, trans: &mut impl GenKill, stmt: &mir::Statement<'tcx>, _: mir::Location) { - match stmt.kind { - mir::StatementKind::StorageLive(l) => trans.gen(l), - mir::StatementKind::StorageDead(l) => trans.kill(l), - _ => (), - } - } - - fn terminator_effect( - &self, - _trans: &mut impl GenKill, - _terminator: &mir::Terminator<'tcx>, - _loc: mir::Location, - ) { - } - - fn call_return_effect( - &self, - _trans: &mut impl GenKill, - _block: mir::BasicBlock, - _return_places: CallReturnPlaces<'_, 'tcx>, - ) { - // Nothing to do when a call returns successfully - } -} - -/// Collects the possible borrowers of each local. -/// For example, `b = &a; c = &a;` will make `b` and (transitively) `c` -/// possible borrowers of `a`. -struct PossibleBorrowerVisitor<'a, 'tcx> { - possible_borrower: TransitiveRelation, - body: &'a mir::Body<'tcx>, - cx: &'a LateContext<'tcx>, - possible_origin: FxHashMap>, -} - -impl<'a, 'tcx> PossibleBorrowerVisitor<'a, 'tcx> { - fn new( - cx: &'a LateContext<'tcx>, - body: &'a mir::Body<'tcx>, - possible_origin: FxHashMap>, - ) -> Self { - Self { - possible_borrower: TransitiveRelation::default(), - cx, - body, - possible_origin, - } - } - - fn into_map( - self, - cx: &LateContext<'tcx>, - maybe_live: ResultsCursor<'tcx, 'tcx, MaybeStorageLive>, - ) -> PossibleBorrowerMap<'a, 'tcx> { - let mut map = FxHashMap::default(); - for row in (1..self.body.local_decls.len()).map(mir::Local::from_usize) { - if is_copy(cx, self.body.local_decls[row].ty) { - continue; - } - - let mut borrowers = self.possible_borrower.reachable_from(row, self.body.local_decls.len()); - borrowers.remove(mir::Local::from_usize(0)); - if !borrowers.is_empty() { - map.insert(row, borrowers); - } - } - - let bs = BitSet::new_empty(self.body.local_decls.len()); - PossibleBorrowerMap { - map, - maybe_live, - bitset: (bs.clone(), bs), - } - } -} - -impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'tcx> { - fn visit_assign(&mut self, place: &mir::Place<'tcx>, rvalue: &mir::Rvalue<'_>, _location: mir::Location) { - let lhs = place.local; - match rvalue { - mir::Rvalue::Ref(_, _, borrowed) => { - self.possible_borrower.add(borrowed.local, lhs); - }, - other => { - if ContainsRegion - .visit_ty(place.ty(&self.body.local_decls, self.cx.tcx).ty) - .is_continue() - { - return; - } - rvalue_locals(other, |rhs| { - if lhs != rhs { - self.possible_borrower.add(rhs, lhs); - } - }); - }, - } - } - - fn visit_terminator(&mut self, terminator: &mir::Terminator<'_>, _loc: mir::Location) { - if let mir::TerminatorKind::Call { - args, - destination: mir::Place { local: dest, .. }, - .. - } = &terminator.kind - { - // TODO add doc - // If the call returns something with lifetimes, - // let's conservatively assume the returned value contains lifetime of all the arguments. - // For example, given `let y: Foo<'a> = foo(x)`, `y` is considered to be a possible borrower of `x`. - - let mut immutable_borrowers = vec![]; - let mut mutable_borrowers = vec![]; - - for op in args { - match op { - mir::Operand::Copy(p) | mir::Operand::Move(p) => { - if let ty::Ref(_, _, Mutability::Mut) = self.body.local_decls[p.local].ty.kind() { - mutable_borrowers.push(p.local); - } else { - immutable_borrowers.push(p.local); - } - }, - mir::Operand::Constant(..) => (), - } - } - - let mut mutable_variables: Vec = mutable_borrowers - .iter() - .filter_map(|r| self.possible_origin.get(r)) - .flat_map(HybridBitSet::iter) - .collect(); - - if ContainsRegion.visit_ty(self.body.local_decls[*dest].ty).is_break() { - mutable_variables.push(*dest); - } - - for y in mutable_variables { - for x in &immutable_borrowers { - self.possible_borrower.add(*x, y); - } - for x in &mutable_borrowers { - self.possible_borrower.add(*x, y); - } - } - } - } -} - -/// Collect possible borrowed for every `&mut` local. -/// For example, `_1 = &mut _2` generate _1: {_2,...} -/// Known Problems: not sure all borrowed are tracked -struct PossibleOriginVisitor<'a, 'tcx> { - possible_origin: TransitiveRelation, - body: &'a mir::Body<'tcx>, -} - -impl<'a, 'tcx> PossibleOriginVisitor<'a, 'tcx> { - fn new(body: &'a mir::Body<'tcx>) -> Self { - Self { - possible_origin: TransitiveRelation::default(), - body, - } - } - - fn into_map(self, cx: &LateContext<'tcx>) -> FxHashMap> { - let mut map = FxHashMap::default(); - for row in (1..self.body.local_decls.len()).map(mir::Local::from_usize) { - if is_copy(cx, self.body.local_decls[row].ty) { - continue; - } - - let mut borrowers = self.possible_origin.reachable_from(row, self.body.local_decls.len()); - borrowers.remove(mir::Local::from_usize(0)); - if !borrowers.is_empty() { - map.insert(row, borrowers); - } - } - map - } -} - -impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleOriginVisitor<'a, 'tcx> { - fn visit_assign(&mut self, place: &mir::Place<'tcx>, rvalue: &mir::Rvalue<'_>, _location: mir::Location) { - let lhs = place.local; - match rvalue { - // Only consider `&mut`, which can modify origin place - mir::Rvalue::Ref(_, rustc_middle::mir::BorrowKind::Mut { .. }, borrowed) | - // _2: &mut _; - // _3 = move _2 - mir::Rvalue::Use(mir::Operand::Move(borrowed)) | - // _3 = move _2 as &mut _; - mir::Rvalue::Cast(_, mir::Operand::Move(borrowed), _) - => { - self.possible_origin.add(lhs, borrowed.local); - }, - _ => {}, - } - } -} - -struct ContainsRegion; - -impl TypeVisitor<'_> for ContainsRegion { - type BreakTy = (); - - fn visit_region(&mut self, _: ty::Region<'_>) -> ControlFlow { - ControlFlow::BREAK - } -} - -fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) { - use rustc_middle::mir::Rvalue::{Aggregate, BinaryOp, Cast, CheckedBinaryOp, Repeat, UnaryOp, Use}; - - let mut visit_op = |op: &mir::Operand<'_>| match op { - mir::Operand::Copy(p) | mir::Operand::Move(p) => visit(p.local), - mir::Operand::Constant(..) => (), - }; - - match rvalue { - Use(op) | Repeat(op, _) | Cast(_, op, _) | UnaryOp(_, op) => visit_op(op), - Aggregate(_, ops) => ops.iter().for_each(visit_op), - BinaryOp(_, box (lhs, rhs)) | CheckedBinaryOp(_, box (lhs, rhs)) => { - visit_op(lhs); - visit_op(rhs); + if let Some(( + LocalUsage { + local_use_locs: cloned_use_locs, + local_consume_or_mutate_locs: cloned_consume_or_mutate_locs, }, - _ => (), - } -} - -/// Result of `PossibleBorrowerVisitor`. -struct PossibleBorrowerMap<'a, 'tcx> { - /// Mapping `Local -> its possible borrowers` - map: FxHashMap>, - maybe_live: ResultsCursor<'a, 'tcx, MaybeStorageLive>, - // Caches to avoid allocation of `BitSet` on every query - bitset: (BitSet, BitSet), -} - -impl PossibleBorrowerMap<'_, '_> { - /// Returns true if the set of borrowers of `borrowed` living at `at` matches with `borrowers`. - fn only_borrowers(&mut self, borrowers: &[mir::Local], borrowed: mir::Local, at: mir::Location) -> bool { - self.maybe_live.seek_after_primary_effect(at); - - self.bitset.0.clear(); - let maybe_live = &mut self.maybe_live; - if let Some(bitset) = self.map.get(&borrowed) { - for b in bitset.iter().filter(move |b| maybe_live.contains(*b)) { - self.bitset.0.insert(b); - } - } else { - return false; + LocalUsage { + local_use_locs: _, + local_consume_or_mutate_locs: clone_consume_or_mutate_locs, + }, + )) = visit_local_usage( + &[cloned, clone], + mir, + mir::Location { + block: bb, + statement_index: mir.basic_blocks[bb].statements.len(), + }, + ) + .map(|mut vec| (vec.remove(0), vec.remove(0))) + { + CloneUsage { + cloned_used: !cloned_use_locs.is_empty(), + cloned_consume_or_mutate_loc: cloned_consume_or_mutate_locs.first().copied(), + // Consider non-temporary clones consumed. + // TODO: Actually check for mutation of non-temporaries. + clone_consumed_or_mutated: mir.local_kind(clone) != mir::LocalKind::Temp + || !clone_consume_or_mutate_locs.is_empty(), } - - self.bitset.1.clear(); - for b in borrowers { - self.bitset.1.insert(*b); + } else { + CloneUsage { + cloned_used: true, + cloned_consume_or_mutate_loc: None, + clone_consumed_or_mutated: true, } - - self.bitset.0 == self.bitset.1 - } - - fn local_is_alive_at(&mut self, local: mir::Local, at: mir::Location) -> bool { - self.maybe_live.seek_after_primary_effect(at); - self.maybe_live.contains(local) - } -} - -#[derive(Default)] -struct TransitiveRelation { - relations: FxHashMap>, -} -impl TransitiveRelation { - fn add(&mut self, a: mir::Local, b: mir::Local) { - self.relations.entry(a).or_default().push(b); - } - - fn reachable_from(&self, a: mir::Local, domain_size: usize) -> HybridBitSet { - let mut seen = HybridBitSet::new_empty(domain_size); - let mut stack = vec![a]; - while let Some(u) = stack.pop() { - if let Some(edges) = self.relations.get(&u) { - for &v in edges { - if seen.insert(v) { - stack.push(v); - } - } - } - } - seen } } diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 42374fdd7baf..3597c58e072f 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -25,10 +25,12 @@ extern crate rustc_data_structures; extern crate rustc_errors; extern crate rustc_hir; extern crate rustc_hir_analysis; +extern crate rustc_index; extern crate rustc_infer; extern crate rustc_lexer; extern crate rustc_lint; extern crate rustc_middle; +extern crate rustc_mir_dataflow; extern crate rustc_parse_format; extern crate rustc_session; extern crate rustc_span; @@ -48,6 +50,7 @@ pub mod eager_or_lazy; pub mod higher; mod hir_utils; pub mod macros; +pub mod mir; pub mod msrvs; pub mod numeric_literal; pub mod paths; diff --git a/clippy_utils/src/mir/maybe_storage_live.rs b/clippy_utils/src/mir/maybe_storage_live.rs new file mode 100644 index 000000000000..d262b335d99d --- /dev/null +++ b/clippy_utils/src/mir/maybe_storage_live.rs @@ -0,0 +1,52 @@ +use rustc_index::bit_set::BitSet; +use rustc_middle::mir; +use rustc_mir_dataflow::{AnalysisDomain, CallReturnPlaces, GenKill, GenKillAnalysis}; + +/// Determines liveness of each local purely based on `StorageLive`/`Dead`. +#[derive(Copy, Clone)] +pub(super) struct MaybeStorageLive; + +impl<'tcx> AnalysisDomain<'tcx> for MaybeStorageLive { + type Domain = BitSet; + const NAME: &'static str = "maybe_storage_live"; + + fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { + // bottom = dead + BitSet::new_empty(body.local_decls.len()) + } + + fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domain) { + for arg in body.args_iter() { + state.insert(arg); + } + } +} + +impl<'tcx> GenKillAnalysis<'tcx> for MaybeStorageLive { + type Idx = mir::Local; + + fn statement_effect(&self, trans: &mut impl GenKill, stmt: &mir::Statement<'tcx>, _: mir::Location) { + match stmt.kind { + mir::StatementKind::StorageLive(l) => trans.gen(l), + mir::StatementKind::StorageDead(l) => trans.kill(l), + _ => (), + } + } + + fn terminator_effect( + &self, + _trans: &mut impl GenKill, + _terminator: &mir::Terminator<'tcx>, + _loc: mir::Location, + ) { + } + + fn call_return_effect( + &self, + _trans: &mut impl GenKill, + _block: mir::BasicBlock, + _return_places: CallReturnPlaces<'_, 'tcx>, + ) { + // Nothing to do when a call returns successfully + } +} diff --git a/clippy_utils/src/mir/mod.rs b/clippy_utils/src/mir/mod.rs new file mode 100644 index 000000000000..c8aa6f3e595d --- /dev/null +++ b/clippy_utils/src/mir/mod.rs @@ -0,0 +1,165 @@ +use rustc_hir::{Expr, HirId}; +use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; +use rustc_middle::mir::{ + traversal, Body, InlineAsmOperand, Local, Location, Place, StatementKind, TerminatorKind, START_BLOCK, +}; +use rustc_middle::ty::TyCtxt; + +mod maybe_storage_live; + +mod possible_borrower; +pub use possible_borrower::PossibleBorrowerMap; + +mod possible_origin; + +mod transitive_relation; + +#[derive(Clone, Debug, Default)] +pub struct LocalUsage { + /// The locations where the local is used, if any. + pub local_use_locs: Vec, + /// The locations where the local is consumed or mutated, if any. + pub local_consume_or_mutate_locs: Vec, +} + +pub fn visit_local_usage(locals: &[Local], mir: &Body<'_>, location: Location) -> Option> { + let init = vec![ + LocalUsage { + local_use_locs: Vec::new(), + local_consume_or_mutate_locs: Vec::new(), + }; + locals.len() + ]; + + traversal::ReversePostorder::new(mir, location.block).try_fold(init, |usage, (tbb, tdata)| { + // Give up on loops + if tdata.terminator().successors().any(|s| s == location.block) { + return None; + } + + let mut v = V { + locals, + location, + results: usage, + }; + v.visit_basic_block_data(tbb, tdata); + Some(v.results) + }) +} + +struct V<'a> { + locals: &'a [Local], + location: Location, + results: Vec, +} + +impl<'a, 'tcx> Visitor<'tcx> for V<'a> { + fn visit_place(&mut self, place: &Place<'tcx>, ctx: PlaceContext, loc: Location) { + if loc.block == self.location.block && loc.statement_index <= self.location.statement_index { + return; + } + + let local = place.local; + + for (i, self_local) in self.locals.iter().enumerate() { + if local == *self_local { + if !matches!( + ctx, + PlaceContext::MutatingUse(MutatingUseContext::Drop) | PlaceContext::NonUse(_) + ) { + self.results[i].local_use_locs.push(loc); + } + if matches!( + ctx, + PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) + | PlaceContext::MutatingUse(MutatingUseContext::Borrow) + ) { + self.results[i].local_consume_or_mutate_locs.push(loc); + } + } + } + } +} + +/// Convenience wrapper around `visit_local_usage`. +pub fn used_exactly_once(mir: &rustc_middle::mir::Body<'_>, local: rustc_middle::mir::Local) -> Option { + visit_local_usage( + &[local], + mir, + Location { + block: START_BLOCK, + statement_index: 0, + }, + ) + .map(|mut vec| { + let LocalUsage { local_use_locs, .. } = vec.remove(0); + local_use_locs + .into_iter() + .filter(|location| !is_local_assignment(mir, local, *location)) + .count() + == 1 + }) +} + +/// Returns the `mir::Body` containing the node associated with `hir_id`. +#[allow(clippy::module_name_repetitions)] +pub fn enclosing_mir(tcx: TyCtxt<'_>, hir_id: HirId) -> &Body<'_> { + let body_owner_local_def_id = tcx.hir().enclosing_body_owner(hir_id); + tcx.optimized_mir(body_owner_local_def_id.to_def_id()) +} + +/// Tries to determine the `Local` corresponding to `expr`, if any. +/// This function is expensive and should be used sparingly. +pub fn expr_local(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> Option { + let mir = enclosing_mir(tcx, expr.hir_id); + mir.local_decls.iter_enumerated().find_map(|(local, local_decl)| { + if local_decl.source_info.span == expr.span { + Some(local) + } else { + None + } + }) +} + +/// Returns a vector of `mir::Location` where `local` is assigned. Each statement referred to has +/// kind `StatementKind::Assign`. +pub fn local_assignments(mir: &Body<'_>, local: Local) -> Vec { + let mut locations = Vec::new(); + for (block, data) in mir.basic_blocks.iter_enumerated() { + for statement_index in 0..=data.statements.len() { + let location = Location { block, statement_index }; + if is_local_assignment(mir, local, location) { + locations.push(location); + } + } + } + locations +} + +// `is_local_assignment` is based on `is_place_assignment`: +// https://github.com/rust-lang/rust/blob/b7413511dc85ec01ef4b91785f86614589ac6103/compiler/rustc_middle/src/mir/visit.rs#L1350 +fn is_local_assignment(mir: &Body<'_>, local: Local, location: Location) -> bool { + let Location { block, statement_index } = location; + let basic_block = &mir.basic_blocks[block]; + if statement_index < basic_block.statements.len() { + let statement = &basic_block.statements[statement_index]; + if let StatementKind::Assign(box (place, _)) = statement.kind { + place.as_local() == Some(local) + } else { + false + } + } else { + let terminator = basic_block.terminator(); + match &terminator.kind { + TerminatorKind::Call { destination, .. } => destination.as_local() == Some(local), + TerminatorKind::InlineAsm { operands, .. } => operands.iter().any(|operand| { + if let InlineAsmOperand::Out { place: Some(place), .. } = operand { + place.as_local() == Some(local) + } else { + false + } + }), + _ => false, + } + } +} diff --git a/clippy_utils/src/mir/possible_borrower.rs b/clippy_utils/src/mir/possible_borrower.rs new file mode 100644 index 000000000000..25717bf3d2fe --- /dev/null +++ b/clippy_utils/src/mir/possible_borrower.rs @@ -0,0 +1,241 @@ +use super::{ + maybe_storage_live::MaybeStorageLive, possible_origin::PossibleOriginVisitor, + transitive_relation::TransitiveRelation, +}; +use crate::ty::is_copy; +use rustc_data_structures::fx::FxHashMap; +use rustc_index::bit_set::{BitSet, HybridBitSet}; +use rustc_lint::LateContext; +use rustc_middle::mir::{self, visit::Visitor as _, Mutability}; +use rustc_middle::ty::{self, visit::TypeVisitor}; +use rustc_mir_dataflow::{Analysis, ResultsCursor}; +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>, + cx: &'a LateContext<'tcx>, + possible_origin: FxHashMap>, +} + +impl<'a, 'b, 'tcx> PossibleBorrowerVisitor<'a, 'b, 'tcx> { + fn new( + cx: &'a LateContext<'tcx>, + body: &'b mir::Body<'tcx>, + possible_origin: FxHashMap>, + ) -> Self { + Self { + possible_borrower: TransitiveRelation::default(), + cx, + body, + possible_origin, + } + } + + fn into_map( + self, + cx: &'a LateContext<'tcx>, + maybe_live: ResultsCursor<'b, 'tcx, MaybeStorageLive>, + ) -> PossibleBorrowerMap<'b, 'tcx> { + let mut map = FxHashMap::default(); + for row in (1..self.body.local_decls.len()).map(mir::Local::from_usize) { + if is_copy(cx, self.body.local_decls[row].ty) { + continue; + } + + let mut borrowers = self.possible_borrower.reachable_from(row, self.body.local_decls.len()); + borrowers.remove(mir::Local::from_usize(0)); + if !borrowers.is_empty() { + map.insert(row, borrowers); + } + } + + let bs = BitSet::new_empty(self.body.local_decls.len()); + PossibleBorrowerMap { + map, + maybe_live, + bitset: (bs.clone(), bs), + } + } +} + +impl<'a, 'b, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'b, 'tcx> { + fn visit_assign(&mut self, place: &mir::Place<'tcx>, rvalue: &mir::Rvalue<'_>, _location: mir::Location) { + let lhs = place.local; + match rvalue { + mir::Rvalue::Ref(_, _, borrowed) => { + self.possible_borrower.add(borrowed.local, lhs); + }, + other => { + if ContainsRegion + .visit_ty(place.ty(&self.body.local_decls, self.cx.tcx).ty) + .is_continue() + { + return; + } + rvalue_locals(other, |rhs| { + if lhs != rhs { + self.possible_borrower.add(rhs, lhs); + } + }); + }, + } + } + + fn visit_terminator(&mut self, terminator: &mir::Terminator<'_>, _loc: mir::Location) { + if let mir::TerminatorKind::Call { + args, + destination: mir::Place { local: dest, .. }, + .. + } = &terminator.kind + { + // TODO add doc + // If the call returns something with lifetimes, + // let's conservatively assume the returned value contains lifetime of all the arguments. + // For example, given `let y: Foo<'a> = foo(x)`, `y` is considered to be a possible borrower of `x`. + + let mut immutable_borrowers = vec![]; + let mut mutable_borrowers = vec![]; + + for op in args { + match op { + mir::Operand::Copy(p) | mir::Operand::Move(p) => { + if let ty::Ref(_, _, Mutability::Mut) = self.body.local_decls[p.local].ty.kind() { + mutable_borrowers.push(p.local); + } else { + immutable_borrowers.push(p.local); + } + }, + mir::Operand::Constant(..) => (), + } + } + + let mut mutable_variables: Vec = mutable_borrowers + .iter() + .filter_map(|r| self.possible_origin.get(r)) + .flat_map(HybridBitSet::iter) + .collect(); + + if ContainsRegion.visit_ty(self.body.local_decls[*dest].ty).is_break() { + mutable_variables.push(*dest); + } + + for y in mutable_variables { + for x in &immutable_borrowers { + self.possible_borrower.add(*x, y); + } + for x in &mutable_borrowers { + self.possible_borrower.add(*x, y); + } + } + } + } +} + +struct ContainsRegion; + +impl TypeVisitor<'_> for ContainsRegion { + type BreakTy = (); + + fn visit_region(&mut self, _: ty::Region<'_>) -> ControlFlow { + ControlFlow::BREAK + } +} + +fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) { + use rustc_middle::mir::Rvalue::{Aggregate, BinaryOp, Cast, CheckedBinaryOp, Repeat, UnaryOp, Use}; + + let mut visit_op = |op: &mir::Operand<'_>| match op { + mir::Operand::Copy(p) | mir::Operand::Move(p) => visit(p.local), + mir::Operand::Constant(..) => (), + }; + + match rvalue { + Use(op) | Repeat(op, _) | Cast(_, op, _) | UnaryOp(_, op) => visit_op(op), + Aggregate(_, ops) => ops.iter().for_each(visit_op), + BinaryOp(_, box (lhs, rhs)) | CheckedBinaryOp(_, box (lhs, rhs)) => { + visit_op(lhs); + visit_op(rhs); + }, + _ => (), + } +} + +/// Result of `PossibleBorrowerVisitor`. +#[allow(clippy::module_name_repetitions)] +pub struct PossibleBorrowerMap<'b, 'tcx> { + /// Mapping `Local -> its possible borrowers` + pub map: FxHashMap>, + maybe_live: ResultsCursor<'b, 'tcx, MaybeStorageLive>, + // Caches to avoid allocation of `BitSet` on every query + pub bitset: (BitSet, BitSet), +} + +impl<'a, 'b, 'tcx> PossibleBorrowerMap<'b, 'tcx> { + pub fn new(cx: &'a LateContext<'tcx>, mir: &'b mir::Body<'tcx>) -> Self { + let possible_origin = { + let mut vis = PossibleOriginVisitor::new(mir); + vis.visit_body(mir); + vis.into_map(cx) + }; + let maybe_storage_live_result = MaybeStorageLive + .into_engine(cx.tcx, mir) + .pass_name("redundant_clone") + .iterate_to_fixpoint() + .into_results_cursor(mir); + let mut vis = PossibleBorrowerVisitor::new(cx, mir, possible_origin); + vis.visit_body(mir); + vis.into_map(cx, maybe_storage_live_result) + } + + /// Returns true if the set of borrowers of `borrowed` living at `at` matches with `borrowers`. + pub fn only_borrowers(&mut self, borrowers: &[mir::Local], borrowed: mir::Local, at: mir::Location) -> bool { + self.bounded_borrowers(borrowers, borrowers, borrowed, at) + } + + /// Returns true if the set of borrowers of `borrowed` living at `at` includes at least `below` + /// but no more than `above`. + pub fn bounded_borrowers( + &mut self, + below: &[mir::Local], + above: &[mir::Local], + borrowed: mir::Local, + at: mir::Location, + ) -> bool { + self.maybe_live.seek_after_primary_effect(at); + + self.bitset.0.clear(); + let maybe_live = &mut self.maybe_live; + if let Some(bitset) = self.map.get(&borrowed) { + for b in bitset.iter().filter(move |b| maybe_live.contains(*b)) { + self.bitset.0.insert(b); + } + } else { + return false; + } + + self.bitset.1.clear(); + for b in below { + self.bitset.1.insert(*b); + } + + if !self.bitset.0.superset(&self.bitset.1) { + return false; + } + + for b in above { + self.bitset.0.remove(*b); + } + + self.bitset.0.is_empty() + } + + pub fn local_is_alive_at(&mut self, local: mir::Local, at: mir::Location) -> bool { + self.maybe_live.seek_after_primary_effect(at); + self.maybe_live.contains(local) + } +} diff --git a/clippy_utils/src/mir/possible_origin.rs b/clippy_utils/src/mir/possible_origin.rs new file mode 100644 index 000000000000..8e7513d740ab --- /dev/null +++ b/clippy_utils/src/mir/possible_origin.rs @@ -0,0 +1,59 @@ +use super::transitive_relation::TransitiveRelation; +use crate::ty::is_copy; +use rustc_data_structures::fx::FxHashMap; +use rustc_index::bit_set::HybridBitSet; +use rustc_lint::LateContext; +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>, +} + +impl<'a, 'tcx> PossibleOriginVisitor<'a, 'tcx> { + pub fn new(body: &'a mir::Body<'tcx>) -> Self { + Self { + possible_origin: TransitiveRelation::default(), + body, + } + } + + pub fn into_map(self, cx: &LateContext<'tcx>) -> FxHashMap> { + let mut map = FxHashMap::default(); + for row in (1..self.body.local_decls.len()).map(mir::Local::from_usize) { + if is_copy(cx, self.body.local_decls[row].ty) { + continue; + } + + let mut borrowers = self.possible_origin.reachable_from(row, self.body.local_decls.len()); + borrowers.remove(mir::Local::from_usize(0)); + if !borrowers.is_empty() { + map.insert(row, borrowers); + } + } + map + } +} + +impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleOriginVisitor<'a, 'tcx> { + fn visit_assign(&mut self, place: &mir::Place<'tcx>, rvalue: &mir::Rvalue<'_>, _location: mir::Location) { + let lhs = place.local; + match rvalue { + // Only consider `&mut`, which can modify origin place + mir::Rvalue::Ref(_, rustc_middle::mir::BorrowKind::Mut { .. }, borrowed) | + // _2: &mut _; + // _3 = move _2 + mir::Rvalue::Use(mir::Operand::Move(borrowed)) | + // _3 = move _2 as &mut _; + mir::Rvalue::Cast(_, mir::Operand::Move(borrowed), _) + => { + self.possible_origin.add(lhs, borrowed.local); + }, + _ => {}, + } + } +} diff --git a/clippy_utils/src/mir/transitive_relation.rs b/clippy_utils/src/mir/transitive_relation.rs new file mode 100644 index 000000000000..7fe2960739fa --- /dev/null +++ b/clippy_utils/src/mir/transitive_relation.rs @@ -0,0 +1,29 @@ +use rustc_data_structures::fx::FxHashMap; +use rustc_index::bit_set::HybridBitSet; +use rustc_middle::mir; + +#[derive(Default)] +pub(super) struct TransitiveRelation { + relations: FxHashMap>, +} + +impl TransitiveRelation { + pub fn add(&mut self, a: mir::Local, b: mir::Local) { + self.relations.entry(a).or_default().push(b); + } + + pub fn reachable_from(&self, a: mir::Local, domain_size: usize) -> HybridBitSet { + let mut seen = HybridBitSet::new_empty(domain_size); + let mut stack = vec![a]; + while let Some(u) = stack.pop() { + if let Some(edges) = self.relations.get(&u) { + for &v in edges { + if seen.insert(v) { + stack.push(v); + } + } + } + } + seen + } +} diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 13938645fc3e..78adc4536543 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -119,17 +119,11 @@ pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"]; pub const RC_PTR_EQ: [&str; 4] = ["alloc", "rc", "Rc", "ptr_eq"]; pub const REFCELL_REF: [&str; 3] = ["core", "cell", "Ref"]; pub const REFCELL_REFMUT: [&str; 3] = ["core", "cell", "RefMut"]; -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const REGEX_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "unicode", "RegexBuilder", "new"]; -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const REGEX_BYTES_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "bytes", "RegexBuilder", "new"]; -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"]; -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const REGEX_BYTES_SET_NEW: [&str; 5] = ["regex", "re_set", "bytes", "RegexSet", "new"]; -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const REGEX_NEW: [&str; 4] = ["regex", "re_unicode", "Regex", "new"]; -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const REGEX_SET_NEW: [&str; 5] = ["regex", "re_set", "unicode", "RegexSet", "new"]; /// Preferably use the diagnostic item `sym::Result` where possible pub const RESULT: [&str; 3] = ["core", "result", "Result"]; diff --git a/tests/ui/needless_borrow.fixed b/tests/ui/needless_borrow.fixed index aa2687159ef4..340e89d2db1d 100644 --- a/tests/ui/needless_borrow.fixed +++ b/tests/ui/needless_borrow.fixed @@ -3,7 +3,11 @@ #[warn(clippy::all, clippy::needless_borrow)] #[allow(unused_variables)] -#[allow(clippy::uninlined_format_args, clippy::unnecessary_mut_passed)] +#[allow( + clippy::uninlined_format_args, + clippy::unnecessary_mut_passed, + clippy::unnecessary_to_owned +)] fn main() { let a = 5; let ref_a = &a; @@ -134,6 +138,7 @@ fn main() { multiple_constraints([[""]]); multiple_constraints_normalizes_to_same(X, X); let _ = Some("").unwrap_or(""); + let _ = std::fs::write("x", "".to_string()); only_sized(&""); // Don't lint. `Sized` is only bound let _ = std::any::Any::type_id(&""); // Don't lint. `Any` is only bound @@ -276,8 +281,9 @@ mod copyable_iterator { fn dont_warn(mut x: Iter) { takes_iter(&mut x); } + #[allow(unused_mut)] fn warn(mut x: &mut Iter) { - takes_iter(&mut x) + takes_iter(x) } } @@ -327,3 +333,55 @@ fn issue9383() { ManuallyDrop::drop(&mut ocean.coral); } } + +#[allow(dead_code)] +fn closure_test() { + let env = "env".to_owned(); + let arg = "arg".to_owned(); + let f = |arg| { + let loc = "loc".to_owned(); + let _ = std::fs::write("x", &env); // Don't lint. In environment + let _ = std::fs::write("x", arg); + let _ = std::fs::write("x", loc); + }; + let _ = std::fs::write("x", &env); // Don't lint. Borrowed by `f` + f(arg); +} + +#[allow(dead_code)] +mod significant_drop { + #[derive(Debug)] + struct X; + + #[derive(Debug)] + struct Y; + + impl Drop for Y { + fn drop(&mut self) {} + } + + fn foo(x: X, y: Y) { + debug(x); + debug(&y); // Don't lint. Has significant drop + } + + fn debug(_: impl std::fmt::Debug) {} +} + +#[allow(dead_code)] +mod used_exactly_once { + fn foo(x: String) { + use_x(x); + } + fn use_x(_: impl AsRef) {} +} + +#[allow(dead_code)] +mod used_more_than_once { + fn foo(x: String) { + use_x(&x); + use_x_again(&x); + } + fn use_x(_: impl AsRef) {} + fn use_x_again(_: impl AsRef) {} +} diff --git a/tests/ui/needless_borrow.rs b/tests/ui/needless_borrow.rs index d41251e8f6aa..c93711ac8e28 100644 --- a/tests/ui/needless_borrow.rs +++ b/tests/ui/needless_borrow.rs @@ -3,7 +3,11 @@ #[warn(clippy::all, clippy::needless_borrow)] #[allow(unused_variables)] -#[allow(clippy::uninlined_format_args, clippy::unnecessary_mut_passed)] +#[allow( + clippy::uninlined_format_args, + clippy::unnecessary_mut_passed, + clippy::unnecessary_to_owned +)] fn main() { let a = 5; let ref_a = &a; @@ -134,6 +138,7 @@ fn main() { multiple_constraints(&[[""]]); multiple_constraints_normalizes_to_same(&X, X); let _ = Some("").unwrap_or(&""); + let _ = std::fs::write("x", &"".to_string()); only_sized(&""); // Don't lint. `Sized` is only bound let _ = std::any::Any::type_id(&""); // Don't lint. `Any` is only bound @@ -276,6 +281,7 @@ mod copyable_iterator { fn dont_warn(mut x: Iter) { takes_iter(&mut x); } + #[allow(unused_mut)] fn warn(mut x: &mut Iter) { takes_iter(&mut x) } @@ -327,3 +333,55 @@ fn issue9383() { ManuallyDrop::drop(&mut ocean.coral); } } + +#[allow(dead_code)] +fn closure_test() { + let env = "env".to_owned(); + let arg = "arg".to_owned(); + let f = |arg| { + let loc = "loc".to_owned(); + let _ = std::fs::write("x", &env); // Don't lint. In environment + let _ = std::fs::write("x", &arg); + let _ = std::fs::write("x", &loc); + }; + let _ = std::fs::write("x", &env); // Don't lint. Borrowed by `f` + f(arg); +} + +#[allow(dead_code)] +mod significant_drop { + #[derive(Debug)] + struct X; + + #[derive(Debug)] + struct Y; + + impl Drop for Y { + fn drop(&mut self) {} + } + + fn foo(x: X, y: Y) { + debug(&x); + debug(&y); // Don't lint. Has significant drop + } + + fn debug(_: impl std::fmt::Debug) {} +} + +#[allow(dead_code)] +mod used_exactly_once { + fn foo(x: String) { + use_x(&x); + } + fn use_x(_: impl AsRef) {} +} + +#[allow(dead_code)] +mod used_more_than_once { + fn foo(x: String) { + use_x(&x); + use_x_again(&x); + } + fn use_x(_: impl AsRef) {} + fn use_x_again(_: impl AsRef) {} +} diff --git a/tests/ui/needless_borrow.stderr b/tests/ui/needless_borrow.stderr index 5af68706d4ba..8b593268bec2 100644 --- a/tests/ui/needless_borrow.stderr +++ b/tests/ui/needless_borrow.stderr @@ -1,5 +1,5 @@ error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:11:15 + --> $DIR/needless_borrow.rs:15:15 | LL | let _ = x(&&a); // warn | ^^^ help: change this to: `&a` @@ -7,172 +7,208 @@ LL | let _ = x(&&a); // warn = note: `-D clippy::needless-borrow` implied by `-D warnings` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:15:13 + --> $DIR/needless_borrow.rs:19:13 | LL | mut_ref(&mut &mut b); // warn | ^^^^^^^^^^^ help: change this to: `&mut b` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:27:13 + --> $DIR/needless_borrow.rs:31:13 | LL | &&a | ^^^ help: change this to: `&a` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:29:15 + --> $DIR/needless_borrow.rs:33:15 | LL | 46 => &&a, | ^^^ help: change this to: `&a` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:35:27 + --> $DIR/needless_borrow.rs:39:27 | LL | break &ref_a; | ^^^^^^ help: change this to: `ref_a` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:42:15 + --> $DIR/needless_borrow.rs:46:15 | LL | let _ = x(&&&a); | ^^^^ help: change this to: `&a` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:43:15 + --> $DIR/needless_borrow.rs:47:15 | LL | let _ = x(&mut &&a); | ^^^^^^^^ help: change this to: `&a` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:44:15 + --> $DIR/needless_borrow.rs:48:15 | LL | let _ = x(&&&mut b); | ^^^^^^^^ help: change this to: `&mut b` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:45:15 + --> $DIR/needless_borrow.rs:49:15 | LL | let _ = x(&&ref_a); | ^^^^^^^ help: change this to: `ref_a` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:48:11 + --> $DIR/needless_borrow.rs:52:11 | LL | x(&b); | ^^ help: change this to: `b` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:55:13 + --> $DIR/needless_borrow.rs:59:13 | LL | mut_ref(&mut x); | ^^^^^^ help: change this to: `x` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:56:13 + --> $DIR/needless_borrow.rs:60:13 | LL | mut_ref(&mut &mut x); | ^^^^^^^^^^^ help: change this to: `x` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:57:23 + --> $DIR/needless_borrow.rs:61:23 | LL | let y: &mut i32 = &mut x; | ^^^^^^ help: change this to: `x` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:58:23 + --> $DIR/needless_borrow.rs:62:23 | LL | let y: &mut i32 = &mut &mut x; | ^^^^^^^^^^^ help: change this to: `x` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:67:14 + --> $DIR/needless_borrow.rs:71:14 | LL | 0 => &mut x, | ^^^^^^ help: change this to: `x` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:73:14 + --> $DIR/needless_borrow.rs:77:14 | LL | 0 => &mut x, | ^^^^^^ help: change this to: `x` error: this expression borrows a value the compiler would automatically borrow - --> $DIR/needless_borrow.rs:85:13 + --> $DIR/needless_borrow.rs:89:13 | LL | let _ = (&x).0; | ^^^^ help: change this to: `x` error: this expression borrows a value the compiler would automatically borrow - --> $DIR/needless_borrow.rs:87:22 + --> $DIR/needless_borrow.rs:91:22 | LL | let _ = unsafe { (&*x).0 }; | ^^^^^ help: change this to: `(*x)` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:97:5 + --> $DIR/needless_borrow.rs:101:5 | LL | (&&()).foo(); | ^^^^^^ help: change this to: `(&())` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:106:5 + --> $DIR/needless_borrow.rs:110:5 | LL | (&&5).foo(); | ^^^^^ help: change this to: `(&5)` error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:131:51 + --> $DIR/needless_borrow.rs:135:51 | LL | let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap(); | ^^^^^^^^^^^^^ help: change this to: `["-a", "-l"]` error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:132:44 + --> $DIR/needless_borrow.rs:136:44 | LL | let _ = std::path::Path::new(".").join(&&"."); | ^^^^^ help: change this to: `"."` error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:133:23 + --> $DIR/needless_borrow.rs:137:23 | LL | deref_target_is_x(&X); | ^^ help: change this to: `X` error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:134:26 + --> $DIR/needless_borrow.rs:138:26 | LL | multiple_constraints(&[[""]]); | ^^^^^^^ help: change this to: `[[""]]` error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:135:45 + --> $DIR/needless_borrow.rs:139:45 | LL | multiple_constraints_normalizes_to_same(&X, X); | ^^ help: change this to: `X` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:136:32 + --> $DIR/needless_borrow.rs:140:32 | LL | let _ = Some("").unwrap_or(&""); | ^^^ help: change this to: `""` +error: the borrowed expression implements the required traits + --> $DIR/needless_borrow.rs:141:33 + | +LL | let _ = std::fs::write("x", &"".to_string()); + | ^^^^^^^^^^^^^^^ help: change this to: `"".to_string()` + error: this expression borrows a value the compiler would automatically borrow - --> $DIR/needless_borrow.rs:187:13 + --> $DIR/needless_borrow.rs:192:13 | LL | (&self.f)() | ^^^^^^^^^ help: change this to: `(self.f)` error: this expression borrows a value the compiler would automatically borrow - --> $DIR/needless_borrow.rs:196:13 + --> $DIR/needless_borrow.rs:201:13 | LL | (&mut self.f)() | ^^^^^^^^^^^^^ help: change this to: `(self.f)` error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:298:55 + --> $DIR/needless_borrow.rs:286:20 + | +LL | takes_iter(&mut x) + | ^^^^^^ help: change this to: `x` + +error: the borrowed expression implements the required traits + --> $DIR/needless_borrow.rs:304:55 | LL | let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap(); | ^^^^^^^^^^^^^ help: change this to: `["-a", "-l"]` -error: aborting due to 29 previous errors +error: the borrowed expression implements the required traits + --> $DIR/needless_borrow.rs:344:37 + | +LL | let _ = std::fs::write("x", &arg); + | ^^^^ help: change this to: `arg` + +error: the borrowed expression implements the required traits + --> $DIR/needless_borrow.rs:345:37 + | +LL | let _ = std::fs::write("x", &loc); + | ^^^^ help: change this to: `loc` + +error: the borrowed expression implements the required traits + --> $DIR/needless_borrow.rs:364:15 + | +LL | debug(&x); + | ^^ help: change this to: `x` + +error: the borrowed expression implements the required traits + --> $DIR/needless_borrow.rs:374:15 + | +LL | use_x(&x); + | ^^ help: change this to: `x` + +error: aborting due to 35 previous errors diff --git a/tests/ui/unnecessary_to_owned.fixed b/tests/ui/unnecessary_to_owned.fixed index f97583aa22f9..fe09aad06bc8 100644 --- a/tests/ui/unnecessary_to_owned.fixed +++ b/tests/ui/unnecessary_to_owned.fixed @@ -1,6 +1,6 @@ // run-rustfix -#![allow(clippy::ptr_arg)] +#![allow(clippy::needless_borrow, clippy::ptr_arg)] #![warn(clippy::unnecessary_to_owned)] #![feature(custom_inner_attributes)] diff --git a/tests/ui/unnecessary_to_owned.rs b/tests/ui/unnecessary_to_owned.rs index aa5394a56579..3de6d0903c0f 100644 --- a/tests/ui/unnecessary_to_owned.rs +++ b/tests/ui/unnecessary_to_owned.rs @@ -1,6 +1,6 @@ // run-rustfix -#![allow(clippy::ptr_arg)] +#![allow(clippy::needless_borrow, clippy::ptr_arg)] #![warn(clippy::unnecessary_to_owned)] #![feature(custom_inner_attributes)] From 9cc8da222b3893bc13bc13c8827e93f8ea246854 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Fri, 7 Oct 2022 05:07:09 -0400 Subject: [PATCH 112/309] Fix adjacent code --- clippy_dev/src/serve.rs | 2 +- clippy_dev/src/update_lints.rs | 2 +- clippy_lints/src/lib.rs | 8 +++--- clippy_lints/src/nonstandard_macro_braces.rs | 2 +- clippy_utils/src/attrs.rs | 2 +- clippy_utils/src/lib.rs | 2 +- lintcheck/src/main.rs | 30 +++++++++----------- tests/compile-test.rs | 2 +- tests/versioncheck.rs | 2 +- 9 files changed, 24 insertions(+), 28 deletions(-) diff --git a/clippy_dev/src/serve.rs b/clippy_dev/src/serve.rs index 2e0794f12fa1..535c25e69f1b 100644 --- a/clippy_dev/src/serve.rs +++ b/clippy_dev/src/serve.rs @@ -49,7 +49,7 @@ fn mtime(path: impl AsRef) -> SystemTime { .into_iter() .flatten() .flatten() - .map(|entry| mtime(&entry.path())) + .map(|entry| mtime(entry.path())) .max() .unwrap_or(SystemTime::UNIX_EPOCH) } else { diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index 0eb443167ecf..8d1bfacd1dc3 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -128,7 +128,7 @@ fn generate_lint_files( for (lint_group, lints) in Lint::by_lint_group(usable_lints.into_iter().chain(internal_lints)) { let content = gen_lint_group_list(&lint_group, lints.iter()); process_file( - &format!("clippy_lints/src/lib.register_{lint_group}.rs"), + format!("clippy_lints/src/lib.register_{lint_group}.rs"), update_mode, &content, ); diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index d9dfb4fc4e0e..ebb0f14fef52 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -417,7 +417,7 @@ pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore, sess: &Se let msrv = conf.msrv.as_ref().and_then(|s| { parse_msrv(s, None, None).or_else(|| { - sess.err(&format!( + sess.err(format!( "error reading Clippy's configuration file. `{s}` is not a valid Rust version" )); None @@ -433,7 +433,7 @@ fn read_msrv(conf: &Conf, sess: &Session) -> Option { .and_then(|v| parse_msrv(&v, None, None)); let clippy_msrv = conf.msrv.as_ref().and_then(|s| { parse_msrv(s, None, None).or_else(|| { - sess.err(&format!( + sess.err(format!( "error reading Clippy's configuration file. `{s}` is not a valid Rust version" )); None @@ -444,7 +444,7 @@ fn read_msrv(conf: &Conf, sess: &Session) -> Option { if let Some(clippy_msrv) = clippy_msrv { // if both files have an msrv, let's compare them and emit a warning if they differ if clippy_msrv != cargo_msrv { - sess.warn(&format!( + sess.warn(format!( "the MSRV in `clippy.toml` and `Cargo.toml` differ; using `{clippy_msrv}` from `clippy.toml`" )); } @@ -473,7 +473,7 @@ pub fn read_conf(sess: &Session) -> Conf { let TryConf { conf, errors, warnings } = utils::conf::read(&file_name); // all conf errors are non-fatal, we just use the default conf in case of error for error in errors { - sess.err(&format!( + sess.err(format!( "error reading Clippy's configuration file `{}`: {}", file_name.display(), format_error(error) diff --git a/clippy_lints/src/nonstandard_macro_braces.rs b/clippy_lints/src/nonstandard_macro_braces.rs index 0ca0befc1351..6c909e5ed73e 100644 --- a/clippy_lints/src/nonstandard_macro_braces.rs +++ b/clippy_lints/src/nonstandard_macro_braces.rs @@ -266,7 +266,7 @@ impl<'de> Deserialize<'de> for MacroMatcher { .iter() .find(|b| b.0 == brace) .map(|(o, c)| ((*o).to_owned(), (*c).to_owned())) - .ok_or_else(|| de::Error::custom(&format!("expected one of `(`, `{{`, `[` found `{brace}`")))?, + .ok_or_else(|| de::Error::custom(format!("expected one of `(`, `{{`, `[` found `{brace}`")))?, }) } } diff --git a/clippy_utils/src/attrs.rs b/clippy_utils/src/attrs.rs index d9b22664fd25..cd8575c90e86 100644 --- a/clippy_utils/src/attrs.rs +++ b/clippy_utils/src/attrs.rs @@ -136,7 +136,7 @@ pub fn get_unique_inner_attr(sess: &Session, attrs: &[ast::Attribute], name: &'s .emit(); }, ast::AttrStyle::Outer => { - sess.span_err(attr.span, &format!("`{name}` cannot be an outer attribute")); + sess.span_err(attr.span, format!("`{name}` cannot be an outer attribute")); }, } } diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 3597c58e072f..5c8ffffc8c8a 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -125,7 +125,7 @@ pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option) -> Opt return Some(version); } else if let Some(sess) = sess { if let Some(span) = span { - sess.span_err(span, &format!("`{msrv}` is not a valid Rust version")); + sess.span_err(span, format!("`{msrv}` is not a valid Rust version")); } } None diff --git a/lintcheck/src/main.rs b/lintcheck/src/main.rs index cc2b3e1acec7..95b20d7f0242 100644 --- a/lintcheck/src/main.rs +++ b/lintcheck/src/main.rs @@ -345,7 +345,7 @@ impl Crate { clippy_args.push(opt); } } else { - clippy_args.extend(&["-Wclippy::pedantic", "-Wclippy::cargo"]) + clippy_args.extend(["-Wclippy::pedantic", "-Wclippy::cargo"]) } if lint_filter.is_empty() { @@ -457,15 +457,11 @@ fn build_clippy() { /// Read a `lintcheck_crates.toml` file fn read_crates(toml_path: &Path) -> (Vec, RecursiveOptions) { let toml_content: String = - std::fs::read_to_string(&toml_path).unwrap_or_else(|_| panic!("Failed to read {}", toml_path.display())); + std::fs::read_to_string(toml_path).unwrap_or_else(|_| panic!("Failed to read {}", toml_path.display())); let crate_list: SourceList = toml::from_str(&toml_content).unwrap_or_else(|e| panic!("Failed to parse {}: \n{}", toml_path.display(), e)); // parse the hashmap of the toml file into a list of crates - let tomlcrates: Vec = crate_list - .crates - .into_iter() - .map(|(_cratename, tomlcrate)| tomlcrate) - .collect(); + let tomlcrates: Vec = crate_list.crates.into_values().collect(); // flatten TomlCrates into CrateSources (one TomlCrates may represent several versions of a crate => // multiple Cratesources) @@ -602,10 +598,10 @@ fn main() { ) { let shared_target_dir = "target/lintcheck/shared_target_dir"; // if we get an Err here, the shared target dir probably does simply not exist - if let Ok(metadata) = std::fs::metadata(&shared_target_dir) { + if let Ok(metadata) = std::fs::metadata(shared_target_dir) { if metadata.is_dir() { println!("Clippy is newer than lint check logs, clearing lintcheck shared target dir..."); - std::fs::remove_dir_all(&shared_target_dir) + std::fs::remove_dir_all(shared_target_dir) .expect("failed to remove target/lintcheck/shared_target_dir"); } } @@ -779,7 +775,7 @@ fn read_stats_from_file(file_path: &Path) -> HashMap { fn print_stats(old_stats: HashMap, new_stats: HashMap<&String, usize>, lint_filter: &Vec) { let same_in_both_hashmaps = old_stats .iter() - .filter(|(old_key, old_val)| new_stats.get::<&String>(&old_key) == Some(old_val)) + .filter(|(old_key, old_val)| new_stats.get::<&String>(old_key) == Some(old_val)) .map(|(k, v)| (k.to_string(), *v)) .collect::>(); @@ -797,7 +793,7 @@ fn print_stats(old_stats: HashMap, new_stats: HashMap<&String, us // list all new counts (key is in new stats but not in old stats) new_stats_deduped .iter() - .filter(|(new_key, _)| old_stats_deduped.get::(&new_key).is_none()) + .filter(|(new_key, _)| old_stats_deduped.get::(new_key).is_none()) .for_each(|(new_key, new_value)| { println!("{} 0 => {}", new_key, new_value); }); @@ -805,16 +801,16 @@ fn print_stats(old_stats: HashMap, new_stats: HashMap<&String, us // list all changed counts (key is in both maps but value differs) new_stats_deduped .iter() - .filter(|(new_key, _new_val)| old_stats_deduped.get::(&new_key).is_some()) + .filter(|(new_key, _new_val)| old_stats_deduped.get::(new_key).is_some()) .for_each(|(new_key, new_val)| { - let old_val = old_stats_deduped.get::(&new_key).unwrap(); + let old_val = old_stats_deduped.get::(new_key).unwrap(); println!("{} {} => {}", new_key, old_val, new_val); }); // list all gone counts (key is in old status but not in new stats) old_stats_deduped .iter() - .filter(|(old_key, _)| new_stats_deduped.get::<&String>(&old_key).is_none()) + .filter(|(old_key, _)| new_stats_deduped.get::<&String>(old_key).is_none()) .filter(|(old_key, _)| lint_filter.is_empty() || lint_filter.contains(old_key)) .for_each(|(old_key, old_value)| { println!("{} {} => 0", old_key, old_value); @@ -832,12 +828,12 @@ fn create_dirs(krate_download_dir: &Path, extract_dir: &Path) { panic!("cannot create lintcheck target dir"); } }); - std::fs::create_dir(&krate_download_dir).unwrap_or_else(|err| { + std::fs::create_dir(krate_download_dir).unwrap_or_else(|err| { if err.kind() != ErrorKind::AlreadyExists { panic!("cannot create crate download dir"); } }); - std::fs::create_dir(&extract_dir).unwrap_or_else(|err| { + std::fs::create_dir(extract_dir).unwrap_or_else(|err| { if err.kind() != ErrorKind::AlreadyExists { panic!("cannot create crate extraction dir"); } @@ -863,7 +859,7 @@ fn lintcheck_test() { "lintcheck/test_sources.toml", ]; let status = std::process::Command::new("cargo") - .args(&args) + .args(args) .current_dir("..") // repo root .status(); //.output(); diff --git a/tests/compile-test.rs b/tests/compile-test.rs index fa769222d1af..c10ee969c014 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -283,7 +283,7 @@ fn run_ui_cargo() { env::set_current_dir(&src_path)?; let cargo_toml_path = case.path().join("Cargo.toml"); - let cargo_content = fs::read(&cargo_toml_path)?; + let cargo_content = fs::read(cargo_toml_path)?; let cargo_parsed: toml::Value = toml::from_str( std::str::from_utf8(&cargo_content).expect("`Cargo.toml` is not a valid utf-8 file!"), ) diff --git a/tests/versioncheck.rs b/tests/versioncheck.rs index 9e07769a8e4f..a6d8d0307ce5 100644 --- a/tests/versioncheck.rs +++ b/tests/versioncheck.rs @@ -48,7 +48,7 @@ fn check_that_clippy_has_the_same_major_version_as_rustc() { // `RUSTC_REAL` if Clippy is build in the Rust repo with `./x.py`. let rustc = std::env::var("RUSTC_REAL").unwrap_or_else(|_| "rustc".to_string()); let rustc_version = String::from_utf8( - std::process::Command::new(&rustc) + std::process::Command::new(rustc) .arg("--version") .output() .expect("failed to run `rustc --version`") From 3b328e704953e34de401166cd76ca062e21d83d8 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Fri, 9 Sep 2022 15:08:06 -0500 Subject: [PATCH 113/309] Introduce TypeErrCtxt TypeErrCtxt optionally has a TypeckResults so that InferCtxt doesn't need to. --- clippy_lints/src/future_not_send.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/future_not_send.rs b/clippy_lints/src/future_not_send.rs index eb2eefe0d5a1..406c842a6d05 100644 --- a/clippy_lints/src/future_not_send.rs +++ b/clippy_lints/src/future_not_send.rs @@ -7,7 +7,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{EarlyBinder, Opaque, PredicateKind::Trait}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{sym, Span}; -use rustc_trait_selection::traits::error_reporting::suggestions::InferCtxtExt; +use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt; use rustc_trait_selection::traits::{self, FulfillmentError}; declare_clippy_lint! { @@ -90,7 +90,7 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend { |db| { cx.tcx.infer_ctxt().enter(|infcx| { for FulfillmentError { obligation, .. } in send_errors { - infcx.maybe_note_obligation_cause_for_async_await(db, &obligation); + infcx.err_ctxt().maybe_note_obligation_cause_for_async_await(db, &obligation); if let Trait(trait_pred) = obligation.predicate.kind().skip_binder() { db.note(&format!( "`{}` doesn't implement `{}`", From 6819e85501348c6fab3c5d40d0e31d5c7d00bb6a Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 19 Sep 2022 22:03:59 -0500 Subject: [PATCH 114/309] Change InferCtxtBuilder from enter to build --- clippy_lints/src/dereference.rs | 14 +++--- clippy_lints/src/escape.rs | 5 +- clippy_lints/src/future_not_send.rs | 29 ++++++----- clippy_lints/src/loops/mut_range_bound.rs | 19 ++++---- .../src/methods/unnecessary_to_owned.rs | 4 +- clippy_lints/src/needless_pass_by_value.rs | 6 +-- .../src/operators/assign_op_pattern.rs | 38 +++++++-------- clippy_utils/src/sugg.rs | 7 ++- clippy_utils/src/ty.rs | 48 +++++++++---------- clippy_utils/src/usage.rs | 19 ++++---- 10 files changed, 87 insertions(+), 102 deletions(-) diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 3cd8f236e7a5..02a16f765b73 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -831,11 +831,10 @@ fn walk_parents<'tcx>( // Trait methods taking `self` arg_ty } && impl_ty.is_ref() - && cx.tcx.infer_ctxt().enter(|infcx| - infcx - .type_implements_trait(trait_id, impl_ty, subs, cx.param_env) - .must_apply_modulo_regions() - ) + && let infcx = cx.tcx.infer_ctxt().build() + && infcx + .type_implements_trait(trait_id, impl_ty, subs, cx.param_env) + .must_apply_modulo_regions() { return Some(Position::MethodReceiverRefImpl) } @@ -1119,9 +1118,8 @@ fn needless_borrow_impl_arg_position<'tcx>( let predicate = EarlyBinder(predicate).subst(cx.tcx, &substs_with_referent_ty); let obligation = Obligation::new(ObligationCause::dummy(), cx.param_env, predicate); - cx.tcx - .infer_ctxt() - .enter(|infcx| infcx.predicate_must_hold_modulo_regions(&obligation)) + let infcx = cx.tcx.infer_ctxt().build(); + infcx.predicate_must_hold_modulo_regions(&obligation) }) }; diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index 2e608fe527fd..eb0455ae404c 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -106,9 +106,8 @@ impl<'tcx> LateLintPass<'tcx> for BoxedLocal { }; let fn_def_id = cx.tcx.hir().local_def_id(hir_id); - cx.tcx.infer_ctxt().enter(|infcx| { - ExprUseVisitor::new(&mut v, &infcx, fn_def_id, cx.param_env, cx.typeck_results()).consume_body(body); - }); + let infcx = cx.tcx.infer_ctxt().build(); + ExprUseVisitor::new(&mut v, &infcx, fn_def_id, cx.param_env, cx.typeck_results()).consume_body(body); for node in v.set { span_lint_hir( diff --git a/clippy_lints/src/future_not_send.rs b/clippy_lints/src/future_not_send.rs index 406c842a6d05..0519f9ac2468 100644 --- a/clippy_lints/src/future_not_send.rs +++ b/clippy_lints/src/future_not_send.rs @@ -77,10 +77,9 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend { if is_future { let send_trait = cx.tcx.get_diagnostic_item(sym::Send).unwrap(); let span = decl.output.span(); - let send_errors = cx.tcx.infer_ctxt().enter(|infcx| { - let cause = traits::ObligationCause::misc(span, hir_id); - traits::fully_solve_bound(&infcx, cause, cx.param_env, ret_ty, send_trait) - }); + let infcx = cx.tcx.infer_ctxt().build(); + let cause = traits::ObligationCause::misc(span, hir_id); + let send_errors = traits::fully_solve_bound(&infcx, cause, cx.param_env, ret_ty, send_trait); if !send_errors.is_empty() { span_lint_and_then( cx, @@ -88,18 +87,18 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend { span, "future cannot be sent between threads safely", |db| { - cx.tcx.infer_ctxt().enter(|infcx| { - for FulfillmentError { obligation, .. } in send_errors { - infcx.err_ctxt().maybe_note_obligation_cause_for_async_await(db, &obligation); - if let Trait(trait_pred) = obligation.predicate.kind().skip_binder() { - db.note(&format!( - "`{}` doesn't implement `{}`", - trait_pred.self_ty(), - trait_pred.trait_ref.print_only_trait_path(), - )); - } + for FulfillmentError { obligation, .. } in send_errors { + infcx + .err_ctxt() + .maybe_note_obligation_cause_for_async_await(db, &obligation); + if let Trait(trait_pred) = obligation.predicate.kind().skip_binder() { + db.note(&format!( + "`{}` doesn't implement `{}`", + trait_pred.self_ty(), + trait_pred.trait_ref.print_only_trait_path(), + )); } - }); + } }, ); } diff --git a/clippy_lints/src/loops/mut_range_bound.rs b/clippy_lints/src/loops/mut_range_bound.rs index 0ee42b61c9a5..db73ab55b37c 100644 --- a/clippy_lints/src/loops/mut_range_bound.rs +++ b/clippy_lints/src/loops/mut_range_bound.rs @@ -65,16 +65,15 @@ fn check_for_mutation<'tcx>( span_low: None, span_high: None, }; - cx.tcx.infer_ctxt().enter(|infcx| { - ExprUseVisitor::new( - &mut delegate, - &infcx, - body.hir_id.owner.def_id, - cx.param_env, - cx.typeck_results(), - ) - .walk_expr(body); - }); + let infcx = cx.tcx.infer_ctxt().build(); + ExprUseVisitor::new( + &mut delegate, + &infcx, + body.hir_id.owner.def_id, + cx.param_env, + cx.typeck_results(), + ) + .walk_expr(body); delegate.mutation_span() } diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index 9ab0d6141146..6017941452c0 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -420,9 +420,7 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty< if trait_predicates.any(|predicate| { let predicate = EarlyBinder(predicate).subst(cx.tcx, new_subst); let obligation = Obligation::new(ObligationCause::dummy(), cx.param_env, predicate); - !cx.tcx - .infer_ctxt() - .enter(|infcx| infcx.predicate_must_hold_modulo_regions(&obligation)) + !cx.tcx.infer_ctxt().build().predicate_must_hold_modulo_regions(&obligation) }) { return false; } diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 178c973981b1..7f881e27dd27 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -138,10 +138,8 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { .. } = { let mut ctx = MovedVariablesCtxt::default(); - cx.tcx.infer_ctxt().enter(|infcx| { - euv::ExprUseVisitor::new(&mut ctx, &infcx, fn_def_id, cx.param_env, cx.typeck_results()) - .consume_body(body); - }); + let infcx = cx.tcx.infer_ctxt().build(); + euv::ExprUseVisitor::new(&mut ctx, &infcx, fn_def_id, cx.param_env, cx.typeck_results()).consume_body(body); ctx }; diff --git a/clippy_lints/src/operators/assign_op_pattern.rs b/clippy_lints/src/operators/assign_op_pattern.rs index 26bca7c306a8..c7e964cf23e2 100644 --- a/clippy_lints/src/operators/assign_op_pattern.rs +++ b/clippy_lints/src/operators/assign_op_pattern.rs @@ -123,16 +123,15 @@ fn imm_borrows_in_expr(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> hir::HirIdSet } let mut s = S(hir::HirIdSet::default()); - cx.tcx.infer_ctxt().enter(|infcx| { - let mut v = ExprUseVisitor::new( - &mut s, - &infcx, - cx.tcx.hir().body_owner_def_id(cx.enclosing_body.unwrap()), - cx.param_env, - cx.typeck_results(), - ); - v.consume_expr(e); - }); + let infcx = cx.tcx.infer_ctxt().build(); + let mut v = ExprUseVisitor::new( + &mut s, + &infcx, + cx.tcx.hir().body_owner_def_id(cx.enclosing_body.unwrap()), + cx.param_env, + cx.typeck_results(), + ); + v.consume_expr(e); s.0 } @@ -156,15 +155,14 @@ fn mut_borrows_in_expr(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> hir::HirIdSet } let mut s = S(hir::HirIdSet::default()); - cx.tcx.infer_ctxt().enter(|infcx| { - let mut v = ExprUseVisitor::new( - &mut s, - &infcx, - cx.tcx.hir().body_owner_def_id(cx.enclosing_body.unwrap()), - cx.param_env, - cx.typeck_results(), - ); - v.consume_expr(e); - }); + let infcx = cx.tcx.infer_ctxt().build(); + let mut v = ExprUseVisitor::new( + &mut s, + &infcx, + cx.tcx.hir().body_owner_def_id(cx.enclosing_body.unwrap()), + cx.param_env, + cx.typeck_results(), + ); + v.consume_expr(e); s.0 } diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index ef836e84829b..3c5dd92b9cd6 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -821,10 +821,9 @@ pub fn deref_closure_args<'tcx>(cx: &LateContext<'_>, closure: &'tcx hir::Expr<' }; let fn_def_id = cx.tcx.hir().local_def_id(closure.hir_id); - cx.tcx.infer_ctxt().enter(|infcx| { - ExprUseVisitor::new(&mut visitor, &infcx, fn_def_id, cx.param_env, cx.typeck_results()) - .consume_body(closure_body); - }); + let infcx = cx.tcx.infer_ctxt().build(); + ExprUseVisitor::new(&mut visitor, &infcx, fn_def_id, cx.param_env, cx.typeck_results()) + .consume_body(closure_body); if !visitor.suggestion_start.is_empty() { return Some(DerefClosure { diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 934470bd135b..a15daec7c3ce 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -172,11 +172,10 @@ pub fn implements_trait_with_env<'tcx>( return false; } let ty_params = tcx.mk_substs(ty_params.iter()); - tcx.infer_ctxt().enter(|infcx| { - infcx - .type_implements_trait(trait_id, ty, ty_params, param_env) - .must_apply_modulo_regions() - }) + let infcx = tcx.infer_ctxt().build(); + infcx + .type_implements_trait(trait_id, ty, ty_params, param_env) + .must_apply_modulo_regions() } /// Checks whether this type implements `Drop`. @@ -242,27 +241,26 @@ fn is_normalizable_helper<'tcx>( } // prevent recursive loops, false-negative is better than endless loop leading to stack overflow cache.insert(ty, false); - let result = cx.tcx.infer_ctxt().enter(|infcx| { - let cause = rustc_middle::traits::ObligationCause::dummy(); - if infcx.at(&cause, param_env).normalize(ty).is_ok() { - match ty.kind() { - ty::Adt(def, substs) => def.variants().iter().all(|variant| { - variant - .fields - .iter() - .all(|field| is_normalizable_helper(cx, param_env, field.ty(cx.tcx, substs), cache)) - }), - _ => ty.walk().all(|generic_arg| match generic_arg.unpack() { - GenericArgKind::Type(inner_ty) if inner_ty != ty => { - is_normalizable_helper(cx, param_env, inner_ty, cache) - }, - _ => true, // if inner_ty == ty, we've already checked it - }), - } - } else { - false + let infcx = cx.tcx.infer_ctxt().build(); + let cause = rustc_middle::traits::ObligationCause::dummy(); + let result = if infcx.at(&cause, param_env).normalize(ty).is_ok() { + match ty.kind() { + ty::Adt(def, substs) => def.variants().iter().all(|variant| { + variant + .fields + .iter() + .all(|field| is_normalizable_helper(cx, param_env, field.ty(cx.tcx, substs), cache)) + }), + _ => ty.walk().all(|generic_arg| match generic_arg.unpack() { + GenericArgKind::Type(inner_ty) if inner_ty != ty => { + is_normalizable_helper(cx, param_env, inner_ty, cache) + }, + _ => true, // if inner_ty == ty, we've already checked it + }), } - }); + } else { + false + }; cache.insert(ty, result); result } diff --git a/clippy_utils/src/usage.rs b/clippy_utils/src/usage.rs index b5ec3fef3e0b..e32bae6ed1fd 100644 --- a/clippy_utils/src/usage.rs +++ b/clippy_utils/src/usage.rs @@ -18,16 +18,15 @@ pub fn mutated_variables<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> used_mutably: HirIdSet::default(), skip: false, }; - cx.tcx.infer_ctxt().enter(|infcx| { - ExprUseVisitor::new( - &mut delegate, - &infcx, - expr.hir_id.owner.def_id, - cx.param_env, - cx.typeck_results(), - ) - .walk_expr(expr); - }); + let infcx = cx.tcx.infer_ctxt().build(); + ExprUseVisitor::new( + &mut delegate, + &infcx, + expr.hir_id.owner.def_id, + cx.param_env, + cx.typeck_results(), + ) + .walk_expr(expr); if delegate.skip { return None; From e91746ed8271850de512fb765a21aa5ddb18a25c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 21 Sep 2022 13:05:20 +0200 Subject: [PATCH 115/309] make const_err a hard error --- clippy_lints/src/indexing_slicing.rs | 1 - tests/ui/crashes/ice-9463.rs | 2 +- tests/ui/crashes/ice-9463.stderr | 2 +- tests/ui/indexing_slicing_index.rs | 2 +- tests/ui/indexing_slicing_index.stderr | 8 +++++++- tests/ui/out_of_bounds_indexing/issue-3102.rs | 2 +- tests/ui/out_of_bounds_indexing/simple.rs | 2 +- 7 files changed, 12 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/indexing_slicing.rs b/clippy_lints/src/indexing_slicing.rs index 4a375752e1d3..af40a5a8187e 100644 --- a/clippy_lints/src/indexing_slicing.rs +++ b/clippy_lints/src/indexing_slicing.rs @@ -19,7 +19,6 @@ declare_clippy_lint! { /// /// ### Example /// ```rust,no_run - /// # #![allow(const_err)] /// let x = [1, 2, 3, 4]; /// /// x[9]; diff --git a/tests/ui/crashes/ice-9463.rs b/tests/ui/crashes/ice-9463.rs index 41ef930d3233..9564e77c24b1 100644 --- a/tests/ui/crashes/ice-9463.rs +++ b/tests/ui/crashes/ice-9463.rs @@ -1,4 +1,4 @@ -#![deny(arithmetic_overflow, const_err)] +#![deny(arithmetic_overflow)] fn main() { let _x = -1_i32 >> -1; let _y = 1u32 >> 10000000000000u32; diff --git a/tests/ui/crashes/ice-9463.stderr b/tests/ui/crashes/ice-9463.stderr index b0ce306d6838..2b425e85a27b 100644 --- a/tests/ui/crashes/ice-9463.stderr +++ b/tests/ui/crashes/ice-9463.stderr @@ -7,7 +7,7 @@ LL | let _x = -1_i32 >> -1; note: the lint level is defined here --> $DIR/ice-9463.rs:1:9 | -LL | #![deny(arithmetic_overflow, const_err)] +LL | #![deny(arithmetic_overflow)] | ^^^^^^^^^^^^^^^^^^^ error: this arithmetic operation will overflow diff --git a/tests/ui/indexing_slicing_index.rs b/tests/ui/indexing_slicing_index.rs index 7ebf6ee993cb..4476e0eb9220 100644 --- a/tests/ui/indexing_slicing_index.rs +++ b/tests/ui/indexing_slicing_index.rs @@ -3,7 +3,7 @@ // We also check the out_of_bounds_indexing lint here, because it lints similar things and // we want to avoid false positives. #![warn(clippy::out_of_bounds_indexing)] -#![allow(const_err, unconditional_panic, clippy::no_effect, clippy::unnecessary_operation)] +#![allow(unconditional_panic, clippy::no_effect, clippy::unnecessary_operation)] const ARR: [i32; 2] = [1, 2]; const REF: &i32 = &ARR[idx()]; // Ok, should not produce stderr. diff --git a/tests/ui/indexing_slicing_index.stderr b/tests/ui/indexing_slicing_index.stderr index a8d8b38163d0..da5bc38b3b66 100644 --- a/tests/ui/indexing_slicing_index.stderr +++ b/tests/ui/indexing_slicing_index.stderr @@ -59,6 +59,12 @@ LL | v[M]; | = help: consider using `.get(n)` or `.get_mut(n)` instead -error: aborting due to 8 previous errors +error[E0080]: evaluation of constant value failed + --> $DIR/indexing_slicing_index.rs:10:24 + | +LL | const REF_ERR: &i32 = &ARR[idx4()]; // Ok, let rustc handle const contexts. + | ^^^^^^^^^^^ index out of bounds: the length is 2 but the index is 4 + +error: aborting due to 9 previous errors For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/out_of_bounds_indexing/issue-3102.rs b/tests/ui/out_of_bounds_indexing/issue-3102.rs index f20a0ede1137..edd2123d48a5 100644 --- a/tests/ui/out_of_bounds_indexing/issue-3102.rs +++ b/tests/ui/out_of_bounds_indexing/issue-3102.rs @@ -1,5 +1,5 @@ #![warn(clippy::out_of_bounds_indexing)] -#![allow(clippy::no_effect, const_err)] +#![allow(clippy::no_effect)] fn main() { let x = [1, 2, 3, 4]; diff --git a/tests/ui/out_of_bounds_indexing/simple.rs b/tests/ui/out_of_bounds_indexing/simple.rs index 590e578d758e..4c541c23f5f4 100644 --- a/tests/ui/out_of_bounds_indexing/simple.rs +++ b/tests/ui/out_of_bounds_indexing/simple.rs @@ -1,5 +1,5 @@ #![warn(clippy::out_of_bounds_indexing)] -#![allow(clippy::no_effect, clippy::unnecessary_operation, const_err)] +#![allow(clippy::no_effect, clippy::unnecessary_operation)] fn main() { let x = [1, 2, 3, 4]; From 5f6e1d397a940447af5e4279718668db473c683c Mon Sep 17 00:00:00 2001 From: Urgau Date: Sat, 24 Sep 2022 17:22:04 +0200 Subject: [PATCH 116/309] Stabilize half_open_range_patterns --- tests/ui/match_overlapping_arm.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/match_overlapping_arm.rs b/tests/ui/match_overlapping_arm.rs index 2f85e6357135..22b04b208f87 100644 --- a/tests/ui/match_overlapping_arm.rs +++ b/tests/ui/match_overlapping_arm.rs @@ -1,5 +1,5 @@ #![feature(exclusive_range_pattern)] -#![feature(half_open_range_patterns)] + #![warn(clippy::match_overlapping_arm)] #![allow(clippy::redundant_pattern_matching)] #![allow(clippy::if_same_then_else, clippy::equatable_if_let)] From 6f4546a4be7b65da15b3170d72d3339ed1d2aeec Mon Sep 17 00:00:00 2001 From: kraktus Date: Sat, 8 Oct 2022 16:15:18 +0200 Subject: [PATCH 117/309] [`unnecessary_cast`] Do not lint negative hexadecimal literals when cast as float Floats cannot be expressed as hexadecimal literals --- clippy_lints/src/casts/unnecessary_cast.rs | 3 --- clippy_utils/src/numeric_literal.rs | 7 ++++--- tests/ui/unnecessary_cast.fixed | 4 ++++ tests/ui/unnecessary_cast.rs | 4 ++++ 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs index 21ed7f4844cc..c8596987e4d7 100644 --- a/clippy_lints/src/casts/unnecessary_cast.rs +++ b/clippy_lints/src/casts/unnecessary_cast.rs @@ -59,9 +59,6 @@ pub(super) fn check<'tcx>( lint_unnecessary_cast(cx, expr, literal_str, cast_from, cast_to); return false; }, - LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed) => { - return false; - }, LitKind::Int(_, LitIntType::Signed(_) | LitIntType::Unsigned(_)) | LitKind::Float(_, LitFloatType::Suffixed(_)) if cast_from.kind() == cast_to.kind() => diff --git a/clippy_utils/src/numeric_literal.rs b/clippy_utils/src/numeric_literal.rs index 80098d9766c6..c5dcd7b31f58 100644 --- a/clippy_utils/src/numeric_literal.rs +++ b/clippy_utils/src/numeric_literal.rs @@ -69,12 +69,13 @@ impl<'a> NumericLiteral<'a> { #[must_use] pub fn new(lit: &'a str, suffix: Option<&'a str>, float: bool) -> Self { + let unsigned_lit = lit.trim_start_matches('-'); // Determine delimiter for radix prefix, if present, and radix. - let radix = if lit.starts_with("0x") { + let radix = if unsigned_lit.starts_with("0x") { Radix::Hexadecimal - } else if lit.starts_with("0b") { + } else if unsigned_lit.starts_with("0b") { Radix::Binary - } else if lit.starts_with("0o") { + } else if unsigned_lit.starts_with("0o") { Radix::Octal } else { Radix::Decimal diff --git a/tests/ui/unnecessary_cast.fixed b/tests/ui/unnecessary_cast.fixed index 94dc96427263..ec8c6abfab91 100644 --- a/tests/ui/unnecessary_cast.fixed +++ b/tests/ui/unnecessary_cast.fixed @@ -111,4 +111,8 @@ mod fixable { let _num = foo(); } + + fn issue_9603() { + let _: f32 = -0x400 as f32; + } } diff --git a/tests/ui/unnecessary_cast.rs b/tests/ui/unnecessary_cast.rs index e5150256f69a..5213cdc269bd 100644 --- a/tests/ui/unnecessary_cast.rs +++ b/tests/ui/unnecessary_cast.rs @@ -111,4 +111,8 @@ mod fixable { let _num = foo() as f32; } + + fn issue_9603() { + let _: f32 = -0x400 as f32; + } } From 39a7d000b640523772ea709bc85d2c9460cf07bf Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sat, 8 Oct 2022 11:23:05 -0400 Subject: [PATCH 118/309] Don't suggest moving tuple structs with a significant drop to late evaluation. --- clippy_utils/src/eager_or_lazy.rs | 2 +- tests/ui/or_fun_call.fixed | 11 +++++++++++ tests/ui/or_fun_call.rs | 11 +++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/clippy_utils/src/eager_or_lazy.rs b/clippy_utils/src/eager_or_lazy.rs index 8724a4cd651d..95b3e651e2b5 100644 --- a/clippy_utils/src/eager_or_lazy.rs +++ b/clippy_utils/src/eager_or_lazy.rs @@ -120,7 +120,7 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS .expr_ty(e) .has_significant_drop(self.cx.tcx, self.cx.param_env) { - self.eagerness = Lazy; + self.eagerness = ForceNoChange; return; } }, diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 896430780ea8..23b1aa8bebd5 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -225,4 +225,15 @@ mod issue8239 { } } +mod issue9608 { + fn sig_drop() { + enum X { + X(std::fs::File), + Y(u32), + } + + let _ = None.unwrap_or(X::Y(0)); + } +} + fn main() {} diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index 2473163d4fd2..039998f22dd7 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -225,4 +225,15 @@ mod issue8239 { } } +mod issue9608 { + fn sig_drop() { + enum X { + X(std::fs::File), + Y(u32), + } + + let _ = None.unwrap_or(X::Y(0)); + } +} + fn main() {} From 8e76d6687e48d9cfaa28553278540682cca6602c Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 9 Oct 2022 07:09:57 +0000 Subject: [PATCH 119/309] ImplItemKind::TyAlias => ImplItemKind::Type --- clippy_lints/src/missing_inline.rs | 2 +- clippy_lints/src/types/mod.rs | 2 +- clippy_utils/src/check_proc_macro.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/missing_inline.rs b/clippy_lints/src/missing_inline.rs index 9d5764ac0926..ef6d1da552bf 100644 --- a/clippy_lints/src/missing_inline.rs +++ b/clippy_lints/src/missing_inline.rs @@ -148,7 +148,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline { let desc = match impl_item.kind { hir::ImplItemKind::Fn(..) => "a method", - hir::ImplItemKind::Const(..) | hir::ImplItemKind::TyAlias(_) => return, + hir::ImplItemKind::Const(..) | hir::ImplItemKind::Type(_) => return, }; let assoc_item = cx.tcx.associated_item(impl_item.def_id); diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index aca55817c525..33eee2a03784 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -372,7 +372,7 @@ impl<'tcx> LateLintPass<'tcx> for Types { // Methods are covered by check_fn. // Type aliases are ignored because oftentimes it's impossible to // make type alias declaration in trait simpler, see #1013 - ImplItemKind::Fn(..) | ImplItemKind::TyAlias(..) => (), + ImplItemKind::Fn(..) | ImplItemKind::Type(..) => (), } } diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs index 7a8d4e8068ed..c6bf98b7b8bb 100644 --- a/clippy_utils/src/check_proc_macro.rs +++ b/clippy_utils/src/check_proc_macro.rs @@ -220,7 +220,7 @@ fn trait_item_search_pat(item: &TraitItem<'_>) -> (Pat, Pat) { fn impl_item_search_pat(item: &ImplItem<'_>) -> (Pat, Pat) { let (start_pat, end_pat) = match &item.kind { ImplItemKind::Const(..) => (Pat::Str("const"), Pat::Str(";")), - ImplItemKind::TyAlias(..) => (Pat::Str("type"), Pat::Str(";")), + ImplItemKind::Type(..) => (Pat::Str("type"), Pat::Str(";")), ImplItemKind::Fn(sig, ..) => (fn_header_search_pat(sig.header), Pat::Str("")), }; if item.vis_span.is_empty() { From 3fc903eb9578287f8e619b597e98b26e7a27b000 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Thu, 25 Aug 2022 14:03:13 +0400 Subject: [PATCH 120/309] Fix clippy tests that trigger `for_loop_over_fallibles` lint --- tests/ui/for_loop_unfixable.rs | 1 + tests/ui/for_loop_unfixable.stderr | 2 +- tests/ui/for_loops_over_fallibles.rs | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/ui/for_loop_unfixable.rs b/tests/ui/for_loop_unfixable.rs index efcaffce24ea..203656fa4d6c 100644 --- a/tests/ui/for_loop_unfixable.rs +++ b/tests/ui/for_loop_unfixable.rs @@ -8,6 +8,7 @@ clippy::for_kv_map )] #[allow(clippy::linkedlist, clippy::unnecessary_mut_passed, clippy::similar_names)] +#[allow(for_loop_over_fallibles)] fn main() { let vec = vec![1, 2, 3, 4]; diff --git a/tests/ui/for_loop_unfixable.stderr b/tests/ui/for_loop_unfixable.stderr index f769b4bdc941..50a86eaa68f7 100644 --- a/tests/ui/for_loop_unfixable.stderr +++ b/tests/ui/for_loop_unfixable.stderr @@ -1,5 +1,5 @@ error: you are iterating over `Iterator::next()` which is an Option; this will compile but is probably not what you want - --> $DIR/for_loop_unfixable.rs:14:15 + --> $DIR/for_loop_unfixable.rs:15:15 | LL | for _v in vec.iter().next() {} | ^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/for_loops_over_fallibles.rs b/tests/ui/for_loops_over_fallibles.rs index 4b2a9297d084..54661ff94f24 100644 --- a/tests/ui/for_loops_over_fallibles.rs +++ b/tests/ui/for_loops_over_fallibles.rs @@ -1,5 +1,6 @@ #![warn(clippy::for_loops_over_fallibles)] #![allow(clippy::uninlined_format_args)] +#![allow(for_loop_over_fallibles)] fn for_loops_over_fallibles() { let option = Some(1); From 05dcfd971a2736cf0eac04bb04f6539f36d05a44 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Fri, 7 Oct 2022 15:59:39 +0000 Subject: [PATCH 121/309] fixup lint name --- tests/ui/for_loop_unfixable.rs | 2 +- tests/ui/for_loops_over_fallibles.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ui/for_loop_unfixable.rs b/tests/ui/for_loop_unfixable.rs index 203656fa4d6c..55fb3788a8b1 100644 --- a/tests/ui/for_loop_unfixable.rs +++ b/tests/ui/for_loop_unfixable.rs @@ -8,7 +8,7 @@ clippy::for_kv_map )] #[allow(clippy::linkedlist, clippy::unnecessary_mut_passed, clippy::similar_names)] -#[allow(for_loop_over_fallibles)] +#[allow(for_loops_over_fallibles)] fn main() { let vec = vec![1, 2, 3, 4]; diff --git a/tests/ui/for_loops_over_fallibles.rs b/tests/ui/for_loops_over_fallibles.rs index 54661ff94f24..75cdcc02353f 100644 --- a/tests/ui/for_loops_over_fallibles.rs +++ b/tests/ui/for_loops_over_fallibles.rs @@ -1,6 +1,6 @@ #![warn(clippy::for_loops_over_fallibles)] #![allow(clippy::uninlined_format_args)] -#![allow(for_loop_over_fallibles)] +#![allow(for_loops_over_fallibles)] fn for_loops_over_fallibles() { let option = Some(1); From 7cfc6fa1f0c847c749929925f9def0fdc690d417 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Fri, 7 Oct 2022 17:08:29 +0000 Subject: [PATCH 122/309] deprecate `clippy::for_loops_over_fallibles` --- clippy_lints/src/lib.register_all.rs | 1 - clippy_lints/src/lib.register_lints.rs | 1 - clippy_lints/src/lib.register_suspicious.rs | 1 - .../src/loops/for_loops_over_fallibles.rs | 65 ------------- clippy_lints/src/loops/iter_next_loop.rs | 5 +- clippy_lints/src/loops/mod.rs | 57 +---------- clippy_lints/src/renamed_lints.rs | 5 +- src/docs.rs | 1 - src/docs/for_loops_over_fallibles.txt | 32 ------- tests/ui/for_loops_over_fallibles.rs | 74 --------------- tests/ui/for_loops_over_fallibles.stderr | 95 ------------------- tests/ui/manual_map_option.fixed | 2 +- tests/ui/manual_map_option.rs | 2 +- tests/ui/rename.fixed | 7 +- tests/ui/rename.rs | 3 +- tests/ui/rename.stderr | 34 ++++--- 16 files changed, 34 insertions(+), 351 deletions(-) delete mode 100644 clippy_lints/src/loops/for_loops_over_fallibles.rs delete mode 100644 src/docs/for_loops_over_fallibles.txt delete mode 100644 tests/ui/for_loops_over_fallibles.rs delete mode 100644 tests/ui/for_loops_over_fallibles.stderr diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 5d26e4b33601..fe1f0b56646c 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -109,7 +109,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(loops::EMPTY_LOOP), LintId::of(loops::EXPLICIT_COUNTER_LOOP), LintId::of(loops::FOR_KV_MAP), - LintId::of(loops::FOR_LOOPS_OVER_FALLIBLES), LintId::of(loops::ITER_NEXT_LOOP), LintId::of(loops::MANUAL_FIND), LintId::of(loops::MANUAL_FLATTEN), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 05d927dbea79..306cb6a61c94 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -227,7 +227,6 @@ store.register_lints(&[ loops::EXPLICIT_INTO_ITER_LOOP, loops::EXPLICIT_ITER_LOOP, loops::FOR_KV_MAP, - loops::FOR_LOOPS_OVER_FALLIBLES, loops::ITER_NEXT_LOOP, loops::MANUAL_FIND, loops::MANUAL_FLATTEN, diff --git a/clippy_lints/src/lib.register_suspicious.rs b/clippy_lints/src/lib.register_suspicious.rs index 6125d0f7a862..d6d95c95c85d 100644 --- a/clippy_lints/src/lib.register_suspicious.rs +++ b/clippy_lints/src/lib.register_suspicious.rs @@ -21,7 +21,6 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec! LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING), LintId::of(formatting::SUSPICIOUS_UNARY_OP_FORMATTING), LintId::of(loops::EMPTY_LOOP), - LintId::of(loops::FOR_LOOPS_OVER_FALLIBLES), LintId::of(loops::MUT_RANGE_BOUND), LintId::of(methods::NO_EFFECT_REPLACE), LintId::of(methods::SUSPICIOUS_MAP), diff --git a/clippy_lints/src/loops/for_loops_over_fallibles.rs b/clippy_lints/src/loops/for_loops_over_fallibles.rs deleted file mode 100644 index 77de90fd7b94..000000000000 --- a/clippy_lints/src/loops/for_loops_over_fallibles.rs +++ /dev/null @@ -1,65 +0,0 @@ -use super::FOR_LOOPS_OVER_FALLIBLES; -use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; -use rustc_hir::{Expr, Pat}; -use rustc_lint::LateContext; -use rustc_span::symbol::sym; - -/// Checks for `for` loops over `Option`s and `Result`s. -pub(super) fn check(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>, method_name: Option<&str>) { - let ty = cx.typeck_results().expr_ty(arg); - if is_type_diagnostic_item(cx, ty, sym::Option) { - let help_string = if let Some(method_name) = method_name { - format!( - "consider replacing `for {0} in {1}.{method_name}()` with `if let Some({0}) = {1}`", - snippet(cx, pat.span, "_"), - snippet(cx, arg.span, "_") - ) - } else { - format!( - "consider replacing `for {0} in {1}` with `if let Some({0}) = {1}`", - snippet(cx, pat.span, "_"), - snippet(cx, arg.span, "_") - ) - }; - span_lint_and_help( - cx, - FOR_LOOPS_OVER_FALLIBLES, - arg.span, - &format!( - "for loop over `{0}`, which is an `Option`. This is more readably written as an \ - `if let` statement", - snippet(cx, arg.span, "_") - ), - None, - &help_string, - ); - } else if is_type_diagnostic_item(cx, ty, sym::Result) { - let help_string = if let Some(method_name) = method_name { - format!( - "consider replacing `for {0} in {1}.{method_name}()` with `if let Ok({0}) = {1}`", - snippet(cx, pat.span, "_"), - snippet(cx, arg.span, "_") - ) - } else { - format!( - "consider replacing `for {0} in {1}` with `if let Ok({0}) = {1}`", - snippet(cx, pat.span, "_"), - snippet(cx, arg.span, "_") - ) - }; - span_lint_and_help( - cx, - FOR_LOOPS_OVER_FALLIBLES, - arg.span, - &format!( - "for loop over `{0}`, which is a `Result`. This is more readably written as an \ - `if let` statement", - snippet(cx, arg.span, "_") - ), - None, - &help_string, - ); - } -} diff --git a/clippy_lints/src/loops/iter_next_loop.rs b/clippy_lints/src/loops/iter_next_loop.rs index e640c62ebdac..b8a263817d29 100644 --- a/clippy_lints/src/loops/iter_next_loop.rs +++ b/clippy_lints/src/loops/iter_next_loop.rs @@ -5,7 +5,7 @@ use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_span::sym; -pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>) -> bool { +pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>) { if is_trait_method(cx, arg, sym::Iterator) { span_lint( cx, @@ -14,8 +14,5 @@ pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>) -> bool { "you are iterating over `Iterator::next()` which is an Option; this will compile but is \ probably not what you want", ); - true - } else { - false } } diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index c0a0444485e3..bcf278d9c833 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -3,7 +3,6 @@ mod explicit_counter_loop; mod explicit_into_iter_loop; mod explicit_iter_loop; mod for_kv_map; -mod for_loops_over_fallibles; mod iter_next_loop; mod manual_find; mod manual_flatten; @@ -173,49 +172,6 @@ declare_clippy_lint! { "for-looping over `_.next()` which is probably not intended" } -declare_clippy_lint! { - /// ### What it does - /// Checks for `for` loops over `Option` or `Result` values. - /// - /// ### Why is this bad? - /// Readability. This is more clearly expressed as an `if - /// let`. - /// - /// ### Example - /// ```rust - /// # let opt = Some(1); - /// # let res: Result = Ok(1); - /// for x in opt { - /// // .. - /// } - /// - /// for x in &res { - /// // .. - /// } - /// - /// for x in res.iter() { - /// // .. - /// } - /// ``` - /// - /// Use instead: - /// ```rust - /// # let opt = Some(1); - /// # let res: Result = Ok(1); - /// if let Some(x) = opt { - /// // .. - /// } - /// - /// if let Ok(x) = res { - /// // .. - /// } - /// ``` - #[clippy::version = "1.45.0"] - pub FOR_LOOPS_OVER_FALLIBLES, - suspicious, - "for-looping over an `Option` or a `Result`, which is more clearly expressed as an `if let`" -} - declare_clippy_lint! { /// ### What it does /// Detects `loop + match` combinations that are easier @@ -648,7 +604,6 @@ declare_lint_pass!(Loops => [ EXPLICIT_ITER_LOOP, EXPLICIT_INTO_ITER_LOOP, ITER_NEXT_LOOP, - FOR_LOOPS_OVER_FALLIBLES, WHILE_LET_LOOP, NEEDLESS_COLLECT, EXPLICIT_COUNTER_LOOP, @@ -739,30 +694,22 @@ fn check_for_loop<'tcx>( manual_find::check(cx, pat, arg, body, span, expr); } -fn check_for_loop_arg(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>) { - let mut next_loop_linted = false; // whether or not ITER_NEXT_LOOP lint was used - +fn check_for_loop_arg(cx: &LateContext<'_>, _: &Pat<'_>, arg: &Expr<'_>) { if let ExprKind::MethodCall(method, self_arg, [], _) = arg.kind { let method_name = method.ident.as_str(); // check for looping over x.iter() or x.iter_mut(), could use &x or &mut x match method_name { "iter" | "iter_mut" => { explicit_iter_loop::check(cx, self_arg, arg, method_name); - for_loops_over_fallibles::check(cx, pat, self_arg, Some(method_name)); }, "into_iter" => { explicit_iter_loop::check(cx, self_arg, arg, method_name); explicit_into_iter_loop::check(cx, self_arg, arg); - for_loops_over_fallibles::check(cx, pat, self_arg, Some(method_name)); }, "next" => { - next_loop_linted = iter_next_loop::check(cx, arg); + iter_next_loop::check(cx, arg); }, _ => {}, } } - - if !next_loop_linted { - for_loops_over_fallibles::check(cx, pat, arg, None); - } } diff --git a/clippy_lints/src/renamed_lints.rs b/clippy_lints/src/renamed_lints.rs index d320eea1c377..76d6ad0b23e6 100644 --- a/clippy_lints/src/renamed_lints.rs +++ b/clippy_lints/src/renamed_lints.rs @@ -11,8 +11,8 @@ pub static RENAMED_LINTS: &[(&str, &str)] = &[ ("clippy::disallowed_method", "clippy::disallowed_methods"), ("clippy::disallowed_type", "clippy::disallowed_types"), ("clippy::eval_order_dependence", "clippy::mixed_read_write_in_expression"), - ("clippy::for_loop_over_option", "clippy::for_loops_over_fallibles"), - ("clippy::for_loop_over_result", "clippy::for_loops_over_fallibles"), + ("clippy::for_loop_over_option", "for_loops_over_fallibles"), + ("clippy::for_loop_over_result", "for_loops_over_fallibles"), ("clippy::identity_conversion", "clippy::useless_conversion"), ("clippy::if_let_some_result", "clippy::match_result_ok"), ("clippy::logic_bug", "clippy::overly_complex_bool_expr"), @@ -31,6 +31,7 @@ pub static RENAMED_LINTS: &[(&str, &str)] = &[ ("clippy::to_string_in_display", "clippy::recursive_format_impl"), ("clippy::zero_width_space", "clippy::invisible_characters"), ("clippy::drop_bounds", "drop_bounds"), + ("clippy::for_loops_over_fallibles", "for_loops_over_fallibles"), ("clippy::into_iter_on_array", "array_into_iter"), ("clippy::invalid_atomic_ordering", "invalid_atomic_ordering"), ("clippy::invalid_ref", "invalid_value"), diff --git a/src/docs.rs b/src/docs.rs index 3bf488ab4779..bd27bc7938f8 100644 --- a/src/docs.rs +++ b/src/docs.rs @@ -170,7 +170,6 @@ docs! { "fn_to_numeric_cast_any", "fn_to_numeric_cast_with_truncation", "for_kv_map", - "for_loops_over_fallibles", "forget_copy", "forget_non_drop", "forget_ref", diff --git a/src/docs/for_loops_over_fallibles.txt b/src/docs/for_loops_over_fallibles.txt deleted file mode 100644 index c5a7508e45d4..000000000000 --- a/src/docs/for_loops_over_fallibles.txt +++ /dev/null @@ -1,32 +0,0 @@ -### What it does -Checks for `for` loops over `Option` or `Result` values. - -### Why is this bad? -Readability. This is more clearly expressed as an `if -let`. - -### Example -``` -for x in opt { - // .. -} - -for x in &res { - // .. -} - -for x in res.iter() { - // .. -} -``` - -Use instead: -``` -if let Some(x) = opt { - // .. -} - -if let Ok(x) = res { - // .. -} -``` \ No newline at end of file diff --git a/tests/ui/for_loops_over_fallibles.rs b/tests/ui/for_loops_over_fallibles.rs deleted file mode 100644 index 75cdcc02353f..000000000000 --- a/tests/ui/for_loops_over_fallibles.rs +++ /dev/null @@ -1,74 +0,0 @@ -#![warn(clippy::for_loops_over_fallibles)] -#![allow(clippy::uninlined_format_args)] -#![allow(for_loops_over_fallibles)] - -fn for_loops_over_fallibles() { - let option = Some(1); - let mut result = option.ok_or("x not found"); - let v = vec![0, 1, 2]; - - // check over an `Option` - for x in option { - println!("{}", x); - } - - // check over an `Option` - for x in option.iter() { - println!("{}", x); - } - - // check over a `Result` - for x in result { - println!("{}", x); - } - - // check over a `Result` - for x in result.iter_mut() { - println!("{}", x); - } - - // check over a `Result` - for x in result.into_iter() { - println!("{}", x); - } - - for x in option.ok_or("x not found") { - println!("{}", x); - } - - // make sure LOOP_OVER_NEXT lint takes clippy::precedence when next() is the last call - // in the chain - for x in v.iter().next() { - println!("{}", x); - } - - // make sure we lint when next() is not the last call in the chain - for x in v.iter().next().and(Some(0)) { - println!("{}", x); - } - - for x in v.iter().next().ok_or("x not found") { - println!("{}", x); - } - - // check for false positives - - // for loop false positive - for x in v { - println!("{}", x); - } - - // while let false positive for Option - while let Some(x) = option { - println!("{}", x); - break; - } - - // while let false positive for Result - while let Ok(x) = result { - println!("{}", x); - break; - } -} - -fn main() {} diff --git a/tests/ui/for_loops_over_fallibles.stderr b/tests/ui/for_loops_over_fallibles.stderr deleted file mode 100644 index f09adccabd1a..000000000000 --- a/tests/ui/for_loops_over_fallibles.stderr +++ /dev/null @@ -1,95 +0,0 @@ -error: for loop over `option`, which is an `Option`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:10:14 - | -LL | for x in option { - | ^^^^^^ - | - = help: consider replacing `for x in option` with `if let Some(x) = option` - = note: `-D clippy::for-loops-over-fallibles` implied by `-D warnings` - -error: for loop over `option`, which is an `Option`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:15:14 - | -LL | for x in option.iter() { - | ^^^^^^ - | - = help: consider replacing `for x in option.iter()` with `if let Some(x) = option` - -error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:20:14 - | -LL | for x in result { - | ^^^^^^ - | - = help: consider replacing `for x in result` with `if let Ok(x) = result` - -error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:25:14 - | -LL | for x in result.iter_mut() { - | ^^^^^^ - | - = help: consider replacing `for x in result.iter_mut()` with `if let Ok(x) = result` - -error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:30:14 - | -LL | for x in result.into_iter() { - | ^^^^^^ - | - = help: consider replacing `for x in result.into_iter()` with `if let Ok(x) = result` - -error: for loop over `option.ok_or("x not found")`, which is a `Result`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:34:14 - | -LL | for x in option.ok_or("x not found") { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider replacing `for x in option.ok_or("x not found")` with `if let Ok(x) = option.ok_or("x not found")` - -error: you are iterating over `Iterator::next()` which is an Option; this will compile but is probably not what you want - --> $DIR/for_loops_over_fallibles.rs:40:14 - | -LL | for x in v.iter().next() { - | ^^^^^^^^^^^^^^^ - | - = note: `#[deny(clippy::iter_next_loop)]` on by default - -error: for loop over `v.iter().next().and(Some(0))`, which is an `Option`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:45:14 - | -LL | for x in v.iter().next().and(Some(0)) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider replacing `for x in v.iter().next().and(Some(0))` with `if let Some(x) = v.iter().next().and(Some(0))` - -error: for loop over `v.iter().next().ok_or("x not found")`, which is a `Result`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:49:14 - | -LL | for x in v.iter().next().ok_or("x not found") { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider replacing `for x in v.iter().next().ok_or("x not found")` with `if let Ok(x) = v.iter().next().ok_or("x not found")` - -error: this loop never actually loops - --> $DIR/for_loops_over_fallibles.rs:61:5 - | -LL | / while let Some(x) = option { -LL | | println!("{}", x); -LL | | break; -LL | | } - | |_____^ - | - = note: `#[deny(clippy::never_loop)]` on by default - -error: this loop never actually loops - --> $DIR/for_loops_over_fallibles.rs:67:5 - | -LL | / while let Ok(x) = result { -LL | | println!("{}", x); -LL | | break; -LL | | } - | |_____^ - -error: aborting due to 11 previous errors - diff --git a/tests/ui/manual_map_option.fixed b/tests/ui/manual_map_option.fixed index a59da4ae10bc..e12ea7ec1450 100644 --- a/tests/ui/manual_map_option.fixed +++ b/tests/ui/manual_map_option.fixed @@ -7,7 +7,7 @@ clippy::unit_arg, clippy::match_ref_pats, clippy::redundant_pattern_matching, - clippy::for_loops_over_fallibles, + for_loops_over_fallibles, dead_code )] diff --git a/tests/ui/manual_map_option.rs b/tests/ui/manual_map_option.rs index 0bdbefa51e8b..325a6db06c4e 100644 --- a/tests/ui/manual_map_option.rs +++ b/tests/ui/manual_map_option.rs @@ -7,7 +7,7 @@ clippy::unit_arg, clippy::match_ref_pats, clippy::redundant_pattern_matching, - clippy::for_loops_over_fallibles, + for_loops_over_fallibles, dead_code )] diff --git a/tests/ui/rename.fixed b/tests/ui/rename.fixed index a6e7bdba77c6..8beae8dee085 100644 --- a/tests/ui/rename.fixed +++ b/tests/ui/rename.fixed @@ -12,7 +12,7 @@ #![allow(clippy::disallowed_methods)] #![allow(clippy::disallowed_types)] #![allow(clippy::mixed_read_write_in_expression)] -#![allow(clippy::for_loops_over_fallibles)] +#![allow(for_loops_over_fallibles)] #![allow(clippy::useless_conversion)] #![allow(clippy::match_result_ok)] #![allow(clippy::overly_complex_bool_expr)] @@ -45,8 +45,8 @@ #![warn(clippy::disallowed_methods)] #![warn(clippy::disallowed_types)] #![warn(clippy::mixed_read_write_in_expression)] -#![warn(clippy::for_loops_over_fallibles)] -#![warn(clippy::for_loops_over_fallibles)] +#![warn(for_loops_over_fallibles)] +#![warn(for_loops_over_fallibles)] #![warn(clippy::useless_conversion)] #![warn(clippy::match_result_ok)] #![warn(clippy::overly_complex_bool_expr)] @@ -65,6 +65,7 @@ #![warn(clippy::recursive_format_impl)] #![warn(clippy::invisible_characters)] #![warn(drop_bounds)] +#![warn(for_loops_over_fallibles)] #![warn(array_into_iter)] #![warn(invalid_atomic_ordering)] #![warn(invalid_value)] diff --git a/tests/ui/rename.rs b/tests/ui/rename.rs index e8f57597d02b..9e665047baae 100644 --- a/tests/ui/rename.rs +++ b/tests/ui/rename.rs @@ -12,7 +12,7 @@ #![allow(clippy::disallowed_methods)] #![allow(clippy::disallowed_types)] #![allow(clippy::mixed_read_write_in_expression)] -#![allow(clippy::for_loops_over_fallibles)] +#![allow(for_loops_over_fallibles)] #![allow(clippy::useless_conversion)] #![allow(clippy::match_result_ok)] #![allow(clippy::overly_complex_bool_expr)] @@ -65,6 +65,7 @@ #![warn(clippy::to_string_in_display)] #![warn(clippy::zero_width_space)] #![warn(clippy::drop_bounds)] +#![warn(clippy::for_loops_over_fallibles)] #![warn(clippy::into_iter_on_array)] #![warn(clippy::invalid_atomic_ordering)] #![warn(clippy::invalid_ref)] diff --git a/tests/ui/rename.stderr b/tests/ui/rename.stderr index 31865a7f66d6..63eb565185f0 100644 --- a/tests/ui/rename.stderr +++ b/tests/ui/rename.stderr @@ -54,17 +54,17 @@ error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_r LL | #![warn(clippy::eval_order_dependence)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression` -error: lint `clippy::for_loop_over_option` has been renamed to `clippy::for_loops_over_fallibles` +error: lint `clippy::for_loop_over_option` has been renamed to `for_loops_over_fallibles` --> $DIR/rename.rs:48:9 | LL | #![warn(clippy::for_loop_over_option)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` -error: lint `clippy::for_loop_over_result` has been renamed to `clippy::for_loops_over_fallibles` +error: lint `clippy::for_loop_over_result` has been renamed to `for_loops_over_fallibles` --> $DIR/rename.rs:49:9 | LL | #![warn(clippy::for_loop_over_result)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` --> $DIR/rename.rs:50:9 @@ -174,59 +174,65 @@ error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` LL | #![warn(clippy::drop_bounds)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` -error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` +error: lint `clippy::for_loops_over_fallibles` has been renamed to `for_loops_over_fallibles` --> $DIR/rename.rs:68:9 | +LL | #![warn(clippy::for_loops_over_fallibles)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` + +error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` + --> $DIR/rename.rs:69: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` - --> $DIR/rename.rs:69:9 + --> $DIR/rename.rs:70:9 | LL | #![warn(clippy::invalid_atomic_ordering)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` error: lint `clippy::invalid_ref` has been renamed to `invalid_value` - --> $DIR/rename.rs:70:9 + --> $DIR/rename.rs:71:9 | LL | #![warn(clippy::invalid_ref)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` - --> $DIR/rename.rs:71:9 + --> $DIR/rename.rs:72:9 | LL | #![warn(clippy::mem_discriminant_non_enum)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` - --> $DIR/rename.rs:72:9 + --> $DIR/rename.rs:73: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` - --> $DIR/rename.rs:73:9 + --> $DIR/rename.rs:74:9 | LL | #![warn(clippy::positional_named_format_parameters)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally` error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr` - --> $DIR/rename.rs:74:9 + --> $DIR/rename.rs:75:9 | LL | #![warn(clippy::temporary_cstring_as_ptr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr` error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints` - --> $DIR/rename.rs:75:9 + --> $DIR/rename.rs:76: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` - --> $DIR/rename.rs:76:9 + --> $DIR/rename.rs:77:9 | LL | #![warn(clippy::unused_label)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` -error: aborting due to 38 previous errors +error: aborting due to 39 previous errors From 1688368b33fa3bc24913440078c415057d659bb4 Mon Sep 17 00:00:00 2001 From: unvalley Date: Sun, 9 Oct 2022 23:35:52 +0900 Subject: [PATCH 123/309] feat: add Default to Lint groups --- util/gh-pages/index.html | 6 ++++++ util/gh-pages/script.js | 17 +++++++++++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/util/gh-pages/index.html b/util/gh-pages/index.html index c5d602ea3035..8dc4513325d1 100644 --- a/util/gh-pages/index.html +++ b/util/gh-pages/index.html @@ -448,6 +448,12 @@ Otherwise, have a great day =^.^= None +
  • + +
  • -
  • - -
  • +
  • + +
  • fn touch(&self)