From 58350799a184663845772417c07262dcbd52dfb5 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 11 Sep 2024 15:15:44 +0200 Subject: [PATCH 001/120] ptr::add/sub: these are *not* equivalent to offset(count as isize) --- library/core/src/intrinsics.rs | 3 +-- library/core/src/ptr/const_ptr.rs | 16 +++++++++------- library/core/src/ptr/mut_ptr.rs | 16 +++++++++------- 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index 7870a62ea81cd..22fc9f72ec414 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -1425,8 +1425,7 @@ extern "rust-intrinsic" { /// /// If the computed offset is non-zero, then both the starting and resulting pointer must be /// either in bounds or at the end of an allocated object. If either pointer is out - /// of bounds or arithmetic overflow occurs then any further use of the returned value will - /// result in undefined behavior. + /// of bounds or arithmetic overflow occurs then this operation is undefined behavior. /// /// The stabilized version of this intrinsic is [`pointer::offset`]. #[must_use = "returns a new pointer rather than modifying its argument"] diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index 3b45d46b31d5e..3df24e0d512e1 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -346,7 +346,7 @@ impl *const T { if self.is_null() { None } else { Some(unsafe { &*(self as *const MaybeUninit) }) } } - /// Adds an offset to a pointer. + /// Adds a signed offset to a pointer. /// /// `count` is in units of T; e.g., a `count` of 3 represents a pointer /// offset of `3 * size_of::()` bytes. @@ -355,7 +355,8 @@ impl *const T { /// /// If any of the following conditions are violated, the result is Undefined Behavior: /// - /// * The computed offset, `count * size_of::()` bytes, must not overflow `isize`. + /// * The computed offset, `count * size_of::()` bytes (using unbounded arithmetic), + /// must fit in an `isize`. /// /// * If the computed offset is non-zero, then `self` must be derived from a pointer to some /// [allocated object], and the entire memory range between `self` and the result must be in @@ -807,7 +808,7 @@ impl *const T { } } - /// Adds an offset to a pointer (convenience for `.offset(count as isize)`). + /// Adds an offset to a pointer. /// /// `count` is in units of T; e.g., a `count` of 3 represents a pointer /// offset of `3 * size_of::()` bytes. @@ -816,7 +817,8 @@ impl *const T { /// /// If any of the following conditions are violated, the result is Undefined Behavior: /// - /// * The computed offset, `count * size_of::()` bytes, must not overflow `isize`. + /// * The computed offset, `count * size_of::()` bytes (using unbounded arithmetic), + /// must fit in an `isize`. /// /// * If the computed offset is non-zero, then `self` must be derived from a pointer to some /// [allocated object], and the entire memory range between `self` and the result must be in @@ -880,8 +882,7 @@ impl *const T { unsafe { self.cast::().add(count).with_metadata_of(self) } } - /// Subtracts an offset from a pointer (convenience for - /// `.offset((count as isize).wrapping_neg())`). + /// Subtracts an offset from a pointer. /// /// `count` is in units of T; e.g., a `count` of 3 represents a pointer /// offset of `3 * size_of::()` bytes. @@ -890,7 +891,8 @@ impl *const T { /// /// If any of the following conditions are violated, the result is Undefined Behavior: /// - /// * The computed offset, `count * size_of::()` bytes, must not overflow `isize`. + /// * The computed offset, `count * size_of::()` bytes (using unbounded arithmetic), + /// must fit in an `isize`. /// /// * If the computed offset is non-zero, then `self` must be derived from a pointer to some /// [allocated object], and the entire memory range between `self` and the result must be in diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index ddb9195d2e7c7..630983c8355f3 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -344,7 +344,7 @@ impl *mut T { if self.is_null() { None } else { Some(unsafe { &*(self as *const MaybeUninit) }) } } - /// Adds an offset to a pointer. + /// Adds a signed offset to a pointer. /// /// `count` is in units of T; e.g., a `count` of 3 represents a pointer /// offset of `3 * size_of::()` bytes. @@ -353,7 +353,8 @@ impl *mut T { /// /// If any of the following conditions are violated, the result is Undefined Behavior: /// - /// * The computed offset, `count * size_of::()` bytes, must not overflow `isize`. + /// * The computed offset, `count * size_of::()` bytes (using unbounded arithmetic), + /// must fit in an `isize`. /// /// * If the computed offset is non-zero, then `self` must be derived from a pointer to some /// [allocated object], and the entire memory range between `self` and the result must be in @@ -888,7 +889,7 @@ impl *mut T { unsafe { (self as *const T).sub_ptr(origin) } } - /// Adds an offset to a pointer (convenience for `.offset(count as isize)`). + /// Adds an offset to a pointer. /// /// `count` is in units of T; e.g., a `count` of 3 represents a pointer /// offset of `3 * size_of::()` bytes. @@ -897,7 +898,8 @@ impl *mut T { /// /// If any of the following conditions are violated, the result is Undefined Behavior: /// - /// * The computed offset, `count * size_of::()` bytes, must not overflow `isize`. + /// * The computed offset, `count * size_of::()` bytes (using unbounded arithmetic), + /// must fit in an `isize`. /// /// * If the computed offset is non-zero, then `self` must be derived from a pointer to some /// [allocated object], and the entire memory range between `self` and the result must be in @@ -961,8 +963,7 @@ impl *mut T { unsafe { self.cast::().add(count).with_metadata_of(self) } } - /// Subtracts an offset from a pointer (convenience for - /// `.offset((count as isize).wrapping_neg())`). + /// Subtracts an offset from a pointer. /// /// `count` is in units of T; e.g., a `count` of 3 represents a pointer /// offset of `3 * size_of::()` bytes. @@ -971,7 +972,8 @@ impl *mut T { /// /// If any of the following conditions are violated, the result is Undefined Behavior: /// - /// * The computed offset, `count * size_of::()` bytes, must not overflow `isize`. + /// * The computed offset, `count * size_of::()` bytes (using unbounded arithmetic), + /// must fit in an `isize`. /// /// * If the computed offset is non-zero, then `self` must be derived from a pointer to some /// [allocated object], and the entire memory range between `self` and the result must be in From 916eb130be623d7d50f9855baa441935e51067f2 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 11 Sep 2024 20:57:05 +0200 Subject: [PATCH 002/120] various updates based on review --- library/core/src/ptr/const_ptr.rs | 31 +++++++++++++++++++------------ library/core/src/ptr/mut_ptr.rs | 31 +++++++++++++++++++------------ 2 files changed, 38 insertions(+), 24 deletions(-) diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index 3df24e0d512e1..afec6f8795645 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -355,8 +355,8 @@ impl *const T { /// /// If any of the following conditions are violated, the result is Undefined Behavior: /// - /// * The computed offset, `count * size_of::()` bytes (using unbounded arithmetic), - /// must fit in an `isize`. + /// * The offset in bytes, `count * size_of::()`, computed on mathematical integers (without + /// "wrapping around"), must fit in an `isize`. /// /// * If the computed offset is non-zero, then `self` must be derived from a pointer to some /// [allocated object], and the entire memory range between `self` and the result must be in @@ -399,7 +399,7 @@ impl *const T { unsafe { intrinsics::offset(self, count) } } - /// Calculates the offset from a pointer in bytes. + /// Adds a signed offset in bytes to a pointer. /// /// `count` is in units of **bytes**. /// @@ -808,7 +808,11 @@ impl *const T { } } - /// Adds an offset to a pointer. + /// Adds an unsigned offset to a pointer. + /// + /// This can only move the pointer forward (or not move it). If you need to move forward or + /// backward depending on the value, then you might want [`offset`](#method.offset) instead + /// which takes a signed offset. /// /// `count` is in units of T; e.g., a `count` of 3 represents a pointer /// offset of `3 * size_of::()` bytes. @@ -817,8 +821,8 @@ impl *const T { /// /// If any of the following conditions are violated, the result is Undefined Behavior: /// - /// * The computed offset, `count * size_of::()` bytes (using unbounded arithmetic), - /// must fit in an `isize`. + /// * The offset in bytes, `count * size_of::()`, computed on mathematical integers (without + /// "wrapping around"), must fit in an `isize`. /// /// * If the computed offset is non-zero, then `self` must be derived from a pointer to some /// [allocated object], and the entire memory range between `self` and the result must be in @@ -861,7 +865,7 @@ impl *const T { unsafe { intrinsics::offset(self, count) } } - /// Calculates the offset from a pointer in bytes (convenience for `.byte_offset(count as isize)`). + /// Adds an unsigned offset in bytes to a pointer. /// /// `count` is in units of bytes. /// @@ -882,7 +886,11 @@ impl *const T { unsafe { self.cast::().add(count).with_metadata_of(self) } } - /// Subtracts an offset from a pointer. + /// Subtracts an unsigned offset from a pointer. + /// + /// This can only move the pointer backward (or not move it). If you need to move forward or + /// backward depending on the value, then you might want [`offset`](#method.offset) instead + /// which takes a signed offset. /// /// `count` is in units of T; e.g., a `count` of 3 represents a pointer /// offset of `3 * size_of::()` bytes. @@ -891,8 +899,8 @@ impl *const T { /// /// If any of the following conditions are violated, the result is Undefined Behavior: /// - /// * The computed offset, `count * size_of::()` bytes (using unbounded arithmetic), - /// must fit in an `isize`. + /// * The offset in bytes, `count * size_of::()`, computed on mathematical integers (without + /// "wrapping around"), must fit in an `isize`. /// /// * If the computed offset is non-zero, then `self` must be derived from a pointer to some /// [allocated object], and the entire memory range between `self` and the result must be in @@ -943,8 +951,7 @@ impl *const T { } } - /// Calculates the offset from a pointer in bytes (convenience for - /// `.byte_offset((count as isize).wrapping_neg())`). + /// Subtracts an unsigned offset in bytes from a pointer. /// /// `count` is in units of bytes. /// diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index 630983c8355f3..11fb45b1e09d9 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -353,8 +353,8 @@ impl *mut T { /// /// If any of the following conditions are violated, the result is Undefined Behavior: /// - /// * The computed offset, `count * size_of::()` bytes (using unbounded arithmetic), - /// must fit in an `isize`. + /// * The offset in bytes, `count * size_of::()`, computed on mathematical integers (without + /// "wrapping around"), must fit in an `isize`. /// /// * If the computed offset is non-zero, then `self` must be derived from a pointer to some /// [allocated object], and the entire memory range between `self` and the result must be in @@ -399,7 +399,7 @@ impl *mut T { unsafe { intrinsics::offset(self, count) } } - /// Calculates the offset from a pointer in bytes. + /// Adds a signed offset in bytes to a pointer. /// /// `count` is in units of **bytes**. /// @@ -889,7 +889,11 @@ impl *mut T { unsafe { (self as *const T).sub_ptr(origin) } } - /// Adds an offset to a pointer. + /// Adds an unsigned offset to a pointer. + /// + /// This can only move the pointer forward (or not move it). If you need to move forward or + /// backward depending on the value, then you might want [`offset`](#method.offset) instead + /// which takes a signed offset. /// /// `count` is in units of T; e.g., a `count` of 3 represents a pointer /// offset of `3 * size_of::()` bytes. @@ -898,8 +902,8 @@ impl *mut T { /// /// If any of the following conditions are violated, the result is Undefined Behavior: /// - /// * The computed offset, `count * size_of::()` bytes (using unbounded arithmetic), - /// must fit in an `isize`. + /// * The offset in bytes, `count * size_of::()`, computed on mathematical integers (without + /// "wrapping around"), must fit in an `isize`. /// /// * If the computed offset is non-zero, then `self` must be derived from a pointer to some /// [allocated object], and the entire memory range between `self` and the result must be in @@ -942,7 +946,7 @@ impl *mut T { unsafe { intrinsics::offset(self, count) } } - /// Calculates the offset from a pointer in bytes (convenience for `.byte_offset(count as isize)`). + /// Adds an unsigned offset in bytes to a pointer. /// /// `count` is in units of bytes. /// @@ -963,7 +967,11 @@ impl *mut T { unsafe { self.cast::().add(count).with_metadata_of(self) } } - /// Subtracts an offset from a pointer. + /// Subtracts an unsigned offset from a pointer. + /// + /// This can only move the pointer backward (or not move it). If you need to move forward or + /// backward depending on the value, then you might want [`offset`](#method.offset) instead + /// which takes a signed offset. /// /// `count` is in units of T; e.g., a `count` of 3 represents a pointer /// offset of `3 * size_of::()` bytes. @@ -972,8 +980,8 @@ impl *mut T { /// /// If any of the following conditions are violated, the result is Undefined Behavior: /// - /// * The computed offset, `count * size_of::()` bytes (using unbounded arithmetic), - /// must fit in an `isize`. + /// * The offset in bytes, `count * size_of::()`, computed on mathematical integers (without + /// "wrapping around"), must fit in an `isize`. /// /// * If the computed offset is non-zero, then `self` must be derived from a pointer to some /// [allocated object], and the entire memory range between `self` and the result must be in @@ -1024,8 +1032,7 @@ impl *mut T { } } - /// Calculates the offset from a pointer in bytes (convenience for - /// `.byte_offset((count as isize).wrapping_neg())`). + /// Subtracts an unsigned offset in bytes from a pointer. /// /// `count` is in units of bytes. /// From bc3d0722062b8e208ba9096b8445ef69a896ed7d Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 12 Sep 2024 16:25:09 +0200 Subject: [PATCH 003/120] also update the wrapping_ docs to use similar wording --- library/core/src/ptr/const_ptr.rs | 16 ++++++---------- library/core/src/ptr/mut_ptr.rs | 17 +++++++---------- 2 files changed, 13 insertions(+), 20 deletions(-) diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index afec6f8795645..57307c149a46e 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -420,7 +420,7 @@ impl *const T { unsafe { self.cast::().offset(count).with_metadata_of(self) } } - /// Calculates the offset from a pointer using wrapping arithmetic. + /// Adds a signed offset to a pointer using wrapping arithmetic. /// /// `count` is in units of T; e.g., a `count` of 3 represents a pointer /// offset of `3 * size_of::()` bytes. @@ -482,7 +482,7 @@ impl *const T { unsafe { intrinsics::arith_offset(self, count) } } - /// Calculates the offset from a pointer in bytes using wrapping arithmetic. + /// Adds a signed offset in bytes to a pointer using wrapping arithmetic. /// /// `count` is in units of **bytes**. /// @@ -972,8 +972,7 @@ impl *const T { unsafe { self.cast::().sub(count).with_metadata_of(self) } } - /// Calculates the offset from a pointer using wrapping arithmetic. - /// (convenience for `.wrapping_offset(count as isize)`) + /// Adds an unsigned offset to a pointer using wrapping arithmetic. /// /// `count` is in units of T; e.g., a `count` of 3 represents a pointer /// offset of `3 * size_of::()` bytes. @@ -1034,8 +1033,7 @@ impl *const T { self.wrapping_offset(count as isize) } - /// Calculates the offset from a pointer in bytes using wrapping arithmetic. - /// (convenience for `.wrapping_byte_offset(count as isize)`) + /// Adds an unsigned offset in bytes to a pointer using wrapping arithmetic. /// /// `count` is in units of bytes. /// @@ -1053,8 +1051,7 @@ impl *const T { self.cast::().wrapping_add(count).with_metadata_of(self) } - /// Calculates the offset from a pointer using wrapping arithmetic. - /// (convenience for `.wrapping_offset((count as isize).wrapping_neg())`) + /// Subtracts an unsigned offset from a pointer using wrapping arithmetic. /// /// `count` is in units of T; e.g., a `count` of 3 represents a pointer /// offset of `3 * size_of::()` bytes. @@ -1115,8 +1112,7 @@ impl *const T { self.wrapping_offset((count as isize).wrapping_neg()) } - /// Calculates the offset from a pointer in bytes using wrapping arithmetic. - /// (convenience for `.wrapping_offset((count as isize).wrapping_neg())`) + /// Subtracts an unsigned offset in bytes from a pointer using wrapping arithmetic. /// /// `count` is in units of bytes. /// diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index 11fb45b1e09d9..e09419ff1a72b 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -420,7 +420,8 @@ impl *mut T { unsafe { self.cast::().offset(count).with_metadata_of(self) } } - /// Calculates the offset from a pointer using wrapping arithmetic. + /// Adds a signed offset to a pointer using wrapping arithmetic. + /// /// `count` is in units of T; e.g., a `count` of 3 represents a pointer /// offset of `3 * size_of::()` bytes. /// @@ -479,7 +480,7 @@ impl *mut T { unsafe { intrinsics::arith_offset(self, count) as *mut T } } - /// Calculates the offset from a pointer in bytes using wrapping arithmetic. + /// Adds a signed offset in bytes to a pointer using wrapping arithmetic. /// /// `count` is in units of **bytes**. /// @@ -1053,8 +1054,7 @@ impl *mut T { unsafe { self.cast::().sub(count).with_metadata_of(self) } } - /// Calculates the offset from a pointer using wrapping arithmetic. - /// (convenience for `.wrapping_offset(count as isize)`) + /// Adds an unsigned offset to a pointer using wrapping arithmetic. /// /// `count` is in units of T; e.g., a `count` of 3 represents a pointer /// offset of `3 * size_of::()` bytes. @@ -1113,8 +1113,7 @@ impl *mut T { self.wrapping_offset(count as isize) } - /// Calculates the offset from a pointer in bytes using wrapping arithmetic. - /// (convenience for `.wrapping_byte_offset(count as isize)`) + /// Adds an unsigned offset in bytes to a pointer using wrapping arithmetic. /// /// `count` is in units of bytes. /// @@ -1132,8 +1131,7 @@ impl *mut T { self.cast::().wrapping_add(count).with_metadata_of(self) } - /// Calculates the offset from a pointer using wrapping arithmetic. - /// (convenience for `.wrapping_offset((count as isize).wrapping_neg())`) + /// Subtracts an unsigned offset from a pointer using wrapping arithmetic. /// /// `count` is in units of T; e.g., a `count` of 3 represents a pointer /// offset of `3 * size_of::()` bytes. @@ -1192,8 +1190,7 @@ impl *mut T { self.wrapping_offset((count as isize).wrapping_neg()) } - /// Calculates the offset from a pointer in bytes using wrapping arithmetic. - /// (convenience for `.wrapping_offset((count as isize).wrapping_neg())`) + /// Subtracts an unsigned offset in bytes from a pointer using wrapping arithmetic. /// /// `count` is in units of bytes. /// From ae5f8570967d71ab56068372f4d08859a3c13d9f Mon Sep 17 00:00:00 2001 From: VictorHugoPilled Date: Wed, 11 Sep 2024 20:37:08 +0000 Subject: [PATCH 004/120] fix: Specifying reason in expect(clippy::needless_return) no longer triggers false positive chore: Moved new tests into needless_return.rs chore: Ran cargo uibless Initial commit --- clippy_lints/src/returns.rs | 2 +- tests/ui/needless_return.fixed | 20 ++++++++++++++++++++ tests/ui/needless_return.rs | 20 ++++++++++++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 1e7096496957f..423b9b397f1f7 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -421,7 +421,7 @@ fn check_final_expr<'tcx>( if matches!(Level::from_attr(attr), Some(Level::Expect(_))) && let metas = attr.meta_item_list() && let Some(lst) = metas - && let [NestedMetaItem::MetaItem(meta_item)] = lst.as_slice() + && let [NestedMetaItem::MetaItem(meta_item), ..] = lst.as_slice() && let [tool, lint_name] = meta_item.path.segments.as_slice() && tool.ident.name == sym::clippy && matches!( diff --git a/tests/ui/needless_return.fixed b/tests/ui/needless_return.fixed index c5c570690b450..ca422e605d666 100644 --- a/tests/ui/needless_return.fixed +++ b/tests/ui/needless_return.fixed @@ -360,3 +360,23 @@ fn issue12907() -> String { } fn main() {} + +fn a(x: Option) -> Option { + match x { + Some(_) => None, + None => { + #[expect(clippy::needless_return, reason = "Use early return for errors.")] + return None; + }, + } +} + +fn b(x: Option) -> Option { + match x { + Some(_) => None, + None => { + #[expect(clippy::needless_return)] + return None; + }, + } +} diff --git a/tests/ui/needless_return.rs b/tests/ui/needless_return.rs index 738611391dfdf..aad6e13136f1a 100644 --- a/tests/ui/needless_return.rs +++ b/tests/ui/needless_return.rs @@ -370,3 +370,23 @@ fn issue12907() -> String { } fn main() {} + +fn a(x: Option) -> Option { + match x { + Some(_) => None, + None => { + #[expect(clippy::needless_return, reason = "Use early return for errors.")] + return None; + }, + } +} + +fn b(x: Option) -> Option { + match x { + Some(_) => None, + None => { + #[expect(clippy::needless_return)] + return None; + }, + } +} From fa125e2be682e22cf0c2a005a08d7fef2520cefa Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 21 Sep 2024 01:51:26 +0900 Subject: [PATCH 005/120] Support clobber_abi and vector/access registers (clobber-only) in s390x inline assembly --- compiler/rustc_codegen_gcc/src/asm.rs | 8 ++ compiler/rustc_codegen_llvm/src/asm.rs | 6 + compiler/rustc_span/src/symbol.rs | 1 + compiler/rustc_target/src/asm/mod.rs | 29 ++++- compiler/rustc_target/src/asm/s390x.rs | 111 ++++++++++++++++-- .../asm-experimental-arch.md | 7 ++ tests/codegen/asm-s390x-clobbers.rs | 50 ++++++++ 7 files changed, 200 insertions(+), 12 deletions(-) create mode 100644 tests/codegen/asm-s390x-clobbers.rs diff --git a/compiler/rustc_codegen_gcc/src/asm.rs b/compiler/rustc_codegen_gcc/src/asm.rs index 13a00f7e08d80..a04cd4735f478 100644 --- a/compiler/rustc_codegen_gcc/src/asm.rs +++ b/compiler/rustc_codegen_gcc/src/asm.rs @@ -682,6 +682,11 @@ fn reg_to_gcc(reg: InlineAsmRegOrRegClass) -> ConstraintOrRegister { InlineAsmRegClass::S390x(S390xInlineAsmRegClass::reg) => "r", InlineAsmRegClass::S390x(S390xInlineAsmRegClass::reg_addr) => "a", InlineAsmRegClass::S390x(S390xInlineAsmRegClass::freg) => "f", + InlineAsmRegClass::S390x( + S390xInlineAsmRegClass::vreg | S390xInlineAsmRegClass::areg, + ) => { + unreachable!("clobber-only") + } InlineAsmRegClass::Err => unreachable!(), }, }; @@ -757,6 +762,9 @@ fn dummy_output_type<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, reg: InlineAsmRegCl S390xInlineAsmRegClass::reg | S390xInlineAsmRegClass::reg_addr, ) => cx.type_i32(), InlineAsmRegClass::S390x(S390xInlineAsmRegClass::freg) => cx.type_f64(), + InlineAsmRegClass::S390x(S390xInlineAsmRegClass::vreg | S390xInlineAsmRegClass::areg) => { + unreachable!("clobber-only") + } InlineAsmRegClass::Msp430(Msp430InlineAsmRegClass::reg) => cx.type_i16(), InlineAsmRegClass::M68k(M68kInlineAsmRegClass::reg) => cx.type_i32(), InlineAsmRegClass::M68k(M68kInlineAsmRegClass::reg_addr) => cx.type_i32(), diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index 430ba7352433b..27f6481c5360e 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -703,6 +703,9 @@ fn reg_to_llvm(reg: InlineAsmRegOrRegClass, layout: Option<&TyAndLayout<'_>>) -> S390x(S390xInlineAsmRegClass::reg) => "r", S390x(S390xInlineAsmRegClass::reg_addr) => "a", S390x(S390xInlineAsmRegClass::freg) => "f", + S390x(S390xInlineAsmRegClass::vreg | S390xInlineAsmRegClass::areg) => { + unreachable!("clobber-only") + } Msp430(Msp430InlineAsmRegClass::reg) => "r", M68k(M68kInlineAsmRegClass::reg) => "r", M68k(M68kInlineAsmRegClass::reg_addr) => "a", @@ -861,6 +864,9 @@ fn dummy_output_type<'ll>(cx: &CodegenCx<'ll, '_>, reg: InlineAsmRegClass) -> &' Avr(AvrInlineAsmRegClass::reg_ptr) => cx.type_i16(), S390x(S390xInlineAsmRegClass::reg | S390xInlineAsmRegClass::reg_addr) => cx.type_i32(), S390x(S390xInlineAsmRegClass::freg) => cx.type_f64(), + S390x(S390xInlineAsmRegClass::vreg | S390xInlineAsmRegClass::areg) => { + unreachable!("clobber-only") + } Msp430(Msp430InlineAsmRegClass::reg) => cx.type_i16(), M68k(M68kInlineAsmRegClass::reg) => cx.type_i32(), M68k(M68kInlineAsmRegClass::reg_addr) => cx.type_i32(), diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 402232a1720b7..e2d965ff91b1d 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -410,6 +410,7 @@ symbols! { arbitrary_enum_discriminant, arbitrary_self_types, arbitrary_self_types_pointers, + areg, args, arith_offset, arm, diff --git a/compiler/rustc_target/src/asm/mod.rs b/compiler/rustc_target/src/asm/mod.rs index 4d8c5cea8a830..df13d24cb9e21 100644 --- a/compiler/rustc_target/src/asm/mod.rs +++ b/compiler/rustc_target/src/asm/mod.rs @@ -439,7 +439,7 @@ impl InlineAsmReg { Self::Hexagon(r) => r.overlapping_regs(|r| cb(Self::Hexagon(r))), Self::LoongArch(_) => cb(self), Self::Mips(_) => cb(self), - Self::S390x(_) => cb(self), + Self::S390x(r) => r.overlapping_regs(|r| cb(Self::S390x(r))), Self::Bpf(r) => r.overlapping_regs(|r| cb(Self::Bpf(r))), Self::Avr(r) => r.overlapping_regs(|r| cb(Self::Avr(r))), Self::Msp430(_) => cb(self), @@ -892,6 +892,7 @@ pub enum InlineAsmClobberAbi { AArch64NoX18, RiscV, LoongArch, + S390x, } impl InlineAsmClobberAbi { @@ -941,6 +942,10 @@ impl InlineAsmClobberAbi { "C" | "system" => Ok(InlineAsmClobberAbi::LoongArch), _ => Err(&["C", "system"]), }, + InlineAsmArch::S390x => match name { + "C" | "system" => Ok(InlineAsmClobberAbi::S390x), + _ => Err(&["C", "system"]), + }, _ => Err(&[]), } } @@ -1098,6 +1103,28 @@ impl InlineAsmClobberAbi { f16, f17, f18, f19, f20, f21, f22, f23, } }, + InlineAsmClobberAbi::S390x => clobbered_regs! { + S390x S390xInlineAsmReg { + r0, r1, r2, r3, r4, r5, + r14, + + // f0-f7, v0-v7 + f0, f1, f2, f3, f4, f5, f6, f7, + v0, v1, v2, v3, v4, v5, v6, v7, + + // Technically the left halves of v8-v15 (i.e., f8-f15) are saved, but + // we have no way of expressing this using clobbers. + v8, v9, v10, v11, v12, v13, v14, v15, + + // Other vector registers are volatile + v16, v17, v18, v19, v20, v21, v22, v23, + v24, v25, v26, v27, v28, v29, v30, v31, + + // a0-a1 are reserved, other access registers are volatile + a2, a3, a4, a5, a6, a7, + a8, a9, a10, a11, a12, a13, a14, a15, + } + }, } } } diff --git a/compiler/rustc_target/src/asm/s390x.rs b/compiler/rustc_target/src/asm/s390x.rs index 4258b23ac57eb..9b31190a72ba4 100644 --- a/compiler/rustc_target/src/asm/s390x.rs +++ b/compiler/rustc_target/src/asm/s390x.rs @@ -9,6 +9,8 @@ def_reg_class! { reg, reg_addr, freg, + vreg, + areg, } } @@ -35,11 +37,13 @@ impl S390xInlineAsmRegClass { pub fn supported_types( self, - arch: InlineAsmArch, + _arch: InlineAsmArch, ) -> &'static [(InlineAsmType, Option)] { - match (self, arch) { - (Self::reg | Self::reg_addr, _) => types! { _: I8, I16, I32, I64; }, - (Self::freg, _) => types! { _: F32, F64; }, + match self { + Self::reg | Self::reg_addr => types! { _: I8, I16, I32, I64; }, + Self::freg => types! { _: F32, F64; }, + Self::vreg => &[], + Self::areg => &[], } } } @@ -76,6 +80,52 @@ def_regs! { f13: freg = ["f13"], f14: freg = ["f14"], f15: freg = ["f15"], + v0: vreg = ["v0"], + v1: vreg = ["v1"], + v2: vreg = ["v2"], + v3: vreg = ["v3"], + v4: vreg = ["v4"], + v5: vreg = ["v5"], + v6: vreg = ["v6"], + v7: vreg = ["v7"], + v8: vreg = ["v8"], + v9: vreg = ["v9"], + v10: vreg = ["v10"], + v11: vreg = ["v11"], + v12: vreg = ["v12"], + v13: vreg = ["v13"], + v14: vreg = ["v14"], + v15: vreg = ["v15"], + v16: vreg = ["v16"], + v17: vreg = ["v17"], + v18: vreg = ["v18"], + v19: vreg = ["v19"], + v20: vreg = ["v20"], + v21: vreg = ["v21"], + v22: vreg = ["v22"], + v23: vreg = ["v23"], + v24: vreg = ["v24"], + v25: vreg = ["v25"], + v26: vreg = ["v26"], + v27: vreg = ["v27"], + v28: vreg = ["v28"], + v29: vreg = ["v29"], + v30: vreg = ["v30"], + v31: vreg = ["v31"], + a2: areg = ["a2"], + a3: areg = ["a3"], + a4: areg = ["a4"], + a5: areg = ["a5"], + a6: areg = ["a6"], + a7: areg = ["a7"], + a8: areg = ["a8"], + a9: areg = ["a9"], + a10: areg = ["a10"], + a11: areg = ["a11"], + a12: areg = ["a12"], + a13: areg = ["a13"], + a14: areg = ["a14"], + a15: areg = ["a15"], #error = ["r11"] => "The frame pointer cannot be used as an operand for inline asm", #error = ["r15"] => @@ -87,13 +137,8 @@ def_regs! { "c12", "c13", "c14", "c15" ] => "control registers are reserved by the kernel and cannot be used as operands for inline asm", - #error = [ - "a0", "a1", "a2", "a3", - "a4", "a5", "a6", "a7", - "a8", "a9", "a10", "a11", - "a12", "a13", "a14", "a15" - ] => - "access registers are not supported and cannot be used as operands for inline asm", + #error = ["a0", "a1"] => + "a0 and a1 are reserved for system use and cannot be used as operands for inline asm", } } @@ -106,4 +151,48 @@ impl S390xInlineAsmReg { ) -> fmt::Result { write!(out, "%{}", self.name()) } + + pub fn overlapping_regs(self, mut cb: impl FnMut(S390xInlineAsmReg)) { + macro_rules! reg_conflicts { + ( + $( + $full:ident : $($field:ident)* + ),*; + ) => { + match self { + $( + Self::$full => { + cb(Self::$full); + $(cb(Self::$field);)* + } + $(Self::$field)|* => { + cb(Self::$full); + cb(self); + } + )* + r => cb(r), + } + }; + } + + // The left halves of v0-v15 are aliased to f0-f15. + reg_conflicts! { + v0 : f0, + v1 : f1, + v2 : f2, + v3 : f3, + v4 : f4, + v5 : f5, + v6 : f6, + v7 : f7, + v8 : f8, + v9 : f9, + v10 : f10, + v11 : f11, + v12 : f12, + v13 : f13, + v14 : f14, + v15 : f15; + } + } } diff --git a/src/doc/unstable-book/src/language-features/asm-experimental-arch.md b/src/doc/unstable-book/src/language-features/asm-experimental-arch.md index 43e11b6d57dcb..b1c429c7676ef 100644 --- a/src/doc/unstable-book/src/language-features/asm-experimental-arch.md +++ b/src/doc/unstable-book/src/language-features/asm-experimental-arch.md @@ -51,7 +51,10 @@ This feature tracks `asm!` and `global_asm!` support for the following architect | CSKY | `reg` | `r[0-31]` | `r` | | CSKY | `freg` | `f[0-31]` | `f` | | s390x | `reg` | `r[0-10]`, `r[12-14]` | `r` | +| s390x | `reg_addr` | `r[1-10]`, `r[12-14]` | `a` | | s390x | `freg` | `f[0-15]` | `f` | +| s390x | `vreg` | `v[0-31]` | Only clobbers | +| s390x | `areg` | `a[2-15]` | Only clobbers | | Arm64EC | `reg` | `x[0-12]`, `x[15-22]`, `x[25-27]`, `x30` | `r` | | Arm64EC | `vreg` | `v[0-15]` | `w` | | Arm64EC | `vreg_low16` | `v[0-15]` | `x` | @@ -90,6 +93,8 @@ This feature tracks `asm!` and `global_asm!` support for the following architect | CSKY | `freg` | None | `f32`, | | s390x | `reg`, `reg_addr` | None | `i8`, `i16`, `i32`, `i64` | | s390x | `freg` | None | `f32`, `f64` | +| s390x | `vreg` | N/A | Only clobbers | +| s390x | `areg` | N/A | Only clobbers | | Arm64EC | `reg` | None | `i8`, `i16`, `i32`, `f32`, `i64`, `f64` | | Arm64EC | `vreg` | None | `i8`, `i16`, `i32`, `f32`, `i64`, `f64`,
`i8x8`, `i16x4`, `i32x2`, `i64x1`, `f32x2`, `f64x1`,
`i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4`, `f64x2` | @@ -157,6 +162,8 @@ This feature tracks `asm!` and `global_asm!` support for the following architect | CSKY | `r15` | This is the link register. | | CSKY | `r[26-30]` | Reserved by its ABI. | | CSKY | `r31` | This is the TLS register. | +| s390x | `c[0-15]` | Reserved by the kernel. | +| s390x | `a[0-1]` | Reserved for system use. | | Arm64EC | `xzr` | This is a constant zero register which can't be modified. | | Arm64EC | `x18` | This is an OS-reserved register. | | Arm64EC | `x13`, `x14`, `x23`, `x24`, `x28`, `v[16-31]` | These are AArch64 registers that are not supported for Arm64EC. | diff --git a/tests/codegen/asm-s390x-clobbers.rs b/tests/codegen/asm-s390x-clobbers.rs new file mode 100644 index 0000000000000..45f72206bdfab --- /dev/null +++ b/tests/codegen/asm-s390x-clobbers.rs @@ -0,0 +1,50 @@ +//@ revisions: s390x +//@[s390x] compile-flags: --target s390x-unknown-linux-gnu +//@[s390x] needs-llvm-components: systemz + +#![crate_type = "rlib"] +#![feature(no_core, rustc_attrs, lang_items, asm_experimental_arch)] +#![no_core] + +#[lang = "sized"] +trait Sized {} + +#[rustc_builtin_macro] +macro_rules! asm { + () => {}; +} + +// CHECK-LABEL: @cc_clobber +// CHECK: call void asm sideeffect "", "~{cc}"() +#[no_mangle] +pub unsafe fn cc_clobber() { + asm!("", options(nostack, nomem)); +} + +// CHECK-LABEL: @no_clobber +// CHECK: call void asm sideeffect "", ""() +#[no_mangle] +pub unsafe fn no_clobber() { + asm!("", options(nostack, nomem, preserves_flags)); +} + +// CHECK-LABEL: @a2_clobber +// CHECK: call void asm sideeffect "", "~{a2}"() +#[no_mangle] +pub unsafe fn a2_clobber() { + asm!("", out("a2") _, options(nostack, nomem, preserves_flags)); +} + +// CHECK-LABEL: @v0_clobber +// CHECK: call void asm sideeffect "", "~{v0}"() +#[no_mangle] +pub unsafe fn v0_clobber() { + asm!("", out("v0") _, options(nostack, nomem, preserves_flags)); +} + +// CHECK-LABEL: @clobber_abi +// CHECK: asm sideeffect "", "={r0},={r1},={r2},={r3},={r4},={r5},={r14},={f0},={f1},={f2},={f3},={f4},={f5},={f6},={f7},~{v0},~{v1},~{v2},~{v3},~{v4},~{v5},~{v6},~{v7},~{v8},~{v9},~{v10},~{v11},~{v12},~{v13},~{v14},~{v15},~{v16},~{v17},~{v18},~{v19},~{v20},~{v21},~{v22},~{v23},~{v24},~{v25},~{v26},~{v27},~{v28},~{v29},~{v30},~{v31},~{a2},~{a3},~{a4},~{a5},~{a6},~{a7},~{a8},~{a9},~{a10},~{a11},~{a12},~{a13},~{a14},~{a15}"() +#[no_mangle] +pub unsafe fn clobber_abi() { + asm!("", clobber_abi("C"), options(nostack, nomem, preserves_flags)); +} From 9cd668beed38a8e98933733854a224c70998bf16 Mon Sep 17 00:00:00 2001 From: Pavel Grigorenko Date: Mon, 23 Sep 2024 02:12:53 +0300 Subject: [PATCH 006/120] Parser: better error messages for `@` in struct patterns --- compiler/rustc_parse/messages.ftl | 7 ++ compiler/rustc_parse/src/errors.rs | 19 +++++ compiler/rustc_parse/src/parser/pat.rs | 54 +++++++++++---- tests/ui/pattern/at-in-struct-patterns.rs | 14 ++++ tests/ui/pattern/at-in-struct-patterns.stderr | 69 +++++++++++++++++++ 5 files changed, 150 insertions(+), 13 deletions(-) create mode 100644 tests/ui/pattern/at-in-struct-patterns.rs create mode 100644 tests/ui/pattern/at-in-struct-patterns.stderr diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 6cb851eb8df6b..5d1c300b45357 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -26,6 +26,13 @@ parse_async_move_block_in_2015 = `async move` blocks are only allowed in Rust 20 parse_async_move_order_incorrect = the order of `move` and `async` is incorrect .suggestion = try switching the order +parse_at_dot_dot_in_struct_pattern = `@ ..` is not supported in struct patterns + .suggestion = bind to each field separately or, if you don't need them, just remove `{$ident} @` + +parse_at_in_struct_pattern = Unexpected `@` in struct pattern + .note = struct patterns use `field: pattern` syntax to bind to fields + .help = consider replacing `new_name @ field_name` with `field_name: new_name` if that is what you intended + parse_attr_after_generic = trailing attribute after generic parameter .label = attributes must go before parameters diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index e9fe2e6c1dd12..e16ab5b4b06f0 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -2583,6 +2583,25 @@ pub(crate) struct EnumPatternInsteadOfIdentifier { pub span: Span, } +#[derive(Diagnostic)] +#[diag(parse_at_dot_dot_in_struct_pattern)] +pub(crate) struct AtDotDotInStructPattern { + #[primary_span] + pub span: Span, + #[suggestion(code = "", style = "verbose", applicability = "machine-applicable")] + pub remove: Span, + pub ident: Ident, +} + +#[derive(Diagnostic)] +#[diag(parse_at_in_struct_pattern)] +#[note] +#[help] +pub(crate) struct AtInStructPattern { + #[primary_span] + pub span: Span, +} + #[derive(Diagnostic)] #[diag(parse_dot_dot_dot_for_remaining_fields)] pub(crate) struct DotDotDotForRemainingFields { diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 647df25c82ef7..b4cbe1accf4ff 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -17,15 +17,16 @@ use thin_vec::{thin_vec, ThinVec}; use super::{ForceCollect, Parser, PathStyle, Restrictions, Trailing, UsePreAttrPos}; use crate::errors::{ - self, AmbiguousRangePattern, DotDotDotForRemainingFields, DotDotDotRangeToPatternNotAllowed, - DotDotDotRestPattern, EnumPatternInsteadOfIdentifier, ExpectedBindingLeftOfAt, - ExpectedCommaAfterPatternField, GenericArgsInPatRequireTurbofishSyntax, - InclusiveRangeExtraEquals, InclusiveRangeMatchArrow, InclusiveRangeNoEnd, InvalidMutInPattern, - ParenRangeSuggestion, PatternOnWrongSideOfAt, RemoveLet, RepeatedMutInPattern, - SwitchRefBoxOrder, TopLevelOrPatternNotAllowed, TopLevelOrPatternNotAllowedSugg, - TrailingVertNotAllowed, UnexpectedExpressionInPattern, UnexpectedExpressionInPatternSugg, - UnexpectedLifetimeInPattern, UnexpectedParenInRangePat, UnexpectedParenInRangePatSugg, - UnexpectedVertVertBeforeFunctionParam, UnexpectedVertVertInPattern, WrapInParens, + self, AmbiguousRangePattern, AtDotDotInStructPattern, AtInStructPattern, + DotDotDotForRemainingFields, DotDotDotRangeToPatternNotAllowed, DotDotDotRestPattern, + EnumPatternInsteadOfIdentifier, ExpectedBindingLeftOfAt, ExpectedCommaAfterPatternField, + GenericArgsInPatRequireTurbofishSyntax, InclusiveRangeExtraEquals, InclusiveRangeMatchArrow, + InclusiveRangeNoEnd, InvalidMutInPattern, ParenRangeSuggestion, PatternOnWrongSideOfAt, + RemoveLet, RepeatedMutInPattern, SwitchRefBoxOrder, TopLevelOrPatternNotAllowed, + TopLevelOrPatternNotAllowedSugg, TrailingVertNotAllowed, UnexpectedExpressionInPattern, + UnexpectedExpressionInPatternSugg, UnexpectedLifetimeInPattern, UnexpectedParenInRangePat, + UnexpectedParenInRangePatSugg, UnexpectedVertVertBeforeFunctionParam, + UnexpectedVertVertInPattern, WrapInParens, }; use crate::parser::expr::{could_be_unclosed_char_literal, DestructuredFloat}; use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole}; @@ -1433,7 +1434,7 @@ impl<'a> Parser<'a> { /// Parses the fields of a struct-like pattern. fn parse_pat_fields(&mut self) -> PResult<'a, (ThinVec, PatFieldsRest)> { - let mut fields = ThinVec::new(); + let mut fields: ThinVec = ThinVec::new(); let mut etc = PatFieldsRest::None; let mut ate_comma = true; let mut delayed_err: Option> = None; @@ -1454,12 +1455,22 @@ impl<'a> Parser<'a> { // check that a comma comes after every field if !ate_comma { - let mut err = - self.dcx().create_err(ExpectedCommaAfterPatternField { span: self.token.span }); + let err = if self.token == token::At { + let prev_field = fields + .last() + .expect("Unreachable on first iteration, not empty otherwise") + .ident; + self.report_misplaced_at_in_struct_pat(prev_field) + } else { + let mut err = self + .dcx() + .create_err(ExpectedCommaAfterPatternField { span: self.token.span }); + self.recover_misplaced_pattern_modifiers(&fields, &mut err); + err + }; if let Some(delayed) = delayed_err { delayed.emit(); } - self.recover_misplaced_pattern_modifiers(&fields, &mut err); return Err(err); } ate_comma = false; @@ -1594,6 +1605,23 @@ impl<'a> Parser<'a> { Ok((fields, etc)) } + #[deny(rustc::untranslatable_diagnostic)] + fn report_misplaced_at_in_struct_pat(&self, prev_field: Ident) -> Diag<'a> { + debug_assert_eq!(self.token, token::At); + let span = prev_field.span.to(self.token.span); + if let Some(dot_dot_span) = + self.look_ahead(1, |t| if t == &token::DotDot { Some(t.span) } else { None }) + { + self.dcx().create_err(AtDotDotInStructPattern { + span: span.to(dot_dot_span), + remove: span.until(dot_dot_span), + ident: prev_field, + }) + } else { + self.dcx().create_err(AtInStructPattern { span: span }) + } + } + /// If the user writes `S { ref field: name }` instead of `S { field: ref name }`, we suggest /// the correct code. fn recover_misplaced_pattern_modifiers(&self, fields: &ThinVec, err: &mut Diag<'a>) { diff --git a/tests/ui/pattern/at-in-struct-patterns.rs b/tests/ui/pattern/at-in-struct-patterns.rs new file mode 100644 index 0000000000000..e8fad61f31784 --- /dev/null +++ b/tests/ui/pattern/at-in-struct-patterns.rs @@ -0,0 +1,14 @@ +struct Foo { + field1: u8, + field2: u8, +} + +fn main() { + let foo = Foo { field1: 1, field2: 2 }; + let Foo { var @ field1, .. } = foo; //~ ERROR Unexpected `@` in struct pattern + dbg!(var); //~ ERROR cannot find value `var` in this scope + let Foo { field1: _, bar @ .. } = foo; //~ ERROR `@ ..` is not supported in struct patterns + let Foo { bar @ .. } = foo; //~ ERROR `@ ..` is not supported in struct patterns + let Foo { @ } = foo; //~ ERROR expected identifier, found `@` + let Foo { @ .. } = foo; //~ ERROR expected identifier, found `@` +} diff --git a/tests/ui/pattern/at-in-struct-patterns.stderr b/tests/ui/pattern/at-in-struct-patterns.stderr new file mode 100644 index 0000000000000..ff75edfe6811f --- /dev/null +++ b/tests/ui/pattern/at-in-struct-patterns.stderr @@ -0,0 +1,69 @@ +error: Unexpected `@` in struct pattern + --> $DIR/at-in-struct-patterns.rs:8:15 + | +LL | let Foo { var @ field1, .. } = foo; + | --- ^^^^^ + | | + | while parsing the fields for this pattern + | + = note: struct patterns use `field: pattern` syntax to bind to fields + = help: consider replacing `new_name @ field_name` with `field_name: new_name` if that is what you intended + +error: `@ ..` is not supported in struct patterns + --> $DIR/at-in-struct-patterns.rs:10:26 + | +LL | let Foo { field1: _, bar @ .. } = foo; + | --- ^^^^^^^^ + | | + | while parsing the fields for this pattern + | +help: bind to each field separately or, if you don't need them, just remove `bar @` + | +LL - let Foo { field1: _, bar @ .. } = foo; +LL + let Foo { field1: _, .. } = foo; + | + +error: `@ ..` is not supported in struct patterns + --> $DIR/at-in-struct-patterns.rs:11:15 + | +LL | let Foo { bar @ .. } = foo; + | --- ^^^^^^^^ + | | + | while parsing the fields for this pattern + | +help: bind to each field separately or, if you don't need them, just remove `bar @` + | +LL - let Foo { bar @ .. } = foo; +LL + let Foo { .. } = foo; + | + +error: expected identifier, found `@` + --> $DIR/at-in-struct-patterns.rs:12:15 + | +LL | let Foo { @ } = foo; + | --- ^ expected identifier + | | + | while parsing the fields for this pattern + +error: expected identifier, found `@` + --> $DIR/at-in-struct-patterns.rs:13:15 + | +LL | let Foo { @ .. } = foo; + | --- ^ expected identifier + | | + | while parsing the fields for this pattern + +error[E0425]: cannot find value `var` in this scope + --> $DIR/at-in-struct-patterns.rs:9:10 + | +LL | dbg!(var); + | ^^^ not found in this scope + | +help: consider importing this function + | +LL + use std::env::var; + | + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0425`. From 6e387c90f9970c1db15494b73cecdffd27176033 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Mon, 23 Sep 2024 15:26:43 +0000 Subject: [PATCH 007/120] Add reasons for or remove some `//@no-rustfix` annotations --- tests/ui/as_ptr_cast_mut.rs | 2 +- tests/ui/borrow_box.fixed | 132 ++++++++++++++++++ tests/ui/borrow_box.rs | 17 ++- tests/ui/borrow_box.stderr | 20 +-- tests/ui/bytecount.rs | 2 +- tests/ui/deref_addrof_double_trigger.rs | 4 +- tests/ui/float_cmp.rs | 2 +- tests/ui/float_cmp_const.rs | 3 +- tests/ui/float_cmp_const.stderr | 16 +-- tests/ui/float_equality_without_abs.rs | 2 +- tests/ui/unused_format_specs.1.fixed | 35 +++++ tests/ui/unused_format_specs.2.fixed | 35 +++++ ...cs_unfixable.rs => unused_format_specs.rs} | 2 +- ...able.stderr => unused_format_specs.stderr} | 8 +- 14 files changed, 242 insertions(+), 38 deletions(-) create mode 100644 tests/ui/borrow_box.fixed create mode 100644 tests/ui/unused_format_specs.1.fixed create mode 100644 tests/ui/unused_format_specs.2.fixed rename tests/ui/{unused_format_specs_unfixable.rs => unused_format_specs.rs} (98%) rename tests/ui/{unused_format_specs_unfixable.stderr => unused_format_specs.stderr} (89%) diff --git a/tests/ui/as_ptr_cast_mut.rs b/tests/ui/as_ptr_cast_mut.rs index 297a53b1bbfb4..9e862320f4efe 100644 --- a/tests/ui/as_ptr_cast_mut.rs +++ b/tests/ui/as_ptr_cast_mut.rs @@ -1,7 +1,7 @@ #![allow(unused)] #![warn(clippy::as_ptr_cast_mut)] #![allow(clippy::wrong_self_convention, clippy::unnecessary_cast)] -//@no-rustfix +//@no-rustfix: incorrect suggestion struct MutPtrWrapper(Vec); impl MutPtrWrapper { diff --git a/tests/ui/borrow_box.fixed b/tests/ui/borrow_box.fixed new file mode 100644 index 0000000000000..5984cc4e93021 --- /dev/null +++ b/tests/ui/borrow_box.fixed @@ -0,0 +1,132 @@ +#![deny(clippy::borrowed_box)] +#![allow(dead_code, unused_variables)] +#![allow( + clippy::uninlined_format_args, + clippy::disallowed_names, + clippy::needless_pass_by_ref_mut +)] + +use std::fmt::Display; + +pub fn test1(foo: &mut Box) { + // Although this function could be changed to "&mut bool", + // avoiding the Box, mutable references to boxes are not + // flagged by this lint. + // + // This omission is intentional: By passing a mutable Box, + // the memory location of the pointed-to object could be + // modified. By passing a mutable reference, the contents + // could change, but not the location. + println!("{:?}", foo) +} + +pub fn test2() { + let foo: &bool; + //~^ ERROR: you seem to be trying to use `&Box`. Consider using just `&T` +} + +struct Test3<'a> { + foo: &'a bool, + //~^ ERROR: you seem to be trying to use `&Box`. Consider using just `&T` +} + +trait Test4 { + fn test4(a: &bool); + //~^ ERROR: you seem to be trying to use `&Box`. Consider using just `&T` +} + +use std::any::Any; + +pub fn test5(foo: &mut Box) { + println!("{:?}", foo) +} + +pub fn test6() { + let foo: &Box; +} + +struct Test7<'a> { + foo: &'a Box, +} + +trait Test8 { + fn test8(a: &Box); +} + +impl<'a> Test8 for Test7<'a> { + fn test8(a: &Box) { + unimplemented!(); + } +} + +pub fn test9(foo: &mut Box) { + let _ = foo; +} + +pub fn test10() { + let foo: &Box; +} + +struct Test11<'a> { + foo: &'a Box, +} + +trait Test12 { + fn test4(a: &Box); +} + +impl<'a> Test12 for Test11<'a> { + fn test4(a: &Box) { + unimplemented!(); + } +} + +pub fn test13(boxed_slice: &mut Box<[i32]>) { + // Unconditionally replaces the box pointer. + // + // This cannot be accomplished if "&mut [i32]" is passed, + // and provides a test case where passing a reference to + // a Box is valid. + let mut data = vec![12]; + *boxed_slice = data.into_boxed_slice(); +} + +// The suggestion should include proper parentheses to avoid a syntax error. +pub fn test14(_display: &dyn Display) {} +//~^ ERROR: you seem to be trying to use `&Box`. Consider using just `&T` +pub fn test15(_display: &(dyn Display + Send)) {} +//~^ ERROR: you seem to be trying to use `&Box`. Consider using just `&T` +pub fn test16<'a>(_display: &'a (dyn Display + 'a)) {} +//~^ ERROR: you seem to be trying to use `&Box`. Consider using just `&T` + +pub fn test17(_display: &impl Display) {} +//~^ ERROR: you seem to be trying to use `&Box`. Consider using just `&T` +pub fn test18(_display: &(impl Display + Send)) {} +//~^ ERROR: you seem to be trying to use `&Box`. Consider using just `&T` +pub fn test19<'a>(_display: &'a (impl Display + 'a)) {} +//~^ ERROR: you seem to be trying to use `&Box`. Consider using just `&T` + +// This exists only to check what happens when parentheses are already present. +// Even though the current implementation doesn't put extra parentheses, +// it's fine that unnecessary parentheses appear in the future for some reason. +pub fn test20(_display: &(dyn Display + Send)) {} +//~^ ERROR: you seem to be trying to use `&Box`. Consider using just `&T` + +#[allow(clippy::borrowed_box)] +trait Trait { + fn f(b: &Box); +} + +// Trait impls are not linted +impl Trait for () { + fn f(_: &Box) {} +} + +fn main() { + test1(&mut Box::new(false)); + test2(); + test5(&mut (Box::new(false) as Box)); + test6(); + test9(&mut (Box::new(false) as Box)); + test10(); +} diff --git a/tests/ui/borrow_box.rs b/tests/ui/borrow_box.rs index e9994aac8454a..7f15fc83a1d57 100644 --- a/tests/ui/borrow_box.rs +++ b/tests/ui/borrow_box.rs @@ -5,7 +5,6 @@ clippy::disallowed_names, clippy::needless_pass_by_ref_mut )] -//@no-rustfix use std::fmt::Display; @@ -36,12 +35,6 @@ trait Test4 { //~^ ERROR: you seem to be trying to use `&Box`. Consider using just `&T` } -impl<'a> Test4 for Test3<'a> { - fn test4(a: &Box) { - unimplemented!(); - } -} - use std::any::Any; pub fn test5(foo: &mut Box) { @@ -119,6 +112,16 @@ pub fn test19<'a>(_display: &'a Box) {} pub fn test20(_display: &Box<(dyn Display + Send)>) {} //~^ ERROR: you seem to be trying to use `&Box`. Consider using just `&T` +#[allow(clippy::borrowed_box)] +trait Trait { + fn f(b: &Box); +} + +// Trait impls are not linted +impl Trait for () { + fn f(_: &Box) {} +} + fn main() { test1(&mut Box::new(false)); test2(); diff --git a/tests/ui/borrow_box.stderr b/tests/ui/borrow_box.stderr index 34e8f11dfd743..ed4308161bbb2 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` - --> tests/ui/borrow_box.rs:25:14 + --> tests/ui/borrow_box.rs:24: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` - --> tests/ui/borrow_box.rs:30:10 + --> tests/ui/borrow_box.rs:29:10 | LL | foo: &'a Box, | ^^^^^^^^^^^^^ help: try: `&'a bool` error: you seem to be trying to use `&Box`. Consider using just `&T` - --> tests/ui/borrow_box.rs:35:17 + --> tests/ui/borrow_box.rs:34:17 | LL | fn test4(a: &Box); | ^^^^^^^^^^ help: try: `&bool` error: you seem to be trying to use `&Box`. Consider using just `&T` - --> tests/ui/borrow_box.rs:102:25 + --> tests/ui/borrow_box.rs:95:25 | LL | pub fn test14(_display: &Box) {} | ^^^^^^^^^^^^^^^^^ help: try: `&dyn Display` error: you seem to be trying to use `&Box`. Consider using just `&T` - --> tests/ui/borrow_box.rs:104:25 + --> tests/ui/borrow_box.rs:97: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` - --> tests/ui/borrow_box.rs:106:29 + --> tests/ui/borrow_box.rs:99: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` - --> tests/ui/borrow_box.rs:109:25 + --> tests/ui/borrow_box.rs:102:25 | LL | pub fn test17(_display: &Box) {} | ^^^^^^^^^^^^^^^^^^ help: try: `&impl Display` error: you seem to be trying to use `&Box`. Consider using just `&T` - --> tests/ui/borrow_box.rs:111:25 + --> tests/ui/borrow_box.rs:104: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` - --> tests/ui/borrow_box.rs:113:29 + --> tests/ui/borrow_box.rs:106: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` - --> tests/ui/borrow_box.rs:119:25 + --> tests/ui/borrow_box.rs:112:25 | LL | pub fn test20(_display: &Box<(dyn Display + Send)>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&(dyn Display + Send)` diff --git a/tests/ui/bytecount.rs b/tests/ui/bytecount.rs index 3794fc5d441c6..f3b02fda8a81e 100644 --- a/tests/ui/bytecount.rs +++ b/tests/ui/bytecount.rs @@ -1,4 +1,4 @@ -//@no-rustfix +//@no-rustfix: suggests external crate #![allow(clippy::needless_borrow, clippy::useless_vec)] diff --git a/tests/ui/deref_addrof_double_trigger.rs b/tests/ui/deref_addrof_double_trigger.rs index 32582a3a8bfa3..6f997875777e4 100644 --- a/tests/ui/deref_addrof_double_trigger.rs +++ b/tests/ui/deref_addrof_double_trigger.rs @@ -1,5 +1,5 @@ -// This test can't work with run-rustfix because it needs two passes of test+fix -//@no-rustfix +//@no-rustfix: this test can't work with run-rustfix because it needs two passes of test+fix + #[warn(clippy::deref_addrof)] #[allow(unused_variables, unused_mut)] fn main() { diff --git a/tests/ui/float_cmp.rs b/tests/ui/float_cmp.rs index 78dd2c6c01c3d..a1dfd1954fced 100644 --- a/tests/ui/float_cmp.rs +++ b/tests/ui/float_cmp.rs @@ -8,7 +8,7 @@ clippy::unnecessary_operation, clippy::cast_lossless )] -//@no-rustfix +//@no-rustfix: suggestions have an error margin placeholder use std::ops::Add; const ZERO: f32 = 0.0; diff --git a/tests/ui/float_cmp_const.rs b/tests/ui/float_cmp_const.rs index 0818055643734..ba760a18f28a1 100644 --- a/tests/ui/float_cmp_const.rs +++ b/tests/ui/float_cmp_const.rs @@ -1,5 +1,4 @@ -// does not test any rustfixable lints -//@no-rustfix +//@no-rustfix: suggestions have an error margin placeholder #![warn(clippy::float_cmp_const)] #![allow(clippy::float_cmp)] #![allow(unused, clippy::no_effect, clippy::unnecessary_operation)] diff --git a/tests/ui/float_cmp_const.stderr b/tests/ui/float_cmp_const.stderr index 4f88746e958e2..e0cd6faf4b3af 100644 --- a/tests/ui/float_cmp_const.stderr +++ b/tests/ui/float_cmp_const.stderr @@ -1,5 +1,5 @@ error: strict comparison of `f32` or `f64` constant - --> tests/ui/float_cmp_const.rs:16:5 + --> tests/ui/float_cmp_const.rs:15:5 | LL | 1f32 == ONE; | ^^^^^^^^^^^ help: consider comparing them within some margin of error: `(1f32 - ONE).abs() < error_margin` @@ -8,43 +8,43 @@ LL | 1f32 == ONE; = help: to override `-D warnings` add `#[allow(clippy::float_cmp_const)]` error: strict comparison of `f32` or `f64` constant - --> tests/ui/float_cmp_const.rs:18:5 + --> tests/ui/float_cmp_const.rs:17:5 | LL | TWO == ONE; | ^^^^^^^^^^ help: consider comparing them within some margin of error: `(TWO - ONE).abs() < error_margin` error: strict comparison of `f32` or `f64` constant - --> tests/ui/float_cmp_const.rs:20:5 + --> tests/ui/float_cmp_const.rs:19:5 | LL | TWO != ONE; | ^^^^^^^^^^ help: consider comparing them within some margin of error: `(TWO - ONE).abs() > error_margin` error: strict comparison of `f32` or `f64` constant - --> tests/ui/float_cmp_const.rs:22:5 + --> tests/ui/float_cmp_const.rs:21:5 | LL | ONE + ONE == TWO; | ^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(ONE + ONE - TWO).abs() < error_margin` error: strict comparison of `f32` or `f64` constant - --> tests/ui/float_cmp_const.rs:25:5 + --> tests/ui/float_cmp_const.rs:24:5 | LL | x as f32 == ONE; | ^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(x as f32 - ONE).abs() < error_margin` error: strict comparison of `f32` or `f64` constant - --> tests/ui/float_cmp_const.rs:29:5 + --> tests/ui/float_cmp_const.rs:28:5 | LL | v == ONE; | ^^^^^^^^ help: consider comparing them within some margin of error: `(v - ONE).abs() < error_margin` error: strict comparison of `f32` or `f64` constant - --> tests/ui/float_cmp_const.rs:31:5 + --> tests/ui/float_cmp_const.rs:30:5 | LL | v != ONE; | ^^^^^^^^ help: consider comparing them within some margin of error: `(v - ONE).abs() > error_margin` error: strict comparison of `f32` or `f64` constant arrays - --> tests/ui/float_cmp_const.rs:64:5 + --> tests/ui/float_cmp_const.rs:63:5 | LL | NON_ZERO_ARRAY == NON_ZERO_ARRAY2; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/float_equality_without_abs.rs b/tests/ui/float_equality_without_abs.rs index 2b67c8bec1039..500b3035390fe 100644 --- a/tests/ui/float_equality_without_abs.rs +++ b/tests/ui/float_equality_without_abs.rs @@ -1,5 +1,5 @@ #![warn(clippy::float_equality_without_abs)] -//@no-rustfix +//@no-rustfix: suggestions cause type ambiguity // FIXME(f16_f128): add tests for these types when abs is available diff --git a/tests/ui/unused_format_specs.1.fixed b/tests/ui/unused_format_specs.1.fixed new file mode 100644 index 0000000000000..b7d1cce287016 --- /dev/null +++ b/tests/ui/unused_format_specs.1.fixed @@ -0,0 +1,35 @@ +#![warn(clippy::unused_format_specs)] +#![allow(unused)] + +macro_rules! format_args_from_macro { + () => { + format_args!("from macro") + }; +} + +fn main() { + // prints `.`, not ` .` + println!("{:5}.", format!("")); + //~^ ERROR: format specifiers have no effect on `format_args!()` + //~| NOTE: `-D clippy::unused-format-specs` implied by `-D warnings` + //prints `abcde`, not `abc` + println!("{:.3}", format!("abcde")); + //~^ ERROR: format specifiers have no effect on `format_args!()` + + println!("{}.", format_args_from_macro!()); + //~^ ERROR: format specifiers have no effect on `format_args!()` + + let args = format_args!(""); + println!("{args}"); + //~^ ERROR: format specifiers have no effect on `format_args!()` +} + +fn should_not_lint() { + println!("{}", format_args!("")); + // Technically the same as `{}`, but the `format_args` docs specifically mention that you can use + // debug formatting so allow it + println!("{:?}", format_args!("")); + + let args = format_args!(""); + println!("{args}"); +} diff --git a/tests/ui/unused_format_specs.2.fixed b/tests/ui/unused_format_specs.2.fixed new file mode 100644 index 0000000000000..94bb6b7036bde --- /dev/null +++ b/tests/ui/unused_format_specs.2.fixed @@ -0,0 +1,35 @@ +#![warn(clippy::unused_format_specs)] +#![allow(unused)] + +macro_rules! format_args_from_macro { + () => { + format_args!("from macro") + }; +} + +fn main() { + // prints `.`, not ` .` + println!("{}.", format_args!("")); + //~^ ERROR: format specifiers have no effect on `format_args!()` + //~| NOTE: `-D clippy::unused-format-specs` implied by `-D warnings` + //prints `abcde`, not `abc` + println!("{}", format_args!("abcde")); + //~^ ERROR: format specifiers have no effect on `format_args!()` + + println!("{}.", format_args_from_macro!()); + //~^ ERROR: format specifiers have no effect on `format_args!()` + + let args = format_args!(""); + println!("{args}"); + //~^ ERROR: format specifiers have no effect on `format_args!()` +} + +fn should_not_lint() { + println!("{}", format_args!("")); + // Technically the same as `{}`, but the `format_args` docs specifically mention that you can use + // debug formatting so allow it + println!("{:?}", format_args!("")); + + let args = format_args!(""); + println!("{args}"); +} diff --git a/tests/ui/unused_format_specs_unfixable.rs b/tests/ui/unused_format_specs.rs similarity index 98% rename from tests/ui/unused_format_specs_unfixable.rs rename to tests/ui/unused_format_specs.rs index be991935366fe..2c85e3711493e 100644 --- a/tests/ui/unused_format_specs_unfixable.rs +++ b/tests/ui/unused_format_specs.rs @@ -1,6 +1,6 @@ #![warn(clippy::unused_format_specs)] #![allow(unused)] -//@no-rustfix + macro_rules! format_args_from_macro { () => { format_args!("from macro") diff --git a/tests/ui/unused_format_specs_unfixable.stderr b/tests/ui/unused_format_specs.stderr similarity index 89% rename from tests/ui/unused_format_specs_unfixable.stderr rename to tests/ui/unused_format_specs.stderr index d3b334c6092ed..2b5c81c63d600 100644 --- a/tests/ui/unused_format_specs_unfixable.stderr +++ b/tests/ui/unused_format_specs.stderr @@ -1,5 +1,5 @@ error: format specifiers have no effect on `format_args!()` - --> tests/ui/unused_format_specs_unfixable.rs:12:15 + --> tests/ui/unused_format_specs.rs:12:15 | LL | println!("{:5}.", format_args!("")); | ^^^^ @@ -17,7 +17,7 @@ LL + println!("{}.", format_args!("")); | error: format specifiers have no effect on `format_args!()` - --> tests/ui/unused_format_specs_unfixable.rs:16:15 + --> tests/ui/unused_format_specs.rs:16:15 | LL | println!("{:.3}", format_args!("abcde")); | ^^^^^ @@ -33,7 +33,7 @@ LL + println!("{}", format_args!("abcde")); | error: format specifiers have no effect on `format_args!()` - --> tests/ui/unused_format_specs_unfixable.rs:19:15 + --> tests/ui/unused_format_specs.rs:19:15 | LL | println!("{:5}.", format_args_from_macro!()); | ^^^^ @@ -46,7 +46,7 @@ LL + println!("{}.", format_args_from_macro!()); | error: format specifiers have no effect on `format_args!()` - --> tests/ui/unused_format_specs_unfixable.rs:23:15 + --> tests/ui/unused_format_specs.rs:23:15 | LL | println!("{args:5}"); | ^^^^^^^^ From f7b940ae7c6fbf81e78056999bcdfedf84aad503 Mon Sep 17 00:00:00 2001 From: Trivikram Kamat <16024985+trivikr@users.noreply.github.com> Date: Mon, 23 Sep 2024 22:08:54 -0700 Subject: [PATCH 008/120] Update actions/setup-node to v4 --- .github/workflows/remark.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/remark.yml b/.github/workflows/remark.yml index 348d52020fd23..a1b011dc32d84 100644 --- a/.github/workflows/remark.yml +++ b/.github/workflows/remark.yml @@ -19,7 +19,7 @@ jobs: uses: actions/checkout@v4 - name: Setup Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: '18.x' From b61fcbee76f8e862c1de7523058e7e8a8b4ed4ef Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Tue, 24 Sep 2024 11:58:04 +0200 Subject: [PATCH 009/120] Merge commit '7901289135257ca0fbed3a5522526f95b0f5edba' into clippy-subtree-update --- .github/deploy.sh | 9 +- .github/workflows/clippy_bors.yml | 4 +- CHANGELOG.md | 65 +++- Cargo.toml | 11 +- book/src/lint_configuration.md | 1 + clippy_config/Cargo.toml | 2 +- clippy_config/src/conf.rs | 5 +- clippy_config/src/lib.rs | 2 +- clippy_config/src/msrvs.rs | 6 +- clippy_config/src/types.rs | 2 +- clippy_dev/src/fmt.rs | 2 +- clippy_dev/src/new_lint.rs | 2 +- clippy_dev/src/serve.rs | 3 +- clippy_dev/src/update_lints.rs | 30 +- clippy_lints/Cargo.toml | 2 +- clippy_lints/src/absolute_paths.rs | 4 +- clippy_lints/src/almost_complete_range.rs | 2 +- clippy_lints/src/approx_const.rs | 4 +- clippy_lints/src/arc_with_non_send_sync.rs | 2 +- clippy_lints/src/assertions_on_constants.rs | 2 +- .../src/assertions_on_result_states.rs | 2 +- clippy_lints/src/assigning_clones.rs | 4 +- .../attrs/allow_attributes_without_reason.rs | 4 +- .../attrs/blanket_clippy_restriction_lints.rs | 4 +- clippy_lints/src/attrs/deprecated_cfg_attr.rs | 2 +- .../src/attrs/duplicated_attributes.rs | 4 +- clippy_lints/src/attrs/empty_line_after.rs | 52 --- clippy_lints/src/attrs/inline_always.rs | 4 +- clippy_lints/src/attrs/mod.rs | 97 +---- clippy_lints/src/attrs/useless_attribute.rs | 3 +- clippy_lints/src/await_holding_invalid.rs | 2 +- clippy_lints/src/booleans.rs | 4 +- clippy_lints/src/box_default.rs | 2 +- clippy_lints/src/byte_char_slices.rs | 2 +- .../src/cargo/lint_groups_priority.rs | 2 +- clippy_lints/src/casts/cast_lossless.rs | 2 +- .../src/casts/cast_possible_truncation.rs | 2 +- clippy_lints/src/casts/cast_possible_wrap.rs | 2 +- clippy_lints/src/casts/cast_precision_loss.rs | 2 +- clippy_lints/src/casts/cast_sign_loss.rs | 2 +- clippy_lints/src/casts/fn_to_numeric_cast.rs | 2 +- .../fn_to_numeric_cast_with_truncation.rs | 2 +- clippy_lints/src/casts/mod.rs | 13 +- clippy_lints/src/casts/ptr_cast_constness.rs | 79 +++- clippy_lints/src/casts/ref_as_ptr.rs | 2 +- clippy_lints/src/casts/unnecessary_cast.rs | 4 +- clippy_lints/src/casts/utils.rs | 2 +- clippy_lints/src/cfg_not_test.rs | 2 +- clippy_lints/src/checked_conversions.rs | 4 +- clippy_lints/src/cognitive_complexity.rs | 4 +- clippy_lints/src/collection_is_never_read.rs | 2 +- clippy_lints/src/comparison_chain.rs | 2 +- clippy_lints/src/copies.rs | 11 +- clippy_lints/src/crate_in_macro_def.rs | 2 +- clippy_lints/src/dbg_macro.rs | 4 +- clippy_lints/src/declared_lints.rs | 13 +- clippy_lints/src/default.rs | 2 +- .../src/default_constructed_unit_structs.rs | 2 +- .../src/default_instead_of_iter_empty.rs | 4 +- clippy_lints/src/default_numeric_fallback.rs | 2 +- clippy_lints/src/dereference.rs | 73 ++-- clippy_lints/src/derivable_impls.rs | 2 +- clippy_lints/src/derive.rs | 4 +- clippy_lints/src/doc/empty_line_after.rs | 342 +++++++++++++++++ clippy_lints/src/doc/lazy_continuation.rs | 24 -- clippy_lints/src/doc/link_with_quotes.rs | 2 +- clippy_lints/src/doc/missing_headers.rs | 2 +- clippy_lints/src/doc/mod.rs | 93 ++++- clippy_lints/src/doc/needless_doctest_main.rs | 2 +- .../src/doc/suspicious_doc_comments.rs | 6 +- .../src/doc/too_long_first_doc_paragraph.rs | 9 +- clippy_lints/src/entry.rs | 8 +- clippy_lints/src/enum_clike.rs | 2 +- clippy_lints/src/escape.rs | 4 +- clippy_lints/src/excessive_bools.rs | 2 +- clippy_lints/src/excessive_nesting.rs | 2 +- clippy_lints/src/explicit_write.rs | 4 +- .../src/extra_unused_type_parameters.rs | 4 +- clippy_lints/src/fallible_impl_from.rs | 2 +- .../src/field_scoped_visibility_modifiers.rs | 2 +- clippy_lints/src/floating_point_arithmetic.rs | 2 +- clippy_lints/src/format.rs | 6 +- clippy_lints/src/format_args.rs | 11 +- clippy_lints/src/format_impl.rs | 4 +- clippy_lints/src/format_push_string.rs | 2 +- clippy_lints/src/from_over_into.rs | 4 +- clippy_lints/src/from_str_radix_10.rs | 2 +- clippy_lints/src/functions/mod.rs | 2 +- clippy_lints/src/functions/must_use.rs | 2 +- .../src/functions/not_unsafe_ptr_arg_deref.rs | 2 +- .../src/functions/renamed_function_params.rs | 2 +- clippy_lints/src/functions/result.rs | 4 +- clippy_lints/src/future_not_send.rs | 2 +- clippy_lints/src/if_then_some_else_none.rs | 4 +- clippy_lints/src/implicit_hasher.rs | 6 +- clippy_lints/src/implicit_saturating_sub.rs | 359 +++++++++++++++--- clippy_lints/src/incompatible_msrv.rs | 4 +- clippy_lints/src/index_refutable_slice.rs | 6 +- clippy_lints/src/ineffective_open_options.rs | 2 +- clippy_lints/src/instant_subtraction.rs | 2 +- clippy_lints/src/item_name_repetitions.rs | 2 +- clippy_lints/src/items_after_test_module.rs | 2 +- .../src/iter_not_returning_iterator.rs | 2 +- clippy_lints/src/iter_over_hash_type.rs | 4 +- clippy_lints/src/large_enum_variant.rs | 2 +- clippy_lints/src/large_stack_arrays.rs | 2 +- clippy_lints/src/legacy_numeric_constants.rs | 4 +- clippy_lints/src/len_zero.rs | 19 +- clippy_lints/src/lib.rs | 19 +- clippy_lints/src/lifetimes.rs | 16 +- .../src/loops/explicit_counter_loop.rs | 2 +- clippy_lints/src/loops/infinite_loop.rs | 2 +- clippy_lints/src/loops/manual_find.rs | 2 +- clippy_lints/src/loops/manual_flatten.rs | 2 +- .../src/loops/manual_while_let_some.rs | 4 +- clippy_lints/src/loops/mod.rs | 18 +- clippy_lints/src/loops/needless_range_loop.rs | 6 +- clippy_lints/src/loops/never_loop.rs | 4 +- clippy_lints/src/loops/same_item_push.rs | 4 +- clippy_lints/src/loops/single_element_loop.rs | 4 +- clippy_lints/src/loops/utils.rs | 4 +- .../src/loops/while_immutable_condition.rs | 2 +- .../src/loops/while_let_on_iterator.rs | 4 +- clippy_lints/src/macro_metavars_in_unsafe.rs | 6 +- clippy_lints/src/macro_use.rs | 2 +- clippy_lints/src/manual_async_fn.rs | 4 +- clippy_lints/src/manual_bits.rs | 2 +- clippy_lints/src/manual_clamp.rs | 8 +- clippy_lints/src/manual_div_ceil.rs | 158 ++++++++ clippy_lints/src/manual_hash_one.rs | 2 +- clippy_lints/src/manual_is_ascii_check.rs | 6 +- clippy_lints/src/manual_is_power_of_two.rs | 142 +++++++ clippy_lints/src/manual_let_else.rs | 4 +- clippy_lints/src/manual_main_separator_str.rs | 2 +- clippy_lints/src/manual_non_exhaustive.rs | 186 ++++----- clippy_lints/src/manual_range_patterns.rs | 7 +- clippy_lints/src/manual_rem_euclid.rs | 2 +- clippy_lints/src/manual_retain.rs | 8 +- clippy_lints/src/manual_string_new.rs | 2 +- clippy_lints/src/manual_strip.rs | 6 +- clippy_lints/src/map_unit_fn.rs | 2 +- clippy_lints/src/matches/collapsible_match.rs | 4 +- clippy_lints/src/matches/manual_filter.rs | 4 +- clippy_lints/src/matches/manual_map.rs | 2 +- clippy_lints/src/matches/manual_unwrap_or.rs | 4 +- clippy_lints/src/matches/manual_utils.rs | 8 +- clippy_lints/src/matches/match_same_arms.rs | 2 +- .../src/matches/match_str_case_mismatch.rs | 4 +- clippy_lints/src/matches/mod.rs | 2 +- clippy_lints/src/matches/overlapping_arms.rs | 2 +- clippy_lints/src/matches/redundant_guards.rs | 4 +- .../src/matches/redundant_pattern_match.rs | 6 +- .../matches/significant_drop_in_scrutinee.rs | 2 +- clippy_lints/src/matches/single_match.rs | 31 +- clippy_lints/src/mem_replace.rs | 4 +- .../src/methods/bind_instead_of_map.rs | 2 +- ...se_sensitive_file_extension_comparisons.rs | 4 +- clippy_lints/src/methods/clear_with_drain.rs | 2 +- clippy_lints/src/methods/clone_on_copy.rs | 2 +- clippy_lints/src/methods/clone_on_ref_ptr.rs | 2 +- .../src/methods/cloned_instead_of_copied.rs | 2 +- .../src/methods/collapsible_str_replace.rs | 2 +- clippy_lints/src/methods/drain_collect.rs | 2 +- clippy_lints/src/methods/err_expect.rs | 2 +- clippy_lints/src/methods/expect_fun_call.rs | 4 +- clippy_lints/src/methods/filetype_is_file.rs | 2 +- clippy_lints/src/methods/filter_map.rs | 4 +- .../src/methods/filter_map_bool_then.rs | 4 +- .../src/methods/filter_map_identity.rs | 2 +- clippy_lints/src/methods/flat_map_identity.rs | 2 +- clippy_lints/src/methods/flat_map_option.rs | 2 +- clippy_lints/src/methods/get_last_with_len.rs | 2 +- .../src/methods/inefficient_to_string.rs | 2 +- clippy_lints/src/methods/inspect_for_each.rs | 2 +- clippy_lints/src/methods/into_iter_on_ref.rs | 2 +- clippy_lints/src/methods/iter_filter.rs | 2 +- clippy_lints/src/methods/iter_kv_map.rs | 1 - clippy_lints/src/methods/iter_nth.rs | 2 +- .../iter_on_single_or_empty_collections.rs | 2 +- clippy_lints/src/methods/iter_with_drain.rs | 2 +- .../src/methods/join_absolute_paths.rs | 2 +- .../src/methods/manual_c_str_literals.rs | 2 +- clippy_lints/src/methods/manual_inspect.rs | 4 +- .../src/methods/manual_is_variant_and.rs | 2 +- clippy_lints/src/methods/manual_ok_or.rs | 2 +- clippy_lints/src/methods/manual_try_fold.rs | 2 +- clippy_lints/src/methods/map_clone.rs | 2 +- clippy_lints/src/methods/map_flatten.rs | 2 +- clippy_lints/src/methods/map_identity.rs | 2 +- clippy_lints/src/methods/mod.rs | 63 ++- clippy_lints/src/methods/mut_mutex_lock.rs | 2 +- .../methods/needless_character_iteration.rs | 2 +- clippy_lints/src/methods/needless_collect.rs | 8 +- .../src/methods/needless_option_as_deref.rs | 2 +- clippy_lints/src/methods/no_effect_replace.rs | 2 +- clippy_lints/src/methods/open_options.rs | 16 +- .../src/methods/option_as_ref_cloned.rs | 4 +- .../src/methods/option_as_ref_deref.rs | 10 +- .../src/methods/option_map_unwrap_or.rs | 4 +- clippy_lints/src/methods/or_fun_call.rs | 2 +- clippy_lints/src/methods/or_then_unwrap.rs | 2 +- .../src/methods/range_zip_with_len.rs | 2 +- clippy_lints/src/methods/search_is_some.rs | 2 +- clippy_lints/src/methods/seek_from_current.rs | 2 +- .../seek_to_start_instead_of_rewind.rs | 4 +- clippy_lints/src/methods/str_splitn.rs | 4 +- .../methods/suspicious_command_arg_space.rs | 2 +- clippy_lints/src/methods/type_id_on_box.rs | 2 +- .../unnecessary_fallible_conversions.rs | 2 +- .../src/methods/unnecessary_filter_map.rs | 2 +- .../methods/unnecessary_first_then_check.rs | 56 +++ clippy_lints/src/methods/unnecessary_fold.rs | 66 +--- .../src/methods/unnecessary_get_then_check.rs | 2 +- .../src/methods/unnecessary_iter_cloned.rs | 2 +- .../src/methods/unnecessary_literal_unwrap.rs | 2 +- .../src/methods/unnecessary_min_or_max.rs | 45 ++- .../methods/unnecessary_result_map_or_else.rs | 2 +- .../src/methods/unnecessary_to_owned.rs | 9 +- .../src/methods/unused_enumerate_index.rs | 4 +- clippy_lints/src/methods/useless_asref.rs | 2 +- clippy_lints/src/methods/utils.rs | 4 +- .../src/methods/vec_resize_to_zero.rs | 2 +- clippy_lints/src/methods/waker_clone_wake.rs | 2 +- clippy_lints/src/min_ident_chars.rs | 2 +- clippy_lints/src/misc.rs | 186 ++++++--- clippy_lints/src/missing_assert_message.rs | 2 +- .../src/missing_asserts_for_indexing.rs | 4 +- clippy_lints/src/missing_const_for_fn.rs | 4 +- .../src/missing_const_for_thread_local.rs | 4 +- clippy_lints/src/missing_doc.rs | 2 +- clippy_lints/src/missing_fields_in_debug.rs | 4 +- clippy_lints/src/missing_inline.rs | 2 +- .../src/mixed_read_write_in_expression.rs | 8 +- .../src/multiple_unsafe_ops_per_block.rs | 25 +- clippy_lints/src/mut_key.rs | 2 +- clippy_lints/src/mutable_debug_assertion.rs | 2 +- .../src/needless_arbitrary_self_type.rs | 2 +- clippy_lints/src/needless_bool.rs | 6 +- .../src/needless_borrows_for_generic_args.rs | 6 +- clippy_lints/src/needless_for_each.rs | 4 +- clippy_lints/src/needless_maybe_sized.rs | 2 +- clippy_lints/src/needless_pass_by_ref_mut.rs | 2 +- clippy_lints/src/needless_pass_by_value.rs | 32 +- clippy_lints/src/no_effect.rs | 4 +- clippy_lints/src/non_copy_const.rs | 4 +- clippy_lints/src/non_expressive_names.rs | 4 +- .../src/non_octal_unix_permissions.rs | 2 +- clippy_lints/src/non_zero_suggestions.rs | 143 +++++++ clippy_lints/src/nonstandard_macro_braces.rs | 4 +- clippy_lints/src/only_used_in_recursion.rs | 2 +- .../operators/absurd_extreme_comparisons.rs | 2 +- .../src/operators/const_comparisons.rs | 4 +- .../operators/float_equality_without_abs.rs | 3 +- clippy_lints/src/option_if_let_else.rs | 6 +- clippy_lints/src/panic_in_result_fn.rs | 16 +- clippy_lints/src/panic_unimplemented.rs | 78 ++-- clippy_lints/src/pass_by_ref_or_value.rs | 2 +- clippy_lints/src/pathbuf_init_then_push.rs | 4 +- clippy_lints/src/pattern_type_mismatch.rs | 4 +- .../src/pointers_in_nomem_asm_block.rs | 88 +++++ clippy_lints/src/ptr.rs | 106 +++--- clippy_lints/src/pub_underscore_fields.rs | 2 +- clippy_lints/src/question_mark.rs | 4 +- clippy_lints/src/ranges.rs | 6 +- clippy_lints/src/rc_clone_in_vec_init.rs | 2 +- clippy_lints/src/read_zero_byte_vec.rs | 4 +- clippy_lints/src/redundant_clone.rs | 20 +- clippy_lints/src/redundant_closure_call.rs | 2 +- clippy_lints/src/redundant_else.rs | 2 +- clippy_lints/src/redundant_field_names.rs | 2 +- clippy_lints/src/redundant_locals.rs | 2 +- .../src/redundant_static_lifetimes.rs | 2 +- clippy_lints/src/reference.rs | 2 +- clippy_lints/src/regex.rs | 15 +- clippy_lints/src/repeat_vec_with_capacity.rs | 2 +- .../src/reserve_after_initialization.rs | 2 +- clippy_lints/src/return_self_not_must_use.rs | 2 +- clippy_lints/src/returns.rs | 28 +- clippy_lints/src/same_name_method.rs | 13 +- clippy_lints/src/set_contains_or_insert.rs | 6 +- .../src/significant_drop_tightening.rs | 4 +- .../src/single_component_path_imports.rs | 2 +- .../src/slow_vector_initialization.rs | 5 +- clippy_lints/src/std_instead_of_core.rs | 4 +- clippy_lints/src/string_patterns.rs | 8 +- clippy_lints/src/strings.rs | 4 +- .../src/suspicious_operation_groupings.rs | 4 +- clippy_lints/src/suspicious_trait_impl.rs | 2 +- clippy_lints/src/swap.rs | 4 +- clippy_lints/src/swap_ptr_to_ref.rs | 2 +- clippy_lints/src/tests_outside_test_module.rs | 2 +- clippy_lints/src/to_digit_is_some.rs | 12 +- clippy_lints/src/trait_bounds.rs | 6 +- clippy_lints/src/transmute/mod.rs | 34 +- .../transmutes_expressible_as_ptr_casts.rs | 2 +- .../transmute/unsound_collection_transmute.rs | 2 +- clippy_lints/src/tuple_array_conversions.rs | 2 +- clippy_lints/src/types/box_collection.rs | 2 +- clippy_lints/src/types/mod.rs | 121 +++--- .../src/types/redundant_allocation.rs | 2 +- clippy_lints/src/types/type_complexity.rs | 2 +- clippy_lints/src/types/vec_box.rs | 2 +- clippy_lints/src/unconditional_recursion.rs | 17 +- .../src/undocumented_unsafe_blocks.rs | 4 +- clippy_lints/src/uninhabited_references.rs | 2 +- clippy_lints/src/uninit_vec.rs | 6 +- clippy_lints/src/unit_return_expecting_ord.rs | 2 +- clippy_lints/src/unit_types/let_unit_value.rs | 2 +- clippy_lints/src/unit_types/unit_arg.rs | 4 +- clippy_lints/src/unnecessary_wraps.rs | 4 +- clippy_lints/src/unnested_or_patterns.rs | 8 +- clippy_lints/src/unsafe_removed_from_name.rs | 2 +- clippy_lints/src/unused_async.rs | 4 +- clippy_lints/src/unused_io_amount.rs | 2 +- clippy_lints/src/unused_peekable.rs | 2 +- clippy_lints/src/unused_trait_names.rs | 94 +++++ clippy_lints/src/unused_unit.rs | 4 +- clippy_lints/src/unwrap.rs | 4 +- clippy_lints/src/unwrap_in_result.rs | 2 +- clippy_lints/src/use_self.rs | 4 +- clippy_lints/src/useless_conversion.rs | 6 +- clippy_lints/src/utils/author.rs | 2 +- .../src/utils/format_args_collector.rs | 4 +- .../utils/internal_lints/collapsible_calls.rs | 2 +- .../src/utils/internal_lints/invalid_paths.rs | 4 +- .../internal_lints/lint_without_lint_pass.rs | 2 +- .../internal_lints/unnecessary_def_path.rs | 4 +- clippy_lints/src/vec.rs | 4 +- clippy_lints/src/vec_init_then_push.rs | 2 +- clippy_lints/src/visibility.rs | 2 +- clippy_lints/src/wildcard_imports.rs | 2 +- clippy_lints/src/write.rs | 6 +- clippy_lints/src/zombie_processes.rs | 334 ++++++++++++++++ clippy_utils/Cargo.toml | 2 +- clippy_utils/src/ast_utils.rs | 15 +- clippy_utils/src/ast_utils/ident_iter.rs | 2 +- clippy_utils/src/attrs.rs | 10 +- clippy_utils/src/check_proc_macro.rs | 4 +- clippy_utils/src/consts.rs | 10 +- clippy_utils/src/eager_or_lazy.rs | 4 +- clippy_utils/src/higher.rs | 4 +- clippy_utils/src/hir_utils.rs | 12 +- clippy_utils/src/lib.rs | 87 +++-- clippy_utils/src/macros.rs | 4 +- clippy_utils/src/mir/mod.rs | 14 +- clippy_utils/src/paths.rs | 6 +- clippy_utils/src/ptr.rs | 2 +- clippy_utils/src/qualify_min_const_fn.rs | 2 +- clippy_utils/src/source.rs | 81 +--- clippy_utils/src/str_utils.rs | 10 +- clippy_utils/src/sugg.rs | 38 +- clippy_utils/src/ty.rs | 15 +- clippy_utils/src/ty/type_certainty/mod.rs | 4 +- clippy_utils/src/usage.rs | 2 +- clippy_utils/src/visitors.rs | 4 +- declare_clippy_lint/Cargo.toml | 2 +- declare_clippy_lint/src/lib.rs | 17 +- lintcheck/src/driver.rs | 2 +- lintcheck/src/recursive.rs | 2 +- rust-toolchain | 2 +- src/driver.rs | 4 +- tests/compile-test.rs | 20 +- tests/config-metadata.rs | 2 +- tests/dogfood.rs | 49 +-- tests/ui-internal/unnecessary_def_path.fixed | 4 +- tests/ui-internal/unnecessary_def_path.rs | 4 +- .../disallowed_names.rs | 2 +- .../disallowed_names.rs | 2 +- .../conf_missing_enforced_import_rename.fixed | 2 +- .../conf_missing_enforced_import_rename.rs | 2 +- ...conf_missing_enforced_import_rename.stderr | 6 +- tests/ui-toml/panic/panic.rs | 5 + tests/ui-toml/panic/panic.stderr | 10 +- tests/ui/allow_attributes.fixed | 7 + tests/ui/allow_attributes.rs | 7 + tests/ui/allow_attributes_without_reason.rs | 1 - .../ui/allow_attributes_without_reason.stderr | 4 +- tests/ui/arithmetic_side_effects.rs | 1 - tests/ui/arithmetic_side_effects.stderr | 246 ++++++------ tests/ui/asm_syntax_not_x86.rs | 3 +- tests/ui/asm_syntax_x86.rs | 4 +- tests/ui/asm_syntax_x86.stderr | 70 ++++ tests/ui/auxiliary/external_item.rs | 7 + tests/ui/auxiliary/proc_macro_attr.rs | 4 +- tests/ui/auxiliary/proc_macro_derive.rs | 2 +- .../proc_macro_suspicious_else_formatting.rs | 2 +- tests/ui/auxiliary/proc_macros.rs | 2 +- .../borrow_interior_mutable_const/others.rs | 2 +- tests/ui/cast_alignment.rs | 8 +- tests/ui/cmp_owned/without_suggestion.rs | 4 +- tests/ui/collapsible_else_if.fixed | 5 +- tests/ui/collapsible_else_if.rs | 5 +- tests/ui/collapsible_else_if.stderr | 16 +- tests/ui/comparison_to_empty.fixed | 8 + tests/ui/comparison_to_empty.rs | 8 + tests/ui/comparison_to_empty.stderr | 26 +- tests/ui/crashes/associated-constant-ice.rs | 2 +- tests/ui/crashes/cc_seme.rs | 4 +- tests/ui/crashes/ice-11230.rs | 2 +- tests/ui/crashes/ice-1588.rs | 2 +- tests/ui/crashes/ice-1969.rs | 2 +- tests/ui/crashes/ice-2499.rs | 6 +- tests/ui/crashes/ice-2594.rs | 1 - tests/ui/crashes/ice-2727.rs | 2 +- tests/ui/crashes/ice-2760.rs | 8 +- tests/ui/crashes/ice-2862.rs | 2 +- tests/ui/crashes/ice-2865.rs | 2 +- tests/ui/crashes/ice-3151.rs | 2 +- tests/ui/crashes/ice-3462.rs | 2 +- tests/ui/crashes/ice-3747.rs | 2 +- tests/ui/crashes/ice-3969.rs | 4 +- tests/ui/crashes/ice-6251.rs | 2 +- tests/ui/crashes/ice-700.rs | 2 +- tests/ui/crashes/ice-7410.rs | 3 +- tests/ui/crashes/ice_exact_size.rs | 2 +- tests/ui/crashes/if_same_then_else.rs | 2 +- tests/ui/crashes/inherent_impl.rs | 2 +- tests/ui/crashes/issue-825.rs | 2 +- tests/ui/crashes/match_same_arms_const.rs | 2 +- ...needless_pass_by_value-w-late-bound.stderr | 2 +- tests/ui/crashes/returns.rs | 2 +- .../entrypoint_recursion.rs | 2 +- .../no_std_main_recursion.rs | 2 +- .../declare_interior_mutable_const/others.rs | 2 +- tests/ui/def_id_nocore.rs | 2 +- tests/ui/diverging_sub_expression.rs | 6 + tests/ui/doc/doc_lazy_blank_line.fixed | 47 --- tests/ui/doc/doc_lazy_blank_line.rs | 43 --- tests/ui/doc/doc_lazy_blank_line.stderr | 56 --- tests/ui/doc/doc_lazy_list.fixed | 9 +- tests/ui/doc/doc_lazy_list.stderr | 27 +- tests/ui/duplicate_underscore_argument.rs | 1 - tests/ui/duplicate_underscore_argument.stderr | 2 +- tests/ui/duplicated_attributes.rs | 4 + .../ui/empty_line_after/doc_comments.1.fixed | 135 +++++++ .../ui/empty_line_after/doc_comments.2.fixed | 144 +++++++ tests/ui/empty_line_after/doc_comments.rs | 147 +++++++ tests/ui/empty_line_after/doc_comments.stderr | 176 +++++++++ .../empty_line_after/outer_attribute.1.fixed | 108 ++++++ .../empty_line_after/outer_attribute.2.fixed | 111 ++++++ tests/ui/empty_line_after/outer_attribute.rs | 119 ++++++ .../empty_line_after/outer_attribute.stderr | 116 ++++++ tests/ui/empty_line_after_doc_comments.rs | 132 ------- tests/ui/empty_line_after_doc_comments.stderr | 37 -- tests/ui/empty_line_after_outer_attribute.rs | 120 ------ .../empty_line_after_outer_attribute.stderr | 54 --- tests/ui/empty_loop_no_std.rs | 2 +- tests/ui/enum_clike_unportable_variant.rs | 2 +- tests/ui/exit1.rs | 2 +- tests/ui/exit2.rs | 2 +- tests/ui/exit3.rs | 2 +- tests/ui/expect_fun_call.fixed | 2 - tests/ui/expect_fun_call.rs | 2 - tests/ui/expect_fun_call.stderr | 30 +- tests/ui/field_reassign_with_default.rs | 2 +- tests/ui/floating_point_arithmetic_nostd.rs | 2 +- tests/ui/format_args.fixed | 2 +- tests/ui/format_args.rs | 2 +- tests/ui/format_args_unfixable.rs | 2 +- tests/ui/if_then_some_else_none.fixed | 4 + tests/ui/if_then_some_else_none.rs | 4 + tests/ui/if_then_some_else_none.stderr | 8 +- tests/ui/ignored_unit_patterns.fixed | 15 +- tests/ui/ignored_unit_patterns.rs | 15 +- tests/ui/ignored_unit_patterns.stderr | 18 +- tests/ui/implicit_saturating_sub.fixed | 13 +- tests/ui/implicit_saturating_sub.rs | 13 +- tests/ui/incompatible_msrv.rs | 2 +- tests/ui/legacy_numeric_constants.fixed | 4 +- tests/ui/legacy_numeric_constants.rs | 4 +- tests/ui/macro_use_imports.fixed | 2 +- tests/ui/macro_use_imports.rs | 2 +- tests/ui/macro_use_imports_expect.rs | 2 +- tests/ui/manual_arithmetic_check-2.rs | 35 ++ tests/ui/manual_arithmetic_check-2.stderr | 75 ++++ tests/ui/manual_arithmetic_check.fixed | 24 ++ tests/ui/manual_arithmetic_check.rs | 24 ++ tests/ui/manual_arithmetic_check.stderr | 29 ++ tests/ui/manual_div_ceil.fixed | 30 ++ tests/ui/manual_div_ceil.rs | 30 ++ tests/ui/manual_div_ceil.stderr | 35 ++ tests/ui/manual_div_ceil_with_feature.fixed | 25 ++ tests/ui/manual_div_ceil_with_feature.rs | 25 ++ tests/ui/manual_div_ceil_with_feature.stderr | 47 +++ tests/ui/manual_is_power_of_two.fixed | 20 + tests/ui/manual_is_power_of_two.rs | 20 + tests/ui/manual_is_power_of_two.stderr | 41 ++ tests/ui/manual_non_exhaustive_enum.rs | 24 +- tests/ui/manual_non_exhaustive_enum.stderr | 22 +- tests/ui/manual_non_exhaustive_struct.rs | 50 ++- tests/ui/manual_non_exhaustive_struct.stderr | 64 ++-- tests/ui/manual_range_patterns.fixed | 8 + tests/ui/manual_range_patterns.rs | 8 + tests/ui/manual_saturating_arithmetic.fixed | 2 +- tests/ui/manual_saturating_arithmetic.rs | 2 +- tests/ui/match_overlapping_arm.rs | 2 - tests/ui/match_overlapping_arm.stderr | 32 +- tests/ui/mixed_attributes_style.rs | 3 +- tests/ui/mixed_attributes_style.stderr | 3 +- tests/ui/must_use_candidates.fixed | 2 +- tests/ui/must_use_candidates.rs | 2 +- tests/ui/mut_key.rs | 2 +- tests/ui/needless_pass_by_value.rs | 12 +- tests/ui/needless_pass_by_value.stderr | 18 +- tests/ui/needless_return.fixed | 4 + tests/ui/needless_return.rs | 4 + tests/ui/needless_return.stderr | 14 +- tests/ui/non_octal_unix_permissions.fixed | 2 +- tests/ui/non_octal_unix_permissions.rs | 2 +- tests/ui/non_zero_suggestions.fixed | 65 ++++ tests/ui/non_zero_suggestions.rs | 65 ++++ tests/ui/non_zero_suggestions.stderr | 41 ++ tests/ui/non_zero_suggestions_unfixable.rs | 23 ++ .../ui/non_zero_suggestions_unfixable.stderr | 23 ++ tests/ui/panic_in_result_fn.rs | 9 + tests/ui/pointers_in_nomem_asm_block.rs | 33 ++ tests/ui/pointers_in_nomem_asm_block.stderr | 34 ++ tests/ui/ptr_arg.rs | 22 ++ tests/ui/ptr_arg.stderr | 14 +- tests/ui/ptr_cast_constness.fixed | 17 + tests/ui/ptr_cast_constness.rs | 17 + tests/ui/ptr_cast_constness.stderr | 42 +- tests/ui/result_large_err.rs | 2 +- tests/ui/single_call_fn.rs | 2 +- tests/ui/single_match.fixed | 26 +- tests/ui/single_match.rs | 44 ++- tests/ui/single_match.stderr | 67 +++- tests/ui/single_match_else.fixed | 4 + tests/ui/single_match_else.rs | 10 + tests/ui/single_match_else.stderr | 14 +- tests/ui/string_slice.rs | 6 +- tests/ui/suspicious_command_arg_space.fixed | 1 + tests/ui/suspicious_command_arg_space.rs | 1 + tests/ui/suspicious_command_arg_space.stderr | 4 +- tests/ui/suspicious_to_owned.rs | 2 +- tests/ui/tabs_in_doc_comments.fixed | 1 - tests/ui/tabs_in_doc_comments.rs | 1 - tests/ui/tabs_in_doc_comments.stderr | 16 +- tests/ui/too_long_first_doc_paragraph.rs | 10 + tests/ui/too_long_first_doc_paragraph.stderr | 28 +- tests/ui/transmute_32bit.rs | 2 +- tests/ui/transmute_64bit.rs | 2 +- tests/ui/transmute_collection.rs | 2 +- tests/ui/transmute_undefined_repr.rs | 2 +- tests/ui/uninit_vec.rs | 32 ++ tests/ui/uninit_vec.stderr | 24 +- tests/ui/unnecessary_first_then_check.fixed | 22 ++ tests/ui/unnecessary_first_then_check.rs | 22 ++ tests/ui/unnecessary_first_then_check.stderr | 47 +++ tests/ui/unnecessary_min_or_max.fixed | 29 ++ tests/ui/unnecessary_min_or_max.rs | 29 ++ tests/ui/unsafe_removed_from_name.rs | 2 +- tests/ui/unused_trait_names.fixed | 286 ++++++++++++++ tests/ui/unused_trait_names.rs | 286 ++++++++++++++ tests/ui/unused_trait_names.stderr | 73 ++++ tests/ui/used_underscore_binding.stderr | 24 +- tests/ui/used_underscore_items.rs | 63 +++ tests/ui/used_underscore_items.stderr | 112 ++++++ tests/ui/zombie_processes.rs | 138 +++++++ tests/ui/zombie_processes.stderr | 64 ++++ tests/ui/zombie_processes_fixable.fixed | 26 ++ tests/ui/zombie_processes_fixable.rs | 26 ++ tests/ui/zombie_processes_fixable.stderr | 40 ++ triagebot.toml | 1 - util/gh-pages/versions.html | 121 +++--- util/versions.py | 41 +- 566 files changed, 7437 insertions(+), 2571 deletions(-) delete mode 100644 clippy_lints/src/attrs/empty_line_after.rs create mode 100644 clippy_lints/src/doc/empty_line_after.rs create mode 100644 clippy_lints/src/manual_div_ceil.rs create mode 100644 clippy_lints/src/manual_is_power_of_two.rs create mode 100644 clippy_lints/src/methods/unnecessary_first_then_check.rs create mode 100644 clippy_lints/src/non_zero_suggestions.rs create mode 100644 clippy_lints/src/pointers_in_nomem_asm_block.rs create mode 100644 clippy_lints/src/unused_trait_names.rs create mode 100644 clippy_lints/src/zombie_processes.rs create mode 100644 tests/ui/asm_syntax_x86.stderr create mode 100644 tests/ui/auxiliary/external_item.rs delete mode 100644 tests/ui/doc/doc_lazy_blank_line.fixed delete mode 100644 tests/ui/doc/doc_lazy_blank_line.rs delete mode 100644 tests/ui/doc/doc_lazy_blank_line.stderr create mode 100644 tests/ui/empty_line_after/doc_comments.1.fixed create mode 100644 tests/ui/empty_line_after/doc_comments.2.fixed create mode 100644 tests/ui/empty_line_after/doc_comments.rs create mode 100644 tests/ui/empty_line_after/doc_comments.stderr create mode 100644 tests/ui/empty_line_after/outer_attribute.1.fixed create mode 100644 tests/ui/empty_line_after/outer_attribute.2.fixed create mode 100644 tests/ui/empty_line_after/outer_attribute.rs create mode 100644 tests/ui/empty_line_after/outer_attribute.stderr delete mode 100644 tests/ui/empty_line_after_doc_comments.rs delete mode 100644 tests/ui/empty_line_after_doc_comments.stderr delete mode 100644 tests/ui/empty_line_after_outer_attribute.rs delete mode 100644 tests/ui/empty_line_after_outer_attribute.stderr create mode 100644 tests/ui/manual_arithmetic_check-2.rs create mode 100644 tests/ui/manual_arithmetic_check-2.stderr create mode 100644 tests/ui/manual_arithmetic_check.fixed create mode 100644 tests/ui/manual_arithmetic_check.rs create mode 100644 tests/ui/manual_arithmetic_check.stderr create mode 100644 tests/ui/manual_div_ceil.fixed create mode 100644 tests/ui/manual_div_ceil.rs create mode 100644 tests/ui/manual_div_ceil.stderr create mode 100644 tests/ui/manual_div_ceil_with_feature.fixed create mode 100644 tests/ui/manual_div_ceil_with_feature.rs create mode 100644 tests/ui/manual_div_ceil_with_feature.stderr create mode 100644 tests/ui/manual_is_power_of_two.fixed create mode 100644 tests/ui/manual_is_power_of_two.rs create mode 100644 tests/ui/manual_is_power_of_two.stderr create mode 100644 tests/ui/non_zero_suggestions.fixed create mode 100644 tests/ui/non_zero_suggestions.rs create mode 100644 tests/ui/non_zero_suggestions.stderr create mode 100644 tests/ui/non_zero_suggestions_unfixable.rs create mode 100644 tests/ui/non_zero_suggestions_unfixable.stderr create mode 100644 tests/ui/pointers_in_nomem_asm_block.rs create mode 100644 tests/ui/pointers_in_nomem_asm_block.stderr create mode 100644 tests/ui/unnecessary_first_then_check.fixed create mode 100644 tests/ui/unnecessary_first_then_check.rs create mode 100644 tests/ui/unnecessary_first_then_check.stderr create mode 100644 tests/ui/unused_trait_names.fixed create mode 100644 tests/ui/unused_trait_names.rs create mode 100644 tests/ui/unused_trait_names.stderr create mode 100644 tests/ui/used_underscore_items.rs create mode 100644 tests/ui/used_underscore_items.stderr create mode 100644 tests/ui/zombie_processes.rs create mode 100644 tests/ui/zombie_processes.stderr create mode 100644 tests/ui/zombie_processes_fixable.fixed create mode 100644 tests/ui/zombie_processes_fixable.rs create mode 100644 tests/ui/zombie_processes_fixable.stderr diff --git a/.github/deploy.sh b/.github/deploy.sh index d937661c0f828..5b4b4be4e36b5 100644 --- a/.github/deploy.sh +++ b/.github/deploy.sh @@ -25,16 +25,15 @@ if [[ $BETA = "true" ]]; then fi # Generate version index that is shown as root index page -cp util/gh-pages/versions.html out/index.html - -echo "Making the versions.json file" -python3 ./util/versions.py out +python3 ./util/versions.py ./util/gh-pages/versions.html out # Now let's go have some fun with the cloned repo cd out git config user.name "GHA CI" git config user.email "gha@ci.invalid" +git status + if [[ -n $TAG_NAME ]]; then # track files, so that the following check works git add --intent-to-add "$TAG_NAME" @@ -46,8 +45,6 @@ if [[ -n $TAG_NAME ]]; then git add "$TAG_NAME" # Update the symlink git add stable - # Update versions file - git add versions.json git commit -m "Add documentation for ${TAG_NAME} release: ${SHA}" elif [[ $BETA = "true" ]]; then if git diff --exit-code --quiet -- beta/; then diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index 2aa13313fa518..026771e6fcf42 100644 --- a/.github/workflows/clippy_bors.yml +++ b/.github/workflows/clippy_bors.yml @@ -162,7 +162,7 @@ jobs: find $DIR ! -executable -o -type d ! -path $DIR | xargs rm -rf - name: Upload Binaries - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: binaries path: target/debug @@ -202,7 +202,7 @@ jobs: # Download - name: Download target dir - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: binaries path: target/debug diff --git a/CHANGELOG.md b/CHANGELOG.md index 9bc4ad9698dbd..41a86e8ce5102 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,61 @@ document. ## Unreleased / Beta / In Rust Nightly -[c9139bd5...master](https://github.com/rust-lang/rust-clippy/compare/c9139bd5...master) +[b794b8e0...master](https://github.com/rust-lang/rust-clippy/compare/b794b8e0...master) + +## Rust 1.81 + +Current stable, released 2024-09-05 + +### New Lints + +* Added [`cfg_not_test`] to `restriction` + [#11293](https://github.com/rust-lang/rust-clippy/pull/11293) +* Added [`byte_char_slices`] to `style` + [#10155](https://github.com/rust-lang/rust-clippy/pull/10155) +* Added [`set_contains_or_insert`] to `nursery` + [#12873](https://github.com/rust-lang/rust-clippy/pull/12873) +* Added [`manual_rotate`] to `style` + [#12983](https://github.com/rust-lang/rust-clippy/pull/12983) +* Added [`unnecessary_min_or_max`] to `complexity` + [#12368](https://github.com/rust-lang/rust-clippy/pull/12368) +* Added [`manual_inspect`] to `complexity` + [#12287](https://github.com/rust-lang/rust-clippy/pull/12287) +* Added [`field_scoped_visibility_modifiers`] to `restriction` + [#12893](https://github.com/rust-lang/rust-clippy/pull/12893) +* Added [`manual_pattern_char_comparison`] to `style` + [#12849](https://github.com/rust-lang/rust-clippy/pull/12849) +* Added [`needless_maybe_sized`] to `suspicious` + [#10632](https://github.com/rust-lang/rust-clippy/pull/10632) +* Added [`needless_character_iteration`] to `suspicious` + [#12815](https://github.com/rust-lang/rust-clippy/pull/12815) + +### Moves and Deprecations + +* [`allow_attributes`], [`allow_attributes_without_reason`]: Now work on stable + [rust#120924](https://github.com/rust-lang/rust/pull/120924) +* Renamed `overflow_check_conditional` to [`panicking_overflow_checks`] + [#12944](https://github.com/rust-lang/rust-clippy/pull/12944) +* Moved [`panicking_overflow_checks`] to `correctness` (From `complexity` now deny-by-default) + [#12944](https://github.com/rust-lang/rust-clippy/pull/12944) +* Renamed `thread_local_initializer_can_be_made_const` to [`missing_const_for_thread_local`] + [#12974](https://github.com/rust-lang/rust-clippy/pull/12974) +* Deprecated [`maybe_misused_cfg`] and [`mismatched_target_os`] as they are now caught by cargo + and rustc + [#12875](https://github.com/rust-lang/rust-clippy/pull/12875) + +### Enhancements + +* [`significant_drop_in_scrutinee`]: Now also checks scrutinies of `while let` and `for let` + expressions + [#12870](https://github.com/rust-lang/rust-clippy/pull/12870) +* [`std_instead_of_core`]: Now respects the `msrv` configuration + [#13168](https://github.com/rust-lang/rust-clippy/pull/13168) + +### ICE Fixes + +* [`suboptimal_flops`]: No longer crashes on custom `.log()` functions + [#12884](https://github.com/rust-lang/rust-clippy/pull/12884) ## Rust 1.80 @@ -5500,6 +5554,7 @@ Released 2018-09-13 [`invalid_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_regex [`invalid_upcast_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_upcast_comparisons [`invalid_utf8_in_unchecked`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_utf8_in_unchecked +[`inverted_saturating_sub`]: https://rust-lang.github.io/rust-clippy/master/index.html#inverted_saturating_sub [`invisible_characters`]: https://rust-lang.github.io/rust-clippy/master/index.html#invisible_characters [`is_digit_ascii_radix`]: https://rust-lang.github.io/rust-clippy/master/index.html#is_digit_ascii_radix [`items_after_statements`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements @@ -5559,6 +5614,7 @@ Released 2018-09-13 [`manual_bits`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits [`manual_c_str_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_c_str_literals [`manual_clamp`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_clamp +[`manual_div_ceil`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_div_ceil [`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 @@ -5570,6 +5626,7 @@ Released 2018-09-13 [`manual_is_ascii_check`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check [`manual_is_finite`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_finite [`manual_is_infinite`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_infinite +[`manual_is_power_of_two`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_power_of_two [`manual_is_variant_and`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_variant_and [`manual_let_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else [`manual_main_separator_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_main_separator_str @@ -5716,6 +5773,7 @@ Released 2018-09-13 [`non_minimal_cfg`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_minimal_cfg [`non_octal_unix_permissions`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_octal_unix_permissions [`non_send_fields_in_send_ty`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_send_fields_in_send_ty +[`non_zero_suggestions`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_zero_suggestions [`nonminimal_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonminimal_bool [`nonsensical_open_options`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonsensical_open_options [`nonstandard_macro_braces`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonstandard_macro_braces @@ -5757,6 +5815,7 @@ Released 2018-09-13 [`pathbuf_init_then_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#pathbuf_init_then_push [`pattern_type_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#pattern_type_mismatch [`permissions_set_readonly_false`]: https://rust-lang.github.io/rust-clippy/master/index.html#permissions_set_readonly_false +[`pointers_in_nomem_asm_block`]: https://rust-lang.github.io/rust-clippy/master/index.html#pointers_in_nomem_asm_block [`positional_named_format_parameters`]: https://rust-lang.github.io/rust-clippy/master/index.html#positional_named_format_parameters [`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma [`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence @@ -5962,6 +6021,7 @@ Released 2018-09-13 [`unnecessary_fallible_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fallible_conversions [`unnecessary_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_filter_map [`unnecessary_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_find_map +[`unnecessary_first_then_check`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_first_then_check [`unnecessary_fold`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fold [`unnecessary_get_then_check`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_get_then_check [`unnecessary_join`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_join @@ -6003,6 +6063,7 @@ Released 2018-09-13 [`unused_result_ok`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_result_ok [`unused_rounding`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_rounding [`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self +[`unused_trait_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_trait_names [`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit [`unusual_byte_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unusual_byte_groupings [`unwrap_in_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_in_result @@ -6013,6 +6074,7 @@ Released 2018-09-13 [`use_debug`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_debug [`use_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_self [`used_underscore_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#used_underscore_binding +[`used_underscore_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#used_underscore_items [`useless_asref`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_asref [`useless_attribute`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_attribute [`useless_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion @@ -6047,6 +6109,7 @@ Released 2018-09-13 [`zero_repeat_side_effects`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_repeat_side_effects [`zero_sized_map_values`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_sized_map_values [`zero_width_space`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_width_space +[`zombie_processes`]: https://rust-lang.github.io/rust-clippy/master/index.html#zombie_processes [`zst_offset`]: https://rust-lang.github.io/rust-clippy/master/index.html#zst_offset diff --git a/Cargo.toml b/Cargo.toml index b48b881097f47..ddc27179ef2f2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.1.82" +version = "0.1.83" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" @@ -31,7 +31,7 @@ anstream = "0.6.0" [dev-dependencies] cargo_metadata = "0.18.1" -ui_test = "0.25" +ui_test = "0.26.4" regex = "1.5.5" serde = { version = "1.0.145", features = ["derive"] } serde_json = "1.0.122" @@ -67,3 +67,10 @@ harness = false [[test]] name = "dogfood" harness = false + +# quine-mc_cluskey makes up a significant part of the runtime in dogfood +# due to the number of conditions in the clippy_lints crate +# and enabling optimizations for that specific dependency helps a bit +# without increasing total build times. +[profile.dev.package.quine-mc_cluskey] +opt-level = 3 diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index 78348797588a6..91159bc79c511 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -727,6 +727,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`uninlined_format_args`](https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args) * [`unnecessary_lazy_evaluations`](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations) * [`unnested_or_patterns`](https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns) +* [`unused_trait_names`](https://rust-lang.github.io/rust-clippy/master/index.html#unused_trait_names) * [`use_self`](https://rust-lang.github.io/rust-clippy/master/index.html#use_self) diff --git a/clippy_config/Cargo.toml b/clippy_config/Cargo.toml index 5c4e0761dbca7..9da7112345de9 100644 --- a/clippy_config/Cargo.toml +++ b/clippy_config/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_config" -version = "0.1.82" +version = "0.1.83" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index a6f1b958bfb17..757620341ccc1 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -1,6 +1,6 @@ +use crate::ClippyConfiguration; use crate::msrvs::Msrv; use crate::types::{DisallowedPath, MacroMatcher, MatchLintBehaviour, PubUnderscoreFieldsBehaviour, Rename}; -use crate::ClippyConfiguration; use rustc_errors::Applicability; use rustc_session::Session; use rustc_span::edit_distance::edit_distance; @@ -563,6 +563,7 @@ define_Conf! { uninlined_format_args, unnecessary_lazy_evaluations, unnested_or_patterns, + unused_trait_names, use_self, )] msrv: Msrv = Msrv::empty(), @@ -864,7 +865,7 @@ fn calculate_dimensions(fields: &[&str]) -> (usize, Vec) { cmp::max(1, terminal_width / (SEPARATOR_WIDTH + max_field_width)) }); - let rows = (fields.len() + (columns - 1)) / columns; + let rows = fields.len().div_ceil(columns); let column_widths = (0..columns) .map(|column| { diff --git a/clippy_config/src/lib.rs b/clippy_config/src/lib.rs index ac838cc81d2f0..c63d98a0a13f9 100644 --- a/clippy_config/src/lib.rs +++ b/clippy_config/src/lib.rs @@ -26,5 +26,5 @@ mod metadata; pub mod msrvs; pub mod types; -pub use conf::{get_configuration_metadata, lookup_conf_file, Conf}; +pub use conf::{Conf, get_configuration_metadata, lookup_conf_file}; pub use metadata::ClippyConfiguration; diff --git a/clippy_config/src/msrvs.rs b/clippy_config/src/msrvs.rs index a2b4b6e256fa7..e30df3d32341a 100644 --- a/clippy_config/src/msrvs.rs +++ b/clippy_config/src/msrvs.rs @@ -1,7 +1,7 @@ use rustc_ast::Attribute; use rustc_attr::parse_version; use rustc_session::{RustcVersion, Session}; -use rustc_span::{sym, Symbol}; +use rustc_span::{Symbol, sym}; use serde::Deserialize; use std::fmt; @@ -23,6 +23,7 @@ msrv_aliases! { 1,80,0 { BOX_INTO_ITER} 1,77,0 { C_STR_LITERALS } 1,76,0 { PTR_FROM_REF, OPTION_RESULT_INSPECT } + 1,73,0 { MANUAL_DIV_CEIL } 1,71,0 { TUPLE_ARRAY_CONVERSIONS, BUILD_HASHER_HASH_ONE } 1,70,0 { OPTION_RESULT_IS_VARIANT_AND, BINARY_HEAP_RETAIN } 1,68,0 { PATH_MAIN_SEPARATOR_STR } @@ -38,7 +39,7 @@ msrv_aliases! { 1,52,0 { STR_SPLIT_ONCE, REM_EUCLID_CONST } 1,51,0 { BORROW_AS_PTR, SEEK_FROM_CURRENT, UNSIGNED_ABS } 1,50,0 { BOOL_THEN, CLAMP } - 1,47,0 { TAU, IS_ASCII_DIGIT_CONST, ARRAY_IMPL_ANY_LEN } + 1,47,0 { TAU, IS_ASCII_DIGIT_CONST, ARRAY_IMPL_ANY_LEN, SATURATING_SUB_CONST } 1,46,0 { CONST_IF_MATCH } 1,45,0 { STR_STRIP_PREFIX } 1,43,0 { LOG2_10, LOG10_2, NUMERIC_ASSOCIATED_CONSTANTS } @@ -50,6 +51,7 @@ msrv_aliases! { 1,36,0 { ITERATOR_COPIED } 1,35,0 { OPTION_COPIED, RANGE_CONTAINS } 1,34,0 { TRY_FROM } + 1,33,0 { UNDERSCORE_IMPORTS } 1,30,0 { ITERATOR_FIND_MAP, TOOL_ATTRIBUTES } 1,29,0 { ITER_FLATTEN } 1,28,0 { FROM_BOOL } diff --git a/clippy_config/src/types.rs b/clippy_config/src/types.rs index d47e34bb5bce5..bab63911182d1 100644 --- a/clippy_config/src/types.rs +++ b/clippy_config/src/types.rs @@ -1,5 +1,5 @@ use serde::de::{self, Deserializer, Visitor}; -use serde::{ser, Deserialize, Serialize}; +use serde::{Deserialize, Serialize, ser}; use std::fmt; #[derive(Debug, Deserialize)] diff --git a/clippy_dev/src/fmt.rs b/clippy_dev/src/fmt.rs index 5fc4365c6e785..8c61c35533cf8 100644 --- a/clippy_dev/src/fmt.rs +++ b/clippy_dev/src/fmt.rs @@ -1,6 +1,6 @@ use crate::clippy_project_root; use itertools::Itertools; -use rustc_lexer::{tokenize, TokenKind}; +use rustc_lexer::{TokenKind, tokenize}; use shell_escape::escape; use std::ffi::{OsStr, OsString}; use std::ops::ControlFlow; diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index 87117832fb952..5fd65017cfb97 100644 --- a/clippy_dev/src/new_lint.rs +++ b/clippy_dev/src/new_lint.rs @@ -441,7 +441,7 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R #[allow(clippy::too_many_lines)] fn setup_mod_file(path: &Path, lint: &LintData<'_>) -> io::Result<&'static str> { - use super::update_lints::{match_tokens, LintDeclSearchResult}; + use super::update_lints::{LintDeclSearchResult, match_tokens}; use rustc_lexer::TokenKind; let lint_name_upper = lint.name.to_uppercase(); diff --git a/clippy_dev/src/serve.rs b/clippy_dev/src/serve.rs index 19560b31fd3e2..cc14cd8dae69e 100644 --- a/clippy_dev/src/serve.rs +++ b/clippy_dev/src/serve.rs @@ -29,7 +29,7 @@ pub fn run(port: u16, lint: Option) -> ! { } if let Some(url) = url.take() { thread::spawn(move || { - Command::new(PYTHON) + let mut child = Command::new(PYTHON) .arg("-m") .arg("http.server") .arg(port.to_string()) @@ -40,6 +40,7 @@ pub fn run(port: u16, lint: Option) -> ! { thread::sleep(Duration::from_millis(500)); // Launch browser after first export.py has completed and http.server is up let _result = opener::open(url); + child.wait().unwrap(); }); } thread::sleep(Duration::from_millis(1000)); diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index 8dbda1c634c28..d6ed36d52f4e0 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -1,7 +1,7 @@ use crate::clippy_project_root; use aho_corasick::AhoCorasickBuilder; use itertools::Itertools; -use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind}; +use rustc_lexer::{LiteralKind, TokenKind, tokenize, unescape}; use std::collections::{HashMap, HashSet}; use std::ffi::OsStr; use std::fmt::{self, Write}; @@ -1048,23 +1048,17 @@ mod tests { Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()), ]; let mut expected: HashMap> = HashMap::new(); - expected.insert( - "group1".to_string(), - vec![ - Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()), - Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()), - ], - ); - expected.insert( - "group2".to_string(), - vec![Lint::new( - "should_assert_eq2", - "group2", - "\"abc\"", - "module_name", - Range::default(), - )], - ); + expected.insert("group1".to_string(), vec![ + Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()), + Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()), + ]); + expected.insert("group2".to_string(), vec![Lint::new( + "should_assert_eq2", + "group2", + "\"abc\"", + "module_name", + Range::default(), + )]); assert_eq!(expected, Lint::by_lint_group(lints.into_iter())); } } diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index fbd4566da58c7..d1188940b46a8 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_lints" -version = "0.1.82" +version = "0.1.83" 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/absolute_paths.rs b/clippy_lints/src/absolute_paths.rs index c72b3f1318c86..1af6d448a93c7 100644 --- a/clippy_lints/src/absolute_paths.rs +++ b/clippy_lints/src/absolute_paths.rs @@ -3,12 +3,12 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::is_from_proc_macro; use rustc_data_structures::fx::FxHashSet; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX}; +use rustc_hir::def_id::{CRATE_DEF_INDEX, DefId}; use rustc_hir::{HirId, ItemKind, Node, Path}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; -use rustc_span::symbol::kw; use rustc_span::Symbol; +use rustc_span::symbol::kw; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/almost_complete_range.rs b/clippy_lints/src/almost_complete_range.rs index 451bae959874f..370f0c482fd54 100644 --- a/clippy_lints/src/almost_complete_range.rs +++ b/clippy_lints/src/almost_complete_range.rs @@ -1,5 +1,5 @@ -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{trim_span, walk_span_to_context}; use rustc_ast::ast::{Expr, ExprKind, LitKind, Pat, PatKind, RangeEnd, RangeLimits}; diff --git a/clippy_lints/src/approx_const.rs b/clippy_lints/src/approx_const.rs index 56f5e903dc3e2..4f8f091a09569 100644 --- a/clippy_lints/src/approx_const.rs +++ b/clippy_lints/src/approx_const.rs @@ -1,10 +1,10 @@ -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_help; use rustc_ast::ast::{FloatTy, LitFloatType, LitKind}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{impl_lint_pass, RustcVersion}; +use rustc_session::{RustcVersion, impl_lint_pass}; use rustc_span::symbol; use std::f64::consts as f64; diff --git a/clippy_lints/src/arc_with_non_send_sync.rs b/clippy_lints/src/arc_with_non_send_sync.rs index 4eafa330fafab..2643f850879cc 100644 --- a/clippy_lints/src/arc_with_non_send_sync.rs +++ b/clippy_lints/src/arc_with_non_send_sync.rs @@ -4,8 +4,8 @@ use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; -use rustc_middle::ty::print::with_forced_trimmed_paths; use rustc_middle::ty::GenericArgKind; +use rustc_middle::ty::print::with_forced_trimmed_paths; use rustc_session::declare_lint_pass; use rustc_span::symbol::sym; diff --git a/clippy_lints/src/assertions_on_constants.rs b/clippy_lints/src/assertions_on_constants.rs index 7eaac80f969f7..b6684825835a9 100644 --- a/clippy_lints/src/assertions_on_constants.rs +++ b/clippy_lints/src/assertions_on_constants.rs @@ -1,7 +1,7 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::is_inside_always_const_context; -use clippy_utils::macros::{find_assert_args, root_macro_call_first_node, PanicExpn}; +use clippy_utils::macros::{PanicExpn, find_assert_args, root_macro_call_first_node}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; diff --git a/clippy_lints/src/assertions_on_result_states.rs b/clippy_lints/src/assertions_on_result_states.rs index f1cb4a05af86c..c073dee855e20 100644 --- a/clippy_lints/src/assertions_on_result_states.rs +++ b/clippy_lints/src/assertions_on_result_states.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::macros::{find_assert_args, root_macro_call_first_node, PanicExpn}; +use clippy_utils::macros::{PanicExpn, find_assert_args, root_macro_call_first_node}; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{has_debug_impl, is_copy, is_type_diagnostic_item}; use clippy_utils::usage::local_used_after_expr; diff --git a/clippy_lints/src/assigning_clones.rs b/clippy_lints/src/assigning_clones.rs index 55645d04eef36..0b82c0cd04c12 100644 --- a/clippy_lints/src/assigning_clones.rs +++ b/clippy_lints/src/assigning_clones.rs @@ -1,7 +1,7 @@ -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::mir::{enclosing_mir, PossibleBorrowerMap}; +use clippy_utils::mir::{PossibleBorrowerMap, enclosing_mir}; use clippy_utils::sugg::Sugg; use clippy_utils::{is_diag_trait_item, is_in_test, last_path_segment, local_is_initialized, path_to_local}; use rustc_errors::Applicability; diff --git a/clippy_lints/src/attrs/allow_attributes_without_reason.rs b/clippy_lints/src/attrs/allow_attributes_without_reason.rs index 4ab97118df1d4..40959eccd3a9f 100644 --- a/clippy_lints/src/attrs/allow_attributes_without_reason.rs +++ b/clippy_lints/src/attrs/allow_attributes_without_reason.rs @@ -1,4 +1,4 @@ -use super::{Attribute, ALLOW_ATTRIBUTES_WITHOUT_REASON}; +use super::{ALLOW_ATTRIBUTES_WITHOUT_REASON, Attribute}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; use rustc_ast::{MetaItemKind, NestedMetaItem}; @@ -26,7 +26,7 @@ pub(super) fn check<'cx>(cx: &LateContext<'cx>, name: Symbol, items: &[NestedMet cx, ALLOW_ATTRIBUTES_WITHOUT_REASON, attr.span, - format!("`{}` attribute without specifying a reason", name.as_str()), + format!("`{name}` attribute without specifying a reason"), |diag| { diag.help("try adding a reason at the end with `, reason = \"..\"`"); }, diff --git a/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs b/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs index 9b08fd6d654a5..508963a20ea71 100644 --- a/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs +++ b/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs @@ -1,10 +1,10 @@ -use super::utils::extract_clippy_lint; use super::BLANKET_CLIPPY_RESTRICTION_LINTS; +use super::utils::extract_clippy_lint; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; use rustc_ast::NestedMetaItem; use rustc_lint::{LateContext, Level, LintContext}; use rustc_span::symbol::Symbol; -use rustc_span::{sym, DUMMY_SP}; +use rustc_span::{DUMMY_SP, sym}; pub(super) fn check(cx: &LateContext<'_>, name: Symbol, items: &[NestedMetaItem]) { for lint in items { diff --git a/clippy_lints/src/attrs/deprecated_cfg_attr.rs b/clippy_lints/src/attrs/deprecated_cfg_attr.rs index e872ab6b4b5f5..abf924f7542e2 100644 --- a/clippy_lints/src/attrs/deprecated_cfg_attr.rs +++ b/clippy_lints/src/attrs/deprecated_cfg_attr.rs @@ -1,4 +1,4 @@ -use super::{unnecessary_clippy_cfg, Attribute, DEPRECATED_CFG_ATTR, DEPRECATED_CLIPPY_CFG_ATTR}; +use super::{Attribute, DEPRECATED_CFG_ATTR, DEPRECATED_CLIPPY_CFG_ATTR, unnecessary_clippy_cfg}; use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_sugg; use rustc_ast::AttrStyle; diff --git a/clippy_lints/src/attrs/duplicated_attributes.rs b/clippy_lints/src/attrs/duplicated_attributes.rs index 40a1c4e288422..55f8e1072db74 100644 --- a/clippy_lints/src/attrs/duplicated_attributes.rs +++ b/clippy_lints/src/attrs/duplicated_attributes.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use rustc_ast::{Attribute, MetaItem}; use rustc_data_structures::fx::FxHashMap; use rustc_lint::LateContext; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use std::collections::hash_map::Entry; fn emit_if_duplicated( @@ -36,7 +36,7 @@ fn check_duplicated_attr( } let Some(ident) = attr.ident() else { return }; let name = ident.name; - if name == sym::doc || name == sym::cfg_attr || name == sym::rustc_on_unimplemented { + if name == sym::doc || name == sym::cfg_attr || name == sym::rustc_on_unimplemented || name == sym::reason { // FIXME: Would be nice to handle `cfg_attr` as well. Only problem is to check that cfg // conditions are the same. // `#[rustc_on_unimplemented]` contains duplicated subattributes, that's expected. diff --git a/clippy_lints/src/attrs/empty_line_after.rs b/clippy_lints/src/attrs/empty_line_after.rs deleted file mode 100644 index 7ff644b4c445f..0000000000000 --- a/clippy_lints/src/attrs/empty_line_after.rs +++ /dev/null @@ -1,52 +0,0 @@ -use super::{EMPTY_LINE_AFTER_DOC_COMMENTS, EMPTY_LINE_AFTER_OUTER_ATTR}; -use clippy_utils::diagnostics::span_lint; -use clippy_utils::source::{is_present_in_source, without_block_comments, SpanRangeExt}; -use rustc_ast::{AttrKind, AttrStyle}; -use rustc_lint::EarlyContext; -use rustc_span::Span; - -/// Check for empty lines after outer attributes. -/// -/// Attributes and documentation comments are both considered outer attributes -/// by the AST. However, the average user likely considers them to be different. -/// Checking for empty lines after each of these attributes is split into two different -/// lints but can share the same logic. -pub(super) fn check(cx: &EarlyContext<'_>, item: &rustc_ast::Item) { - let mut iter = item.attrs.iter().peekable(); - while let Some(attr) = iter.next() { - if (matches!(attr.kind, AttrKind::Normal(..)) || matches!(attr.kind, AttrKind::DocComment(..))) - && attr.style == AttrStyle::Outer - && is_present_in_source(cx, attr.span) - { - let begin_of_attr_to_item = Span::new(attr.span.lo(), item.span.lo(), item.span.ctxt(), item.span.parent()); - let end_of_attr_to_next_attr_or_item = Span::new( - attr.span.hi(), - iter.peek().map_or(item.span.lo(), |next_attr| next_attr.span.lo()), - item.span.ctxt(), - item.span.parent(), - ); - - if let Some(snippet) = end_of_attr_to_next_attr_or_item.get_source_text(cx) { - let lines = snippet.split('\n').collect::>(); - let lines = without_block_comments(lines); - - if lines.iter().filter(|l| l.trim().is_empty()).count() > 2 { - let (lint_msg, lint_type) = match attr.kind { - AttrKind::DocComment(..) => ( - "found an empty line after a doc comment. \ - Perhaps you need to use `//!` to make a comment on a module, remove the empty line, or make a regular comment with `//`?", - EMPTY_LINE_AFTER_DOC_COMMENTS, - ), - AttrKind::Normal(..) => ( - "found an empty line after an outer attribute. \ - Perhaps you forgot to add a `!` to make it an inner attribute?", - EMPTY_LINE_AFTER_OUTER_ATTR, - ), - }; - - span_lint(cx, lint_type, begin_of_attr_to_item, lint_msg); - } - } - } - } -} diff --git a/clippy_lints/src/attrs/inline_always.rs b/clippy_lints/src/attrs/inline_always.rs index 3b5b80ffefaf2..d41bb580c6cd9 100644 --- a/clippy_lints/src/attrs/inline_always.rs +++ b/clippy_lints/src/attrs/inline_always.rs @@ -1,10 +1,10 @@ -use super::utils::is_word; use super::INLINE_ALWAYS; +use super::utils::is_word; use clippy_utils::diagnostics::span_lint; use rustc_ast::Attribute; use rustc_lint::LateContext; use rustc_span::symbol::Symbol; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; pub(super) fn check(cx: &LateContext<'_>, span: Span, name: Symbol, attrs: &[Attribute]) { if span.from_expansion() { diff --git a/clippy_lints/src/attrs/mod.rs b/clippy_lints/src/attrs/mod.rs index 3b14e9aee7fc0..888f28fa2258c 100644 --- a/clippy_lints/src/attrs/mod.rs +++ b/clippy_lints/src/attrs/mod.rs @@ -4,7 +4,6 @@ mod blanket_clippy_restriction_lints; mod deprecated_cfg_attr; mod deprecated_semver; mod duplicated_attributes; -mod empty_line_after; mod inline_always; mod mixed_attributes_style; mod non_minimal_cfg; @@ -13,8 +12,8 @@ mod unnecessary_clippy_cfg; mod useless_attribute; mod utils; -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use rustc_ast::{Attribute, MetaItemKind, NestedMetaItem}; use rustc_hir::{ImplItem, Item, ItemKind, TraitItem}; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; @@ -126,94 +125,6 @@ declare_clippy_lint! { "use of `#[deprecated(since = \"x\")]` where x is not semver" } -declare_clippy_lint! { - /// ### What it does - /// Checks for empty lines after outer attributes - /// - /// ### Why is this bad? - /// Most likely the attribute was meant to be an inner attribute using a '!'. - /// If it was meant to be an outer attribute, then the following item - /// should not be separated by empty lines. - /// - /// ### Known problems - /// Can cause false positives. - /// - /// From the clippy side it's difficult to detect empty lines between an attributes and the - /// following item because empty lines and comments are not part of the AST. The parsing - /// currently works for basic cases but is not perfect. - /// - /// ### Example - /// ```no_run - /// #[allow(dead_code)] - /// - /// fn not_quite_good_code() { } - /// ``` - /// - /// Use instead: - /// ```no_run - /// // Good (as inner attribute) - /// #![allow(dead_code)] - /// - /// fn this_is_fine() { } - /// - /// // or - /// - /// // Good (as outer attribute) - /// #[allow(dead_code)] - /// fn this_is_fine_too() { } - /// ``` - #[clippy::version = "pre 1.29.0"] - pub EMPTY_LINE_AFTER_OUTER_ATTR, - nursery, - "empty line after outer attribute" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for empty lines after documentation comments. - /// - /// ### Why is this bad? - /// The documentation comment was most likely meant to be an inner attribute or regular comment. - /// If it was intended to be a documentation comment, then the empty line should be removed to - /// be more idiomatic. - /// - /// ### Known problems - /// Only detects empty lines immediately following the documentation. If the doc comment is followed - /// by an attribute and then an empty line, this lint will not trigger. Use `empty_line_after_outer_attr` - /// in combination with this lint to detect both cases. - /// - /// Does not detect empty lines after doc attributes (e.g. `#[doc = ""]`). - /// - /// ### Example - /// ```no_run - /// /// Some doc comment with a blank line after it. - /// - /// fn not_quite_good_code() { } - /// ``` - /// - /// Use instead: - /// ```no_run - /// /// Good (no blank line) - /// fn this_is_fine() { } - /// ``` - /// - /// ```no_run - /// // Good (convert to a regular comment) - /// - /// fn this_is_fine_too() { } - /// ``` - /// - /// ```no_run - /// //! Good (convert to a comment on an inner attribute) - /// - /// fn this_is_fine_as_well() { } - /// ``` - #[clippy::version = "1.70.0"] - pub EMPTY_LINE_AFTER_DOC_COMMENTS, - nursery, - "empty line after documentation comments" -} - declare_clippy_lint! { /// ### What it does /// Checks for `warn`/`deny`/`forbid` attributes targeting the whole clippy::restriction category. @@ -601,18 +512,12 @@ impl EarlyAttributes { impl_lint_pass!(EarlyAttributes => [ DEPRECATED_CFG_ATTR, - EMPTY_LINE_AFTER_OUTER_ATTR, - EMPTY_LINE_AFTER_DOC_COMMENTS, NON_MINIMAL_CFG, DEPRECATED_CLIPPY_CFG_ATTR, UNNECESSARY_CLIPPY_CFG, ]); impl EarlyLintPass for EarlyAttributes { - fn check_item(&mut self, cx: &EarlyContext<'_>, item: &rustc_ast::Item) { - empty_line_after::check(cx, item); - } - fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) { deprecated_cfg_attr::check(cx, attr, &self.msrv); deprecated_cfg_attr::check_clippy(cx, attr); diff --git a/clippy_lints/src/attrs/useless_attribute.rs b/clippy_lints/src/attrs/useless_attribute.rs index 67ba605a59fa5..12668c616c13e 100644 --- a/clippy_lints/src/attrs/useless_attribute.rs +++ b/clippy_lints/src/attrs/useless_attribute.rs @@ -1,7 +1,7 @@ use super::utils::{extract_clippy_lint, is_lint_level, is_word}; use super::{Attribute, USELESS_ATTRIBUTE}; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::{first_line_of_span, SpanRangeExt}; +use clippy_utils::source::{SpanRangeExt, first_line_of_span}; use rustc_ast::NestedMetaItem; use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind}; @@ -51,6 +51,7 @@ pub(super) fn check(cx: &LateContext<'_>, item: &Item<'_>, attrs: &[Attribute]) | "module_name_repetitions" | "single_component_path_imports" | "disallowed_types" + | "unused_trait_names" ) }) { return; diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index d5f017b26509b..9952d0af99e54 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -7,7 +7,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::CoroutineLayout; use rustc_middle::ty::TyCtxt; use rustc_session::impl_lint_pass; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index e933fdf1d6e6e..3c2af72624f60 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -4,12 +4,12 @@ use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; -use rustc_hir::intravisit::{walk_expr, FnKind, Visitor}; +use rustc_hir::intravisit::{FnKind, Visitor, walk_expr}; use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, UnOp}; use rustc_lint::{LateContext, LateLintPass, Level}; use rustc_session::declare_lint_pass; use rustc_span::def_id::LocalDefId; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/box_default.rs b/clippy_lints/src/box_default.rs index 8459f051d3d9a..8261c65354fd5 100644 --- a/clippy_lints/src/box_default.rs +++ b/clippy_lints/src/box_default.rs @@ -4,7 +4,7 @@ use clippy_utils::ty::expr_sig; use clippy_utils::{is_default_equivalent, path_def_id}; use rustc_errors::Applicability; use rustc_hir::def::Res; -use rustc_hir::intravisit::{walk_ty, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_ty}; use rustc_hir::{Block, Expr, ExprKind, LetStmt, Node, QPath, Ty, TyKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; diff --git a/clippy_lints/src/byte_char_slices.rs b/clippy_lints/src/byte_char_slices.rs index a9fe190f1777e..dd2620b0b9df9 100644 --- a/clippy_lints/src/byte_char_slices.rs +++ b/clippy_lints/src/byte_char_slices.rs @@ -22,7 +22,7 @@ declare_clippy_lint! { /// ```ignore /// b"Hello" /// ``` - #[clippy::version = "1.68.0"] + #[clippy::version = "1.81.0"] pub BYTE_CHAR_SLICES, style, "hard to read byte char slice" diff --git a/clippy_lints/src/cargo/lint_groups_priority.rs b/clippy_lints/src/cargo/lint_groups_priority.rs index e924542fea2aa..ffd6c520c9ae8 100644 --- a/clippy_lints/src/cargo/lint_groups_priority.rs +++ b/clippy_lints/src/cargo/lint_groups_priority.rs @@ -2,7 +2,7 @@ use super::LINT_GROUPS_PRIORITY; use clippy_utils::diagnostics::span_lint_and_then; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; -use rustc_lint::{unerased_lint_store, LateContext}; +use rustc_lint::{LateContext, unerased_lint_store}; use rustc_span::{BytePos, Pos, SourceFile, Span, SyntaxContext}; use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; diff --git a/clippy_lints/src/casts/cast_lossless.rs b/clippy_lints/src/casts/cast_lossless.rs index 346aed7e9f117..84a44b14dde5b 100644 --- a/clippy_lints/src/casts/cast_lossless.rs +++ b/clippy_lints/src/casts/cast_lossless.rs @@ -10,7 +10,7 @@ use rustc_lint::LateContext; use rustc_middle::ty::{self, FloatTy, Ty}; use rustc_span::hygiene; -use super::{utils, CAST_LOSSLESS}; +use super::{CAST_LOSSLESS, utils}; pub(super) fn check( cx: &LateContext<'_>, diff --git a/clippy_lints/src/casts/cast_possible_truncation.rs b/clippy_lints/src/casts/cast_possible_truncation.rs index 5708aae3f3eca..40a1a9d1ce8fb 100644 --- a/clippy_lints/src/casts/cast_possible_truncation.rs +++ b/clippy_lints/src/casts/cast_possible_truncation.rs @@ -12,7 +12,7 @@ use rustc_middle::ty::{self, FloatTy, Ty}; use rustc_span::Span; use rustc_target::abi::IntegerType; -use super::{utils, CAST_ENUM_TRUNCATION, CAST_POSSIBLE_TRUNCATION}; +use super::{CAST_ENUM_TRUNCATION, CAST_POSSIBLE_TRUNCATION, utils}; fn constant_int(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { if let Some(Constant::Int(c)) = ConstEvalCtxt::new(cx).eval(expr) { diff --git a/clippy_lints/src/casts/cast_possible_wrap.rs b/clippy_lints/src/casts/cast_possible_wrap.rs index 11274383595a8..3cf4a43b0d4c7 100644 --- a/clippy_lints/src/casts/cast_possible_wrap.rs +++ b/clippy_lints/src/casts/cast_possible_wrap.rs @@ -3,7 +3,7 @@ use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty::Ty; -use super::{utils, CAST_POSSIBLE_WRAP}; +use super::{CAST_POSSIBLE_WRAP, utils}; // this should be kept in sync with the allowed bit widths of `usize` and `isize` const ALLOWED_POINTER_SIZES: [u64; 3] = [16, 32, 64]; diff --git a/clippy_lints/src/casts/cast_precision_loss.rs b/clippy_lints/src/casts/cast_precision_loss.rs index 035666e4d4c92..1eb115ce6bdc0 100644 --- a/clippy_lints/src/casts/cast_precision_loss.rs +++ b/clippy_lints/src/casts/cast_precision_loss.rs @@ -4,7 +4,7 @@ use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty::{self, FloatTy, Ty}; -use super::{utils, CAST_PRECISION_LOSS}; +use super::{CAST_PRECISION_LOSS, utils}; pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) { if !cast_from.is_integral() || cast_to.is_integral() { diff --git a/clippy_lints/src/casts/cast_sign_loss.rs b/clippy_lints/src/casts/cast_sign_loss.rs index 9daf237344a4b..4be53ace6871e 100644 --- a/clippy_lints/src/casts/cast_sign_loss.rs +++ b/clippy_lints/src/casts/cast_sign_loss.rs @@ -3,7 +3,7 @@ use std::ops::ControlFlow; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint; -use clippy_utils::visitors::{for_each_expr_without_closures, Descend}; +use clippy_utils::visitors::{Descend, for_each_expr_without_closures}; use clippy_utils::{method_chain_args, sext}; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; diff --git a/clippy_lints/src/casts/fn_to_numeric_cast.rs b/clippy_lints/src/casts/fn_to_numeric_cast.rs index dbe03e4ae8095..ac1a355c8d962 100644 --- a/clippy_lints/src/casts/fn_to_numeric_cast.rs +++ b/clippy_lints/src/casts/fn_to_numeric_cast.rs @@ -5,7 +5,7 @@ use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty, UintTy}; -use super::{utils, FN_TO_NUMERIC_CAST}; +use super::{FN_TO_NUMERIC_CAST, utils}; pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) { // We only want to check casts to `ty::Uint` or `ty::Int` 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 dfbae1618ac62..18e7798452ead 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 @@ -5,7 +5,7 @@ use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; -use super::{utils, FN_TO_NUMERIC_CAST_WITH_TRUNCATION}; +use super::{FN_TO_NUMERIC_CAST_WITH_TRUNCATION, utils}; pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) { // We only want to check casts to `ty::Uint` or `ty::Int` diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index c31716fbcee1d..3acd4eca420e0 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -23,8 +23,8 @@ mod unnecessary_cast; mod utils; mod zero_ptr; -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::is_hir_ty_cfg_dependant; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -410,19 +410,27 @@ declare_clippy_lint! { /// ### Why is this bad? /// Though `as` casts between raw pointers are not terrible, `pointer::cast_mut` and /// `pointer::cast_const` are safer because they cannot accidentally cast the pointer to another - /// type. + /// type. Or, when null pointers are involved, `null()` and `null_mut()` can be used directly. /// /// ### Example /// ```no_run /// let ptr: *const u32 = &42_u32; /// let mut_ptr = ptr as *mut u32; /// let ptr = mut_ptr as *const u32; + /// let ptr1 = std::ptr::null::() as *mut u32; + /// let ptr2 = std::ptr::null_mut::() as *const u32; + /// let ptr3 = std::ptr::null::().cast_mut(); + /// let ptr4 = std::ptr::null_mut::().cast_const(); /// ``` /// Use instead: /// ```no_run /// let ptr: *const u32 = &42_u32; /// let mut_ptr = ptr.cast_mut(); /// let ptr = mut_ptr.cast_const(); + /// let ptr1 = std::ptr::null_mut::(); + /// let ptr2 = std::ptr::null::(); + /// let ptr3 = std::ptr::null_mut::(); + /// let ptr4 = std::ptr::null::(); /// ``` #[clippy::version = "1.72.0"] pub PTR_CAST_CONSTNESS, @@ -809,6 +817,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts { char_lit_as_u8::check(cx, expr); ptr_as_ptr::check(cx, expr, &self.msrv); cast_slice_different_sizes::check(cx, expr, &self.msrv); + ptr_cast_constness::check_null_ptr_cast_method(cx, expr); } extract_msrv_attr!(LateContext); diff --git a/clippy_lints/src/casts/ptr_cast_constness.rs b/clippy_lints/src/casts/ptr_cast_constness.rs index 7513e18d408b1..7518dd2435aee 100644 --- a/clippy_lints/src/casts/ptr_cast_constness.rs +++ b/clippy_lints/src/casts/ptr_cast_constness.rs @@ -1,10 +1,12 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::std_or_core; use clippy_utils::sugg::Sugg; use rustc_errors::Applicability; -use rustc_hir::{Expr, Mutability}; +use rustc_hir::{Expr, ExprKind, Mutability, QPath}; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty, TypeVisitableExt}; +use rustc_span::sym; use super::PTR_CAST_CONSTNESS; @@ -16,8 +18,7 @@ pub(super) fn check<'tcx>( cast_to: Ty<'tcx>, msrv: &Msrv, ) { - if msrv.meets(msrvs::POINTER_CAST_CONSTNESS) - && let ty::RawPtr(from_ty, from_mutbl) = cast_from.kind() + if let ty::RawPtr(from_ty, from_mutbl) = cast_from.kind() && let ty::RawPtr(to_ty, to_mutbl) = cast_to.kind() && matches!( (from_mutbl, to_mutbl), @@ -26,20 +27,74 @@ pub(super) fn check<'tcx>( && from_ty == to_ty && !from_ty.has_erased_regions() { - let sugg = Sugg::hir(cx, cast_expr, "_"); - let constness = match *to_mutbl { - Mutability::Not => "const", - Mutability::Mut => "mut", - }; + if let ExprKind::Call(func, []) = cast_expr.kind + && let ExprKind::Path(QPath::Resolved(None, path)) = func.kind + && let Some(defid) = path.res.opt_def_id() + && let Some(prefix) = std_or_core(cx) + && let mut app = Applicability::MachineApplicable + && let sugg = format!("{}", Sugg::hir_with_applicability(cx, cast_expr, "_", &mut app)) + && let Some((_, after_lt)) = sugg.split_once("::<") + && let Some((source, target, target_func)) = match cx.tcx.get_diagnostic_name(defid) { + Some(sym::ptr_null) => Some(("const", "mutable", "null_mut")), + Some(sym::ptr_null_mut) => Some(("mutable", "const", "null")), + _ => None, + } + { + span_lint_and_sugg( + cx, + PTR_CAST_CONSTNESS, + expr.span, + format!("`as` casting to make a {source} null pointer into a {target} null pointer"), + format!("use `{target_func}()` directly instead"), + format!("{prefix}::ptr::{target_func}::<{after_lt}"), + app, + ); + return; + } + if msrv.meets(msrvs::POINTER_CAST_CONSTNESS) { + let sugg = Sugg::hir(cx, cast_expr, "_"); + let constness = match *to_mutbl { + Mutability::Not => "const", + Mutability::Mut => "mut", + }; + + span_lint_and_sugg( + cx, + PTR_CAST_CONSTNESS, + expr.span, + "`as` casting between raw pointers while changing only its constness", + format!("try `pointer::cast_{constness}`, a safer alternative"), + format!("{}.cast_{constness}()", sugg.maybe_par()), + Applicability::MachineApplicable, + ); + } + } +} + +pub(super) fn check_null_ptr_cast_method(cx: &LateContext<'_>, expr: &Expr<'_>) { + if let ExprKind::MethodCall(method, cast_expr, [], _) = expr.kind + && let ExprKind::Call(func, []) = cast_expr.kind + && let ExprKind::Path(QPath::Resolved(None, path)) = func.kind + && let Some(defid) = path.res.opt_def_id() + && let method = match (cx.tcx.get_diagnostic_name(defid), method.ident.as_str()) { + (Some(sym::ptr_null), "cast_mut") => "null_mut", + (Some(sym::ptr_null_mut), "cast_const") => "null", + _ => return, + } + && let Some(prefix) = std_or_core(cx) + && let mut app = Applicability::MachineApplicable + && let sugg = format!("{}", Sugg::hir_with_applicability(cx, cast_expr, "_", &mut app)) + && let Some((_, after_lt)) = sugg.split_once("::<") + { span_lint_and_sugg( cx, PTR_CAST_CONSTNESS, expr.span, - "`as` casting between raw pointers while changing only its constness", - format!("try `pointer::cast_{constness}`, a safer alternative"), - format!("{}.cast_{constness}()", sugg.maybe_par()), - Applicability::MachineApplicable, + "changing constness of a null pointer", + format!("use `{method}()` directly instead"), + format!("{prefix}::ptr::{method}::<{after_lt}"), + app, ); } } diff --git a/clippy_lints/src/casts/ref_as_ptr.rs b/clippy_lints/src/casts/ref_as_ptr.rs index 5f48b8bd2063e..dfa240ccec623 100644 --- a/clippy_lints/src/casts/ref_as_ptr.rs +++ b/clippy_lints/src/casts/ref_as_ptr.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; -use clippy_utils::{expr_use_ctxt, is_no_std_crate, ExprUseNode}; +use clippy_utils::{ExprUseNode, expr_use_ctxt, is_no_std_crate}; use rustc_errors::Applicability; use rustc_hir::{Expr, Mutability, Ty, TyKind}; use rustc_lint::LateContext; diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs index 566adc83d6903..811d33c8bde2f 100644 --- a/clippy_lints/src/casts/unnecessary_cast.rs +++ b/clippy_lints/src/casts/unnecessary_cast.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::numeric_literal::NumericLiteral; -use clippy_utils::source::{snippet_opt, SpanRangeExt}; -use clippy_utils::visitors::{for_each_expr_without_closures, Visitable}; +use clippy_utils::source::{SpanRangeExt, snippet_opt}; +use clippy_utils::visitors::{Visitable, for_each_expr_without_closures}; use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant, is_ty_alias, path_to_local}; use rustc_ast::{LitFloatType, LitIntType, LitKind}; use rustc_errors::Applicability; diff --git a/clippy_lints/src/casts/utils.rs b/clippy_lints/src/casts/utils.rs index 5a4f20f099060..5ccba92a0afe8 100644 --- a/clippy_lints/src/casts/utils.rs +++ b/clippy_lints/src/casts/utils.rs @@ -1,4 +1,4 @@ -use clippy_utils::ty::{read_explicit_enum_value, EnumValue}; +use clippy_utils::ty::{EnumValue, read_explicit_enum_value}; use rustc_middle::ty::{self, AdtDef, IntTy, Ty, TyCtxt, UintTy, VariantDiscr}; /// Returns the size in bits of an integral type. diff --git a/clippy_lints/src/cfg_not_test.rs b/clippy_lints/src/cfg_not_test.rs index d820c1e0720b8..884d15cabffcb 100644 --- a/clippy_lints/src/cfg_not_test.rs +++ b/clippy_lints/src/cfg_not_test.rs @@ -22,7 +22,7 @@ declare_clippy_lint! { /// # fn important_check() {} /// important_check(); /// ``` - #[clippy::version = "1.73.0"] + #[clippy::version = "1.81.0"] pub CFG_NOT_TEST, restriction, "enforce against excluding code from test builds" diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs index dd7c34d1e4687..d3aa2fd1ea12c 100644 --- a/clippy_lints/src/checked_conversions.rs +++ b/clippy_lints/src/checked_conversions.rs @@ -1,8 +1,8 @@ -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{is_in_const_context, is_integer_literal, SpanlessEq}; +use clippy_utils::{SpanlessEq, is_in_const_context, is_integer_literal}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, QPath, TyKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; diff --git a/clippy_lints/src/cognitive_complexity.rs b/clippy_lints/src/cognitive_complexity.rs index 0099eefbc8dc1..495d8ce3fa701 100644 --- a/clippy_lints/src/cognitive_complexity.rs +++ b/clippy_lints/src/cognitive_complexity.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::{IntoSpan, SpanRangeExt}; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::for_each_expr_without_closures; -use clippy_utils::{get_async_fn_body, is_async_fn, LimitStack}; +use clippy_utils::{LimitStack, get_async_fn_body, is_async_fn}; use core::ops::ControlFlow; use rustc_ast::ast::Attribute; use rustc_hir::intravisit::FnKind; @@ -11,7 +11,7 @@ use rustc_hir::{Body, Expr, ExprKind, FnDecl}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; use rustc_span::def_id::LocalDefId; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/collection_is_never_read.rs b/clippy_lints/src/collection_is_never_read.rs index c6847411c75ab..8276e53648c0e 100644 --- a/clippy_lints/src/collection_is_never_read.rs +++ b/clippy_lints/src/collection_is_never_read.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::{get_type_diagnostic_name, is_type_lang_item}; -use clippy_utils::visitors::{for_each_expr, Visitable}; +use clippy_utils::visitors::{Visitable, for_each_expr}; use clippy_utils::{get_enclosing_block, path_to_local_id}; use core::ops::ControlFlow; use rustc_hir::{Body, ExprKind, HirId, LangItem, LetStmt, Node, PatKind}; diff --git a/clippy_lints/src/comparison_chain.rs b/clippy_lints/src/comparison_chain.rs index 5d78744e9b5e2..b9baf9af248ed 100644 --- a/clippy_lints/src/comparison_chain.rs +++ b/clippy_lints/src/comparison_chain.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::implements_trait; -use clippy_utils::{if_sequence, is_else_clause, is_in_const_context, SpanlessEq}; +use clippy_utils::{SpanlessEq, if_sequence, is_else_clause, is_in_const_context}; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index 86e0368c4e423..d90a22bb62a60 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -1,16 +1,17 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then}; -use clippy_utils::source::{first_line_of_span, indent_of, reindent_multiline, snippet, IntoSpan, SpanRangeExt}; -use clippy_utils::ty::{needs_ordered_drop, InteriorMut}; +use clippy_utils::source::{IntoSpan, SpanRangeExt, first_line_of_span, indent_of, reindent_multiline, snippet}; +use clippy_utils::ty::{InteriorMut, needs_ordered_drop}; use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{ - capture_local_usage, eq_expr_value, find_binding_init, get_enclosing_block, hash_expr, hash_stmt, if_sequence, - is_else_clause, is_lint_allowed, path_to_local, search_same, ContainsName, HirEqInterExpr, SpanlessEq, + ContainsName, HirEqInterExpr, SpanlessEq, capture_local_usage, eq_expr_value, find_binding_init, + get_enclosing_block, hash_expr, hash_stmt, if_sequence, is_else_clause, is_lint_allowed, path_to_local, + search_same, }; use core::iter; use core::ops::ControlFlow; use rustc_errors::Applicability; -use rustc_hir::{intravisit, BinOpKind, Block, Expr, ExprKind, HirId, HirIdSet, Stmt, StmtKind}; +use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, HirIdSet, Stmt, StmtKind, intravisit}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::TyCtxt; use rustc_session::impl_lint_pass; diff --git a/clippy_lints/src/crate_in_macro_def.rs b/clippy_lints/src/crate_in_macro_def.rs index 678bdbc0ffb8a..c8f814137289c 100644 --- a/clippy_lints/src/crate_in_macro_def.rs +++ b/clippy_lints/src/crate_in_macro_def.rs @@ -5,8 +5,8 @@ use rustc_ast::tokenstream::{TokenStream, TokenTree}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::symbol::sym; use rustc_span::Span; +use rustc_span::symbol::sym; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/dbg_macro.rs b/clippy_lints/src/dbg_macro.rs index 93c8fff05e9ed..a96c86f076574 100644 --- a/clippy_lints/src/dbg_macro.rs +++ b/clippy_lints/src/dbg_macro.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_in_test; -use clippy_utils::macros::{macro_backtrace, MacroCall}; +use clippy_utils::macros::{MacroCall, macro_backtrace}; use clippy_utils::source::snippet_with_applicability; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -9,7 +9,7 @@ use rustc_hir::{Expr, ExprKind, Node}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::impl_lint_pass; -use rustc_span::{sym, Span, SyntaxContext}; +use rustc_span::{Span, SyntaxContext, sym}; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 60e517131737a..2b229d2fe6ac5 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -49,8 +49,6 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::attrs::DEPRECATED_CLIPPY_CFG_ATTR_INFO, crate::attrs::DEPRECATED_SEMVER_INFO, crate::attrs::DUPLICATED_ATTRIBUTES_INFO, - crate::attrs::EMPTY_LINE_AFTER_DOC_COMMENTS_INFO, - crate::attrs::EMPTY_LINE_AFTER_OUTER_ATTR_INFO, crate::attrs::INLINE_ALWAYS_INFO, crate::attrs::MIXED_ATTRIBUTES_STYLE_INFO, crate::attrs::NON_MINIMAL_CFG_INFO, @@ -138,6 +136,8 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::doc::DOC_LINK_WITH_QUOTES_INFO, crate::doc::DOC_MARKDOWN_INFO, crate::doc::EMPTY_DOCS_INFO, + crate::doc::EMPTY_LINE_AFTER_DOC_COMMENTS_INFO, + crate::doc::EMPTY_LINE_AFTER_OUTER_ATTR_INFO, crate::doc::MISSING_ERRORS_DOC_INFO, crate::doc::MISSING_PANICS_DOC_INFO, crate::doc::MISSING_SAFETY_DOC_INFO, @@ -217,6 +217,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::implicit_return::IMPLICIT_RETURN_INFO, crate::implicit_saturating_add::IMPLICIT_SATURATING_ADD_INFO, crate::implicit_saturating_sub::IMPLICIT_SATURATING_SUB_INFO, + crate::implicit_saturating_sub::INVERTED_SATURATING_SUB_INFO, crate::implied_bounds_in_impls::IMPLIED_BOUNDS_IN_IMPLS_INFO, crate::incompatible_msrv::INCOMPATIBLE_MSRV_INFO, crate::inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR_INFO, @@ -300,10 +301,12 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::manual_async_fn::MANUAL_ASYNC_FN_INFO, crate::manual_bits::MANUAL_BITS_INFO, crate::manual_clamp::MANUAL_CLAMP_INFO, + crate::manual_div_ceil::MANUAL_DIV_CEIL_INFO, crate::manual_float_methods::MANUAL_IS_FINITE_INFO, crate::manual_float_methods::MANUAL_IS_INFINITE_INFO, crate::manual_hash_one::MANUAL_HASH_ONE_INFO, crate::manual_is_ascii_check::MANUAL_IS_ASCII_CHECK_INFO, + crate::manual_is_power_of_two::MANUAL_IS_POWER_OF_TWO_INFO, crate::manual_let_else::MANUAL_LET_ELSE_INFO, crate::manual_main_separator_str::MANUAL_MAIN_SEPARATOR_STR_INFO, crate::manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE_INFO, @@ -464,6 +467,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::methods::UNNECESSARY_FALLIBLE_CONVERSIONS_INFO, crate::methods::UNNECESSARY_FILTER_MAP_INFO, crate::methods::UNNECESSARY_FIND_MAP_INFO, + crate::methods::UNNECESSARY_FIRST_THEN_CHECK_INFO, crate::methods::UNNECESSARY_FOLD_INFO, crate::methods::UNNECESSARY_GET_THEN_CHECK_INFO, crate::methods::UNNECESSARY_JOIN_INFO, @@ -486,6 +490,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::misc::SHORT_CIRCUIT_STATEMENT_INFO, crate::misc::TOPLEVEL_REF_ARG_INFO, crate::misc::USED_UNDERSCORE_BINDING_INFO, + crate::misc::USED_UNDERSCORE_ITEMS_INFO, crate::misc_early::BUILTIN_TYPE_SHADOW_INFO, crate::misc_early::DOUBLE_NEG_INFO, crate::misc_early::DUPLICATE_UNDERSCORE_ARGUMENT_INFO, @@ -553,6 +558,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::non_expressive_names::SIMILAR_NAMES_INFO, crate::non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS_INFO, crate::non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY_INFO, + crate::non_zero_suggestions::NON_ZERO_SUGGESTIONS_INFO, crate::nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES_INFO, crate::octal_escapes::OCTAL_ESCAPES_INFO, crate::only_used_in_recursion::ONLY_USED_IN_RECURSION_INFO, @@ -598,6 +604,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::pathbuf_init_then_push::PATHBUF_INIT_THEN_PUSH_INFO, crate::pattern_type_mismatch::PATTERN_TYPE_MISMATCH_INFO, crate::permissions_set_readonly_false::PERMISSIONS_SET_READONLY_FALSE_INFO, + crate::pointers_in_nomem_asm_block::POINTERS_IN_NOMEM_ASM_BLOCK_INFO, crate::precedence::PRECEDENCE_INFO, crate::ptr::CMP_NULL_INFO, crate::ptr::INVALID_NULL_PTR_USAGE_INFO, @@ -741,6 +748,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::unused_result_ok::UNUSED_RESULT_OK_INFO, crate::unused_rounding::UNUSED_ROUNDING_INFO, crate::unused_self::UNUSED_SELF_INFO, + crate::unused_trait_names::UNUSED_TRAIT_NAMES_INFO, crate::unused_unit::UNUSED_UNIT_INFO, crate::unwrap::PANICKING_UNWRAP_INFO, crate::unwrap::UNNECESSARY_UNWRAP_INFO, @@ -767,4 +775,5 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::zero_div_zero::ZERO_DIVIDED_BY_ZERO_INFO, crate::zero_repeat_side_effects::ZERO_REPEAT_SIDE_EFFECTS_INFO, crate::zero_sized_map_values::ZERO_SIZED_MAP_VALUES_INFO, + crate::zombie_processes::ZOMBIE_PROCESSES_INFO, ]; diff --git a/clippy_lints/src/default.rs b/clippy_lints/src/default.rs index 0b7279f2b360d..dc10b64698b4f 100644 --- a/clippy_lints/src/default.rs +++ b/clippy_lints/src/default.rs @@ -11,7 +11,7 @@ use rustc_middle::ty; use rustc_middle::ty::print::with_forced_trimmed_paths; use rustc_session::impl_lint_pass; use rustc_span::symbol::{Ident, Symbol}; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/default_constructed_unit_structs.rs b/clippy_lints/src/default_constructed_unit_structs.rs index 137781754966a..33a97222b8f8f 100644 --- a/clippy_lints/src/default_constructed_unit_structs.rs +++ b/clippy_lints/src/default_constructed_unit_structs.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::is_ty_alias; -use hir::def::Res; use hir::ExprKind; +use hir::def::Res; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/default_instead_of_iter_empty.rs b/clippy_lints/src/default_instead_of_iter_empty.rs index ac49e6f1a482a..056e39c02af90 100644 --- a/clippy_lints/src/default_instead_of_iter_empty.rs +++ b/clippy_lints/src/default_instead_of_iter_empty.rs @@ -2,10 +2,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_context; use clippy_utils::{last_path_segment, std_or_core}; use rustc_errors::Applicability; -use rustc_hir::{def, Expr, ExprKind, GenericArg, QPath, TyKind}; +use rustc_hir::{Expr, ExprKind, GenericArg, QPath, TyKind, def}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::{sym, SyntaxContext}; +use rustc_span::{SyntaxContext, sym}; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs index 05c3cd3c81407..a065dc2cf7e52 100644 --- a/clippy_lints/src/default_numeric_fallback.rs +++ b/clippy_lints/src/default_numeric_fallback.rs @@ -3,7 +3,7 @@ use clippy_utils::numeric_literal; use clippy_utils::source::snippet_opt; use rustc_ast::ast::{LitFloatType, LitIntType, LitKind}; use rustc_errors::Applicability; -use rustc_hir::intravisit::{walk_expr, walk_stmt, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_expr, walk_stmt}; use rustc_hir::{Block, Body, ConstContext, Expr, ExprKind, FnRetTy, HirId, Lit, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 0e55d3db469ae..f34f5e056064d 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -3,14 +3,14 @@ use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; use clippy_utils::ty::{implements_trait, is_manually_drop}; use clippy_utils::{ - expr_use_ctxt, get_parent_expr, is_block_like, is_lint_allowed, path_to_local, peel_middle_ty_refs, DefinedTy, - ExprUseNode, + DefinedTy, ExprUseNode, expr_use_ctxt, get_parent_expr, is_block_like, is_lint_allowed, path_to_local, + peel_middle_ty_refs, }; use core::mem; use rustc_ast::util::parser::{PREC_PREFIX, PREC_UNAMBIGUOUS}; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::Applicability; -use rustc_hir::intravisit::{walk_ty, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_ty}; use rustc_hir::{ self as hir, BindingMode, Body, BodyId, BorrowKind, Expr, ExprKind, HirId, MatchSource, Mutability, Node, Pat, PatKind, Path, QPath, TyKind, UnOp, @@ -290,13 +290,10 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { && let Some(ty) = use_node.defined_ty(cx) && TyCoercionStability::for_defined_ty(cx, ty, use_node.is_return()).is_deref_stable() { - self.state = Some(( - State::ExplicitDeref { mutability: None }, - StateData { - first_expr: expr, - adjusted_ty, - }, - )); + self.state = Some((State::ExplicitDeref { mutability: None }, StateData { + first_expr: expr, + adjusted_ty, + })); } }, RefOp::Method { mutbl, is_ufcs } @@ -458,13 +455,10 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { && next_adjust.map_or(true, |a| matches!(a.kind, Adjust::Deref(_) | Adjust::Borrow(_))) && iter.all(|a| matches!(a.kind, Adjust::Deref(_) | Adjust::Borrow(_))) { - self.state = Some(( - State::Borrow { mutability }, - StateData { - first_expr: expr, - adjusted_ty, - }, - )); + self.state = Some((State::Borrow { mutability }, StateData { + first_expr: expr, + adjusted_ty, + })); } }, _ => {}, @@ -508,13 +502,10 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { let stability = state.stability; report(cx, expr, State::DerefedBorrow(state), data, typeck); if stability.is_deref_stable() { - self.state = Some(( - State::Borrow { mutability }, - StateData { - first_expr: expr, - adjusted_ty, - }, - )); + self.state = Some((State::Borrow { mutability }, StateData { + first_expr: expr, + adjusted_ty, + })); } }, (Some((State::DerefedBorrow(state), data)), RefOp::Deref) => { @@ -539,13 +530,10 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { } else if stability.is_deref_stable() && let Some(parent) = get_parent_expr(cx, expr) { - self.state = Some(( - State::ExplicitDeref { mutability: None }, - StateData { - first_expr: parent, - adjusted_ty, - }, - )); + self.state = Some((State::ExplicitDeref { mutability: None }, StateData { + first_expr: parent, + adjusted_ty, + })); } }, @@ -1138,20 +1126,17 @@ impl<'tcx> Dereferencing<'tcx> { if let Some(outer_pat) = self.ref_locals.get_mut(&local) { if let Some(pat) = outer_pat { // Check for auto-deref - if !matches!( - cx.typeck_results().expr_adjustments(e), - [ - Adjustment { - kind: Adjust::Deref(_), - .. - }, - Adjustment { - kind: Adjust::Deref(_), - .. - }, + if !matches!(cx.typeck_results().expr_adjustments(e), [ + Adjustment { + kind: Adjust::Deref(_), + .. + }, + Adjustment { + kind: Adjust::Deref(_), .. - ] - ) { + }, + .. + ]) { match get_parent_expr(cx, e) { // Field accesses are the same no matter the number of references. Some(Expr { diff --git a/clippy_lints/src/derivable_impls.rs b/clippy_lints/src/derivable_impls.rs index f27f68e2cbc5f..2920bbb4c81e8 100644 --- a/clippy_lints/src/derivable_impls.rs +++ b/clippy_lints/src/derivable_impls.rs @@ -1,5 +1,5 @@ -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::indent_of; use clippy_utils::{is_default_equivalent, peel_blocks}; diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 636698e96f6d1..61c151d4f9d68 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -3,7 +3,7 @@ use clippy_utils::ty::{implements_trait, implements_trait_with_env, is_copy}; use clippy_utils::{has_non_exhaustive_attr, is_lint_allowed, match_def_path, paths}; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; -use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, Visitor}; +use rustc_hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn, walk_item}; use rustc_hir::{ self as hir, BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, Impl, Item, ItemKind, Safety, UnsafeSource, }; @@ -15,7 +15,7 @@ use rustc_middle::ty::{ }; use rustc_session::declare_lint_pass; use rustc_span::def_id::LocalDefId; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/doc/empty_line_after.rs b/clippy_lints/src/doc/empty_line_after.rs new file mode 100644 index 0000000000000..de7a2c2433f46 --- /dev/null +++ b/clippy_lints/src/doc/empty_line_after.rs @@ -0,0 +1,342 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::{SpanRangeExt, snippet_indent}; +use clippy_utils::tokenize_with_text; +use itertools::Itertools; +use rustc_ast::token::CommentKind; +use rustc_ast::{AttrKind, AttrStyle, Attribute}; +use rustc_errors::{Applicability, Diag, SuggestionStyle}; +use rustc_hir::{ItemKind, Node}; +use rustc_lexer::TokenKind; +use rustc_lint::LateContext; +use rustc_span::{BytePos, ExpnKind, InnerSpan, Span, SpanData}; + +use super::{EMPTY_LINE_AFTER_DOC_COMMENTS, EMPTY_LINE_AFTER_OUTER_ATTR}; + +#[derive(Debug, PartialEq, Clone, Copy)] +enum StopKind { + Attr, + Doc(CommentKind), +} + +impl StopKind { + fn is_doc(self) -> bool { + matches!(self, StopKind::Doc(_)) + } +} + +#[derive(Debug)] +struct Stop { + span: Span, + kind: StopKind, + first: usize, + last: usize, +} + +impl Stop { + fn convert_to_inner(&self) -> (Span, String) { + let inner = match self.kind { + // #|[...] + StopKind::Attr => InnerSpan::new(1, 1), + // /// or /** + // ^ ^ + StopKind::Doc(_) => InnerSpan::new(2, 3), + }; + (self.span.from_inner(inner), "!".into()) + } + + fn comment_out(&self, cx: &LateContext<'_>, suggestions: &mut Vec<(Span, String)>) { + match self.kind { + StopKind::Attr => { + if cx.tcx.sess.source_map().is_multiline(self.span) { + suggestions.extend([ + (self.span.shrink_to_lo(), "/* ".into()), + (self.span.shrink_to_hi(), " */".into()), + ]); + } else { + suggestions.push((self.span.shrink_to_lo(), "// ".into())); + } + }, + StopKind::Doc(CommentKind::Line) => suggestions.push((self.span.shrink_to_lo(), "// ".into())), + StopKind::Doc(CommentKind::Block) => { + // /** outer */ /*! inner */ + // ^ ^ + let asterisk = self.span.from_inner(InnerSpan::new(1, 2)); + suggestions.push((asterisk, String::new())); + }, + } + } + + fn from_attr(cx: &LateContext<'_>, attr: &Attribute) -> Option { + let SpanData { lo, hi, .. } = attr.span.data(); + let file = cx.tcx.sess.source_map().lookup_source_file(lo); + + Some(Self { + span: attr.span, + kind: match attr.kind { + AttrKind::Normal(_) => StopKind::Attr, + AttrKind::DocComment(comment_kind, _) => StopKind::Doc(comment_kind), + }, + first: file.lookup_line(file.relative_position(lo))?, + last: file.lookup_line(file.relative_position(hi))?, + }) + } +} + +/// Represents a set of attrs/doc comments separated by 1 or more empty lines +/// +/// ```ignore +/// /// chunk 1 docs +/// // not an empty line so also part of chunk 1 +/// #[chunk_1_attrs] // <-- prev_stop +/// +/// /* gap */ +/// +/// /// chunk 2 docs // <-- next_stop +/// #[chunk_2_attrs] +/// ``` +struct Gap<'a> { + /// The span of individual empty lines including the newline at the end of the line + empty_lines: Vec, + has_comment: bool, + next_stop: &'a Stop, + prev_stop: &'a Stop, + /// The chunk that includes [`prev_stop`](Self::prev_stop) + prev_chunk: &'a [Stop], +} + +impl<'a> Gap<'a> { + fn new(cx: &LateContext<'_>, prev_chunk: &'a [Stop], next_chunk: &'a [Stop]) -> Option { + let prev_stop = prev_chunk.last()?; + let next_stop = next_chunk.first()?; + let gap_span = prev_stop.span.between(next_stop.span); + let gap_snippet = gap_span.get_source_text(cx)?; + + let mut has_comment = false; + let mut empty_lines = Vec::new(); + + for (token, source, inner_span) in tokenize_with_text(&gap_snippet) { + match token { + TokenKind::BlockComment { + doc_style: None, + terminated: true, + } + | TokenKind::LineComment { doc_style: None } => has_comment = true, + TokenKind::Whitespace => { + let newlines = source.bytes().positions(|b| b == b'\n'); + empty_lines.extend( + newlines + .tuple_windows() + .map(|(a, b)| InnerSpan::new(inner_span.start + a + 1, inner_span.start + b)) + .map(|inner_span| gap_span.from_inner(inner_span)), + ); + }, + // Ignore cfg_attr'd out attributes as they may contain empty lines, could also be from macro + // shenanigans + _ => return None, + } + } + + (!empty_lines.is_empty()).then_some(Self { + empty_lines, + has_comment, + next_stop, + prev_stop, + prev_chunk, + }) + } + + fn contiguous_empty_lines(&self) -> impl Iterator + '_ { + self.empty_lines + // The `+ BytePos(1)` means "next line", because each empty line span is "N:1-N:1". + .chunk_by(|a, b| a.hi() + BytePos(1) == b.lo()) + .map(|chunk| { + let first = chunk.first().expect("at least one empty line"); + let last = chunk.last().expect("at least one empty line"); + // The BytePos subtraction here is safe, as before an empty line, there must be at least one + // attribute/comment. The span needs to start at the end of the previous line. + first.with_lo(first.lo() - BytePos(1)).with_hi(last.hi()) + }) + } +} + +/// If the node the attributes/docs apply to is the first in the module/crate suggest converting +/// them to inner attributes/docs +fn suggest_inner(cx: &LateContext<'_>, diag: &mut Diag<'_, ()>, kind: StopKind, gaps: &[Gap<'_>]) { + let Some(owner) = cx.last_node_with_lint_attrs.as_owner() else { + return; + }; + let parent_desc = match cx.tcx.parent_hir_node(owner.into()) { + Node::Item(item) + if let ItemKind::Mod(parent_mod) = item.kind + && let [first, ..] = parent_mod.item_ids + && first.owner_id == owner => + { + "parent module" + }, + Node::Crate(crate_mod) + if let Some(first) = crate_mod + .item_ids + .iter() + .map(|&id| cx.tcx.hir().item(id)) + // skip prelude imports + .find(|item| !matches!(item.span.ctxt().outer_expn_data().kind, ExpnKind::AstPass(_))) + && first.owner_id == owner => + { + "crate" + }, + _ => return, + }; + + diag.multipart_suggestion_verbose( + match kind { + StopKind::Attr => format!("if the attribute should apply to the {parent_desc} use an inner attribute"), + StopKind::Doc(_) => format!("if the comment should document the {parent_desc} use an inner doc comment"), + }, + gaps.iter() + .flat_map(|gap| gap.prev_chunk) + .map(Stop::convert_to_inner) + .collect(), + Applicability::MaybeIncorrect, + ); +} + +fn check_gaps(cx: &LateContext<'_>, gaps: &[Gap<'_>]) -> bool { + let Some(first_gap) = gaps.first() else { + return false; + }; + let empty_lines = || gaps.iter().flat_map(|gap| gap.empty_lines.iter().copied()); + let contiguous_empty_lines = || gaps.iter().flat_map(Gap::contiguous_empty_lines); + let mut has_comment = false; + let mut has_attr = false; + for gap in gaps { + has_comment |= gap.has_comment; + if !has_attr { + has_attr = gap.prev_chunk.iter().any(|stop| stop.kind == StopKind::Attr); + } + } + let kind = first_gap.prev_stop.kind; + let (lint, kind_desc) = match kind { + StopKind::Attr => (EMPTY_LINE_AFTER_OUTER_ATTR, "outer attribute"), + StopKind::Doc(_) => (EMPTY_LINE_AFTER_DOC_COMMENTS, "doc comment"), + }; + let (lines, are, them) = if empty_lines().nth(1).is_some() { + ("lines", "are", "them") + } else { + ("line", "is", "it") + }; + span_lint_and_then( + cx, + lint, + first_gap.prev_stop.span.to(empty_lines().last().unwrap()), + format!("empty {lines} after {kind_desc}"), + |diag| { + if let Some(owner) = cx.last_node_with_lint_attrs.as_owner() { + let def_id = owner.to_def_id(); + let def_descr = cx.tcx.def_descr(def_id); + diag.span_label(cx.tcx.def_span(def_id), match kind { + StopKind::Attr => format!("the attribute applies to this {def_descr}"), + StopKind::Doc(_) => format!("the comment documents this {def_descr}"), + }); + } + + diag.multipart_suggestion_with_style( + format!("if the empty {lines} {are} unintentional remove {them}"), + contiguous_empty_lines() + .map(|empty_lines| (empty_lines, String::new())) + .collect(), + Applicability::MaybeIncorrect, + SuggestionStyle::HideCodeAlways, + ); + + if has_comment && kind.is_doc() { + // Likely doc comments that applied to some now commented out code + // + // /// Old docs for Foo + // // struct Foo; + + let mut suggestions = Vec::new(); + for stop in gaps.iter().flat_map(|gap| gap.prev_chunk) { + stop.comment_out(cx, &mut suggestions); + } + let name = match cx.tcx.hir().opt_name(cx.last_node_with_lint_attrs) { + Some(name) => format!("`{name}`"), + None => "this".into(), + }; + diag.multipart_suggestion_verbose( + format!("if the doc comment should not document {name} comment it out"), + suggestions, + Applicability::MaybeIncorrect, + ); + } else { + suggest_inner(cx, diag, kind, gaps); + } + + if kind == StopKind::Doc(CommentKind::Line) + && gaps + .iter() + .all(|gap| !gap.has_comment && gap.next_stop.kind == StopKind::Doc(CommentKind::Line)) + { + // Commentless empty gaps between line doc comments, possibly intended to be part of the markdown + + let indent = snippet_indent(cx, first_gap.prev_stop.span).unwrap_or_default(); + diag.multipart_suggestion_verbose( + format!("if the documentation should include the empty {lines} include {them} in the comment"), + empty_lines() + .map(|empty_line| (empty_line, format!("{indent}///"))) + .collect(), + Applicability::MaybeIncorrect, + ); + } + }, + ); + kind.is_doc() +} + +/// Returns `true` if [`EMPTY_LINE_AFTER_DOC_COMMENTS`] triggered, used to skip other doc comment +/// lints where they would be confusing +/// +/// [`EMPTY_LINE_AFTER_OUTER_ATTR`] is also here to share an implementation but does not return +/// `true` if it triggers +pub(super) fn check(cx: &LateContext<'_>, attrs: &[Attribute]) -> bool { + let mut outer = attrs + .iter() + .filter(|attr| attr.style == AttrStyle::Outer && !attr.span.from_expansion()) + .map(|attr| Stop::from_attr(cx, attr)) + .collect::>>() + .unwrap_or_default(); + + if outer.is_empty() { + return false; + } + + // Push a fake attribute Stop for the item itself so we check for gaps between the last outer + // attr/doc comment and the item they apply to + let span = cx.tcx.hir().span(cx.last_node_with_lint_attrs); + if !span.from_expansion() + && let Ok(line) = cx.tcx.sess.source_map().lookup_line(span.lo()) + { + outer.push(Stop { + span, + kind: StopKind::Attr, + first: line.line, + // last doesn't need to be accurate here, we don't compare it with anything + last: line.line, + }); + } + + let mut gaps = Vec::new(); + let mut last = 0; + for pos in outer + .array_windows() + .positions(|[a, b]| b.first.saturating_sub(a.last) > 1) + { + // we want to be after the first stop in the window + let pos = pos + 1; + if let Some(gap) = Gap::new(cx, &outer[last..pos], &outer[pos..]) { + last = pos; + gaps.push(gap); + } + } + + check_gaps(cx, &gaps) +} diff --git a/clippy_lints/src/doc/lazy_continuation.rs b/clippy_lints/src/doc/lazy_continuation.rs index 771bcac244187..f9e4a43c0e7a8 100644 --- a/clippy_lints/src/doc/lazy_continuation.rs +++ b/clippy_lints/src/doc/lazy_continuation.rs @@ -22,7 +22,6 @@ pub(super) fn check( range: Range, mut span: Span, containers: &[super::Container], - line_break_span: Span, ) { if doc[range.clone()].contains('\t') { // We don't do tab stops correctly. @@ -52,29 +51,6 @@ pub(super) fn check( "doc list item without indentation" }; span_lint_and_then(cx, DOC_LAZY_CONTINUATION, span, msg, |diag| { - let snippet = clippy_utils::source::snippet(cx, line_break_span, ""); - if snippet.chars().filter(|&c| c == '\n').count() > 1 - && let Some(doc_comment_start) = snippet.rfind('\n') - && let doc_comment = snippet[doc_comment_start..].trim() - && (doc_comment == "///" || doc_comment == "//!") - { - // suggest filling in a blank line - diag.span_suggestion_verbose( - line_break_span.shrink_to_lo(), - "if this should be its own paragraph, add a blank doc comment line", - format!("\n{doc_comment}"), - Applicability::MaybeIncorrect, - ); - if ccount > 0 || blockquote_level > 0 { - diag.help("if this not intended to be a quote at all, escape it with `\\>`"); - } else { - let indent = list_indentation - lcount; - diag.help(format!( - "if this is intended to be part of the list, indent {indent} spaces" - )); - } - return; - } if ccount == 0 && blockquote_level == 0 { // simpler suggestion style for indentation let indent = list_indentation - lcount; diff --git a/clippy_lints/src/doc/link_with_quotes.rs b/clippy_lints/src/doc/link_with_quotes.rs index 01191e811b003..1d4345e4541d7 100644 --- a/clippy_lints/src/doc/link_with_quotes.rs +++ b/clippy_lints/src/doc/link_with_quotes.rs @@ -3,7 +3,7 @@ use std::ops::Range; use clippy_utils::diagnostics::span_lint; use rustc_lint::LateContext; -use super::{Fragments, DOC_LINK_WITH_QUOTES}; +use super::{DOC_LINK_WITH_QUOTES, Fragments}; pub fn check(cx: &LateContext<'_>, trimmed_text: &str, range: Range, fragments: Fragments<'_>) { if ((trimmed_text.starts_with('\'') && trimmed_text.ends_with('\'')) diff --git a/clippy_lints/src/doc/missing_headers.rs b/clippy_lints/src/doc/missing_headers.rs index e3d7484072612..c9173030a55e1 100644 --- a/clippy_lints/src/doc/missing_headers.rs +++ b/clippy_lints/src/doc/missing_headers.rs @@ -5,7 +5,7 @@ use clippy_utils::{is_doc_hidden, return_ty}; use rustc_hir::{BodyId, FnSig, OwnerId, Safety}; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; pub fn check( cx: &LateContext<'_>, diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index 790579b21c902..0ae1fad569213 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -23,15 +23,16 @@ use rustc_middle::hir::nested_filter; use rustc_middle::lint::in_external_macro; use rustc_middle::ty; use rustc_resolve::rustdoc::{ - add_doc_fragment, attrs_to_doc_fragments, main_body_opts, source_span_for_markdown_range, span_of_fragments, - DocFragment, + DocFragment, add_doc_fragment, attrs_to_doc_fragments, main_body_opts, source_span_for_markdown_range, + span_of_fragments, }; use rustc_session::impl_lint_pass; use rustc_span::edition::Edition; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use std::ops::Range; use url::Url; +mod empty_line_after; mod link_with_quotes; mod markdown; mod missing_headers; @@ -449,13 +450,88 @@ declare_clippy_lint! { /// /// and probably spanning a many rows. /// struct Foo {} /// ``` - #[clippy::version = "1.81.0"] + #[clippy::version = "1.82.0"] pub TOO_LONG_FIRST_DOC_PARAGRAPH, style, "ensure that the first line of a documentation paragraph isn't too long" } -#[derive(Clone)] +declare_clippy_lint! { + /// ### What it does + /// Checks for empty lines after outer attributes + /// + /// ### Why is this bad? + /// The attribute may have meant to be an inner attribute (`#![attr]`). If + /// it was meant to be an outer attribute (`#[attr]`) then the empty line + /// should be removed + /// + /// ### Example + /// ```no_run + /// #[allow(dead_code)] + /// + /// fn not_quite_good_code() {} + /// ``` + /// + /// Use instead: + /// ```no_run + /// // Good (as inner attribute) + /// #![allow(dead_code)] + /// + /// fn this_is_fine() {} + /// + /// // or + /// + /// // Good (as outer attribute) + /// #[allow(dead_code)] + /// fn this_is_fine_too() {} + /// ``` + #[clippy::version = "pre 1.29.0"] + pub EMPTY_LINE_AFTER_OUTER_ATTR, + suspicious, + "empty line after outer attribute" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for empty lines after doc comments. + /// + /// ### Why is this bad? + /// The doc comment may have meant to be an inner doc comment, regular + /// comment or applied to some old code that is now commented out. If it was + /// intended to be a doc comment, then the empty line should be removed. + /// + /// ### Example + /// ```no_run + /// /// Some doc comment with a blank line after it. + /// + /// fn f() {} + /// + /// /// Docs for `old_code` + /// // fn old_code() {} + /// + /// fn new_code() {} + /// ``` + /// + /// Use instead: + /// ```no_run + /// //! Convert it to an inner doc comment + /// + /// // Or a regular comment + /// + /// /// Or remove the empty line + /// fn f() {} + /// + /// // /// Docs for `old_code` + /// // fn old_code() {} + /// + /// fn new_code() {} + /// ``` + #[clippy::version = "1.70.0"] + pub EMPTY_LINE_AFTER_DOC_COMMENTS, + suspicious, + "empty line after doc comments" +} + pub struct Documentation { valid_idents: FxHashSet, check_private_items: bool, @@ -482,6 +558,8 @@ impl_lint_pass!(Documentation => [ SUSPICIOUS_DOC_COMMENTS, EMPTY_DOCS, DOC_LAZY_CONTINUATION, + EMPTY_LINE_AFTER_OUTER_ATTR, + EMPTY_LINE_AFTER_DOC_COMMENTS, TOO_LONG_FIRST_DOC_PARAGRAPH, ]); @@ -612,12 +690,10 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs: &[ Some(("fake".into(), "fake".into())) } - if is_doc_hidden(attrs) { + if suspicious_doc_comments::check(cx, attrs) || empty_line_after::check(cx, attrs) || is_doc_hidden(attrs) { return None; } - suspicious_doc_comments::check(cx, attrs); - let (fragments, _) = attrs_to_doc_fragments( attrs.iter().filter_map(|attr| { if in_external_macro(cx.sess(), attr.span) { @@ -816,7 +892,6 @@ fn check_doc<'a, Events: Iterator, Range, attrs: &[Attribute]) { +pub fn check(cx: &LateContext<'_>, attrs: &[Attribute]) -> bool { let replacements: Vec<_> = collect_doc_replacements(attrs); if let Some((&(lo_span, _), &(hi_span, _))) = replacements.first().zip(replacements.last()) { @@ -24,6 +24,10 @@ pub fn check(cx: &LateContext<'_>, attrs: &[Attribute]) { ); }, ); + + true + } else { + false } } diff --git a/clippy_lints/src/doc/too_long_first_doc_paragraph.rs b/clippy_lints/src/doc/too_long_first_doc_paragraph.rs index 7bb3bb12f2ca9..0165d24c7df27 100644 --- a/clippy_lints/src/doc/too_long_first_doc_paragraph.rs +++ b/clippy_lints/src/doc/too_long_first_doc_paragraph.rs @@ -79,10 +79,17 @@ pub(super) fn check( && let new_span = first_span.with_hi(second_span.lo()).with_lo(first_span.hi()) && let Some(snippet) = snippet_opt(cx, new_span) { + let Some(first) = snippet_opt(cx, first_span) else { + return; + }; + let Some(comment_form) = first.get(..3) else { + return; + }; + diag.span_suggestion( new_span, "add an empty line", - format!("{snippet}///\n"), + format!("{snippet}{comment_form}{snippet}"), Applicability::MachineApplicable, ); } diff --git a/clippy_lints/src/entry.rs b/clippy_lints/src/entry.rs index 9c5100a8c1af8..70524e458c78b 100644 --- a/clippy_lints/src/entry.rs +++ b/clippy_lints/src/entry.rs @@ -1,17 +1,17 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{reindent_multiline, snippet_indent, snippet_with_applicability, snippet_with_context}; use clippy_utils::{ - can_move_expr_to_closure_no_visit, higher, is_expr_final_block_expr, is_expr_used_or_unified, - peel_hir_expr_while, SpanlessEq, + SpanlessEq, can_move_expr_to_closure_no_visit, higher, is_expr_final_block_expr, is_expr_used_or_unified, + peel_hir_expr_while, }; use core::fmt::{self, Write}; use rustc_errors::Applicability; use rustc_hir::hir_id::HirIdSet; -use rustc_hir::intravisit::{walk_expr, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{Block, Expr, ExprKind, HirId, Pat, Stmt, StmtKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::{sym, Span, SyntaxContext, DUMMY_SP}; +use rustc_span::{DUMMY_SP, Span, SyntaxContext, sym}; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/enum_clike.rs b/clippy_lints/src/enum_clike.rs index 4755cefe784c5..e9e9d00907ea0 100644 --- a/clippy_lints/src/enum_clike.rs +++ b/clippy_lints/src/enum_clike.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{mir_to_const, Constant}; +use clippy_utils::consts::{Constant, mir_to_const}; use clippy_utils::diagnostics::span_lint; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index 803606979410b..5588124e791ea 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -1,15 +1,15 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_hir; -use rustc_hir::{intravisit, AssocItemKind, Body, FnDecl, HirId, HirIdSet, Impl, ItemKind, Node, Pat, PatKind}; +use rustc_hir::{AssocItemKind, Body, FnDecl, HirId, HirIdSet, Impl, ItemKind, Node, Pat, PatKind, intravisit}; use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::FakeReadCause; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, TraitRef, Ty, TyCtxt}; use rustc_session::impl_lint_pass; +use rustc_span::Span; use rustc_span::def_id::LocalDefId; use rustc_span::symbol::kw; -use rustc_span::Span; use rustc_target::spec::abi::Abi; pub struct BoxedLocal { diff --git a/clippy_lints/src/excessive_bools.rs b/clippy_lints/src/excessive_bools.rs index 8f469efb1b502..c88fb50b5afc8 100644 --- a/clippy_lints/src/excessive_bools.rs +++ b/clippy_lints/src/excessive_bools.rs @@ -5,8 +5,8 @@ use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, FnDecl, Item, ItemKind, TraitFn, TraitItem, TraitItemKind, Ty}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; -use rustc_span::def_id::LocalDefId; use rustc_span::Span; +use rustc_span::def_id::LocalDefId; use rustc_target::spec::abi::Abi; declare_clippy_lint! { diff --git a/clippy_lints/src/excessive_nesting.rs b/clippy_lints/src/excessive_nesting.rs index 5154edd4399b2..ce0e0faa01489 100644 --- a/clippy_lints/src/excessive_nesting.rs +++ b/clippy_lints/src/excessive_nesting.rs @@ -2,7 +2,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::snippet; use rustc_ast::node_id::NodeSet; -use rustc_ast::visit::{walk_block, walk_item, Visitor}; +use rustc_ast::visit::{Visitor, walk_block, walk_item}; use rustc_ast::{Block, Crate, Inline, Item, ItemKind, ModKind, NodeId}; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; diff --git a/clippy_lints/src/explicit_write.rs b/clippy_lints/src/explicit_write.rs index 3c4a043a732b7..5b423a96918f6 100644 --- a/clippy_lints/src/explicit_write.rs +++ b/clippy_lints/src/explicit_write.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::macros::{format_args_inputs_span, FormatArgsStorage}; +use clippy_utils::macros::{FormatArgsStorage, format_args_inputs_span}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{is_expn_of, path_def_id}; use rustc_errors::Applicability; @@ -7,7 +7,7 @@ use rustc_hir::def::Res; use rustc_hir::{BindingMode, Block, BlockCheckMode, Expr, ExprKind, Node, PatKind, QPath, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; -use rustc_span::{sym, ExpnId}; +use rustc_span::{ExpnId, sym}; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/extra_unused_type_parameters.rs b/clippy_lints/src/extra_unused_type_parameters.rs index bfe4e253ae47f..bf9388b4a70fd 100644 --- a/clippy_lints/src/extra_unused_type_parameters.rs +++ b/clippy_lints/src/extra_unused_type_parameters.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; use clippy_utils::{is_from_proc_macro, trait_ref_of_method}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::Applicability; -use rustc_hir::intravisit::{walk_impl_item, walk_item, walk_param_bound, walk_ty, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_impl_item, walk_item, walk_param_bound, walk_ty}; use rustc_hir::{ BodyId, ExprKind, GenericBound, GenericParam, GenericParamKind, Generics, ImplItem, ImplItemKind, Item, ItemKind, PredicateOrigin, Ty, TyKind, WherePredicate, @@ -12,8 +12,8 @@ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::nested_filter; use rustc_middle::lint::in_external_macro; use rustc_session::impl_lint_pass; -use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::Span; +use rustc_span::def_id::{DefId, LocalDefId}; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/fallible_impl_from.rs b/clippy_lints/src/fallible_impl_from.rs index 0446943321aa9..747ea9a434464 100644 --- a/clippy_lints/src/fallible_impl_from.rs +++ b/clippy_lints/src/fallible_impl_from.rs @@ -6,7 +6,7 @@ use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::declare_lint_pass; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/field_scoped_visibility_modifiers.rs b/clippy_lints/src/field_scoped_visibility_modifiers.rs index 95b8e882da792..ba2b37fbf11a3 100644 --- a/clippy_lints/src/field_scoped_visibility_modifiers.rs +++ b/clippy_lints/src/field_scoped_visibility_modifiers.rs @@ -41,7 +41,7 @@ declare_clippy_lint! { /// } /// } /// ``` - #[clippy::version = "1.78.0"] + #[clippy::version = "1.81.0"] pub FIELD_SCOPED_VISIBILITY_MODIFIERS, restriction, "checks for usage of a scoped visibility modifier, like `pub(crate)`, on fields" diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index bf4bcabfe8911..8da6623f34de9 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::Constant::{Int, F32, F64}; +use clippy_utils::consts::Constant::{F32, F64, Int}; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::{ diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs index 4dedff69b9abd..5e3f6b6a13707 100644 --- a/clippy_lints/src/format.rs +++ b/clippy_lints/src/format.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::macros::{find_format_arg_expr, root_macro_call_first_node, FormatArgsStorage}; -use clippy_utils::source::{snippet_with_context, SpanRangeExt}; +use clippy_utils::macros::{FormatArgsStorage, find_format_arg_expr, root_macro_call_first_node}; +use clippy_utils::source::{SpanRangeExt, snippet_with_context}; use clippy_utils::sugg::Sugg; use rustc_ast::{FormatArgsPiece, FormatOptions, FormatTrait}; use rustc_errors::Applicability; @@ -8,7 +8,7 @@ use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::impl_lint_pass; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index 020c0c5440d93..83ab9f6557bdd 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -1,11 +1,12 @@ use arrayvec::ArrayVec; -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::is_diag_trait_item; use clippy_utils::macros::{ - find_format_arg_expr, format_arg_removal_span, format_placeholder_format_span, is_assert_macro, is_format_macro, - is_panic, matching_root_macro_call, root_macro_call_first_node, FormatArgsStorage, FormatParamUsage, MacroCall, + FormatArgsStorage, FormatParamUsage, MacroCall, find_format_arg_expr, format_arg_removal_span, + format_placeholder_format_span, is_assert_macro, is_format_macro, is_panic, matching_root_macro_call, + root_macro_call_first_node, }; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::{implements_trait, is_type_lang_item}; @@ -18,11 +19,11 @@ use rustc_errors::Applicability; use rustc_errors::SuggestionStyle::{CompletelyHidden, ShowCode}; use rustc_hir::{Expr, ExprKind, LangItem}; use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::ty::adjustment::{Adjust, Adjustment}; use rustc_middle::ty::Ty; +use rustc_middle::ty::adjustment::{Adjust, Adjustment}; use rustc_session::impl_lint_pass; use rustc_span::edition::Edition::Edition2021; -use rustc_span::{sym, Span, Symbol}; +use rustc_span::{Span, Symbol, sym}; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/format_impl.rs b/clippy_lints/src/format_impl.rs index 7cd12ac7db852..c196f404ce677 100644 --- a/clippy_lints/src/format_impl.rs +++ b/clippy_lints/src/format_impl.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; -use clippy_utils::macros::{find_format_arg_expr, is_format_macro, root_macro_call_first_node, FormatArgsStorage}; +use clippy_utils::macros::{FormatArgsStorage, find_format_arg_expr, is_format_macro, root_macro_call_first_node}; use clippy_utils::{get_parent_as_impl, is_diag_trait_item, path_to_local, peel_ref_operators}; use rustc_ast::{FormatArgsPiece, FormatTrait}; use rustc_errors::Applicability; @@ -7,7 +7,7 @@ use rustc_hir::{Expr, ExprKind, Impl, ImplItem, ImplItemKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; use rustc_span::symbol::kw; -use rustc_span::{sym, Symbol}; +use rustc_span::{Symbol, sym}; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/format_push_string.rs b/clippy_lints/src/format_push_string.rs index c6f03c3a7cf9a..8b1f86cbb9139 100644 --- a/clippy_lints/src/format_push_string.rs +++ b/clippy_lints/src/format_push_string.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ty::is_type_lang_item; use clippy_utils::higher; +use clippy_utils::ty::is_type_lang_item; use rustc_hir::{BinOpKind, Expr, ExprKind, LangItem, MatchSource}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs index b84bf7c821ce8..8832cd42dd98f 100644 --- a/clippy_lints/src/from_over_into.rs +++ b/clippy_lints/src/from_over_into.rs @@ -1,11 +1,11 @@ -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::span_is_local; use clippy_utils::path_def_id; use clippy_utils::source::SpanRangeExt; use rustc_errors::Applicability; -use rustc_hir::intravisit::{walk_path, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_path}; use rustc_hir::{ FnRetTy, GenericArg, GenericArgs, HirId, Impl, ImplItemKind, ImplItemRef, Item, ItemKind, PatKind, Path, PathSegment, Ty, TyKind, diff --git a/clippy_lints/src/from_str_radix_10.rs b/clippy_lints/src/from_str_radix_10.rs index e5fa67d696427..7361546153c27 100644 --- a/clippy_lints/src/from_str_radix_10.rs +++ b/clippy_lints/src/from_str_radix_10.rs @@ -3,7 +3,7 @@ use clippy_utils::sugg::Sugg; use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; use clippy_utils::{is_in_const_context, is_integer_literal}; use rustc_errors::Applicability; -use rustc_hir::{def, Expr, ExprKind, LangItem, PrimTy, QPath, TyKind}; +use rustc_hir::{Expr, ExprKind, LangItem, PrimTy, QPath, TyKind, def}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::Ty; use rustc_session::declare_lint_pass; diff --git a/clippy_lints/src/functions/mod.rs b/clippy_lints/src/functions/mod.rs index 0f48941783b26..ba8a459c917e6 100644 --- a/clippy_lints/src/functions/mod.rs +++ b/clippy_lints/src/functions/mod.rs @@ -14,8 +14,8 @@ use rustc_hir::intravisit; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::TyCtxt; use rustc_session::impl_lint_pass; -use rustc_span::def_id::{DefIdSet, LocalDefId}; use rustc_span::Span; +use rustc_span::def_id::{DefIdSet, LocalDefId}; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/functions/must_use.rs b/clippy_lints/src/functions/must_use.rs index 9382812104706..cfd11e9339fb7 100644 --- a/clippy_lints/src/functions/must_use.rs +++ b/clippy_lints/src/functions/must_use.rs @@ -8,7 +8,7 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, Ty}; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use clippy_utils::attrs::is_proc_macro; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; 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 1aeefe73cf68f..c2b40ae01d4cc 100644 --- a/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs +++ b/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs @@ -1,4 +1,4 @@ -use rustc_hir::{self as hir, intravisit, HirId, HirIdSet}; +use rustc_hir::{self as hir, HirId, HirIdSet, intravisit}; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_span::def_id::LocalDefId; diff --git a/clippy_lints/src/functions/renamed_function_params.rs b/clippy_lints/src/functions/renamed_function_params.rs index c7de0385c021b..ac2e866e4ff7e 100644 --- a/clippy_lints/src/functions/renamed_function_params.rs +++ b/clippy_lints/src/functions/renamed_function_params.rs @@ -4,8 +4,8 @@ use rustc_hir::def_id::{DefId, DefIdSet}; use rustc_hir::hir_id::OwnerId; use rustc_hir::{Impl, ImplItem, ImplItemKind, ImplItemRef, ItemKind, Node, TraitRef}; use rustc_lint::LateContext; -use rustc_span::symbol::{kw, Ident, Symbol}; use rustc_span::Span; +use rustc_span::symbol::{Ident, Symbol, kw}; use super::RENAMED_FUNCTION_PARAMS; diff --git a/clippy_lints/src/functions/result.rs b/clippy_lints/src/functions/result.rs index c3a0b40a677aa..d4eaa1663208c 100644 --- a/clippy_lints/src/functions/result.rs +++ b/clippy_lints/src/functions/result.rs @@ -3,11 +3,11 @@ use rustc_hir as hir; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, Ty}; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; use clippy_utils::trait_ref_of_method; -use clippy_utils::ty::{approx_ty_size, is_type_diagnostic_item, AdtVariantInfo}; +use clippy_utils::ty::{AdtVariantInfo, approx_ty_size, is_type_diagnostic_item}; use super::{RESULT_LARGE_ERR, RESULT_UNIT_ERR}; diff --git a/clippy_lints/src/future_not_send.rs b/clippy_lints/src/future_not_send.rs index 9488ba7568656..a4dbe134f3650 100644 --- a/clippy_lints/src/future_not_send.rs +++ b/clippy_lints/src/future_not_send.rs @@ -8,7 +8,7 @@ use rustc_middle::ty::print::PrintTraitRefExt; use rustc_middle::ty::{self, AliasTy, ClauseKind, PredicateKind}; use rustc_session::declare_lint_pass; use rustc_span::def_id::LocalDefId; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::traits::{self, FulfillmentError, ObligationCtxt}; diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs index 0bca53c1536de..d63c18c0edaab 100644 --- a/clippy_lints/src/if_then_some_else_none.rs +++ b/clippy_lints/src/if_then_some_else_none.rs @@ -1,5 +1,5 @@ -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::source::snippet_with_context; @@ -105,6 +105,8 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { snippet_with_context(cx, first_stmt.span.until(then_arg.span), ctxt, "..", &mut app); let closure = if method_name == "then" { "|| " } else { "" }; format!("{closure} {{ {block_snippet}; {arg_snip} }}") + } else if method_name == "then" { + (std::borrow::Cow::Borrowed("|| ") + arg_snip).into_owned() } else { arg_snip.into_owned() }; diff --git a/clippy_lints/src/implicit_hasher.rs b/clippy_lints/src/implicit_hasher.rs index e56f33f8dcfe2..f683925145a28 100644 --- a/clippy_lints/src/implicit_hasher.rs +++ b/clippy_lints/src/implicit_hasher.rs @@ -3,18 +3,18 @@ use std::collections::BTreeMap; use rustc_errors::{Applicability, Diag}; use rustc_hir as hir; -use rustc_hir::intravisit::{walk_body, walk_expr, walk_inf, walk_ty, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_body, walk_expr, walk_inf, walk_ty}; use rustc_hir::{Body, Expr, ExprKind, GenericArg, Item, ItemKind, QPath, TyKind}; use rustc_hir_analysis::lower_ty; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{Ty, TypeckResults}; use rustc_session::declare_lint_pass; -use rustc_span::symbol::sym; use rustc_span::Span; +use rustc_span::symbol::sym; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::{snippet, IntoSpan, SpanRangeExt}; +use clippy_utils::source::{IntoSpan, SpanRangeExt, snippet}; use clippy_utils::ty::is_type_diagnostic_item; declare_clippy_lint! { diff --git a/clippy_lints/src/implicit_saturating_sub.rs b/clippy_lints/src/implicit_saturating_sub.rs index 127c73ed637b2..f4a64f5c20b44 100644 --- a/clippy_lints/src/implicit_saturating_sub.rs +++ b/clippy_lints/src/implicit_saturating_sub.rs @@ -1,11 +1,17 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::{higher, is_integer_literal, peel_blocks_with_stmt, SpanlessEq}; +use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::source::snippet_opt; +use clippy_utils::{ + SpanlessEq, higher, is_in_const_context, is_integer_literal, path_to_local, peel_blocks, peel_blocks_with_stmt, +}; use rustc_ast::ast::LitKind; use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Expr, ExprKind, QPath}; +use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, HirId, QPath}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::declare_lint_pass; +use rustc_session::impl_lint_pass; +use rustc_span::Span; declare_clippy_lint! { /// ### What it does @@ -39,7 +45,49 @@ declare_clippy_lint! { "Perform saturating subtraction instead of implicitly checking lower bound of data type" } -declare_lint_pass!(ImplicitSaturatingSub => [IMPLICIT_SATURATING_SUB]); +declare_clippy_lint! { + /// ### What it does + /// Checks for comparisons between integers, followed by subtracting the greater value from the + /// lower one. + /// + /// ### Why is this bad? + /// This could result in an underflow and is most likely not what the user wants. If this was + /// intended to be a saturated subtraction, consider using the `saturating_sub` method directly. + /// + /// ### Example + /// ```no_run + /// let a = 12u32; + /// let b = 13u32; + /// + /// let result = if a > b { b - a } else { 0 }; + /// ``` + /// + /// Use instead: + /// ```no_run + /// let a = 12u32; + /// let b = 13u32; + /// + /// let result = a.saturating_sub(b); + /// ``` + #[clippy::version = "1.44.0"] + pub INVERTED_SATURATING_SUB, + correctness, + "Check if a variable is smaller than another one and still subtract from it even if smaller" +} + +pub struct ImplicitSaturatingSub { + msrv: Msrv, +} + +impl_lint_pass!(ImplicitSaturatingSub => [IMPLICIT_SATURATING_SUB, INVERTED_SATURATING_SUB]); + +impl ImplicitSaturatingSub { + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } + } +} impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { @@ -50,73 +98,260 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub { // Check if the conditional expression is a binary operation && let ExprKind::Binary(ref cond_op, cond_left, cond_right) = cond.kind + { + check_with_condition(cx, expr, cond_op.node, cond_left, cond_right, then); + } else if let Some(higher::If { + cond, + then: if_block, + r#else: Some(else_block), + }) = higher::If::hir(expr) + && let ExprKind::Binary(ref cond_op, cond_left, cond_right) = cond.kind + { + check_manual_check( + cx, expr, cond_op, cond_left, cond_right, if_block, else_block, &self.msrv, + ); + } + } + + extract_msrv_attr!(LateContext); +} - // Ensure that the binary operator is >, !=, or < - && (BinOpKind::Ne == cond_op.node || BinOpKind::Gt == cond_op.node || BinOpKind::Lt == cond_op.node) +#[allow(clippy::too_many_arguments)] +fn check_manual_check<'tcx>( + cx: &LateContext<'tcx>, + expr: &Expr<'tcx>, + condition: &BinOp, + left_hand: &Expr<'tcx>, + right_hand: &Expr<'tcx>, + if_block: &Expr<'tcx>, + else_block: &Expr<'tcx>, + msrv: &Msrv, +) { + let ty = cx.typeck_results().expr_ty(left_hand); + if ty.is_numeric() && !ty.is_signed() { + match condition.node { + BinOpKind::Gt | BinOpKind::Ge => check_gt( + cx, + condition.span, + expr.span, + left_hand, + right_hand, + if_block, + else_block, + msrv, + ), + BinOpKind::Lt | BinOpKind::Le => check_gt( + cx, + condition.span, + expr.span, + right_hand, + left_hand, + if_block, + else_block, + msrv, + ), + _ => {}, + } + } +} - // Check if assign operation is done - && let Some(target) = subtracts_one(cx, then) +#[allow(clippy::too_many_arguments)] +fn check_gt( + cx: &LateContext<'_>, + condition_span: Span, + expr_span: Span, + big_var: &Expr<'_>, + little_var: &Expr<'_>, + if_block: &Expr<'_>, + else_block: &Expr<'_>, + msrv: &Msrv, +) { + if let Some(big_var) = Var::new(big_var) + && let Some(little_var) = Var::new(little_var) + { + check_subtraction( + cx, + condition_span, + expr_span, + big_var, + little_var, + if_block, + else_block, + msrv, + ); + } +} - // Extracting out the variable name - && let ExprKind::Path(QPath::Resolved(_, ares_path)) = target.kind +struct Var { + span: Span, + hir_id: HirId, +} + +impl Var { + fn new(expr: &Expr<'_>) -> Option { + path_to_local(expr).map(|hir_id| Self { + span: expr.span, + hir_id, + }) + } +} + +#[allow(clippy::too_many_arguments)] +fn check_subtraction( + cx: &LateContext<'_>, + condition_span: Span, + expr_span: Span, + big_var: Var, + little_var: Var, + if_block: &Expr<'_>, + else_block: &Expr<'_>, + msrv: &Msrv, +) { + let if_block = peel_blocks(if_block); + let else_block = peel_blocks(else_block); + if is_integer_literal(if_block, 0) { + // We need to check this case as well to prevent infinite recursion. + if is_integer_literal(else_block, 0) { + // Well, seems weird but who knows? + return; + } + // If the subtraction is done in the `else` block, then we need to also revert the two + // variables as it means that the check was reverted too. + check_subtraction( + cx, + condition_span, + expr_span, + little_var, + big_var, + else_block, + if_block, + msrv, + ); + return; + } + if is_integer_literal(else_block, 0) + && let ExprKind::Binary(op, left, right) = if_block.kind + && let BinOpKind::Sub = op.node + { + let local_left = path_to_local(left); + let local_right = path_to_local(right); + if Some(big_var.hir_id) == local_left && Some(little_var.hir_id) == local_right { + // This part of the condition is voluntarily split from the one before to ensure that + // if `snippet_opt` fails, it won't try the next conditions. + if let Some(big_var_snippet) = snippet_opt(cx, big_var.span) + && let Some(little_var_snippet) = snippet_opt(cx, little_var.span) + && (!is_in_const_context(cx) || msrv.meets(msrvs::SATURATING_SUB_CONST)) + { + span_lint_and_sugg( + cx, + IMPLICIT_SATURATING_SUB, + expr_span, + "manual arithmetic check found", + "replace it with", + format!("{big_var_snippet}.saturating_sub({little_var_snippet})"), + Applicability::MachineApplicable, + ); + } + } else if Some(little_var.hir_id) == local_left + && Some(big_var.hir_id) == local_right + && let Some(big_var_snippet) = snippet_opt(cx, big_var.span) + && let Some(little_var_snippet) = snippet_opt(cx, little_var.span) { - // Handle symmetric conditions in the if statement - let (cond_var, cond_num_val) = if SpanlessEq::new(cx).eq_expr(cond_left, target) { - if BinOpKind::Gt == cond_op.node || BinOpKind::Ne == cond_op.node { - (cond_left, cond_right) - } else { - return; - } - } else if SpanlessEq::new(cx).eq_expr(cond_right, target) { - if BinOpKind::Lt == cond_op.node || BinOpKind::Ne == cond_op.node { - (cond_right, cond_left) - } else { - return; - } + span_lint_and_then( + cx, + INVERTED_SATURATING_SUB, + condition_span, + "inverted arithmetic check before subtraction", + |diag| { + diag.span_note( + if_block.span, + format!("this subtraction underflows when `{little_var_snippet} < {big_var_snippet}`"), + ); + diag.span_suggestion( + if_block.span, + "try replacing it with", + format!("{big_var_snippet} - {little_var_snippet}"), + Applicability::MaybeIncorrect, + ); + }, + ); + } + } +} + +fn check_with_condition<'tcx>( + cx: &LateContext<'tcx>, + expr: &Expr<'tcx>, + cond_op: BinOpKind, + cond_left: &Expr<'tcx>, + cond_right: &Expr<'tcx>, + then: &Expr<'tcx>, +) { + // Ensure that the binary operator is >, !=, or < + if (BinOpKind::Ne == cond_op || BinOpKind::Gt == cond_op || BinOpKind::Lt == cond_op) + + // Check if assign operation is done + && let Some(target) = subtracts_one(cx, then) + + // Extracting out the variable name + && let ExprKind::Path(QPath::Resolved(_, ares_path)) = target.kind + { + // Handle symmetric conditions in the if statement + let (cond_var, cond_num_val) = if SpanlessEq::new(cx).eq_expr(cond_left, target) { + if BinOpKind::Gt == cond_op || BinOpKind::Ne == cond_op { + (cond_left, cond_right) } else { return; - }; - - // Check if the variable in the condition statement is an integer - if !cx.typeck_results().expr_ty(cond_var).is_integral() { + } + } else if SpanlessEq::new(cx).eq_expr(cond_right, target) { + if BinOpKind::Lt == cond_op || BinOpKind::Ne == cond_op { + (cond_right, cond_left) + } else { return; } + } else { + return; + }; - // Get the variable name - let var_name = ares_path.segments[0].ident.name.as_str(); - match cond_num_val.kind { - ExprKind::Lit(cond_lit) => { - // Check if the constant is zero - if let LitKind::Int(Pu128(0), _) = cond_lit.node { - if cx.typeck_results().expr_ty(cond_left).is_signed() { - } else { - print_lint_and_sugg(cx, var_name, expr); - }; - } - }, - ExprKind::Path(QPath::TypeRelative(_, name)) => { - if name.ident.as_str() == "MIN" - && let Some(const_id) = cx.typeck_results().type_dependent_def_id(cond_num_val.hir_id) - && let Some(impl_id) = cx.tcx.impl_of_method(const_id) - && let None = cx.tcx.impl_trait_ref(impl_id) // An inherent impl - && cx.tcx.type_of(impl_id).instantiate_identity().is_integral() - { - print_lint_and_sugg(cx, var_name, expr); - } - }, - ExprKind::Call(func, []) => { - if let ExprKind::Path(QPath::TypeRelative(_, name)) = func.kind - && name.ident.as_str() == "min_value" - && let Some(func_id) = cx.typeck_results().type_dependent_def_id(func.hir_id) - && let Some(impl_id) = cx.tcx.impl_of_method(func_id) - && let None = cx.tcx.impl_trait_ref(impl_id) // An inherent impl - && cx.tcx.type_of(impl_id).instantiate_identity().is_integral() - { + // Check if the variable in the condition statement is an integer + if !cx.typeck_results().expr_ty(cond_var).is_integral() { + return; + } + + // Get the variable name + let var_name = ares_path.segments[0].ident.name.as_str(); + match cond_num_val.kind { + ExprKind::Lit(cond_lit) => { + // Check if the constant is zero + if let LitKind::Int(Pu128(0), _) = cond_lit.node { + if cx.typeck_results().expr_ty(cond_left).is_signed() { + } else { print_lint_and_sugg(cx, var_name, expr); - } - }, - _ => (), - } + }; + } + }, + ExprKind::Path(QPath::TypeRelative(_, name)) => { + if name.ident.as_str() == "MIN" + && let Some(const_id) = cx.typeck_results().type_dependent_def_id(cond_num_val.hir_id) + && let Some(impl_id) = cx.tcx.impl_of_method(const_id) + && let None = cx.tcx.impl_trait_ref(impl_id) // An inherent impl + && cx.tcx.type_of(impl_id).instantiate_identity().is_integral() + { + print_lint_and_sugg(cx, var_name, expr); + } + }, + ExprKind::Call(func, []) => { + if let ExprKind::Path(QPath::TypeRelative(_, name)) = func.kind + && name.ident.as_str() == "min_value" + && let Some(func_id) = cx.typeck_results().type_dependent_def_id(func.hir_id) + && let Some(impl_id) = cx.tcx.impl_of_method(func_id) + && let None = cx.tcx.impl_trait_ref(impl_id) // An inherent impl + && cx.tcx.type_of(impl_id).instantiate_identity().is_integral() + { + print_lint_and_sugg(cx, var_name, expr); + } + }, + _ => (), } } } diff --git a/clippy_lints/src/incompatible_msrv.rs b/clippy_lints/src/incompatible_msrv.rs index 2ad045e12686c..0b3a6ee1beaeb 100644 --- a/clippy_lints/src/incompatible_msrv.rs +++ b/clippy_lints/src/incompatible_msrv.rs @@ -1,5 +1,5 @@ -use clippy_config::msrvs::Msrv; use clippy_config::Conf; +use clippy_config::msrvs::Msrv; use clippy_utils::diagnostics::span_lint; use clippy_utils::is_in_test; use rustc_attr::{StabilityLevel, StableSince}; @@ -7,7 +7,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_hir::{Expr, ExprKind, HirId}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::TyCtxt; -use rustc_session::{impl_lint_pass, RustcVersion}; +use rustc_session::{RustcVersion, impl_lint_pass}; use rustc_span::def_id::DefId; use rustc_span::{ExpnKind, Span}; diff --git a/clippy_lints/src/index_refutable_slice.rs b/clippy_lints/src/index_refutable_slice.rs index 2f9661c9ea385..39afb6810b8e2 100644 --- a/clippy_lints/src/index_refutable_slice.rs +++ b/clippy_lints/src/index_refutable_slice.rs @@ -1,5 +1,5 @@ -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::IfLet; @@ -8,14 +8,14 @@ use clippy_utils::{is_expn_of, is_lint_allowed, path_to_local}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::HirId; +use rustc_hir::intravisit::{self, Visitor}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; use rustc_middle::ty; use rustc_session::impl_lint_pass; -use rustc_span::symbol::Ident; use rustc_span::Span; +use rustc_span::symbol::Ident; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/ineffective_open_options.rs b/clippy_lints/src/ineffective_open_options.rs index af5b1f9573991..3a28553b55e25 100644 --- a/clippy_lints/src/ineffective_open_options.rs +++ b/clippy_lints/src/ineffective_open_options.rs @@ -7,7 +7,7 @@ use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::declare_lint_pass; -use rustc_span::{sym, BytePos, Span}; +use rustc_span::{BytePos, Span, sym}; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/instant_subtraction.rs b/clippy_lints/src/instant_subtraction.rs index 6d6820311b679..66931a7f98c36 100644 --- a/clippy_lints/src/instant_subtraction.rs +++ b/clippy_lints/src/instant_subtraction.rs @@ -1,5 +1,5 @@ -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_context; use clippy_utils::sugg::Sugg; diff --git a/clippy_lints/src/item_name_repetitions.rs b/clippy_lints/src/item_name_repetitions.rs index 74bf82f58bdcf..66a8a3167a4f2 100644 --- a/clippy_lints/src/item_name_repetitions.rs +++ b/clippy_lints/src/item_name_repetitions.rs @@ -8,8 +8,8 @@ use rustc_data_structures::fx::FxHashSet; use rustc_hir::{EnumDef, FieldDef, Item, ItemKind, OwnerId, Variant, VariantData}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; -use rustc_span::symbol::Symbol; use rustc_span::Span; +use rustc_span::symbol::Symbol; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/items_after_test_module.rs b/clippy_lints/src/items_after_test_module.rs index 8caa484bb9934..1ac549b74ac66 100644 --- a/clippy_lints/src/items_after_test_module.rs +++ b/clippy_lints/src/items_after_test_module.rs @@ -6,7 +6,7 @@ use rustc_hir::{HirId, Item, ItemKind, Mod}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::hygiene::AstPass; -use rustc_span::{sym, ExpnKind}; +use rustc_span::{ExpnKind, sym}; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/iter_not_returning_iterator.rs b/clippy_lints/src/iter_not_returning_iterator.rs index ba0cd5d6eb359..b19b348c7430d 100644 --- a/clippy_lints/src/iter_not_returning_iterator.rs +++ b/clippy_lints/src/iter_not_returning_iterator.rs @@ -4,7 +4,7 @@ use rustc_hir::def_id::LocalDefId; use rustc_hir::{FnSig, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::{sym, Symbol}; +use rustc_span::{Symbol, sym}; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/iter_over_hash_type.rs b/clippy_lints/src/iter_over_hash_type.rs index f162948bb4457..5131f5b7269b6 100644 --- a/clippy_lints/src/iter_over_hash_type.rs +++ b/clippy_lints/src/iter_over_hash_type.rs @@ -55,7 +55,9 @@ impl LateLintPass<'_> for IterOverHashType { if let Some(for_loop) = ForLoop::hir(expr) && !for_loop.body.span.from_expansion() && let ty = cx.typeck_results().expr_ty(for_loop.arg).peel_refs() - && hash_iter_tys.into_iter().any(|sym| is_type_diagnostic_item(cx, ty, sym)) + && hash_iter_tys + .into_iter() + .any(|sym| is_type_diagnostic_item(cx, ty, sym)) { span_lint( cx, diff --git a/clippy_lints/src/large_enum_variant.rs b/clippy_lints/src/large_enum_variant.rs index 2f027c1170728..923089c722322 100644 --- a/clippy_lints/src/large_enum_variant.rs +++ b/clippy_lints/src/large_enum_variant.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::{approx_ty_size, is_copy, AdtVariantInfo}; +use clippy_utils::ty::{AdtVariantInfo, approx_ty_size, is_copy}; use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/large_stack_arrays.rs b/clippy_lints/src/large_stack_arrays.rs index 15a75b0608965..0f061d6de504f 100644 --- a/clippy_lints/src/large_stack_arrays.rs +++ b/clippy_lints/src/large_stack_arrays.rs @@ -8,7 +8,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, ConstKind}; use rustc_session::impl_lint_pass; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/legacy_numeric_constants.rs b/clippy_lints/src/legacy_numeric_constants.rs index ccab1e27d3b0c..e17d6213679a6 100644 --- a/clippy_lints/src/legacy_numeric_constants.rs +++ b/clippy_lints/src/legacy_numeric_constants.rs @@ -1,5 +1,5 @@ -use clippy_config::msrvs::{Msrv, NUMERIC_ASSOCIATED_CONSTANTS}; use clippy_config::Conf; +use clippy_config::msrvs::{Msrv, NUMERIC_ASSOCIATED_CONSTANTS}; use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::{get_parent_expr, is_from_proc_macro}; use hir::def_id::DefId; @@ -10,7 +10,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::impl_lint_pass; use rustc_span::symbol::kw; -use rustc_span::{sym, Symbol}; +use rustc_span::{Symbol, sym}; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 0bc7fc2dd9d1e..3cd5f76e6b674 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::source::{snippet_with_context, SpanRangeExt}; -use clippy_utils::sugg::{has_enclosing_paren, Sugg}; -use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed, peel_ref_operators}; +use clippy_utils::source::{SpanRangeExt, snippet_with_context}; +use clippy_utils::sugg::{Sugg, has_enclosing_paren}; +use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed, is_trait_method, peel_ref_operators}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::def::Res; @@ -185,6 +185,19 @@ impl<'tcx> LateLintPass<'tcx> for LenZero { ); } + if let ExprKind::MethodCall(method, lhs_expr, [rhs_expr], _) = expr.kind + && is_trait_method(cx, expr, sym::PartialEq) + && !expr.span.from_expansion() + { + check_empty_expr( + cx, + expr.span, + lhs_expr, + peel_ref_operators(cx, rhs_expr), + (method.ident.name == sym::ne).then_some("!").unwrap_or_default(), + ); + } + if let ExprKind::Binary(Spanned { node: cmp, .. }, left, right) = expr.kind && !expr.span.from_expansion() { diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 2ac06b360bea4..1d41f568f3785 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -203,9 +203,11 @@ mod manual_assert; mod manual_async_fn; mod manual_bits; mod manual_clamp; +mod manual_div_ceil; mod manual_float_methods; mod manual_hash_one; mod manual_is_ascii_check; +mod manual_is_power_of_two; mod manual_let_else; mod manual_main_separator_str; mod manual_non_exhaustive; @@ -271,6 +273,7 @@ mod non_copy_const; mod non_expressive_names; mod non_octal_unix_permissions; mod non_send_fields_in_send_ty; +mod non_zero_suggestions; mod nonstandard_macro_braces; mod octal_escapes; mod only_used_in_recursion; @@ -287,6 +290,7 @@ mod pass_by_ref_or_value; mod pathbuf_init_then_push; mod pattern_type_mismatch; mod permissions_set_readonly_false; +mod pointers_in_nomem_asm_block; mod precedence; mod ptr; mod ptr_offset_with_cast; @@ -372,6 +376,7 @@ mod unused_peekable; mod unused_result_ok; mod unused_rounding; mod unused_self; +mod unused_trait_names; mod unused_unit; mod unwrap; mod unwrap_in_result; @@ -386,9 +391,10 @@ mod write; mod zero_div_zero; mod zero_repeat_side_effects; mod zero_sized_map_values; +mod zombie_processes; // end lints modules, do not remove this comment, it’s used in `update_lints` -use clippy_config::{get_configuration_metadata, Conf}; +use clippy_config::{Conf, get_configuration_metadata}; use clippy_utils::macros::FormatArgsStorage; use rustc_data_structures::fx::FxHashSet; use rustc_lint::{Lint, LintId}; @@ -623,7 +629,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(unit_return_expecting_ord::UnitReturnExpectingOrd)); store.register_late_pass(|_| Box::new(strings::StringAdd)); store.register_late_pass(|_| Box::new(implicit_return::ImplicitReturn)); - store.register_late_pass(|_| Box::new(implicit_saturating_sub::ImplicitSaturatingSub)); + store.register_late_pass(move |_| Box::new(implicit_saturating_sub::ImplicitSaturatingSub::new(conf))); store.register_late_pass(|_| Box::new(default_numeric_fallback::DefaultNumericFallback)); store.register_late_pass(|_| Box::new(inconsistent_struct_constructor::InconsistentStructConstructor)); store.register_late_pass(|_| Box::new(non_octal_unix_permissions::NonOctalUnixPermissions)); @@ -632,8 +638,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { let format_args = format_args_storage.clone(); store.register_late_pass(move |_| Box::new(methods::Methods::new(conf, format_args.clone()))); store.register_late_pass(move |_| Box::new(matches::Matches::new(conf))); - store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveStruct::new(conf))); - store.register_late_pass(move |_| Box::new(manual_non_exhaustive::ManualNonExhaustiveEnum::new(conf))); + store.register_late_pass(move |_| Box::new(manual_non_exhaustive::ManualNonExhaustive::new(conf))); store.register_late_pass(move |_| Box::new(manual_strip::ManualStrip::new(conf))); store.register_early_pass(move || Box::new(redundant_static_lifetimes::RedundantStaticLifetimes::new(conf))); store.register_early_pass(move || Box::new(redundant_field_names::RedundantFieldNames::new(conf))); @@ -933,5 +938,11 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(set_contains_or_insert::SetContainsOrInsert)); store.register_early_pass(|| Box::new(byte_char_slices::ByteCharSlice)); store.register_early_pass(|| Box::new(cfg_not_test::CfgNotTest)); + store.register_late_pass(|_| Box::new(zombie_processes::ZombieProcesses)); + store.register_late_pass(|_| Box::new(pointers_in_nomem_asm_block::PointersInNomemAsmBlock)); + store.register_late_pass(move |_| Box::new(manual_div_ceil::ManualDivCeil::new(conf))); + store.register_late_pass(|_| Box::new(manual_is_power_of_two::ManualIsPowerOfTwo)); + store.register_late_pass(|_| Box::new(non_zero_suggestions::NonZeroSuggestions)); + store.register_late_pass(move |_| Box::new(unused_trait_names::UnusedTraitNames::new(conf))); // 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 ba34af9c100ea..755afe959b37a 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -3,25 +3,25 @@ use clippy_utils::trait_ref_of_method; use itertools::Itertools; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::Applicability; +use rustc_hir::FnRetTy::Return; use rustc_hir::intravisit::nested_filter::{self as hir_nested_filter, NestedFilter}; use rustc_hir::intravisit::{ - walk_fn_decl, walk_generic_param, walk_generics, walk_impl_item_ref, walk_item, walk_param_bound, - walk_poly_trait_ref, walk_trait_ref, walk_ty, Visitor, + Visitor, walk_fn_decl, walk_generic_param, walk_generics, walk_impl_item_ref, walk_item, walk_param_bound, + walk_poly_trait_ref, walk_trait_ref, walk_ty, }; -use rustc_hir::FnRetTy::Return; use rustc_hir::{ - lang_items, BareFnTy, BodyId, FnDecl, FnSig, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, - Impl, ImplItem, ImplItemKind, Item, ItemKind, Lifetime, LifetimeName, LifetimeParamKind, Node, PolyTraitRef, - PredicateOrigin, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WherePredicate, + BareFnTy, BodyId, FnDecl, FnSig, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, Impl, + ImplItem, ImplItemKind, Item, ItemKind, Lifetime, LifetimeName, LifetimeParamKind, Node, PolyTraitRef, + PredicateOrigin, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WherePredicate, lang_items, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::map::Map; use rustc_middle::hir::nested_filter as middle_nested_filter; use rustc_middle::lint::in_external_macro; use rustc_session::declare_lint_pass; -use rustc_span::def_id::LocalDefId; -use rustc_span::symbol::{kw, Ident, Symbol}; use rustc_span::Span; +use rustc_span::def_id::LocalDefId; +use rustc_span::symbol::{Ident, Symbol, kw}; use std::ops::ControlFlow; declare_clippy_lint! { diff --git a/clippy_lints/src/loops/explicit_counter_loop.rs b/clippy_lints/src/loops/explicit_counter_loop.rs index 73a23615c2dc7..9aa4d2f0adc2c 100644 --- a/clippy_lints/src/loops/explicit_counter_loop.rs +++ b/clippy_lints/src/loops/explicit_counter_loop.rs @@ -1,4 +1,4 @@ -use super::{make_iterator_snippet, IncrementVisitor, InitializeVisitor, EXPLICIT_COUNTER_LOOP}; +use super::{EXPLICIT_COUNTER_LOOP, IncrementVisitor, InitializeVisitor, make_iterator_snippet}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{get_enclosing_block, is_integer_const}; diff --git a/clippy_lints/src/loops/infinite_loop.rs b/clippy_lints/src/loops/infinite_loop.rs index 5b5bb88c17908..858e3be5093ef 100644 --- a/clippy_lints/src/loops/infinite_loop.rs +++ b/clippy_lints/src/loops/infinite_loop.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::{fn_def_id, is_from_proc_macro, is_lint_allowed}; -use hir::intravisit::{walk_expr, Visitor}; +use hir::intravisit::{Visitor, walk_expr}; use hir::{Expr, ExprKind, FnRetTy, FnSig, Node}; use rustc_ast::Label; use rustc_errors::Applicability; diff --git a/clippy_lints/src/loops/manual_find.rs b/clippy_lints/src/loops/manual_find.rs index b27528c11d488..bfe2e68b5d1fa 100644 --- a/clippy_lints/src/loops/manual_find.rs +++ b/clippy_lints/src/loops/manual_find.rs @@ -1,5 +1,5 @@ -use super::utils::make_iterator_snippet; use super::MANUAL_FIND; +use super::utils::make_iterator_snippet; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::implements_trait; diff --git a/clippy_lints/src/loops/manual_flatten.rs b/clippy_lints/src/loops/manual_flatten.rs index dbc094a6d73fc..366c310592f57 100644 --- a/clippy_lints/src/loops/manual_flatten.rs +++ b/clippy_lints/src/loops/manual_flatten.rs @@ -1,5 +1,5 @@ -use super::utils::make_iterator_snippet; use super::MANUAL_FLATTEN; +use super::utils::make_iterator_snippet; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::visitors::is_local_used; use clippy_utils::{higher, path_to_local_id, peel_blocks_with_stmt}; diff --git a/clippy_lints/src/loops/manual_while_let_some.rs b/clippy_lints/src/loops/manual_while_let_some.rs index 55d1b9ee67671..7476a87267f3b 100644 --- a/clippy_lints/src/loops/manual_while_let_some.rs +++ b/clippy_lints/src/loops/manual_while_let_some.rs @@ -1,10 +1,10 @@ +use clippy_utils::SpanlessEq; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; -use clippy_utils::SpanlessEq; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Pat, Stmt, StmtKind, UnOp}; use rustc_lint::LateContext; -use rustc_span::{sym, Symbol, Span}; +use rustc_span::{Span, Symbol, sym}; use std::borrow::Cow; use super::MANUAL_WHILE_LET_SOME; diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index 92ccc0cc0a15e..17215621d2aa1 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -22,15 +22,15 @@ mod while_immutable_condition; mod while_let_loop; mod while_let_on_iterator; -use clippy_config::msrvs::Msrv; use clippy_config::Conf; +use clippy_config::msrvs::Msrv; use clippy_utils::higher; use rustc_ast::Label; use rustc_hir::{Expr, ExprKind, LoopSource, Pat}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; use rustc_span::Span; -use utils::{make_iterator_snippet, IncrementVisitor, InitializeVisitor}; +use utils::{IncrementVisitor, InitializeVisitor, make_iterator_snippet}; declare_clippy_lint! { /// ### What it does @@ -188,22 +188,22 @@ declare_clippy_lint! { /// The `while let` loop is usually shorter and more /// readable. /// - /// ### Known problems - /// Sometimes the wrong binding is displayed ([#383](https://github.com/rust-lang/rust-clippy/issues/383)). - /// /// ### Example /// ```rust,no_run - /// # let y = Some(1); + /// let y = Some(1); /// loop { /// let x = match y { /// Some(x) => x, /// None => break, /// }; - /// // .. do something with x + /// // .. /// } - /// // is easier written as + /// ``` + /// Use instead: + /// ```rust,no_run + /// let y = Some(1); /// while let Some(x) = y { - /// // .. do something with x + /// // .. /// }; /// ``` #[clippy::version = "pre 1.29.0"] diff --git a/clippy_lints/src/loops/needless_range_loop.rs b/clippy_lints/src/loops/needless_range_loop.rs index e18e4374667de..20dd5a311dca0 100644 --- a/clippy_lints/src/loops/needless_range_loop.rs +++ b/clippy_lints/src/loops/needless_range_loop.rs @@ -3,17 +3,17 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use clippy_utils::ty::has_iter_method; use clippy_utils::visitors::is_local_used; -use clippy_utils::{contains_name, higher, is_integer_const, sugg, SpanlessEq}; +use clippy_utils::{SpanlessEq, contains_name, higher, is_integer_const, sugg}; use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::intravisit::{walk_expr, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{BinOpKind, BorrowKind, Closure, Expr, ExprKind, HirId, Mutability, Pat, PatKind, QPath}; use rustc_lint::LateContext; use rustc_middle::middle::region; use rustc_middle::ty::{self, Ty}; -use rustc_span::symbol::{sym, Symbol}; +use rustc_span::symbol::{Symbol, sym}; use std::{iter, mem}; /// Checks for looping over a range and then indexing a sequence with it. diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index 313a5bfefbc8d..1c55e3e22e8c1 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -1,5 +1,5 @@ -use super::utils::make_iterator_snippet; use super::NEVER_LOOP; +use super::utils::make_iterator_snippet; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::ForLoop; use clippy_utils::macros::root_macro_call_first_node; @@ -7,7 +7,7 @@ use clippy_utils::source::snippet; use rustc_errors::Applicability; use rustc_hir::{Block, Destination, Expr, ExprKind, HirId, InlineAsmOperand, Pat, Stmt, StmtKind}; use rustc_lint::LateContext; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use std::iter::once; pub(super) fn check<'tcx>( diff --git a/clippy_lints/src/loops/same_item_push.rs b/clippy_lints/src/loops/same_item_push.rs index 9185cf1f8b2ee..5662d3013e15b 100644 --- a/clippy_lints/src/loops/same_item_push.rs +++ b/clippy_lints/src/loops/same_item_push.rs @@ -6,11 +6,11 @@ use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::intravisit::{walk_expr, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{BindingMode, Block, Expr, ExprKind, HirId, Mutability, Node, Pat, PatKind, Stmt, StmtKind}; use rustc_lint::LateContext; -use rustc_span::symbol::sym; use rustc_span::SyntaxContext; +use rustc_span::symbol::sym; /// Detects for loop pushing the same item into a Vec pub(super) fn check<'tcx>( diff --git a/clippy_lints/src/loops/single_element_loop.rs b/clippy_lints/src/loops/single_element_loop.rs index 2b41911e8ec79..70f76ced09a43 100644 --- a/clippy_lints/src/loops/single_element_loop.rs +++ b/clippy_lints/src/loops/single_element_loop.rs @@ -2,10 +2,10 @@ use super::SINGLE_ELEMENT_LOOP; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{indent_of, snippet, snippet_with_applicability}; use clippy_utils::visitors::contains_break_or_continue; -use rustc_ast::util::parser::PREC_PREFIX; use rustc_ast::Mutability; +use rustc_ast::util::parser::PREC_PREFIX; use rustc_errors::Applicability; -use rustc_hir::{is_range_literal, BorrowKind, Expr, ExprKind, Pat, PatKind}; +use rustc_hir::{BorrowKind, Expr, ExprKind, Pat, PatKind, is_range_literal}; use rustc_lint::LateContext; use rustc_span::edition::Edition; use rustc_span::sym; diff --git a/clippy_lints/src/loops/utils.rs b/clippy_lints/src/loops/utils.rs index 7b45cc95431c2..9a89a41d2b395 100644 --- a/clippy_lints/src/loops/utils.rs +++ b/clippy_lints/src/loops/utils.rs @@ -2,13 +2,13 @@ use clippy_utils::ty::{has_iter_method, implements_trait}; use clippy_utils::{get_parent_expr, is_integer_const, path_to_local, path_to_local_id, sugg}; use rustc_ast::ast::{LitIntType, LitKind}; use rustc_errors::Applicability; -use rustc_hir::intravisit::{walk_expr, walk_local, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_expr, walk_local}; use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, HirId, HirIdMap, LetStmt, Mutability, PatKind}; 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_span::symbol::{Symbol, sym}; #[derive(Debug, PartialEq, Eq)] enum IncrementVisitorVarState { diff --git a/clippy_lints/src/loops/while_immutable_condition.rs b/clippy_lints/src/loops/while_immutable_condition.rs index cc1bd5929d0fc..eab096e9a227d 100644 --- a/clippy_lints/src/loops/while_immutable_condition.rs +++ b/clippy_lints/src/loops/while_immutable_condition.rs @@ -4,7 +4,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::usage::mutated_variables; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefIdMap; -use rustc_hir::intravisit::{walk_expr, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{Expr, ExprKind, HirIdSet, QPath}; use rustc_lint::LateContext; use std::ops::ControlFlow; diff --git a/clippy_lints/src/loops/while_let_on_iterator.rs b/clippy_lints/src/loops/while_let_on_iterator.rs index c171fa1c622af..7d9fbaf3ceabf 100644 --- a/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/clippy_lints/src/loops/while_let_on_iterator.rs @@ -5,13 +5,13 @@ use clippy_utils::visitors::is_res_used; use clippy_utils::{get_enclosing_loop_or_multi_call_closure, higher, is_refutable, is_res_lang_ctor, is_trait_method}; use rustc_errors::Applicability; use rustc_hir::def::Res; -use rustc_hir::intravisit::{walk_expr, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{Closure, Expr, ExprKind, HirId, LangItem, LetStmt, Mutability, PatKind, UnOp}; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter::OnlyBodies; use rustc_middle::ty::adjustment::Adjust; -use rustc_span::symbol::sym; use rustc_span::Symbol; +use rustc_span::symbol::sym; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let Some(higher::WhileLet { if_then, let_pat, let_expr, label, .. }) = higher::WhileLet::hir(expr) diff --git a/clippy_lints/src/macro_metavars_in_unsafe.rs b/clippy_lints/src/macro_metavars_in_unsafe.rs index e215097142be9..ccc554042d685 100644 --- a/clippy_lints/src/macro_metavars_in_unsafe.rs +++ b/clippy_lints/src/macro_metavars_in_unsafe.rs @@ -3,13 +3,13 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::is_lint_allowed; use itertools::Itertools; use rustc_hir::def_id::LocalDefId; -use rustc_hir::intravisit::{walk_block, walk_expr, walk_stmt, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_stmt}; use rustc_hir::{BlockCheckMode, Expr, ExprKind, HirId, Stmt, UnsafeSource}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; -use rustc_span::{sym, Span, SyntaxContext}; -use std::collections::btree_map::Entry; +use rustc_span::{Span, SyntaxContext, sym}; use std::collections::BTreeMap; +use std::collections::btree_map::Entry; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/macro_use.rs b/clippy_lints/src/macro_use.rs index 067384b09015a..bd6b3f1a47b16 100644 --- a/clippy_lints/src/macro_use.rs +++ b/clippy_lints/src/macro_use.rs @@ -8,7 +8,7 @@ use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; use rustc_span::edition::Edition; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use std::collections::BTreeMap; declare_clippy_lint! { diff --git a/clippy_lints/src/manual_async_fn.rs b/clippy_lints/src/manual_async_fn.rs index 61723aec59049..fc3bba9e51232 100644 --- a/clippy_lints/src/manual_async_fn.rs +++ b/clippy_lints/src/manual_async_fn.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::{position_before_rarrow, snippet_block, SpanRangeExt}; +use clippy_utils::source::{SpanRangeExt, position_before_rarrow, snippet_block}; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::{ @@ -9,7 +9,7 @@ use rustc_hir::{ use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::def_id::LocalDefId; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/manual_bits.rs b/clippy_lints/src/manual_bits.rs index 5cbdc7f864a75..c0e87e8a1faed 100644 --- a/clippy_lints/src/manual_bits.rs +++ b/clippy_lints/src/manual_bits.rs @@ -1,5 +1,5 @@ -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::get_parent_expr; use clippy_utils::source::snippet_with_context; diff --git a/clippy_lints/src/manual_clamp.rs b/clippy_lints/src/manual_clamp.rs index 4123c933660bc..788649fd4f90e 100644 --- a/clippy_lints/src/manual_clamp.rs +++ b/clippy_lints/src/manual_clamp.rs @@ -1,5 +1,5 @@ -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::higher::If; @@ -7,8 +7,8 @@ use clippy_utils::sugg::Sugg; use clippy_utils::ty::implements_trait; use clippy_utils::visitors::is_const_evaluatable; use clippy_utils::{ - eq_expr_value, is_diag_trait_item, is_in_const_context, is_trait_method, path_res, path_to_local_id, peel_blocks, - peel_blocks_with_stmt, MaybePath, + MaybePath, eq_expr_value, is_diag_trait_item, is_in_const_context, is_trait_method, path_res, path_to_local_id, + peel_blocks, peel_blocks_with_stmt, }; use itertools::Itertools; use rustc_errors::{Applicability, Diag}; @@ -17,8 +17,8 @@ use rustc_hir::{Arm, BinOpKind, Block, Expr, ExprKind, HirId, PatKind, PathSegme use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::Ty; use rustc_session::impl_lint_pass; -use rustc_span::symbol::sym; use rustc_span::Span; +use rustc_span::symbol::sym; use std::cmp::Ordering; use std::ops::Deref; diff --git a/clippy_lints/src/manual_div_ceil.rs b/clippy_lints/src/manual_div_ceil.rs new file mode 100644 index 0000000000000..00e02e2a33652 --- /dev/null +++ b/clippy_lints/src/manual_div_ceil.rs @@ -0,0 +1,158 @@ +use clippy_config::msrvs::{self, Msrv}; +use clippy_utils::SpanlessEq; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::sugg::Sugg; +use rustc_ast::{BinOpKind, LitKind}; +use rustc_data_structures::packed::Pu128; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{self}; +use rustc_session::impl_lint_pass; +use rustc_span::symbol::Symbol; + +use clippy_config::Conf; + +declare_clippy_lint! { + /// ### What it does + /// Checks for an expression like `(x + (y - 1)) / y` which is a common manual reimplementation + /// of `x.div_ceil(y)`. + /// + /// ### Why is this bad? + /// It's simpler, clearer and more readable. + /// + /// ### Example + /// ```no_run + /// let x: i32 = 7; + /// let y: i32 = 4; + /// let div = (x + (y - 1)) / y; + /// ``` + /// Use instead: + /// ```no_run + /// #![feature(int_roundings)] + /// let x: i32 = 7; + /// let y: i32 = 4; + /// let div = x.div_ceil(y); + /// ``` + #[clippy::version = "1.81.0"] + pub MANUAL_DIV_CEIL, + complexity, + "manually reimplementing `div_ceil`" +} + +pub struct ManualDivCeil { + msrv: Msrv, +} + +impl ManualDivCeil { + #[must_use] + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } + } +} + +impl_lint_pass!(ManualDivCeil => [MANUAL_DIV_CEIL]); + +impl<'tcx> LateLintPass<'tcx> for ManualDivCeil { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { + if !self.msrv.meets(msrvs::MANUAL_DIV_CEIL) { + return; + } + + let mut applicability = Applicability::MachineApplicable; + + if let ExprKind::Binary(div_op, div_lhs, div_rhs) = expr.kind + && div_op.node == BinOpKind::Div + && check_int_ty_and_feature(cx, div_lhs) + && check_int_ty_and_feature(cx, div_rhs) + && let ExprKind::Binary(inner_op, inner_lhs, inner_rhs) = div_lhs.kind + { + // (x + (y - 1)) / y + if let ExprKind::Binary(sub_op, sub_lhs, sub_rhs) = inner_rhs.kind + && inner_op.node == BinOpKind::Add + && sub_op.node == BinOpKind::Sub + && check_literal(sub_rhs) + && check_eq_expr(cx, sub_lhs, div_rhs) + { + build_suggestion(cx, expr, inner_lhs, div_rhs, &mut applicability); + return; + } + + // ((y - 1) + x) / y + if let ExprKind::Binary(sub_op, sub_lhs, sub_rhs) = inner_lhs.kind + && inner_op.node == BinOpKind::Add + && sub_op.node == BinOpKind::Sub + && check_literal(sub_rhs) + && check_eq_expr(cx, sub_lhs, div_rhs) + { + build_suggestion(cx, expr, inner_rhs, div_rhs, &mut applicability); + return; + } + + // (x + y - 1) / y + if let ExprKind::Binary(add_op, add_lhs, add_rhs) = inner_lhs.kind + && inner_op.node == BinOpKind::Sub + && add_op.node == BinOpKind::Add + && check_literal(inner_rhs) + && check_eq_expr(cx, add_rhs, div_rhs) + { + build_suggestion(cx, expr, add_lhs, div_rhs, &mut applicability); + } + } + } + + extract_msrv_attr!(LateContext); +} + +fn check_int_ty_and_feature(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + let expr_ty = cx.typeck_results().expr_ty(expr); + match expr_ty.peel_refs().kind() { + ty::Uint(_) => true, + ty::Int(_) => cx + .tcx + .features() + .declared_features + .contains(&Symbol::intern("int_roundings")), + + _ => false, + } +} + +fn check_literal(expr: &Expr<'_>) -> bool { + if let ExprKind::Lit(lit) = expr.kind + && let LitKind::Int(Pu128(1), _) = lit.node + { + return true; + } + false +} + +fn check_eq_expr(cx: &LateContext<'_>, lhs: &Expr<'_>, rhs: &Expr<'_>) -> bool { + SpanlessEq::new(cx).eq_expr(lhs, rhs) +} + +fn build_suggestion( + cx: &LateContext<'_>, + expr: &Expr<'_>, + lhs: &Expr<'_>, + rhs: &Expr<'_>, + applicability: &mut Applicability, +) { + let dividend_sugg = Sugg::hir_with_applicability(cx, lhs, "..", applicability).maybe_par(); + let divisor_snippet = snippet_with_applicability(cx, rhs.span.source_callsite(), "..", applicability); + + let sugg = format!("{dividend_sugg}.div_ceil({divisor_snippet})"); + + span_lint_and_sugg( + cx, + MANUAL_DIV_CEIL, + expr.span, + "manually reimplementing `div_ceil`", + "consider using `.div_ceil()`", + sugg, + *applicability, + ); +} diff --git a/clippy_lints/src/manual_hash_one.rs b/clippy_lints/src/manual_hash_one.rs index 11716a539ab5c..c62bd65bea683 100644 --- a/clippy_lints/src/manual_hash_one.rs +++ b/clippy_lints/src/manual_hash_one.rs @@ -1,5 +1,5 @@ -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::source::SpanRangeExt; use clippy_utils::visitors::{is_local_used, local_used_once}; diff --git a/clippy_lints/src/manual_is_ascii_check.rs b/clippy_lints/src/manual_is_ascii_check.rs index 31c37c3bc3b14..24207705743d1 100644 --- a/clippy_lints/src/manual_is_ascii_check.rs +++ b/clippy_lints/src/manual_is_ascii_check.rs @@ -1,17 +1,17 @@ -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::matching_root_macro_call; use clippy_utils::sugg::Sugg; use clippy_utils::{higher, is_in_const_context, path_to_local, peel_ref_operators}; -use rustc_ast::ast::RangeLimits; use rustc_ast::LitKind::{Byte, Char}; +use rustc_ast::ast::RangeLimits; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Node, Param, PatKind, RangeEnd}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::impl_lint_pass; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/manual_is_power_of_two.rs b/clippy_lints/src/manual_is_power_of_two.rs new file mode 100644 index 0000000000000..da2a982ee170a --- /dev/null +++ b/clippy_lints/src/manual_is_power_of_two.rs @@ -0,0 +1,142 @@ +use clippy_utils::SpanlessEq; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use rustc_ast::LitKind; +use rustc_data_structures::packed::Pu128; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::Uint; +use rustc_session::declare_lint_pass; + +declare_clippy_lint! { + /// ### What it does + /// Checks for expressions like `x.count_ones() == 1` or `x & (x - 1) == 0`, with x and unsigned integer, which are manual + /// reimplementations of `x.is_power_of_two()`. + /// ### Why is this bad? + /// Manual reimplementations of `is_power_of_two` increase code complexity for little benefit. + /// ### Example + /// ```no_run + /// let a: u32 = 4; + /// let result = a.count_ones() == 1; + /// ``` + /// Use instead: + /// ```no_run + /// let a: u32 = 4; + /// let result = a.is_power_of_two(); + /// ``` + #[clippy::version = "1.82.0"] + pub MANUAL_IS_POWER_OF_TWO, + complexity, + "manually reimplementing `is_power_of_two`" +} + +declare_lint_pass!(ManualIsPowerOfTwo => [MANUAL_IS_POWER_OF_TWO]); + +impl LateLintPass<'_> for ManualIsPowerOfTwo { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + let mut applicability = Applicability::MachineApplicable; + + if let ExprKind::Binary(bin_op, left, right) = expr.kind + && bin_op.node == BinOpKind::Eq + { + // a.count_ones() == 1 + if let ExprKind::MethodCall(method_name, reciever, _, _) = left.kind + && method_name.ident.as_str() == "count_ones" + && let &Uint(_) = cx.typeck_results().expr_ty(reciever).kind() + && check_lit(right, 1) + { + build_sugg(cx, expr, reciever, &mut applicability); + } + + // 1 == a.count_ones() + if let ExprKind::MethodCall(method_name, reciever, _, _) = right.kind + && method_name.ident.as_str() == "count_ones" + && let &Uint(_) = cx.typeck_results().expr_ty(reciever).kind() + && check_lit(left, 1) + { + build_sugg(cx, expr, reciever, &mut applicability); + } + + // a & (a - 1) == 0 + if let ExprKind::Binary(op1, left1, right1) = left.kind + && op1.node == BinOpKind::BitAnd + && let ExprKind::Binary(op2, left2, right2) = right1.kind + && op2.node == BinOpKind::Sub + && check_eq_expr(cx, left1, left2) + && let &Uint(_) = cx.typeck_results().expr_ty(left1).kind() + && check_lit(right2, 1) + && check_lit(right, 0) + { + build_sugg(cx, expr, left1, &mut applicability); + } + + // (a - 1) & a == 0; + if let ExprKind::Binary(op1, left1, right1) = left.kind + && op1.node == BinOpKind::BitAnd + && let ExprKind::Binary(op2, left2, right2) = left1.kind + && op2.node == BinOpKind::Sub + && check_eq_expr(cx, right1, left2) + && let &Uint(_) = cx.typeck_results().expr_ty(right1).kind() + && check_lit(right2, 1) + && check_lit(right, 0) + { + build_sugg(cx, expr, right1, &mut applicability); + } + + // 0 == a & (a - 1); + if let ExprKind::Binary(op1, left1, right1) = right.kind + && op1.node == BinOpKind::BitAnd + && let ExprKind::Binary(op2, left2, right2) = right1.kind + && op2.node == BinOpKind::Sub + && check_eq_expr(cx, left1, left2) + && let &Uint(_) = cx.typeck_results().expr_ty(left1).kind() + && check_lit(right2, 1) + && check_lit(left, 0) + { + build_sugg(cx, expr, left1, &mut applicability); + } + + // 0 == (a - 1) & a + if let ExprKind::Binary(op1, left1, right1) = right.kind + && op1.node == BinOpKind::BitAnd + && let ExprKind::Binary(op2, left2, right2) = left1.kind + && op2.node == BinOpKind::Sub + && check_eq_expr(cx, right1, left2) + && let &Uint(_) = cx.typeck_results().expr_ty(right1).kind() + && check_lit(right2, 1) + && check_lit(left, 0) + { + build_sugg(cx, expr, right1, &mut applicability); + } + } + } +} + +fn build_sugg(cx: &LateContext<'_>, expr: &Expr<'_>, reciever: &Expr<'_>, applicability: &mut Applicability) { + let snippet = snippet_with_applicability(cx, reciever.span, "..", applicability); + + span_lint_and_sugg( + cx, + MANUAL_IS_POWER_OF_TWO, + expr.span, + "manually reimplementing `is_power_of_two`", + "consider using `.is_power_of_two()`", + format!("{snippet}.is_power_of_two()"), + *applicability, + ); +} + +fn check_lit(expr: &Expr<'_>, expected_num: u128) -> bool { + if let ExprKind::Lit(lit) = expr.kind + && let LitKind::Int(Pu128(num), _) = lit.node + && num == expected_num + { + return true; + } + false +} + +fn check_eq_expr(cx: &LateContext<'_>, lhs: &Expr<'_>, rhs: &Expr<'_>) -> bool { + SpanlessEq::new(cx).eq_expr(lhs, rhs) +} diff --git a/clippy_lints/src/manual_let_else.rs b/clippy_lints/src/manual_let_else.rs index ebebdf679a863..17185df5d76fa 100644 --- a/clippy_lints/src/manual_let_else.rs +++ b/clippy_lints/src/manual_let_else.rs @@ -1,4 +1,4 @@ -use crate::question_mark::{QuestionMark, QUESTION_MARK}; +use crate::question_mark::{QUESTION_MARK, QuestionMark}; use clippy_config::msrvs; use clippy_config::types::MatchLintBehaviour; use clippy_utils::diagnostics::span_lint_and_then; @@ -12,8 +12,8 @@ use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatKind, QPath, Stmt, StmtKind use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_span::symbol::{sym, Symbol}; use rustc_span::Span; +use rustc_span::symbol::{Symbol, sym}; use std::slice; declare_clippy_lint! { diff --git a/clippy_lints/src/manual_main_separator_str.rs b/clippy_lints/src/manual_main_separator_str.rs index 5198d7838a281..85e99a92cf598 100644 --- a/clippy_lints/src/manual_main_separator_str.rs +++ b/clippy_lints/src/manual_main_separator_str.rs @@ -1,5 +1,5 @@ -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::{is_trait_method, peel_hir_expr_refs}; use rustc_errors::Applicability; diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs index 6b1d90483ccd1..25868ccae4003 100644 --- a/clippy_lints/src/manual_non_exhaustive.rs +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -1,18 +1,18 @@ -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::is_doc_hidden; -use clippy_utils::source::SpanRangeExt; -use rustc_ast::ast::{self, VisibilityKind}; +use clippy_utils::source::snippet_indent; +use itertools::Itertools; use rustc_ast::attr; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; -use rustc_hir::{self as hir, Expr, ExprKind, QPath}; -use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; +use rustc_hir::{Expr, ExprKind, Item, ItemKind, QPath, TyKind, VariantData}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; -use rustc_span::def_id::{DefId, LocalDefId}; -use rustc_span::{sym, Span}; +use rustc_span::def_id::LocalDefId; +use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does @@ -62,29 +62,13 @@ declare_clippy_lint! { "manual implementations of the non-exhaustive pattern can be simplified using #[non_exhaustive]" } -#[expect(clippy::module_name_repetitions)] -pub struct ManualNonExhaustiveStruct { +pub struct ManualNonExhaustive { msrv: Msrv, -} - -impl ManualNonExhaustiveStruct { - pub fn new(conf: &'static Conf) -> Self { - Self { - msrv: conf.msrv.clone(), - } - } -} - -impl_lint_pass!(ManualNonExhaustiveStruct => [MANUAL_NON_EXHAUSTIVE]); - -#[expect(clippy::module_name_repetitions)] -pub struct ManualNonExhaustiveEnum { - msrv: Msrv, - constructed_enum_variants: FxHashSet<(DefId, DefId)>, + constructed_enum_variants: FxHashSet, potential_enums: Vec<(LocalDefId, LocalDefId, Span, Span)>, } -impl ManualNonExhaustiveEnum { +impl ManualNonExhaustive { pub fn new(conf: &'static Conf) -> Self { Self { msrv: conf.msrv.clone(), @@ -94,96 +78,78 @@ impl ManualNonExhaustiveEnum { } } -impl_lint_pass!(ManualNonExhaustiveEnum => [MANUAL_NON_EXHAUSTIVE]); - -impl EarlyLintPass for ManualNonExhaustiveStruct { - fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { - if let ast::ItemKind::Struct(variant_data, _) = &item.kind - && let (fields, delimiter) = match variant_data { - ast::VariantData::Struct { fields, .. } => (&**fields, '{'), - ast::VariantData::Tuple(fields, _) => (&**fields, '('), - ast::VariantData::Unit(_) => return, - } - && fields.len() > 1 - && self.msrv.meets(msrvs::NON_EXHAUSTIVE) - { - let mut iter = fields.iter().filter_map(|f| match f.vis.kind { - VisibilityKind::Public => None, - VisibilityKind::Inherited => Some(Ok(f)), - VisibilityKind::Restricted { .. } => Some(Err(())), - }); - if let Some(Ok(field)) = iter.next() - && iter.next().is_none() - && field.ty.kind.is_unit() - { - span_lint_and_then( - cx, - MANUAL_NON_EXHAUSTIVE, - item.span, - "this seems like a manual implementation of the non-exhaustive pattern", - |diag| { - if !item.attrs.iter().any(|attr| attr.has_name(sym::non_exhaustive)) - && let header_span = cx.sess().source_map().span_until_char(item.span, delimiter) - && let Some(snippet) = header_span.get_source_text(cx) - { - diag.span_suggestion( - header_span, - "add the attribute", - format!("#[non_exhaustive] {snippet}"), - Applicability::Unspecified, - ); - } - diag.span_help(field.span, "remove this field"); - }, - ); - } - } - } - - extract_msrv_attr!(EarlyContext); -} +impl_lint_pass!(ManualNonExhaustive => [MANUAL_NON_EXHAUSTIVE]); -impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum { - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { - if !self.msrv.meets(msrvs::NON_EXHAUSTIVE) { +impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustive { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + if !self.msrv.meets(msrvs::NON_EXHAUSTIVE) || !cx.effective_visibilities.is_exported(item.owner_id.def_id) { return; } - if let hir::ItemKind::Enum(def, _) = &item.kind - && def.variants.len() > 1 - { - let mut iter = def.variants.iter().filter_map(|v| { - (matches!(v.data, hir::VariantData::Unit(_, _)) - && is_doc_hidden(cx.tcx.hir().attrs(v.hir_id)) - && !attr::contains_name(cx.tcx.hir().attrs(item.hir_id()), sym::non_exhaustive)) - .then_some((v.def_id, v.span)) - }); - if let Some((id, span)) = iter.next() - && iter.next().is_none() - { - self.potential_enums.push((item.owner_id.def_id, id, item.span, span)); - } + match item.kind { + ItemKind::Enum(def, _) if def.variants.len() > 1 => { + let iter = def.variants.iter().filter_map(|v| { + (matches!(v.data, VariantData::Unit(_, _)) && is_doc_hidden(cx.tcx.hir().attrs(v.hir_id))) + .then_some((v.def_id, v.span)) + }); + if let Ok((id, span)) = iter.exactly_one() + && !attr::contains_name(cx.tcx.hir().attrs(item.hir_id()), sym::non_exhaustive) + { + self.potential_enums.push((item.owner_id.def_id, id, item.span, span)); + } + }, + ItemKind::Struct(variant_data, _) => { + let fields = variant_data.fields(); + let private_fields = fields + .iter() + .filter(|field| !cx.effective_visibilities.is_exported(field.def_id)); + if fields.len() > 1 + && let Ok(field) = private_fields.exactly_one() + && let TyKind::Tup([]) = field.ty.kind + { + span_lint_and_then( + cx, + MANUAL_NON_EXHAUSTIVE, + item.span, + "this seems like a manual implementation of the non-exhaustive pattern", + |diag| { + if let Some(non_exhaustive) = + attr::find_by_name(cx.tcx.hir().attrs(item.hir_id()), sym::non_exhaustive) + { + diag.span_note(non_exhaustive.span, "the struct is already non-exhaustive"); + } else { + let indent = snippet_indent(cx, item.span).unwrap_or_default(); + diag.span_suggestion_verbose( + item.span.shrink_to_lo(), + "use the `#[non_exhaustive]` attribute instead", + format!("#[non_exhaustive]\n{indent}"), + Applicability::MaybeIncorrect, + ); + } + diag.span_help(field.span, "remove this field"); + }, + ); + } + }, + _ => {}, } } fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { if let ExprKind::Path(QPath::Resolved(None, p)) = &e.kind - && let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), id) = p.res + && let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), ctor_id) = p.res + && let Some(local_ctor) = ctor_id.as_local() { - let variant_id = cx.tcx.parent(id); - let enum_id = cx.tcx.parent(variant_id); - - self.constructed_enum_variants.insert((enum_id, variant_id)); + let variant_id = cx.tcx.local_parent(local_ctor); + self.constructed_enum_variants.insert(variant_id); } } fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { - for &(enum_id, _, enum_span, variant_span) in - self.potential_enums.iter().filter(|&&(enum_id, variant_id, _, _)| { - !self - .constructed_enum_variants - .contains(&(enum_id.to_def_id(), variant_id.to_def_id())) - }) + for &(enum_id, _, enum_span, variant_span) in self + .potential_enums + .iter() + .filter(|(_, variant_id, _, _)| !self.constructed_enum_variants.contains(variant_id)) { let hir_id = cx.tcx.local_def_id_to_hir_id(enum_id); span_lint_hir_and_then( @@ -193,15 +159,13 @@ impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum { enum_span, "this seems like a manual implementation of the non-exhaustive pattern", |diag| { - let header_span = cx.sess().source_map().span_until_char(enum_span, '{'); - if let Some(snippet) = header_span.get_source_text(cx) { - diag.span_suggestion( - header_span, - "add the attribute", - format!("#[non_exhaustive] {snippet}"), - Applicability::Unspecified, - ); - } + let indent = snippet_indent(cx, enum_span).unwrap_or_default(); + diag.span_suggestion_verbose( + enum_span.shrink_to_lo(), + "use the `#[non_exhaustive]` attribute instead", + format!("#[non_exhaustive]\n{indent}"), + Applicability::MaybeIncorrect, + ); diag.span_help(variant_span, "remove this variant"); }, ); diff --git a/clippy_lints/src/manual_range_patterns.rs b/clippy_lints/src/manual_range_patterns.rs index 9afb2b5bc84cd..ffa3eacf3544f 100644 --- a/clippy_lints/src/manual_range_patterns.rs +++ b/clippy_lints/src/manual_range_patterns.rs @@ -7,7 +7,7 @@ use rustc_hir::{Expr, ExprKind, PatKind, RangeEnd, UnOp}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::declare_lint_pass; -use rustc_span::{Span, DUMMY_SP}; +use rustc_span::{DUMMY_SP, Span}; declare_clippy_lint! { /// ### What it does @@ -77,9 +77,10 @@ impl Num { impl LateLintPass<'_> for ManualRangePatterns { fn check_pat(&mut self, cx: &LateContext<'_>, pat: &'_ rustc_hir::Pat<'_>) { // a pattern like 1 | 2 seems fine, lint if there are at least 3 alternatives - // or at least one range + // or more then one range (exclude triggering on stylistic using OR with one element + // like described https://github.com/rust-lang/rust-clippy/issues/11825) if let PatKind::Or(pats) = pat.kind - && (pats.len() >= 3 || pats.iter().any(|p| matches!(p.kind, PatKind::Range(..)))) + && (pats.len() >= 3 || (pats.len() > 1 && pats.iter().any(|p| matches!(p.kind, PatKind::Range(..))))) && !in_external_macro(cx.sess(), pat.span) { let mut min = Num::dummy(i128::MAX); diff --git a/clippy_lints/src/manual_rem_euclid.rs b/clippy_lints/src/manual_rem_euclid.rs index 6cf5d272d7d82..86293169ea28c 100644 --- a/clippy_lints/src/manual_rem_euclid.rs +++ b/clippy_lints/src/manual_rem_euclid.rs @@ -1,5 +1,5 @@ -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::consts::{ConstEvalCtxt, FullInt}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_context; diff --git a/clippy_lints/src/manual_retain.rs b/clippy_lints/src/manual_retain.rs index a35b0f914e058..a60163be770d0 100644 --- a/clippy_lints/src/manual_retain.rs +++ b/clippy_lints/src/manual_retain.rs @@ -1,17 +1,17 @@ -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; +use clippy_utils::SpanlessEq; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::{get_type_diagnostic_name, is_type_lang_item}; -use clippy_utils::SpanlessEq; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_hir::def_id::DefId; use rustc_hir::ExprKind::Assign; +use rustc_hir::def_id::DefId; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; -use rustc_span::symbol::{sym, Symbol}; use rustc_span::Span; +use rustc_span::symbol::{Symbol, sym}; const ACCEPTABLE_METHODS: [Symbol; 5] = [ sym::binaryheap_iter, diff --git a/clippy_lints/src/manual_string_new.rs b/clippy_lints/src/manual_string_new.rs index 781db4b97f083..198f7aaddc71b 100644 --- a/clippy_lints/src/manual_string_new.rs +++ b/clippy_lints/src/manual_string_new.rs @@ -5,7 +5,7 @@ use rustc_hir::{Expr, ExprKind, PathSegment, QPath, TyKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::declare_lint_pass; -use rustc_span::{sym, symbol, Span}; +use rustc_span::{Span, sym, symbol}; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index 02d433ecc5aaa..9aceca66bf774 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -1,5 +1,5 @@ -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; @@ -8,13 +8,13 @@ use clippy_utils::{eq_expr_value, higher}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::def::Res; -use rustc_hir::intravisit::{walk_expr, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::impl_lint_pass; use rustc_span::source_map::Spanned; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use std::iter; declare_clippy_lint! { diff --git a/clippy_lints/src/map_unit_fn.rs b/clippy_lints/src/map_unit_fn.rs index 2db71b1f7a388..a97dbbbc33fd0 100644 --- a/clippy_lints/src/map_unit_fn.rs +++ b/clippy_lints/src/map_unit_fn.rs @@ -7,7 +7,7 @@ use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty}; use rustc_session::declare_lint_pass; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/matches/collapsible_match.rs b/clippy_lints/src/matches/collapsible_match.rs index b666e77c09ecb..50e6dfc6298dc 100644 --- a/clippy_lints/src/matches/collapsible_match.rs +++ b/clippy_lints/src/matches/collapsible_match.rs @@ -4,7 +4,7 @@ use clippy_utils::higher::IfLetOrMatch; use clippy_utils::source::snippet; use clippy_utils::visitors::is_local_used; use clippy_utils::{ - is_res_lang_ctor, is_unit_expr, path_to_local, peel_blocks_with_stmt, peel_ref_operators, SpanlessEq, + SpanlessEq, is_res_lang_ctor, is_unit_expr, path_to_local, peel_blocks_with_stmt, peel_ref_operators, }; use rustc_errors::MultiSpan; use rustc_hir::LangItem::OptionNone; @@ -12,7 +12,7 @@ use rustc_hir::{Arm, Expr, HirId, Pat, PatKind}; use rustc_lint::LateContext; use rustc_span::Span; -use super::{pat_contains_disallowed_or, COLLAPSIBLE_MATCH}; +use super::{COLLAPSIBLE_MATCH, pat_contains_disallowed_or}; pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], msrv: &Msrv) { if let Some(els_arm) = arms.iter().rfind(|arm| arm_is_wild_like(cx, arm)) { diff --git a/clippy_lints/src/matches/manual_filter.rs b/clippy_lints/src/matches/manual_filter.rs index 619ec83127aba..cfa054706d6bf 100644 --- a/clippy_lints/src/matches/manual_filter.rs +++ b/clippy_lints/src/matches/manual_filter.rs @@ -6,10 +6,10 @@ use clippy_utils::{is_res_lang_ctor, path_res, path_to_local_id}; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::{Arm, Expr, ExprKind, HirId, Pat, PatKind}; use rustc_lint::LateContext; -use rustc_span::{sym, SyntaxContext}; +use rustc_span::{SyntaxContext, sym}; -use super::manual_utils::{check_with, SomeExpr}; use super::MANUAL_FILTER; +use super::manual_utils::{SomeExpr, check_with}; // Function called on the of `[&+]Some((ref | ref mut) x) => ` // Need to check if it's of the form `=if {} else {}` diff --git a/clippy_lints/src/matches/manual_map.rs b/clippy_lints/src/matches/manual_map.rs index ed3d8b09fdcda..de57d1eee9249 100644 --- a/clippy_lints/src/matches/manual_map.rs +++ b/clippy_lints/src/matches/manual_map.rs @@ -1,5 +1,5 @@ -use super::manual_utils::{check_with, SomeExpr}; use super::MANUAL_MAP; +use super::manual_utils::{SomeExpr, check_with}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::{is_res_lang_ctor, path_res}; diff --git a/clippy_lints/src/matches/manual_unwrap_or.rs b/clippy_lints/src/matches/manual_unwrap_or.rs index d5d83df93471c..59d3752001178 100644 --- a/clippy_lints/src/matches/manual_unwrap_or.rs +++ b/clippy_lints/src/matches/manual_unwrap_or.rs @@ -1,12 +1,12 @@ use clippy_utils::consts::ConstEvalCtxt; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{indent_of, reindent_multiline, SpanRangeExt}; +use clippy_utils::source::{SpanRangeExt, indent_of, reindent_multiline}; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::usage::contains_return_break_continue_macro; use clippy_utils::{is_res_lang_ctor, path_to_local_id, peel_blocks, sugg}; use rustc_errors::Applicability; -use rustc_hir::def::{DefKind, Res}; use rustc_hir::LangItem::{OptionNone, ResultErr}; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Arm, Expr, Pat, PatKind}; use rustc_lint::LateContext; use rustc_middle::ty::Ty; diff --git a/clippy_lints/src/matches/manual_utils.rs b/clippy_lints/src/matches/manual_utils.rs index be80aebed6dfb..d38560998a5a7 100644 --- a/clippy_lints/src/matches/manual_utils.rs +++ b/clippy_lints/src/matches/manual_utils.rs @@ -4,16 +4,16 @@ use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{is_copy, is_type_diagnostic_item, 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, + 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, }; use rustc_ast::util::parser::PREC_UNAMBIGUOUS; use rustc_errors::Applicability; -use rustc_hir::def::Res; use rustc_hir::LangItem::{OptionNone, OptionSome}; +use rustc_hir::def::Res; use rustc_hir::{BindingMode, Expr, ExprKind, HirId, Mutability, Pat, PatKind, Path, QPath}; use rustc_lint::LateContext; -use rustc_span::{sym, SyntaxContext}; +use rustc_span::{SyntaxContext, sym}; #[expect(clippy::too_many_arguments)] #[expect(clippy::too_many_lines)] diff --git a/clippy_lints/src/matches/match_same_arms.rs b/clippy_lints/src/matches/match_same_arms.rs index da8c918a62bb6..f9ffbc5dc0b8e 100644 --- a/clippy_lints/src/matches/match_same_arms.rs +++ b/clippy_lints/src/matches/match_same_arms.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{is_lint_allowed, path_to_local, search_same, SpanlessEq, SpanlessHash}; +use clippy_utils::{SpanlessEq, SpanlessHash, is_lint_allowed, path_to_local, search_same}; use core::cmp::Ordering; use core::{iter, slice}; use rustc_arena::DroplessArena; diff --git a/clippy_lints/src/matches/match_str_case_mismatch.rs b/clippy_lints/src/matches/match_str_case_mismatch.rs index 322e9c3ebe5b3..40518ce2ca775 100644 --- a/clippy_lints/src/matches/match_str_case_mismatch.rs +++ b/clippy_lints/src/matches/match_str_case_mismatch.rs @@ -2,12 +2,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::ty::is_type_lang_item; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; -use rustc_hir::intravisit::{walk_expr, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{Arm, Expr, ExprKind, LangItem, PatKind}; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::symbol::Symbol; use rustc_span::Span; +use rustc_span::symbol::Symbol; use super::MATCH_STR_CASE_MISMATCH; diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index cf5377e0725dd..686fc4a0fa0a5 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -24,8 +24,8 @@ mod single_match; mod try_err; mod wild_in_or_pats; -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::source::walk_span_to_context; use clippy_utils::{higher, is_direct_expn_of, is_in_const_context, is_span_match, span_contains_cfg}; use rustc_hir::{Arm, Expr, ExprKind, LetStmt, MatchSource, Pat, PatKind}; diff --git a/clippy_lints/src/matches/overlapping_arms.rs b/clippy_lints/src/matches/overlapping_arms.rs index 71f211be925bf..6a4c553cee0ee 100644 --- a/clippy_lints/src/matches/overlapping_arms.rs +++ b/clippy_lints/src/matches/overlapping_arms.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{mir_to_const, ConstEvalCtxt, FullInt}; +use clippy_utils::consts::{ConstEvalCtxt, FullInt, mir_to_const}; use clippy_utils::diagnostics::span_lint_and_note; use core::cmp::Ordering; use rustc_hir::{Arm, Expr, PatKind, RangeEnd}; diff --git a/clippy_lints/src/matches/redundant_guards.rs b/clippy_lints/src/matches/redundant_guards.rs index 8222c3057f73e..564c598a334c7 100644 --- a/clippy_lints/src/matches/redundant_guards.rs +++ b/clippy_lints/src/matches/redundant_guards.rs @@ -10,11 +10,11 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Arm, BinOpKind, Expr, ExprKind, MatchSource, Node, PatKind, UnOp}; use rustc_lint::LateContext; use rustc_span::symbol::Ident; -use rustc_span::{sym, Span, Symbol}; +use rustc_span::{Span, Symbol, sym}; use std::borrow::Cow; use std::ops::ControlFlow; -use super::{pat_contains_disallowed_or, REDUNDANT_GUARDS}; +use super::{REDUNDANT_GUARDS, pat_contains_disallowed_or}; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>], msrv: &Msrv) { for outer_arm in arms { diff --git a/clippy_lints/src/matches/redundant_pattern_match.rs b/clippy_lints/src/matches/redundant_pattern_match.rs index 0e4b2d9d34a02..b646e87a43930 100644 --- a/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/clippy_lints/src/matches/redundant_pattern_match.rs @@ -1,18 +1,18 @@ use super::REDUNDANT_PATTERN_MATCHING; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{snippet, walk_span_to_context}; -use clippy_utils::sugg::{make_unop, Sugg}; +use clippy_utils::sugg::{Sugg, make_unop}; use clippy_utils::ty::{is_type_diagnostic_item, needs_ordered_drop}; use clippy_utils::visitors::{any_temporaries_need_ordered_drop, for_each_expr_without_closures}; use clippy_utils::{higher, is_expn_of, is_trait_method}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; -use rustc_hir::def::{DefKind, Res}; use rustc_hir::LangItem::{self, OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk}; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Arm, Expr, ExprKind, Node, Pat, PatKind, QPath, UnOp}; use rustc_lint::LateContext; use rustc_middle::ty::{self, GenericArgKind, Ty}; -use rustc_span::{sym, Span, Symbol}; +use rustc_span::{Span, Symbol, sym}; use std::fmt::Write; use std::ops::ControlFlow; diff --git a/clippy_lints/src/matches/significant_drop_in_scrutinee.rs b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs index 9c2f42d218702..537f7272f7f34 100644 --- a/clippy_lints/src/matches/significant_drop_in_scrutinee.rs +++ b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs @@ -8,7 +8,7 @@ use clippy_utils::{get_attr, is_lint_allowed}; use itertools::Itertools; use rustc_ast::Mutability; use rustc_errors::{Applicability, Diag}; -use rustc_hir::intravisit::{walk_expr, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{Arm, Expr, ExprKind, MatchSource}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty::{GenericArgKind, Region, RegionKind, Ty, TyCtxt, TypeVisitable, TypeVisitor}; diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index b6930f7b9d10f..2eda238ae8cdb 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{expr_block, snippet, SpanRangeExt}; +use clippy_utils::source::{SpanRangeExt, expr_block, snippet, snippet_block_with_context}; use clippy_utils::ty::implements_trait; use clippy_utils::{ is_lint_allowed, is_unit_expr, peel_blocks, peel_hir_pat_refs, peel_middle_ty_refs, peel_n_hir_expr_refs, @@ -8,11 +8,11 @@ use core::ops::ControlFlow; use rustc_arena::DroplessArena; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::intravisit::{walk_pat, Visitor}; -use rustc_hir::{Arm, Expr, ExprKind, HirId, Pat, PatKind, QPath}; +use rustc_hir::intravisit::{Visitor, walk_pat}; +use rustc_hir::{Arm, Expr, ExprKind, HirId, Node, Pat, PatKind, QPath, StmtKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, AdtDef, ParamEnv, TyCtxt, TypeckResults, VariantDef}; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use super::{MATCH_BOOL, SINGLE_MATCH, SINGLE_MATCH_ELSE}; @@ -91,6 +91,29 @@ fn report_single_pattern(cx: &LateContext<'_>, ex: &Expr<'_>, arm: &Arm<'_>, exp format!(" else {}", expr_block(cx, els, ctxt, "..", Some(expr.span), &mut app)) }); + if snippet(cx, ex.span, "..") == snippet(cx, arm.pat.span, "..") { + let msg = "this pattern is irrefutable, `match` is useless"; + let (sugg, help) = if is_unit_expr(arm.body) { + (String::new(), "`match` expression can be removed") + } else { + let mut sugg = snippet_block_with_context(cx, arm.body.span, ctxt, "..", Some(expr.span), &mut app) + .0 + .to_string(); + if let Node::Stmt(stmt) = cx.tcx.parent_hir_node(expr.hir_id) + && let StmtKind::Expr(_) = stmt.kind + && match arm.body.kind { + ExprKind::Block(block, _) => block.span.from_expansion(), + _ => true, + } + { + sugg.push(';'); + } + (sugg, "try") + }; + span_lint_and_sugg(cx, lint, expr.span, msg, help, sugg.to_string(), app); + return; + } + let (pat, pat_ref_count) = peel_hir_pat_refs(arm.pat); let (msg, sugg) = if let PatKind::Path(_) | PatKind::Lit(_) = pat.kind && let (ty, ty_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(ex)) diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs index 72a5945ad9bca..146748734cff4 100644 --- a/clippy_lints/src/mem_replace.rs +++ b/clippy_lints/src/mem_replace.rs @@ -1,5 +1,5 @@ -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; 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::sugg::Sugg; @@ -13,8 +13,8 @@ use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; use rustc_session::impl_lint_pass; -use rustc_span::symbol::sym; use rustc_span::Span; +use rustc_span::symbol::sym; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/methods/bind_instead_of_map.rs b/clippy_lints/src/methods/bind_instead_of_map.rs index 2a8a5fcc3b6f9..91a5de16e9670 100644 --- a/clippy_lints/src/methods/bind_instead_of_map.rs +++ b/clippy_lints/src/methods/bind_instead_of_map.rs @@ -1,4 +1,4 @@ -use super::{contains_return, BIND_INSTEAD_OF_MAP}; +use super::{BIND_INSTEAD_OF_MAP, contains_return}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::peel_blocks; use clippy_utils::source::{snippet, snippet_with_context}; diff --git a/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs b/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs index 740cce0a6c235..76bdbe55e2f3b 100644 --- a/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs +++ b/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs @@ -1,12 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::{indent_of, reindent_multiline, SpanRangeExt}; +use clippy_utils::source::{SpanRangeExt, indent_of, reindent_multiline}; use clippy_utils::ty::is_type_lang_item; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem}; use rustc_lint::LateContext; -use rustc_span::source_map::Spanned; use rustc_span::Span; +use rustc_span::source_map::Spanned; use super::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS; diff --git a/clippy_lints/src/methods/clear_with_drain.rs b/clippy_lints/src/methods/clear_with_drain.rs index 5389861245a28..6e24cabca8bba 100644 --- a/clippy_lints/src/methods/clear_with_drain.rs +++ b/clippy_lints/src/methods/clear_with_drain.rs @@ -4,8 +4,8 @@ use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem, QPath}; use rustc_lint::LateContext; -use rustc_span::symbol::sym; use rustc_span::Span; +use rustc_span::symbol::sym; use super::CLEAR_WITH_DRAIN; diff --git a/clippy_lints/src/methods/clone_on_copy.rs b/clippy_lints/src/methods/clone_on_copy.rs index e7a2060be046a..79a473e0e6f58 100644 --- a/clippy_lints/src/methods/clone_on_copy.rs +++ b/clippy_lints/src/methods/clone_on_copy.rs @@ -7,7 +7,7 @@ use rustc_lint::LateContext; use rustc_middle::ty::adjustment::Adjust; use rustc_middle::ty::print::with_forced_trimmed_paths; use rustc_middle::ty::{self}; -use rustc_span::symbol::{sym, Symbol}; +use rustc_span::symbol::{Symbol, sym}; use super::CLONE_ON_COPY; diff --git a/clippy_lints/src/methods/clone_on_ref_ptr.rs b/clippy_lints/src/methods/clone_on_ref_ptr.rs index e0826b53004b6..96e2de0dc1cbd 100644 --- a/clippy_lints/src/methods/clone_on_ref_ptr.rs +++ b/clippy_lints/src/methods/clone_on_ref_ptr.rs @@ -4,7 +4,7 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::symbol::{sym, Symbol}; +use rustc_span::symbol::{Symbol, sym}; use super::CLONE_ON_REF_PTR; diff --git a/clippy_lints/src/methods/cloned_instead_of_copied.rs b/clippy_lints/src/methods/cloned_instead_of_copied.rs index fcafa16223658..fa04f74eec107 100644 --- a/clippy_lints/src/methods/cloned_instead_of_copied.rs +++ b/clippy_lints/src/methods/cloned_instead_of_copied.rs @@ -6,7 +6,7 @@ use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use super::CLONED_INSTEAD_OF_COPIED; diff --git a/clippy_lints/src/methods/collapsible_str_replace.rs b/clippy_lints/src/methods/collapsible_str_replace.rs index 1fab6c0e499d4..f7bf8764bdebc 100644 --- a/clippy_lints/src/methods/collapsible_str_replace.rs +++ b/clippy_lints/src/methods/collapsible_str_replace.rs @@ -8,7 +8,7 @@ use rustc_hir as hir; use rustc_lint::LateContext; use std::collections::VecDeque; -use super::{method_call, COLLAPSIBLE_STR_REPLACE}; +use super::{COLLAPSIBLE_STR_REPLACE, method_call}; pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, diff --git a/clippy_lints/src/methods/drain_collect.rs b/clippy_lints/src/methods/drain_collect.rs index 56171a134522d..10360b4817bc9 100644 --- a/clippy_lints/src/methods/drain_collect.rs +++ b/clippy_lints/src/methods/drain_collect.rs @@ -8,7 +8,7 @@ use rustc_hir::{Expr, ExprKind, LangItem, Path, QPath}; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_middle::ty::Ty; -use rustc_span::{sym, Symbol}; +use rustc_span::{Symbol, sym}; /// Checks if both types match the given diagnostic item, e.g.: /// diff --git a/clippy_lints/src/methods/err_expect.rs b/clippy_lints/src/methods/err_expect.rs index dc978c8a58456..3b1adb16b80e9 100644 --- a/clippy_lints/src/methods/err_expect.rs +++ b/clippy_lints/src/methods/err_expect.rs @@ -6,7 +6,7 @@ use rustc_errors::Applicability; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_middle::ty::Ty; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; pub(super) fn check( cx: &LateContext<'_>, diff --git a/clippy_lints/src/methods/expect_fun_call.rs b/clippy_lints/src/methods/expect_fun_call.rs index c9f56e1d98097..2922086522c2a 100644 --- a/clippy_lints/src/methods/expect_fun_call.rs +++ b/clippy_lints/src/methods/expect_fun_call.rs @@ -1,13 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::macros::{format_args_inputs_span, root_macro_call_first_node, FormatArgsStorage}; +use clippy_utils::macros::{FormatArgsStorage, format_args_inputs_span, root_macro_call_first_node}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::symbol::sym; use rustc_span::Span; +use rustc_span::symbol::sym; use std::borrow::Cow; use super::EXPECT_FUN_CALL; diff --git a/clippy_lints/src/methods/filetype_is_file.rs b/clippy_lints/src/methods/filetype_is_file.rs index 2ab0401947c4c..35008c39c084d 100644 --- a/clippy_lints/src/methods/filetype_is_file.rs +++ b/clippy_lints/src/methods/filetype_is_file.rs @@ -3,7 +3,7 @@ use clippy_utils::get_parent_expr; use clippy_utils::ty::is_type_diagnostic_item; use rustc_hir as hir; use rustc_lint::LateContext; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use super::FILETYPE_IS_FILE; diff --git a/clippy_lints/src/methods/filter_map.rs b/clippy_lints/src/methods/filter_map.rs index 02a11257007a9..06482a743ddf4 100644 --- a/clippy_lints/src/methods/filter_map.rs +++ b/clippy_lints/src/methods/filter_map.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::macros::{is_panic, matching_root_macro_call, root_macro_call}; use clippy_utils::source::{indent_of, reindent_multiline, snippet}; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{higher, is_trait_method, path_to_local_id, peel_blocks, SpanlessEq}; +use clippy_utils::{SpanlessEq, higher, is_trait_method, path_to_local_id, peel_blocks}; use hir::{Body, HirId, MatchSource, Pat}; use rustc_errors::Applicability; use rustc_hir as hir; @@ -10,8 +10,8 @@ use rustc_hir::def::Res; use rustc_hir::{Closure, Expr, ExprKind, PatKind, PathSegment, QPath, UnOp}; use rustc_lint::LateContext; use rustc_middle::ty::adjustment::Adjust; -use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::Span; +use rustc_span::symbol::{Ident, Symbol, sym}; use std::borrow::Cow; use super::{MANUAL_FILTER_MAP, MANUAL_FIND_MAP, OPTION_FILTER_MAP, RESULT_FILTER_MAP}; diff --git a/clippy_lints/src/methods/filter_map_bool_then.rs b/clippy_lints/src/methods/filter_map_bool_then.rs index 77a62fbb4fb92..129e692542894 100644 --- a/clippy_lints/src/methods/filter_map_bool_then.rs +++ b/clippy_lints/src/methods/filter_map_bool_then.rs @@ -7,9 +7,9 @@ use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::adjustment::Adjust; use rustc_middle::ty::Binder; -use rustc_span::{sym, Span}; +use rustc_middle::ty::adjustment::Adjust; +use rustc_span::{Span, sym}; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: &Expr<'_>, call_span: Span) { if !in_external_macro(cx.sess(), expr.span) diff --git a/clippy_lints/src/methods/filter_map_identity.rs b/clippy_lints/src/methods/filter_map_identity.rs index 999df875c753f..cf7f276dabb10 100644 --- a/clippy_lints/src/methods/filter_map_identity.rs +++ b/clippy_lints/src/methods/filter_map_identity.rs @@ -3,7 +3,7 @@ use clippy_utils::{is_expr_identity_function, is_expr_untyped_identity_function, use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use super::FILTER_MAP_IDENTITY; diff --git a/clippy_lints/src/methods/flat_map_identity.rs b/clippy_lints/src/methods/flat_map_identity.rs index 651ea34f9d0e8..0c2ecfbc8ffdf 100644 --- a/clippy_lints/src/methods/flat_map_identity.rs +++ b/clippy_lints/src/methods/flat_map_identity.rs @@ -3,7 +3,7 @@ use clippy_utils::{is_expr_untyped_identity_function, is_trait_method}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use super::FLAT_MAP_IDENTITY; diff --git a/clippy_lints/src/methods/flat_map_option.rs b/clippy_lints/src/methods/flat_map_option.rs index 0a4a381b86186..3242dcadb7013 100644 --- a/clippy_lints/src/methods/flat_map_option.rs +++ b/clippy_lints/src/methods/flat_map_option.rs @@ -4,7 +4,7 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use super::FLAT_MAP_OPTION; use clippy_utils::ty::is_type_diagnostic_item; diff --git a/clippy_lints/src/methods/get_last_with_len.rs b/clippy_lints/src/methods/get_last_with_len.rs index 6203765113428..5f6fb4c821d57 100644 --- a/clippy_lints/src/methods/get_last_with_len.rs +++ b/clippy_lints/src/methods/get_last_with_len.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{is_integer_literal, SpanlessEq}; +use clippy_utils::{SpanlessEq, is_integer_literal}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; diff --git a/clippy_lints/src/methods/inefficient_to_string.rs b/clippy_lints/src/methods/inefficient_to_string.rs index 230a8eb2ec47a..4ed7de81ea3d0 100644 --- a/clippy_lints/src/methods/inefficient_to_string.rs +++ b/clippy_lints/src/methods/inefficient_to_string.rs @@ -5,7 +5,7 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; -use rustc_span::symbol::{sym, Symbol}; +use rustc_span::symbol::{Symbol, sym}; use super::INEFFICIENT_TO_STRING; diff --git a/clippy_lints/src/methods/inspect_for_each.rs b/clippy_lints/src/methods/inspect_for_each.rs index ad4b6fa130e35..3706f3b670ba1 100644 --- a/clippy_lints/src/methods/inspect_for_each.rs +++ b/clippy_lints/src/methods/inspect_for_each.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::is_trait_method; use rustc_hir as hir; use rustc_lint::LateContext; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use super::INSPECT_FOR_EACH; diff --git a/clippy_lints/src/methods/into_iter_on_ref.rs b/clippy_lints/src/methods/into_iter_on_ref.rs index bbc7ce8d78ab9..bedeb63367d06 100644 --- a/clippy_lints/src/methods/into_iter_on_ref.rs +++ b/clippy_lints/src/methods/into_iter_on_ref.rs @@ -5,8 +5,8 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; -use rustc_span::symbol::{sym, Symbol}; use rustc_span::Span; +use rustc_span::symbol::{Symbol, sym}; use super::INTO_ITER_ON_REF; diff --git a/clippy_lints/src/methods/iter_filter.rs b/clippy_lints/src/methods/iter_filter.rs index b93d51eac647b..f6612c984a7eb 100644 --- a/clippy_lints/src/methods/iter_filter.rs +++ b/clippy_lints/src/methods/iter_filter.rs @@ -10,8 +10,8 @@ use clippy_utils::{get_parent_expr, is_trait_method, peel_blocks, span_contains_ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::QPath; -use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::Span; +use rustc_span::symbol::{Ident, Symbol, sym}; use std::borrow::Cow; /// diff --git a/clippy_lints/src/methods/iter_kv_map.rs b/clippy_lints/src/methods/iter_kv_map.rs index 33de3b87abc82..390dd24b5058d 100644 --- a/clippy_lints/src/methods/iter_kv_map.rs +++ b/clippy_lints/src/methods/iter_kv_map.rs @@ -14,7 +14,6 @@ use rustc_span::sym; /// - `hashmap.into_iter().map(|(_, v)| v)` /// /// on `HashMaps` and `BTreeMaps` in std - pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, map_type: &'tcx str, // iter / into_iter diff --git a/clippy_lints/src/methods/iter_nth.rs b/clippy_lints/src/methods/iter_nth.rs index e31fa2f777d8e..82bda5d951225 100644 --- a/clippy_lints/src/methods/iter_nth.rs +++ b/clippy_lints/src/methods/iter_nth.rs @@ -3,8 +3,8 @@ use clippy_utils::ty::get_type_diagnostic_name; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; -use rustc_span::symbol::sym; use rustc_span::Span; +use rustc_span::symbol::sym; use super::ITER_NTH; 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 7f6b666e434e9..9d562f5e51d2c 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 @@ -5,9 +5,9 @@ use clippy_utils::source::snippet; use clippy_utils::{get_expr_use_or_unification_node, is_res_lang_ctor, path_res, std_or_core}; use rustc_errors::Applicability; +use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::def_id::DefId; use rustc_hir::hir_id::HirId; -use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::{Expr, ExprKind, Node}; use rustc_lint::LateContext; diff --git a/clippy_lints/src/methods/iter_with_drain.rs b/clippy_lints/src/methods/iter_with_drain.rs index 1378a07cbc477..163058713377b 100644 --- a/clippy_lints/src/methods/iter_with_drain.rs +++ b/clippy_lints/src/methods/iter_with_drain.rs @@ -3,8 +3,8 @@ use clippy_utils::is_range_full; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::LateContext; -use rustc_span::symbol::sym; use rustc_span::Span; +use rustc_span::symbol::sym; use super::ITER_WITH_DRAIN; diff --git a/clippy_lints/src/methods/join_absolute_paths.rs b/clippy_lints/src/methods/join_absolute_paths.rs index 2dad7fcf3c129..2ad070793cbb2 100644 --- a/clippy_lints/src/methods/join_absolute_paths.rs +++ b/clippy_lints/src/methods/join_absolute_paths.rs @@ -6,8 +6,8 @@ use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; -use rustc_span::symbol::sym; use rustc_span::Span; +use rustc_span::symbol::sym; use super::JOIN_ABSOLUTE_PATHS; diff --git a/clippy_lints/src/methods/manual_c_str_literals.rs b/clippy_lints/src/methods/manual_c_str_literals.rs index cb9fb373c10a6..96af9db1af709 100644 --- a/clippy_lints/src/methods/manual_c_str_literals.rs +++ b/clippy_lints/src/methods/manual_c_str_literals.rs @@ -6,7 +6,7 @@ use rustc_ast::{LitKind, StrStyle}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Node, QPath, TyKind}; use rustc_lint::LateContext; -use rustc_span::{sym, Span, Symbol}; +use rustc_span::{Span, Symbol, sym}; use super::MANUAL_C_STR_LITERALS; diff --git a/clippy_lints/src/methods/manual_inspect.rs b/clippy_lints/src/methods/manual_inspect.rs index cac2e11f59166..64c19c327b254 100644 --- a/clippy_lints/src/methods/manual_inspect.rs +++ b/clippy_lints/src/methods/manual_inspect.rs @@ -3,13 +3,13 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{IntoSpan, SpanRangeExt}; use clippy_utils::ty::get_field_by_name; use clippy_utils::visitors::{for_each_expr, for_each_expr_without_closures}; -use clippy_utils::{expr_use_ctxt, is_diag_item_method, is_diag_trait_item, path_to_local_id, ExprUseNode}; +use clippy_utils::{ExprUseNode, expr_use_ctxt, is_diag_item_method, is_diag_trait_item, path_to_local_id}; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::{BindingMode, BorrowKind, ByRef, ClosureKind, Expr, ExprKind, Mutability, Node, PatKind}; use rustc_lint::LateContext; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; -use rustc_span::{sym, Span, Symbol, DUMMY_SP}; +use rustc_span::{DUMMY_SP, Span, Symbol, sym}; use super::MANUAL_INSPECT; diff --git a/clippy_lints/src/methods/manual_is_variant_and.rs b/clippy_lints/src/methods/manual_is_variant_and.rs index d29acd4622a0b..c377abd62377a 100644 --- a/clippy_lints/src/methods/manual_is_variant_and.rs +++ b/clippy_lints/src/methods/manual_is_variant_and.rs @@ -4,7 +4,7 @@ use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_lint::LateContext; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use super::MANUAL_IS_VARIANT_AND; diff --git a/clippy_lints/src/methods/manual_ok_or.rs b/clippy_lints/src/methods/manual_ok_or.rs index b1a8e1e5e4709..4321dd6b0e098 100644 --- a/clippy_lints/src/methods/manual_ok_or.rs +++ b/clippy_lints/src/methods/manual_ok_or.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{indent_of, reindent_multiline, SpanRangeExt}; +use clippy_utils::source::{SpanRangeExt, indent_of, reindent_multiline}; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{is_res_lang_ctor, path_res, path_to_local_id}; use rustc_errors::Applicability; diff --git a/clippy_lints/src/methods/manual_try_fold.rs b/clippy_lints/src/methods/manual_try_fold.rs index 11fba35c16ea3..31449d417701c 100644 --- a/clippy_lints/src/methods/manual_try_fold.rs +++ b/clippy_lints/src/methods/manual_try_fold.rs @@ -8,7 +8,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use super::MANUAL_TRY_FOLD; diff --git a/clippy_lints/src/methods/map_clone.rs b/clippy_lints/src/methods/map_clone.rs index 08ce7e204dd27..ac378ff3702bb 100644 --- a/clippy_lints/src/methods/map_clone.rs +++ b/clippy_lints/src/methods/map_clone.rs @@ -11,7 +11,7 @@ use rustc_middle::mir::Mutability; use rustc_middle::ty; use rustc_middle::ty::adjustment::Adjust; use rustc_span::symbol::Ident; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use super::MAP_CLONE; diff --git a/clippy_lints/src/methods/map_flatten.rs b/clippy_lints/src/methods/map_flatten.rs index 22a03825194e0..07a7a12b16279 100644 --- a/clippy_lints/src/methods/map_flatten.rs +++ b/clippy_lints/src/methods/map_flatten.rs @@ -6,8 +6,8 @@ use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::symbol::sym; use rustc_span::Span; +use rustc_span::symbol::sym; use super::MAP_FLATTEN; diff --git a/clippy_lints/src/methods/map_identity.rs b/clippy_lints/src/methods/map_identity.rs index 5dd7b1b02adef..1f204de01da06 100644 --- a/clippy_lints/src/methods/map_identity.rs +++ b/clippy_lints/src/methods/map_identity.rs @@ -4,7 +4,7 @@ use clippy_utils::{is_expr_untyped_identity_function, is_trait_method}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use super::MAP_IDENTITY; diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index f61bb3a6bf481..7696dd16b251c 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -111,6 +111,7 @@ mod uninit_assumed_init; mod unit_hash; mod unnecessary_fallible_conversions; mod unnecessary_filter_map; +mod unnecessary_first_then_check; mod unnecessary_fold; mod unnecessary_get_then_check; mod unnecessary_iter_cloned; @@ -131,8 +132,8 @@ mod waker_clone_wake; mod wrong_self_convention; mod zst_offset; -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::macros::FormatArgsStorage; @@ -146,7 +147,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, TraitRef, Ty}; use rustc_session::impl_lint_pass; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does @@ -441,6 +442,17 @@ declare_clippy_lint! { /// } /// } /// ``` + /// + /// Use instead: + /// ```no_run + /// # struct X; + /// impl X { + /// fn as_str(&self) -> &'static str { + /// // .. + /// # "" + /// } + /// } + /// ``` #[clippy::version = "pre 1.29.0"] pub WRONG_SELF_CONVENTION, style, @@ -3964,7 +3976,7 @@ declare_clippy_lint! { /// ```no_run /// let _ = 0; /// ``` - #[clippy::version = "1.78.0"] + #[clippy::version = "1.81.0"] pub UNNECESSARY_MIN_OR_MAX, complexity, "using 'min()/max()' when there is no need for it" @@ -4025,7 +4037,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.78.0"] pub MANUAL_C_STR_LITERALS, - pedantic, + complexity, r#"creating a `CStr` through functions when `c""` literals can be used"# } @@ -4099,7 +4111,7 @@ declare_clippy_lint! { /// ```no_run /// "foo".is_ascii(); /// ``` - #[clippy::version = "1.80.0"] + #[clippy::version = "1.81.0"] pub NEEDLESS_CHARACTER_ITERATION, suspicious, "is_ascii() called on a char iterator" @@ -4126,6 +4138,34 @@ declare_clippy_lint! { "use of `map` returning the original item" } +declare_clippy_lint! { + /// ### What it does + /// Checks the usage of `.first().is_some()` or `.first().is_none()` to check if a slice is + /// empty. + /// + /// ### Why is this bad? + /// Using `.is_empty()` is shorter and better communicates the intention. + /// + /// ### Example + /// ```no_run + /// let v = vec![1, 2, 3]; + /// if v.first().is_none() { + /// // The vector is empty... + /// } + /// ``` + /// Use instead: + /// ```no_run + /// let v = vec![1, 2, 3]; + /// if v.is_empty() { + /// // The vector is empty... + /// } + /// ``` + #[clippy::version = "1.83.0"] + pub UNNECESSARY_FIRST_THEN_CHECK, + complexity, + "calling `.first().is_some()` or `.first().is_none()` instead of `.is_empty()`" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Msrv, @@ -4283,6 +4323,7 @@ impl_lint_pass!(Methods => [ UNNECESSARY_RESULT_MAP_OR_ELSE, MANUAL_C_STR_LITERALS, UNNECESSARY_GET_THEN_CHECK, + UNNECESSARY_FIRST_THEN_CHECK, NEEDLESS_CHARACTER_ITERATION, MANUAL_INSPECT, UNNECESSARY_MIN_OR_MAX, @@ -5055,6 +5096,9 @@ fn check_is_some_is_none(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, Some(("get", f_recv, [arg], _, _)) => { unnecessary_get_then_check::check(cx, call_span, recv, f_recv, arg, is_some); }, + Some(("first", f_recv, [], _, _)) => { + unnecessary_first_then_check::check(cx, call_span, recv, f_recv, is_some); + }, _ => {}, } } @@ -5130,12 +5174,9 @@ impl ShouldImplTraitCase { fn lifetime_param_cond(&self, impl_item: &hir::ImplItem<'_>) -> bool { self.lint_explicit_lifetime || !impl_item.generics.params.iter().any(|p| { - matches!( - p.kind, - hir::GenericParamKind::Lifetime { - kind: hir::LifetimeParamKind::Explicit - } - ) + matches!(p.kind, hir::GenericParamKind::Lifetime { + kind: hir::LifetimeParamKind::Explicit + }) }) } } diff --git a/clippy_lints/src/methods/mut_mutex_lock.rs b/clippy_lints/src/methods/mut_mutex_lock.rs index 1a0fce2876d40..83e8370f939bc 100644 --- a/clippy_lints/src/methods/mut_mutex_lock.rs +++ b/clippy_lints/src/methods/mut_mutex_lock.rs @@ -5,7 +5,7 @@ use rustc_errors::Applicability; use rustc_hir::{Expr, Mutability}; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use super::MUT_MUTEX_LOCK; diff --git a/clippy_lints/src/methods/needless_character_iteration.rs b/clippy_lints/src/methods/needless_character_iteration.rs index 332da722a37dc..348f740e7ddf7 100644 --- a/clippy_lints/src/methods/needless_character_iteration.rs +++ b/clippy_lints/src/methods/needless_character_iteration.rs @@ -4,8 +4,8 @@ use rustc_lint::LateContext; use rustc_middle::ty; use rustc_span::Span; -use super::utils::get_last_chain_binding_hir_id; use super::NEEDLESS_CHARACTER_ITERATION; +use super::utils::get_last_chain_binding_hir_id; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::SpanRangeExt; use clippy_utils::{match_def_path, path_to_local_id, peel_blocks}; diff --git a/clippy_lints/src/methods/needless_collect.rs b/clippy_lints/src/methods/needless_collect.rs index f61923e5bf569..421c7a5e070d9 100644 --- a/clippy_lints/src/methods/needless_collect.rs +++ b/clippy_lints/src/methods/needless_collect.rs @@ -4,12 +4,12 @@ use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{get_type_diagnostic_name, make_normalized_projection, make_projection}; use clippy_utils::{ - can_move_expr_to_closure, fn_def_id, get_enclosing_block, higher, is_trait_method, path_to_local, path_to_local_id, - CaptureKind, + CaptureKind, can_move_expr_to_closure, fn_def_id, get_enclosing_block, higher, is_trait_method, path_to_local, + path_to_local_id, }; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, MultiSpan}; -use rustc_hir::intravisit::{walk_block, walk_expr, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_block, walk_expr}; use rustc_hir::{ BindingMode, Block, Expr, ExprKind, HirId, HirIdSet, LetStmt, Mutability, Node, PatKind, Stmt, StmtKind, }; @@ -17,7 +17,7 @@ use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, AssocKind, ClauseKind, EarlyBinder, GenericArg, GenericArgKind, Ty}; use rustc_span::symbol::Ident; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; const NEEDLESS_COLLECT_MSG: &str = "avoid using `collect()` when not needed"; diff --git a/clippy_lints/src/methods/needless_option_as_deref.rs b/clippy_lints/src/methods/needless_option_as_deref.rs index 9f714fdd47bb3..538aa9097a40f 100644 --- a/clippy_lints/src/methods/needless_option_as_deref.rs +++ b/clippy_lints/src/methods/needless_option_as_deref.rs @@ -4,8 +4,8 @@ use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::usage::local_used_after_expr; use rustc_errors::Applicability; -use rustc_hir::def::Res; use rustc_hir::Expr; +use rustc_hir::def::Res; use rustc_lint::LateContext; use rustc_span::sym; diff --git a/clippy_lints/src/methods/no_effect_replace.rs b/clippy_lints/src/methods/no_effect_replace.rs index a301a5f7d65be..32f32f1b21673 100644 --- a/clippy_lints/src/methods/no_effect_replace.rs +++ b/clippy_lints/src/methods/no_effect_replace.rs @@ -1,6 +1,6 @@ +use clippy_utils::SpanlessEq; use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::is_type_lang_item; -use clippy_utils::SpanlessEq; use rustc_ast::LitKind; use rustc_hir::{ExprKind, LangItem}; use rustc_lint::LateContext; diff --git a/clippy_lints/src/methods/open_options.rs b/clippy_lints/src/methods/open_options.rs index f1a3c81cebbdb..da084871402a7 100644 --- a/clippy_lints/src/methods/open_options.rs +++ b/clippy_lints/src/methods/open_options.rs @@ -8,7 +8,7 @@ use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::Ty; use rustc_span::source_map::Spanned; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use super::{NONSENSICAL_OPEN_OPTIONS, SUSPICIOUS_OPEN_OPTIONS}; @@ -126,17 +126,13 @@ fn get_open_options( && let ExprKind::Path(path) = callee.kind && let Some(did) = cx.qpath_res(&path, callee.hir_id).opt_def_id() { - let std_file_options = [ - sym::file_options, - sym::open_options_new, - ]; + let std_file_options = [sym::file_options, sym::open_options_new]; - let tokio_file_options: &[&[&str]] = &[ - &paths::TOKIO_IO_OPEN_OPTIONS_NEW, - &paths::TOKIO_FILE_OPTIONS, - ]; + let tokio_file_options: &[&[&str]] = &[&paths::TOKIO_IO_OPEN_OPTIONS_NEW, &paths::TOKIO_FILE_OPTIONS]; - let is_std_options = std_file_options.into_iter().any(|sym| cx.tcx.is_diagnostic_item(sym, did)); + let is_std_options = std_file_options + .into_iter() + .any(|sym| cx.tcx.is_diagnostic_item(sym, did)); is_std_options || match_any_def_paths(cx, did, tokio_file_options).is_some() } else { false diff --git a/clippy_lints/src/methods/option_as_ref_cloned.rs b/clippy_lints/src/methods/option_as_ref_cloned.rs index ba167f9d9c230..9b22494888fb2 100644 --- a/clippy_lints/src/methods/option_as_ref_cloned.rs +++ b/clippy_lints/src/methods/option_as_ref_cloned.rs @@ -3,9 +3,9 @@ use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; -use super::{method_call, OPTION_AS_REF_CLONED}; +use super::{OPTION_AS_REF_CLONED, method_call}; pub(super) fn check(cx: &LateContext<'_>, cloned_recv: &Expr<'_>, cloned_ident_span: Span) { if let Some((method @ ("as_ref" | "as_mut"), as_ref_recv, [], as_ref_ident_span, _)) = method_call(cloned_recv) diff --git a/clippy_lints/src/methods/option_as_ref_deref.rs b/clippy_lints/src/methods/option_as_ref_deref.rs index 9a18d8a14219f..389e02056b2b8 100644 --- a/clippy_lints/src/methods/option_as_ref_deref.rs +++ b/clippy_lints/src/methods/option_as_ref_deref.rs @@ -7,7 +7,7 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::{sym, Symbol}; +use rustc_span::{Symbol, sym}; use super::OPTION_AS_REF_DEREF; @@ -48,7 +48,9 @@ pub(super) fn check( .map_or(false, |fun_def_id| { cx.tcx.is_diagnostic_item(sym::deref_method, fun_def_id) || cx.tcx.is_diagnostic_item(sym::deref_mut_method, fun_def_id) - || deref_aliases.iter().any(|&sym| cx.tcx.is_diagnostic_item(sym, fun_def_id)) + || deref_aliases + .iter() + .any(|&sym| cx.tcx.is_diagnostic_item(sym, fun_def_id)) }) }, hir::ExprKind::Closure(&hir::Closure { body, .. }) => { @@ -69,7 +71,9 @@ pub(super) fn check( let method_did = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id).unwrap(); cx.tcx.is_diagnostic_item(sym::deref_method, method_did) || cx.tcx.is_diagnostic_item(sym::deref_mut_method, method_did) - || deref_aliases.iter().any(|&sym| cx.tcx.is_diagnostic_item(sym, method_did)) + || deref_aliases + .iter() + .any(|&sym| cx.tcx.is_diagnostic_item(sym, method_did)) } else { false } diff --git a/clippy_lints/src/methods/option_map_unwrap_or.rs b/clippy_lints/src/methods/option_map_unwrap_or.rs index ed3bed42eb3a9..b160ab6de8e95 100644 --- a/clippy_lints/src/methods/option_map_unwrap_or.rs +++ b/clippy_lints/src/methods/option_map_unwrap_or.rs @@ -5,11 +5,11 @@ use clippy_utils::ty::{is_copy, is_type_diagnostic_item}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::def::Res; -use rustc_hir::intravisit::{walk_path, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_path}; use rustc_hir::{ExprKind, HirId, Node, PatKind, Path, QPath}; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use std::ops::ControlFlow; use super::MAP_UNWRAP_OR; diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index e4326cb958e5f..8a7e72b63bb5e 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -11,8 +11,8 @@ use clippy_utils::{ use rustc_errors::Applicability; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::symbol::{self, sym, Symbol}; use rustc_span::Span; +use rustc_span::symbol::{self, Symbol, sym}; use {rustc_ast as ast, rustc_hir as hir}; use super::{OR_FUN_CALL, UNWRAP_OR_DEFAULT}; diff --git a/clippy_lints/src/methods/or_then_unwrap.rs b/clippy_lints/src/methods/or_then_unwrap.rs index 7b0bdcf99e736..3e64e15dc8602 100644 --- a/clippy_lints/src/methods/or_then_unwrap.rs +++ b/clippy_lints/src/methods/or_then_unwrap.rs @@ -6,7 +6,7 @@ use rustc_errors::Applicability; use rustc_hir::lang_items::LangItem; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use super::OR_THEN_UNWRAP; diff --git a/clippy_lints/src/methods/range_zip_with_len.rs b/clippy_lints/src/methods/range_zip_with_len.rs index 28ca76832eb3a..f4d206c5307c1 100644 --- a/clippy_lints/src/methods/range_zip_with_len.rs +++ b/clippy_lints/src/methods/range_zip_with_len.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::source::snippet; -use clippy_utils::{higher, is_integer_const, is_trait_method, SpanlessEq}; +use clippy_utils::{SpanlessEq, higher, is_integer_const, is_trait_method}; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::LateContext; use rustc_span::sym; diff --git a/clippy_lints/src/methods/search_is_some.rs b/clippy_lints/src/methods/search_is_some.rs index 3b2dd285b8c99..4ab165a5528bb 100644 --- a/clippy_lints/src/methods/search_is_some.rs +++ b/clippy_lints/src/methods/search_is_some.rs @@ -8,8 +8,8 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::PatKind; use rustc_lint::LateContext; -use rustc_span::symbol::sym; use rustc_span::Span; +use rustc_span::symbol::sym; use super::SEARCH_IS_SOME; diff --git a/clippy_lints/src/methods/seek_from_current.rs b/clippy_lints/src/methods/seek_from_current.rs index 8bc535ac47a84..7ef07fe899c08 100644 --- a/clippy_lints/src/methods/seek_from_current.rs +++ b/clippy_lints/src/methods/seek_from_current.rs @@ -6,9 +6,9 @@ use rustc_lint::LateContext; use rustc_span::sym; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_enum_variant_ctor; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::implements_trait; -use clippy_utils::is_enum_variant_ctor; use super::SEEK_FROM_CURRENT; diff --git a/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs b/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs index 7c09cc252e13a..9c966f010f130 100644 --- a/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs +++ b/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs @@ -1,12 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::implements_trait; -use clippy_utils::{is_expr_used_or_unified, is_enum_variant_ctor}; +use clippy_utils::{is_enum_variant_ctor, is_expr_used_or_unified}; use rustc_ast::ast::{LitIntType, LitKind}; use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use super::SEEK_TO_START_INSTEAD_OF_REWIND; diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs index 12cabd43cb1b2..69032776b2b9f 100644 --- a/clippy_lints/src/methods/str_splitn.rs +++ b/clippy_lints/src/methods/str_splitn.rs @@ -3,7 +3,7 @@ use clippy_utils::consts::{ConstEvalCtxt, 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::{for_each_expr, Descend}; +use clippy_utils::visitors::{Descend, for_each_expr}; use clippy_utils::{is_diag_item_method, match_def_path, path_to_local_id, paths}; use core::ops::ControlFlow; use rustc_errors::Applicability; @@ -12,7 +12,7 @@ use rustc_hir::{ }; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::{sym, Span, Symbol, SyntaxContext}; +use rustc_span::{Span, Symbol, SyntaxContext, sym}; use super::{MANUAL_SPLIT_ONCE, NEEDLESS_SPLITN}; diff --git a/clippy_lints/src/methods/suspicious_command_arg_space.rs b/clippy_lints/src/methods/suspicious_command_arg_space.rs index 38f2c9169124a..c60a49067ec09 100644 --- a/clippy_lints/src/methods/suspicious_command_arg_space.rs +++ b/clippy_lints/src/methods/suspicious_command_arg_space.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::{Applicability, Diag}; use rustc_lint::LateContext; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use {rustc_ast as ast, rustc_hir as hir}; use super::SUSPICIOUS_COMMAND_ARG_SPACE; diff --git a/clippy_lints/src/methods/type_id_on_box.rs b/clippy_lints/src/methods/type_id_on_box.rs index db8cc4595d4df..e67ba5c4d3148 100644 --- a/clippy_lints/src/methods/type_id_on_box.rs +++ b/clippy_lints/src/methods/type_id_on_box.rs @@ -7,7 +7,7 @@ use rustc_lint::LateContext; use rustc_middle::ty::adjustment::{Adjust, Adjustment}; use rustc_middle::ty::print::with_forced_trimmed_paths; use rustc_middle::ty::{self, ExistentialPredicate, Ty}; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; /// Checks if the given type is `dyn Any`, or a trait object that has `Any` as a supertrait. /// Only in those cases will its vtable have a `type_id` method that returns the implementor's diff --git a/clippy_lints/src/methods/unnecessary_fallible_conversions.rs b/clippy_lints/src/methods/unnecessary_fallible_conversions.rs index d46b584f8f444..ce81282ddfeb0 100644 --- a/clippy_lints/src/methods/unnecessary_fallible_conversions.rs +++ b/clippy_lints/src/methods/unnecessary_fallible_conversions.rs @@ -6,7 +6,7 @@ use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_middle::ty::print::with_forced_trimmed_paths; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use super::UNNECESSARY_FALLIBLE_CONVERSIONS; diff --git a/clippy_lints/src/methods/unnecessary_filter_map.rs b/clippy_lints/src/methods/unnecessary_filter_map.rs index c9b9d98dbe607..ca46da81fa053 100644 --- a/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -2,7 +2,7 @@ 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::visitors::{for_each_expr_without_closures, Descend}; +use clippy_utils::visitors::{Descend, for_each_expr_without_closures}; use clippy_utils::{is_res_lang_ctor, is_trait_method, path_res, path_to_local_id}; use core::ops::ControlFlow; use rustc_hir as hir; diff --git a/clippy_lints/src/methods/unnecessary_first_then_check.rs b/clippy_lints/src/methods/unnecessary_first_then_check.rs new file mode 100644 index 0000000000000..7ae1bb54e6098 --- /dev/null +++ b/clippy_lints/src/methods/unnecessary_first_then_check.rs @@ -0,0 +1,56 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::SpanRangeExt; + +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_span::Span; + +use super::UNNECESSARY_FIRST_THEN_CHECK; + +pub(super) fn check( + cx: &LateContext<'_>, + call_span: Span, + first_call: &Expr<'_>, + first_caller: &Expr<'_>, + is_some: bool, +) { + if !cx + .typeck_results() + .expr_ty_adjusted(first_caller) + .peel_refs() + .is_slice() + { + return; + } + + let ExprKind::MethodCall(_, _, _, first_call_span) = first_call.kind else { + return; + }; + + let both_calls_span = first_call_span.with_hi(call_span.hi()); + if let Some(both_calls_snippet) = both_calls_span.get_source_text(cx) + && let Some(first_caller_snippet) = first_caller.span.get_source_text(cx) + { + let (sugg_span, suggestion) = if is_some { + ( + first_caller.span.with_hi(call_span.hi()), + format!("!{first_caller_snippet}.is_empty()"), + ) + } else { + (both_calls_span, "is_empty()".to_owned()) + }; + span_lint_and_sugg( + cx, + UNNECESSARY_FIRST_THEN_CHECK, + sugg_span, + format!( + "unnecessary use of `{both_calls_snippet}` to check if slice {}", + if is_some { "is not empty" } else { "is empty" } + ), + "replace this with", + suggestion, + Applicability::MaybeIncorrect, + ); + } +} diff --git a/clippy_lints/src/methods/unnecessary_fold.rs b/clippy_lints/src/methods/unnecessary_fold.rs index ccc8d17970ece..b5d8972d7aad5 100644 --- a/clippy_lints/src/methods/unnecessary_fold.rs +++ b/clippy_lints/src/methods/unnecessary_fold.rs @@ -8,7 +8,7 @@ use rustc_hir as hir; use rustc_hir::PatKind; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use super::UNNECESSARY_FOLD; @@ -123,58 +123,32 @@ pub(super) fn check( if let hir::ExprKind::Lit(lit) = init.kind { match lit.node { ast::LitKind::Bool(false) => { - check_fold_with_op( - cx, - expr, - acc, - fold_span, - hir::BinOpKind::Or, - Replacement { - has_args: true, - has_generic_return: false, - method_name: "any", - }, - ); + check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Or, Replacement { + has_args: true, + has_generic_return: false, + method_name: "any", + }); }, ast::LitKind::Bool(true) => { - check_fold_with_op( - cx, - expr, - acc, - fold_span, - hir::BinOpKind::And, - Replacement { - has_args: true, - has_generic_return: false, - method_name: "all", - }, - ); + check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::And, Replacement { + has_args: true, + has_generic_return: false, + method_name: "all", + }); }, - ast::LitKind::Int(Pu128(0), _) => check_fold_with_op( - cx, - expr, - acc, - fold_span, - hir::BinOpKind::Add, - Replacement { + ast::LitKind::Int(Pu128(0), _) => { + check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Add, Replacement { has_args: false, has_generic_return: needs_turbofish(cx, expr), method_name: "sum", - }, - ), + }); + }, ast::LitKind::Int(Pu128(1), _) => { - check_fold_with_op( - cx, - expr, - acc, - fold_span, - hir::BinOpKind::Mul, - Replacement { - has_args: false, - has_generic_return: needs_turbofish(cx, expr), - method_name: "product", - }, - ); + check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Mul, Replacement { + has_args: false, + has_generic_return: needs_turbofish(cx, expr), + method_name: "product", + }); }, _ => (), } diff --git a/clippy_lints/src/methods/unnecessary_get_then_check.rs b/clippy_lints/src/methods/unnecessary_get_then_check.rs index 64eb84117954c..39fce2c40c91d 100644 --- a/clippy_lints/src/methods/unnecessary_get_then_check.rs +++ b/clippy_lints/src/methods/unnecessary_get_then_check.rs @@ -6,7 +6,7 @@ use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::Ty; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use super::UNNECESSARY_GET_THEN_CHECK; diff --git a/clippy_lints/src/methods/unnecessary_iter_cloned.rs b/clippy_lints/src/methods/unnecessary_iter_cloned.rs index 4d18bc7ac7777..029704882dde2 100644 --- a/clippy_lints/src/methods/unnecessary_iter_cloned.rs +++ b/clippy_lints/src/methods/unnecessary_iter_cloned.rs @@ -10,7 +10,7 @@ use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_hir::{BindingMode, Expr, ExprKind, Node, PatKind}; use rustc_lint::LateContext; -use rustc_span::{sym, Symbol}; +use rustc_span::{Symbol, sym}; use super::UNNECESSARY_TO_OWNED; diff --git a/clippy_lints/src/methods/unnecessary_literal_unwrap.rs b/clippy_lints/src/methods/unnecessary_literal_unwrap.rs index 494d71fc053f6..6dc8adb42dfbe 100644 --- a/clippy_lints/src/methods/unnecessary_literal_unwrap.rs +++ b/clippy_lints/src/methods/unnecessary_literal_unwrap.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{is_res_lang_ctor, last_path_segment, path_res, MaybePath}; +use clippy_utils::{MaybePath, is_res_lang_ctor, last_path_segment, path_res}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; diff --git a/clippy_lints/src/methods/unnecessary_min_or_max.rs b/clippy_lints/src/methods/unnecessary_min_or_max.rs index 86c0a6322b666..062d1348555cd 100644 --- a/clippy_lints/src/methods/unnecessary_min_or_max.rs +++ b/clippy_lints/src/methods/unnecessary_min_or_max.rs @@ -1,16 +1,15 @@ use std::cmp::Ordering; use super::UNNECESSARY_MIN_OR_MAX; -use clippy_utils::diagnostics::span_lint_and_sugg; - use clippy_utils::consts::{ConstEvalCtxt, Constant, ConstantSource, FullInt}; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::Span; +use rustc_span::{Span, sym}; pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, @@ -21,26 +20,30 @@ pub(super) fn check<'tcx>( ) { let typeck_results = cx.typeck_results(); let ecx = ConstEvalCtxt::with_env(cx.tcx, cx.param_env, typeck_results); - if let Some((left, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(recv) - && let Some((right, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(arg) + if let Some(id) = typeck_results.type_dependent_def_id(expr.hir_id) + && (cx.tcx.is_diagnostic_item(sym::cmp_ord_min, id) || cx.tcx.is_diagnostic_item(sym::cmp_ord_max, id)) { - let Some(ord) = Constant::partial_cmp(cx.tcx, typeck_results.expr_ty(recv), &left, &right) else { - return; - }; + if let Some((left, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(recv) + && let Some((right, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(arg) + { + let Some(ord) = Constant::partial_cmp(cx.tcx, typeck_results.expr_ty(recv), &left, &right) else { + return; + }; - lint(cx, expr, name, recv.span, arg.span, ord); - } else if let Some(extrema) = detect_extrema(cx, recv) { - let ord = match extrema { - Extrema::Minimum => Ordering::Less, - Extrema::Maximum => Ordering::Greater, - }; - lint(cx, expr, name, recv.span, arg.span, ord); - } else if let Some(extrema) = detect_extrema(cx, arg) { - let ord = match extrema { - Extrema::Minimum => Ordering::Greater, - Extrema::Maximum => Ordering::Less, - }; - lint(cx, expr, name, recv.span, arg.span, ord); + lint(cx, expr, name, recv.span, arg.span, ord); + } else if let Some(extrema) = detect_extrema(cx, recv) { + let ord = match extrema { + Extrema::Minimum => Ordering::Less, + Extrema::Maximum => Ordering::Greater, + }; + lint(cx, expr, name, recv.span, arg.span, ord); + } else if let Some(extrema) = detect_extrema(cx, arg) { + let ord = match extrema { + Extrema::Minimum => Ordering::Greater, + Extrema::Maximum => Ordering::Less, + }; + lint(cx, expr, name, recv.span, arg.span, ord); + } } } diff --git a/clippy_lints/src/methods/unnecessary_result_map_or_else.rs b/clippy_lints/src/methods/unnecessary_result_map_or_else.rs index c14a87c153414..dc50717112d89 100644 --- a/clippy_lints/src/methods/unnecessary_result_map_or_else.rs +++ b/clippy_lints/src/methods/unnecessary_result_map_or_else.rs @@ -8,8 +8,8 @@ use rustc_hir::{Closure, Expr, ExprKind, HirId, QPath}; use rustc_lint::LateContext; use rustc_span::symbol::sym; -use super::utils::get_last_chain_binding_hir_id; use super::UNNECESSARY_RESULT_MAP_OR_ELSE; +use super::utils::get_last_chain_binding_hir_id; fn emit_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, def_arg: &Expr<'_>) { let msg = "unused \"map closure\" when calling `Result::map_or_else` value"; diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index bf7555a29e220..cfa1fdb81372f 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -2,12 +2,11 @@ use super::implicit_clone::is_clone_like; use super::unnecessary_iter_cloned::{self, is_into_iter}; use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::source::{snippet, SpanRangeExt}; +use clippy_utils::source::{SpanRangeExt, snippet}; use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, is_type_diagnostic_item, is_type_lang_item}; use clippy_utils::visitors::find_all_ret_expressions; use clippy_utils::{ - fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, peel_middle_ty_refs, - return_ty, + fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, peel_middle_ty_refs, return_ty, }; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -20,7 +19,7 @@ use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref}; use rustc_middle::ty::{ self, ClauseKind, GenericArg, GenericArgKind, GenericArgsRef, ParamTy, ProjectionPredicate, TraitPredicate, Ty, }; -use rustc_span::{sym, Symbol}; +use rustc_span::{Symbol, sym}; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_trait_selection::traits::{Obligation, ObligationCause}; @@ -717,7 +716,7 @@ fn check_if_applicable_to_argument<'tcx>(cx: &LateContext<'tcx>, arg: &Expr<'tcx // check that: // 1. This is a method with only one argument that doesn't come from a trait. // 2. That it has `Borrow` in its generic predicates. -// 3. `Self` is a std "map type" (ie `HashSet`, `HashMap`, BTreeSet`, `BTreeMap`). +// 3. `Self` is a std "map type" (ie `HashSet`, `HashMap`, `BTreeSet`, `BTreeMap`). fn check_borrow_predicate<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { if let ExprKind::MethodCall(_, caller, &[arg], _) = expr.kind && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) diff --git a/clippy_lints/src/methods/unused_enumerate_index.rs b/clippy_lints/src/methods/unused_enumerate_index.rs index ee5177d1ffaa1..6c2ae9cc6bffd 100644 --- a/clippy_lints/src/methods/unused_enumerate_index.rs +++ b/clippy_lints/src/methods/unused_enumerate_index.rs @@ -1,11 +1,11 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::source::{snippet, SpanRangeExt}; +use clippy_utils::source::{SpanRangeExt, snippet}; use clippy_utils::{expr_or_init, is_trait_method, pat_is_wild}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, FnDecl, PatKind, TyKind}; use rustc_lint::LateContext; use rustc_middle::ty::AdtDef; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use crate::loops::UNUSED_ENUMERATE_INDEX; diff --git a/clippy_lints/src/methods/useless_asref.rs b/clippy_lints/src/methods/useless_asref.rs index ebad4ae6ee95d..eafe7486bb05a 100644 --- a/clippy_lints/src/methods/useless_asref.rs +++ b/clippy_lints/src/methods/useless_asref.rs @@ -9,7 +9,7 @@ use rustc_hir::{self as hir, LangItem}; use rustc_lint::LateContext; use rustc_middle::ty::adjustment::Adjust; use rustc_middle::ty::{Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor}; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use core::ops::ControlFlow; diff --git a/clippy_lints/src/methods/utils.rs b/clippy_lints/src/methods/utils.rs index fe860e5ae2601..4e33dc1df54d8 100644 --- a/clippy_lints/src/methods/utils.rs +++ b/clippy_lints/src/methods/utils.rs @@ -1,12 +1,12 @@ use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{get_parent_expr, path_to_local_id, usage}; -use rustc_hir::intravisit::{walk_expr, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, Mutability, Pat, QPath, Stmt, StmtKind}; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, Ty}; -use rustc_span::symbol::sym; use rustc_span::Span; +use rustc_span::symbol::sym; pub(super) fn derefs_to_slice<'tcx>( cx: &LateContext<'tcx>, diff --git a/clippy_lints/src/methods/vec_resize_to_zero.rs b/clippy_lints/src/methods/vec_resize_to_zero.rs index 3e271a606115a..5ea4ada128a24 100644 --- a/clippy_lints/src/methods/vec_resize_to_zero.rs +++ b/clippy_lints/src/methods/vec_resize_to_zero.rs @@ -6,7 +6,7 @@ use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; use rustc_span::source_map::Spanned; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use super::VEC_RESIZE_TO_ZERO; diff --git a/clippy_lints/src/methods/waker_clone_wake.rs b/clippy_lints/src/methods/waker_clone_wake.rs index 9b64cc7589ccc..b5f34a9be2e23 100644 --- a/clippy_lints/src/methods/waker_clone_wake.rs +++ b/clippy_lints/src/methods/waker_clone_wake.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_with_applicability; use clippy_utils::is_trait_method; +use clippy_utils::source::snippet_with_applicability; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; diff --git a/clippy_lints/src/min_ident_chars.rs b/clippy_lints/src/min_ident_chars.rs index c83e5198c2748..a99e21d938c64 100644 --- a/clippy_lints/src/min_ident_chars.rs +++ b/clippy_lints/src/min_ident_chars.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::is_from_proc_macro; use rustc_data_structures::fx::FxHashSet; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::intravisit::{walk_item, walk_trait_item, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_item, walk_trait_item}; use rustc_hir::{GenericParamKind, HirId, Item, ItemKind, ItemLocalId, Node, Pat, PatKind, TraitItem, UsePath}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index a9aafe7ed56cb..408dbef9cb16a 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -2,11 +2,12 @@ use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir, span_lint_hir use clippy_utils::source::{snippet, snippet_with_context}; use clippy_utils::sugg::Sugg; use clippy_utils::{ - fulfill_or_allowed, get_parent_expr, in_automatically_derived, is_lint_allowed, iter_input_pats, last_path_segment, - SpanlessEq, + SpanlessEq, fulfill_or_allowed, get_parent_expr, in_automatically_derived, is_lint_allowed, iter_input_pats, + last_path_segment, }; use rustc_errors::Applicability; use rustc_hir::def::Res; +use rustc_hir::def_id::LOCAL_CRATE; use rustc_hir::intravisit::FnKind; use rustc_hir::{ BinOpKind, BindingMode, Body, ByRef, Expr, ExprKind, FnDecl, Mutability, PatKind, QPath, Stmt, StmtKind, @@ -14,8 +15,8 @@ use rustc_hir::{ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::declare_lint_pass; -use rustc_span::def_id::LocalDefId; use rustc_span::Span; +use rustc_span::def_id::LocalDefId; use crate::ref_patterns::REF_PATTERNS; @@ -80,6 +81,45 @@ declare_clippy_lint! { "using a binding which is prefixed with an underscore" } +declare_clippy_lint! { + /// ### What it does + /// Checks for the use of item with a single leading + /// underscore. + /// + /// ### Why is this bad? + /// A single leading underscore is usually used to indicate + /// that a item will not be used. Using such a item breaks this + /// expectation. + /// + /// ### Example + /// ```no_run + /// fn _foo() {} + /// + /// struct _FooStruct {} + /// + /// fn main() { + /// _foo(); + /// let _ = _FooStruct{}; + /// } + /// ``` + /// + /// Use instead: + /// ```no_run + /// fn foo() {} + /// + /// struct FooStruct {} + /// + /// fn main() { + /// foo(); + /// let _ = FooStruct{}; + /// } + /// ``` + #[clippy::version = "pre 1.29.0"] + pub USED_UNDERSCORE_ITEMS, + pedantic, + "using a item which is prefixed with an underscore" +} + declare_clippy_lint! { /// ### What it does /// Checks for the use of short circuit boolean conditions as @@ -104,6 +144,7 @@ declare_clippy_lint! { declare_lint_pass!(LintPass => [ TOPLEVEL_REF_ARG, USED_UNDERSCORE_BINDING, + USED_UNDERSCORE_ITEMS, SHORT_CIRCUIT_STATEMENT, ]); @@ -205,51 +246,104 @@ impl<'tcx> LateLintPass<'tcx> for LintPass { { return; } - let (definition_hir_id, ident) = match expr.kind { - ExprKind::Path(ref qpath) => { - if let QPath::Resolved(None, path) = qpath - && let Res::Local(id) = path.res - && is_used(cx, expr) - { - (id, last_path_segment(qpath).ident) - } else { - return; - } - }, - ExprKind::Field(recv, ident) => { - if let Some(adt_def) = cx.typeck_results().expr_ty_adjusted(recv).ty_adt_def() - && let Some(field) = adt_def.all_fields().find(|field| field.name == ident.name) - && let Some(local_did) = field.did.as_local() - && !cx.tcx.type_of(field.did).skip_binder().is_phantom_data() - { - (cx.tcx.local_def_id_to_hir_id(local_did), ident) - } else { - return; - } + + used_underscore_binding(cx, expr); + used_underscore_items(cx, expr); + } +} + +fn used_underscore_items<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + let (def_id, ident) = match expr.kind { + ExprKind::Call(func, ..) => { + if let ExprKind::Path(QPath::Resolved(.., path)) = func.kind + && let Some(last_segment) = path.segments.last() + && let Res::Def(_, def_id) = last_segment.res + { + (def_id, last_segment.ident) + } else { + return; + } + }, + ExprKind::MethodCall(path, ..) => { + if let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) { + (def_id, path.ident) + } else { + return; + } + }, + ExprKind::Struct(QPath::Resolved(_, path), ..) => { + if let Some(last_segment) = path.segments.last() + && let Res::Def(_, def_id) = last_segment.res + { + (def_id, last_segment.ident) + } else { + return; + } + }, + _ => return, + }; + + let name = ident.name.as_str(); + let definition_span = cx.tcx.def_span(def_id); + if name.starts_with('_') + && !name.starts_with("__") + && !definition_span.from_expansion() + && def_id.krate == LOCAL_CRATE + { + span_lint_and_then( + cx, + USED_UNDERSCORE_ITEMS, + expr.span, + "used underscore-prefixed item".to_string(), + |diag| { + diag.span_note(definition_span, "item is defined here".to_string()); }, - _ => return, - }; + ); + } +} - let name = ident.name.as_str(); - if name.starts_with('_') - && !name.starts_with("__") - && let definition_span = cx.tcx.hir().span(definition_hir_id) - && !definition_span.from_expansion() - && !fulfill_or_allowed(cx, USED_UNDERSCORE_BINDING, [expr.hir_id, definition_hir_id]) - { - span_lint_and_then( - cx, - USED_UNDERSCORE_BINDING, - expr.span, - format!( - "used binding `{name}` which is prefixed with an underscore. A leading \ - underscore signals that a binding will not be used" - ), - |diag| { - diag.span_note(definition_span, format!("`{name}` is defined here")); - }, - ); - } +fn used_underscore_binding<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + let (definition_hir_id, ident) = match expr.kind { + ExprKind::Path(ref qpath) => { + if let QPath::Resolved(None, path) = qpath + && let Res::Local(id) = path.res + && is_used(cx, expr) + { + (id, last_path_segment(qpath).ident) + } else { + return; + } + }, + ExprKind::Field(recv, ident) => { + if let Some(adt_def) = cx.typeck_results().expr_ty_adjusted(recv).ty_adt_def() + && let Some(field) = adt_def.all_fields().find(|field| field.name == ident.name) + && let Some(local_did) = field.did.as_local() + && !cx.tcx.type_of(field.did).skip_binder().is_phantom_data() + { + (cx.tcx.local_def_id_to_hir_id(local_did), ident) + } else { + return; + } + }, + _ => return, + }; + + let name = ident.name.as_str(); + if name.starts_with('_') + && !name.starts_with("__") + && let definition_span = cx.tcx.hir().span(definition_hir_id) + && !definition_span.from_expansion() + && !fulfill_or_allowed(cx, USED_UNDERSCORE_BINDING, [expr.hir_id, definition_hir_id]) + { + span_lint_and_then( + cx, + USED_UNDERSCORE_BINDING, + expr.span, + "used underscore-prefixed binding".to_string(), + |diag| { + diag.span_note(definition_span, "binding is defined here".to_string()); + }, + ); } } diff --git a/clippy_lints/src/missing_assert_message.rs b/clippy_lints/src/missing_assert_message.rs index a9ea11f4c2b06..86348f04600b8 100644 --- a/clippy_lints/src/missing_assert_message.rs +++ b/clippy_lints/src/missing_assert_message.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_in_test; -use clippy_utils::macros::{find_assert_args, find_assert_eq_args, root_macro_call_first_node, PanicExpn}; +use clippy_utils::macros::{PanicExpn, find_assert_args, find_assert_eq_args, root_macro_call_first_node}; use rustc_hir::Expr; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; diff --git a/clippy_lints/src/missing_asserts_for_indexing.rs b/clippy_lints/src/missing_asserts_for_indexing.rs index 94c91b095171f..b40d7eba15e5e 100644 --- a/clippy_lints/src/missing_asserts_for_indexing.rs +++ b/clippy_lints/src/missing_asserts_for_indexing.rs @@ -1,7 +1,7 @@ use std::mem; use std::ops::ControlFlow; -use clippy_utils::comparisons::{normalize_comparison, Rel}; +use clippy_utils::comparisons::{Rel, normalize_comparison}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use clippy_utils::visitors::for_each_expr_without_closures; @@ -14,7 +14,7 @@ use rustc_hir::{BinOp, Block, Body, Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::source_map::Spanned; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs index 859afe1b9636e..eea0459e026e8 100644 --- a/clippy_lints/src/missing_const_for_fn.rs +++ b/clippy_lints/src/missing_const_for_fn.rs @@ -1,5 +1,5 @@ -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::qualify_min_const_fn::is_min_const_fn; use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, is_from_proc_macro, trait_ref_of_method}; @@ -11,8 +11,8 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty; use rustc_session::impl_lint_pass; -use rustc_span::def_id::LocalDefId; use rustc_span::Span; +use rustc_span::def_id::LocalDefId; use rustc_target::spec::abi::Abi; declare_clippy_lint! { diff --git a/clippy_lints/src/missing_const_for_thread_local.rs b/clippy_lints/src/missing_const_for_thread_local.rs index 954216038fb58..c2f524a635316 100644 --- a/clippy_lints/src/missing_const_for_thread_local.rs +++ b/clippy_lints/src/missing_const_for_thread_local.rs @@ -1,12 +1,12 @@ -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::macros::macro_backtrace; use clippy_utils::qualify_min_const_fn::is_min_const_fn; use clippy_utils::source::snippet; use clippy_utils::{fn_has_unsatisfiable_preds, peel_blocks}; use rustc_errors::Applicability; -use rustc_hir::{intravisit, Expr, ExprKind}; +use rustc_hir::{Expr, ExprKind, intravisit}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; use rustc_span::sym::{self, thread_local_macro}; diff --git a/clippy_lints/src/missing_doc.rs b/clippy_lints/src/missing_doc.rs index 2166b0fe5a06d..64fc1a8a1a580 100644 --- a/clippy_lints/src/missing_doc.rs +++ b/clippy_lints/src/missing_doc.rs @@ -17,7 +17,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty::Visibility; use rustc_session::impl_lint_pass; use rustc_span::def_id::CRATE_DEF_ID; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/missing_fields_in_debug.rs b/clippy_lints/src/missing_fields_in_debug.rs index 77595b121aadb..fc01b6753f1c7 100644 --- a/clippy_lints/src/missing_fields_in_debug.rs +++ b/clippy_lints/src/missing_fields_in_debug.rs @@ -3,7 +3,7 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_path_lang_item; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::visitors::{for_each_expr, Visitable}; +use clippy_utils::visitors::{Visitable, for_each_expr}; use rustc_ast::LitKind; use rustc_data_structures::fx::FxHashSet; use rustc_hir::def::{DefKind, Res}; @@ -13,7 +13,7 @@ use rustc_hir::{ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{Ty, TypeckResults}; use rustc_session::declare_lint_pass; -use rustc_span::{sym, Span, Symbol}; +use rustc_span::{Span, Symbol, sym}; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/missing_inline.rs b/clippy_lints/src/missing_inline.rs index 33a14d8b7fed3..d342be4545cea 100644 --- a/clippy_lints/src/missing_inline.rs +++ b/clippy_lints/src/missing_inline.rs @@ -3,7 +3,7 @@ use rustc_ast::ast; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::declare_lint_pass; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/mixed_read_write_in_expression.rs b/clippy_lints/src/mixed_read_write_in_expression.rs index 0b3769ecb7cc4..d333b71edb1ae 100644 --- a/clippy_lints/src/mixed_read_write_in_expression.rs +++ b/clippy_lints/src/mixed_read_write_in_expression.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; +use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::{get_parent_expr, path_to_local, path_to_local_id}; -use rustc_hir::intravisit::{walk_expr, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, LetStmt, Node, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; @@ -134,6 +135,11 @@ impl<'a, 'tcx> DivergenceVisitor<'a, 'tcx> { } fn report_diverging_sub_expr(&mut self, e: &Expr<'_>) { + if let Some(macro_call) = root_macro_call_first_node(self.cx, e) { + if self.cx.tcx.item_name(macro_call.def_id).as_str() == "todo" { + return; + } + } span_lint(self.cx, DIVERGING_SUB_EXPRESSION, e.span, "sub-expression diverges"); } } diff --git a/clippy_lints/src/multiple_unsafe_ops_per_block.rs b/clippy_lints/src/multiple_unsafe_ops_per_block.rs index 0bde0da3cd814..12bcc608174f2 100644 --- a/clippy_lints/src/multiple_unsafe_ops_per_block.rs +++ b/clippy_lints/src/multiple_unsafe_ops_per_block.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::visitors::{for_each_expr, Descend, Visitable}; +use clippy_utils::visitors::{Descend, Visitable, for_each_expr}; use core::ops::ControlFlow::Continue; use hir::def::{DefKind, Res}; use hir::{BlockCheckMode, ExprKind, QPath, Safety, UnOp}; @@ -153,19 +153,16 @@ fn collect_unsafe_exprs<'tcx>( ExprKind::AssignOp(_, lhs, rhs) | ExprKind::Assign(lhs, rhs, _) => { if matches!( lhs.kind, - ExprKind::Path(QPath::Resolved( - _, - hir::Path { - res: Res::Def( - DefKind::Static { - mutability: Mutability::Mut, - .. - }, - _ - ), - .. - } - )) + ExprKind::Path(QPath::Resolved(_, hir::Path { + res: Res::Def( + DefKind::Static { + mutability: Mutability::Mut, + .. + }, + _ + ), + .. + })) ) { unsafe_ops.push(("modification of a mutable static occurs here", expr.span)); collect_unsafe_exprs(cx, rhs, unsafe_ops); diff --git a/clippy_lints/src/mut_key.rs b/clippy_lints/src/mut_key.rs index f52b3a6a5a160..8118c14bd4a28 100644 --- a/clippy_lints/src/mut_key.rs +++ b/clippy_lints/src/mut_key.rs @@ -6,9 +6,9 @@ use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::impl_lint_pass; +use rustc_span::Span; use rustc_span::def_id::LocalDefId; use rustc_span::symbol::sym; -use rustc_span::Span; use std::iter; declare_clippy_lint! { diff --git a/clippy_lints/src/mutable_debug_assertion.rs b/clippy_lints/src/mutable_debug_assertion.rs index e92ba93942eff..785bf70a3ec9f 100644 --- a/clippy_lints/src/mutable_debug_assertion.rs +++ b/clippy_lints/src/mutable_debug_assertion.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::macros::{find_assert_eq_args, root_macro_call_first_node}; -use rustc_hir::intravisit::{walk_expr, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{BorrowKind, Expr, ExprKind, MatchSource, Mutability}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; diff --git a/clippy_lints/src/needless_arbitrary_self_type.rs b/clippy_lints/src/needless_arbitrary_self_type.rs index 60c44382059a5..3c47d0edfdc5b 100644 --- a/clippy_lints/src/needless_arbitrary_self_type.rs +++ b/clippy_lints/src/needless_arbitrary_self_type.rs @@ -3,8 +3,8 @@ use rustc_ast::ast::{BindingMode, ByRef, Lifetime, Mutability, Param, PatKind, P use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::symbol::kw; use rustc_span::Span; +use rustc_span::symbol::kw; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/needless_bool.rs b/clippy_lints/src/needless_bool.rs index df155a7a41257..2eacd6875d6b4 100644 --- a/clippy_lints/src/needless_bool.rs +++ b/clippy_lints/src/needless_bool.rs @@ -2,16 +2,16 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; use clippy_utils::{ - get_parent_expr, higher, is_block_like, is_else_clause, is_expn_of, is_parent_stmt, is_receiver_of_method_call, - peel_blocks, peel_blocks_with_stmt, span_extract_comment, SpanlessEq, + SpanlessEq, get_parent_expr, higher, is_block_like, is_else_clause, is_expn_of, is_parent_stmt, + is_receiver_of_method_call, peel_blocks, peel_blocks_with_stmt, span_extract_comment, }; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::source_map::Spanned; use rustc_span::Span; +use rustc_span::source_map::Spanned; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/needless_borrows_for_generic_args.rs b/clippy_lints/src/needless_borrows_for_generic_args.rs index 32e7fde03b2c8..f6db12ed84e86 100644 --- a/clippy_lints/src/needless_borrows_for_generic_args.rs +++ b/clippy_lints/src/needless_borrows_for_generic_args.rs @@ -1,10 +1,10 @@ -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::mir::{enclosing_mir, expr_local, local_assignments, used_exactly_once, PossibleBorrowerMap}; +use clippy_utils::mir::{PossibleBorrowerMap, enclosing_mir, expr_local, local_assignments, used_exactly_once}; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{implements_trait, is_copy}; -use clippy_utils::{expr_use_ctxt, peel_n_hir_expr_refs, DefinedTy, ExprUseNode}; +use clippy_utils::{DefinedTy, ExprUseNode, expr_use_ctxt, peel_n_hir_expr_refs}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; diff --git a/clippy_lints/src/needless_for_each.rs b/clippy_lints/src/needless_for_each.rs index 6390e51f916b2..b54eb164e81d5 100644 --- a/clippy_lints/src/needless_for_each.rs +++ b/clippy_lints/src/needless_for_each.rs @@ -1,9 +1,9 @@ use rustc_errors::Applicability; -use rustc_hir::intravisit::{walk_expr, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{Block, BlockCheckMode, Closure, Expr, ExprKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_trait_method; diff --git a/clippy_lints/src/needless_maybe_sized.rs b/clippy_lints/src/needless_maybe_sized.rs index 62e41088f3708..bb44ff37b203b 100644 --- a/clippy_lints/src/needless_maybe_sized.rs +++ b/clippy_lints/src/needless_maybe_sized.rs @@ -26,7 +26,7 @@ declare_clippy_lint! { /// /// // or choose alternative bounds for `T` so that it can be unsized /// ``` - #[clippy::version = "1.79.0"] + #[clippy::version = "1.81.0"] pub NEEDLESS_MAYBE_SIZED, suspicious, "a `?Sized` bound that is unusable due to a `Sized` requirement" diff --git a/clippy_lints/src/needless_pass_by_ref_mut.rs b/clippy_lints/src/needless_pass_by_ref_mut.rs index d543fd467abe7..19cbf59590840 100644 --- a/clippy_lints/src/needless_pass_by_ref_mut.rs +++ b/clippy_lints/src/needless_pass_by_ref_mut.rs @@ -17,9 +17,9 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::FakeReadCause; use rustc_middle::ty::{self, Ty, TyCtxt, UpvarId, UpvarPath}; use rustc_session::impl_lint_pass; +use rustc_span::Span; use rustc_span::def_id::LocalDefId; use rustc_span::symbol::kw; -use rustc_span::Span; use rustc_target::spec::abi::Abi; declare_clippy_lint! { diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index addb4b1aee807..0775d7abdbb37 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_self; use clippy_utils::ptr::get_spans; -use clippy_utils::source::{snippet, SpanRangeExt}; +use clippy_utils::source::{SpanRangeExt, snippet}; use clippy_utils::ty::{ implements_trait, implements_trait_with_env_from_iter, is_copy, is_type_diagnostic_item, is_type_lang_item, }; @@ -19,7 +19,7 @@ use rustc_middle::ty::{self, Ty, TypeVisitableExt}; use rustc_session::declare_lint_pass; use rustc_span::def_id::LocalDefId; use rustc_span::symbol::kw; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use rustc_target::spec::abi::Abi; use rustc_trait_selection::traits; use rustc_trait_selection::traits::misc::type_allowed_to_implement_copy; @@ -129,7 +129,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { }) .collect::>(); - // Collect moved variables and spans which will need dereferencings from the + // Collect moved variables and spans which will need dereferencing from the // function body. let MovedVariablesCtxt { moved_vars } = { let mut ctx = MovedVariablesCtxt::default(); @@ -148,12 +148,13 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { return; } - // Ignore `self`s. - if idx == 0 { - if let PatKind::Binding(.., ident, _) = arg.pat.kind { - if ident.name == kw::SelfLower { - continue; - } + // Ignore `self`s and params whose variable name starts with an underscore + if let PatKind::Binding(.., ident, _) = arg.pat.kind { + if idx == 0 && ident.name == kw::SelfLower { + continue; + } + if ident.name.as_str().starts_with('_') { + continue; } } @@ -181,14 +182,9 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { && !is_copy(cx, ty) && ty.is_sized(cx.tcx, cx.param_env) && !allowed_traits.iter().any(|&t| { - implements_trait_with_env_from_iter( - cx.tcx, - cx.param_env, - ty, - t, - None, - [Option::>::None], - ) + implements_trait_with_env_from_iter(cx.tcx, cx.param_env, ty, t, None, [Option::< + ty::GenericArg<'tcx>, + >::None]) }) && !implements_borrow_trait && !all_borrowable_trait @@ -207,7 +203,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { ) .is_ok() { - diag.span_help(span, "consider marking this type as `Copy`"); + diag.span_help(span, "or consider marking this type as `Copy`"); } } } diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs index b181791699a5b..392cfcb813e85 100644 --- a/clippy_lints/src/no_effect.rs +++ b/clippy_lints/src/no_effect.rs @@ -7,8 +7,8 @@ use clippy_utils::{ use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{ - is_range_literal, BinOpKind, BlockCheckMode, Expr, ExprKind, HirId, HirIdMap, ItemKind, LocalSource, Node, PatKind, - Stmt, StmtKind, UnsafeSource, + BinOpKind, BlockCheckMode, Expr, ExprKind, HirId, HirIdMap, ItemKind, LocalSource, Node, PatKind, Stmt, StmtKind, + UnsafeSource, is_range_literal, }; use rustc_infer::infer::TyCtxtInferExt as _; use rustc_lint::{LateContext, LateLintPass, LintContext}; diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index 25f547f1a43c0..5e20b40642607 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -4,7 +4,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_in_const_context; use clippy_utils::macros::macro_backtrace; -use clippy_utils::ty::{implements_trait, InteriorMut}; +use clippy_utils::ty::{InteriorMut, implements_trait}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::{ @@ -15,7 +15,7 @@ use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult, GlobalId}; use rustc_middle::ty::adjustment::Adjust; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::impl_lint_pass; -use rustc_span::{sym, Span, DUMMY_SP}; +use rustc_span::{DUMMY_SP, Span, sym}; use rustc_target::abi::VariantIdx; // FIXME: this is a correctness problem but there's no suitable diff --git a/clippy_lints/src/non_expressive_names.rs b/clippy_lints/src/non_expressive_names.rs index 832518d2d35b4..d85032e9eee84 100644 --- a/clippy_lints/src/non_expressive_names.rs +++ b/clippy_lints/src/non_expressive_names.rs @@ -3,12 +3,12 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use rustc_ast::ast::{ self, Arm, AssocItem, AssocItemKind, Attribute, Block, FnDecl, Item, ItemKind, Local, Pat, PatKind, }; -use rustc_ast::visit::{walk_block, walk_expr, walk_pat, Visitor}; +use rustc_ast::visit::{Visitor, walk_block, walk_expr, walk_pat}; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::impl_lint_pass; use rustc_span::symbol::{Ident, Symbol}; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use std::cmp::Ordering; declare_clippy_lint! { diff --git a/clippy_lints/src/non_octal_unix_permissions.rs b/clippy_lints/src/non_octal_unix_permissions.rs index cfc15d9271583..3f156aa551040 100644 --- a/clippy_lints/src/non_octal_unix_permissions.rs +++ b/clippy_lints/src/non_octal_unix_permissions.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{snippet_with_applicability, SpanRangeExt}; +use clippy_utils::source::{SpanRangeExt, snippet_with_applicability}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/non_zero_suggestions.rs b/clippy_lints/src/non_zero_suggestions.rs new file mode 100644 index 0000000000000..90a9f2e994b81 --- /dev/null +++ b/clippy_lints/src/non_zero_suggestions.rs @@ -0,0 +1,143 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet; +use rustc_ast::ast::BinOpKind; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{self, Ty}; +use rustc_session::declare_lint_pass; +use rustc_span::symbol::sym; + +declare_clippy_lint! { + /// ### What it does + /// Checks for conversions from `NonZero` types to regular integer types, + /// and suggests using `NonZero` types for the target as well. + /// + /// ### Why is this bad? + /// Converting from `NonZero` types to regular integer types and then back to `NonZero` + /// types is less efficient and loses the type-safety guarantees provided by `NonZero` types. + /// Using `NonZero` types consistently can lead to more optimized code and prevent + /// certain classes of errors related to zero values. + /// + /// ### Example + /// ```no_run + /// use std::num::{NonZeroU32, NonZeroU64}; + /// + /// fn example(x: u64, y: NonZeroU32) { + /// // Bad: Converting NonZeroU32 to u64 unnecessarily + /// let r1 = x / u64::from(y.get()); + /// let r2 = x % u64::from(y.get()); + /// } + /// ``` + /// Use instead: + /// ```no_run + /// use std::num::{NonZeroU32, NonZeroU64}; + /// + /// fn example(x: u64, y: NonZeroU32) { + /// // Good: Preserving the NonZero property + /// let r1 = x / NonZeroU64::from(y); + /// let r2 = x % NonZeroU64::from(y); + /// } + /// ``` + #[clippy::version = "1.81.0"] + pub NON_ZERO_SUGGESTIONS, + restriction, + "suggests using `NonZero#` from `u#` or `i#` for more efficient and type-safe conversions" +} + +declare_lint_pass!(NonZeroSuggestions => [NON_ZERO_SUGGESTIONS]); + +impl<'tcx> LateLintPass<'tcx> for NonZeroSuggestions { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if let ExprKind::Binary(op, _, rhs) = expr.kind + && matches!(op.node, BinOpKind::Div | BinOpKind::Rem) + { + check_non_zero_conversion(cx, rhs, Applicability::MachineApplicable); + } else { + // Check if the parent expression is a binary operation + let parent_is_binary = cx.tcx.hir().parent_iter(expr.hir_id).any(|(_, node)| { + matches!(node, rustc_hir::Node::Expr(parent_expr) if matches!(parent_expr.kind, ExprKind::Binary(..))) + }); + + if !parent_is_binary { + check_non_zero_conversion(cx, expr, Applicability::MaybeIncorrect); + } + } + } +} + +fn check_non_zero_conversion(cx: &LateContext<'_>, expr: &Expr<'_>, applicability: Applicability) { + // Check if the expression is a function call with one argument + if let ExprKind::Call(func, [arg]) = expr.kind + && let ExprKind::Path(qpath) = &func.kind + && let Some(def_id) = cx.qpath_res(qpath, func.hir_id).opt_def_id() + && let ExprKind::MethodCall(rcv_path, receiver, _, _) = &arg.kind + && rcv_path.ident.name.as_str() == "get" + { + let fn_name = cx.tcx.item_name(def_id); + let target_ty = cx.typeck_results().expr_ty(expr); + let receiver_ty = cx.typeck_results().expr_ty(receiver); + + // Check if the receiver type is a NonZero type + if let ty::Adt(adt_def, _) = receiver_ty.kind() + && adt_def.is_struct() + && cx.tcx.get_diagnostic_name(adt_def.did()) == Some(sym::NonZero) + { + if let Some(target_non_zero_type) = get_target_non_zero_type(target_ty) { + let arg_snippet = get_arg_snippet(cx, arg, rcv_path); + suggest_non_zero_conversion(cx, expr, fn_name, target_non_zero_type, &arg_snippet, applicability); + } + } + } +} + +fn get_arg_snippet(cx: &LateContext<'_>, arg: &Expr<'_>, rcv_path: &rustc_hir::PathSegment<'_>) -> String { + let arg_snippet = snippet(cx, arg.span, ".."); + if let Some(index) = arg_snippet.rfind(&format!(".{}", rcv_path.ident.name)) { + arg_snippet[..index].trim().to_string() + } else { + arg_snippet.to_string() + } +} + +fn suggest_non_zero_conversion( + cx: &LateContext<'_>, + expr: &Expr<'_>, + fn_name: rustc_span::Symbol, + target_non_zero_type: &str, + arg_snippet: &str, + applicability: Applicability, +) { + let suggestion = format!("{target_non_zero_type}::{fn_name}({arg_snippet})"); + span_lint_and_sugg( + cx, + NON_ZERO_SUGGESTIONS, + expr.span, + format!("consider using `{target_non_zero_type}::{fn_name}()` for more efficient and type-safe conversion"), + "replace with", + suggestion, + applicability, + ); +} + +fn get_target_non_zero_type(ty: Ty<'_>) -> Option<&'static str> { + match ty.kind() { + ty::Uint(uint_ty) => Some(match uint_ty { + ty::UintTy::U8 => "NonZeroU8", + ty::UintTy::U16 => "NonZeroU16", + ty::UintTy::U32 => "NonZeroU32", + ty::UintTy::U64 => "NonZeroU64", + ty::UintTy::U128 => "NonZeroU128", + ty::UintTy::Usize => "NonZeroUsize", + }), + ty::Int(int_ty) => Some(match int_ty { + ty::IntTy::I8 => "NonZeroI8", + ty::IntTy::I16 => "NonZeroI16", + ty::IntTy::I32 => "NonZeroI32", + ty::IntTy::I64 => "NonZeroI64", + ty::IntTy::I128 => "NonZeroI128", + ty::IntTy::Isize => "NonZeroIsize", + }), + _ => None, + } +} diff --git a/clippy_lints/src/nonstandard_macro_braces.rs b/clippy_lints/src/nonstandard_macro_braces.rs index 51ba29d7389b9..a13391a59457a 100644 --- a/clippy_lints/src/nonstandard_macro_braces.rs +++ b/clippy_lints/src/nonstandard_macro_braces.rs @@ -1,5 +1,5 @@ -use clippy_config::types::MacroMatcher; use clippy_config::Conf; +use clippy_config::types::MacroMatcher; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{SourceText, SpanRangeExt}; use rustc_ast::ast; @@ -8,8 +8,8 @@ use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::impl_lint_pass; -use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::Span; +use rustc_span::hygiene::{ExpnKind, MacroKind}; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/only_used_in_recursion.rs b/clippy_lints/src/only_used_in_recursion.rs index aadd729f32a45..372128ac16f0f 100644 --- a/clippy_lints/src/only_used_in_recursion.rs +++ b/clippy_lints/src/only_used_in_recursion.rs @@ -9,8 +9,8 @@ use rustc_hir::{Body, Expr, ExprKind, HirId, ImplItem, ImplItemKind, Node, PatKi use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, ConstKind, EarlyBinder, GenericArgKind, GenericArgsRef}; use rustc_session::impl_lint_pass; -use rustc_span::symbol::{kw, Ident}; use rustc_span::Span; +use rustc_span::symbol::{Ident, kw}; use std::iter; declare_clippy_lint! { diff --git a/clippy_lints/src/operators/absurd_extreme_comparisons.rs b/clippy_lints/src/operators/absurd_extreme_comparisons.rs index a0de5ea711ca0..dbc9948eeedc7 100644 --- a/clippy_lints/src/operators/absurd_extreme_comparisons.rs +++ b/clippy_lints/src/operators/absurd_extreme_comparisons.rs @@ -2,7 +2,7 @@ use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty; -use clippy_utils::comparisons::{normalize_comparison, Rel}; +use clippy_utils::comparisons::{Rel, normalize_comparison}; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::snippet; diff --git a/clippy_lints/src/operators/const_comparisons.rs b/clippy_lints/src/operators/const_comparisons.rs index c131752439628..5d94cfab3b02a 100644 --- a/clippy_lints/src/operators/const_comparisons.rs +++ b/clippy_lints/src/operators/const_comparisons.rs @@ -7,12 +7,12 @@ use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::layout::HasTyCtxt; use rustc_middle::ty::{Ty, TypeckResults}; -use rustc_span::source_map::Spanned; use rustc_span::Span; +use rustc_span::source_map::Spanned; +use clippy_utils::SpanlessEq; use clippy_utils::diagnostics::span_lint_and_note; use clippy_utils::source::snippet; -use clippy_utils::SpanlessEq; use super::{IMPOSSIBLE_COMPARISONS, REDUNDANT_COMPARISONS}; diff --git a/clippy_lints/src/operators/float_equality_without_abs.rs b/clippy_lints/src/operators/float_equality_without_abs.rs index be97ad389bf60..34f7dbea84e49 100644 --- a/clippy_lints/src/operators/float_equality_without_abs.rs +++ b/clippy_lints/src/operators/float_equality_without_abs.rs @@ -6,7 +6,8 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::{sym, source_map::Spanned}; +use rustc_span::source_map::Spanned; +use rustc_span::sym; use super::FLOAT_EQUALITY_WITHOUT_ABS; diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index ae02d22512e31..6d9e75f51d66e 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -1,12 +1,12 @@ 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, is_else_clause, is_in_const_context, is_res_lang_ctor, - peel_blocks, peel_hir_expr_while, CaptureKind, + CaptureKind, can_move_expr_to_closure, eager_or_lazy, higher, is_else_clause, is_in_const_context, + is_res_lang_ctor, peel_blocks, peel_hir_expr_while, }; use rustc_errors::Applicability; -use rustc_hir::def::Res; use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; +use rustc_hir::def::Res; use rustc_hir::{Arm, BindingMode, Expr, ExprKind, MatchSource, Mutability, Pat, PatKind, Path, QPath, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; diff --git a/clippy_lints/src/panic_in_result_fn.rs b/clippy_lints/src/panic_in_result_fn.rs index 381975199d285..eebc62e2a5a96 100644 --- a/clippy_lints/src/panic_in_result_fn.rs +++ b/clippy_lints/src/panic_in_result_fn.rs @@ -1,15 +1,15 @@ 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::{for_each_expr, Descend}; +use clippy_utils::visitors::{Descend, for_each_expr}; +use clippy_utils::{is_inside_always_const_context, return_ty}; use core::ops::ControlFlow; use rustc_hir as hir; use rustc_hir::intravisit::FnKind; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::def_id::LocalDefId; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does @@ -68,10 +68,12 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir 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(), - "panic" | "assert" | "assert_eq" | "assert_ne" - ) { + if !is_inside_always_const_context(cx.tcx, e.hir_id) + && matches!( + cx.tcx.item_name(macro_call.def_id).as_str(), + "panic" | "assert" | "assert_eq" | "assert_ne" + ) + { panics.push(macro_call.span); ControlFlow::Continue(Descend::No) } else { diff --git a/clippy_lints/src/panic_unimplemented.rs b/clippy_lints/src/panic_unimplemented.rs index 4eefd0065f65c..fa5b02a5a41ba 100644 --- a/clippy_lints/src/panic_unimplemented.rs +++ b/clippy_lints/src/panic_unimplemented.rs @@ -1,8 +1,9 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint; -use clippy_utils::is_in_test; use clippy_utils::macros::{is_panic, root_macro_call_first_node}; -use rustc_hir::Expr; +use clippy_utils::{is_in_test, match_def_path, paths}; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; @@ -95,10 +96,49 @@ impl_lint_pass!(PanicUnimplemented => [UNIMPLEMENTED, UNREACHABLE, TODO, PANIC]) impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - let Some(macro_call) = root_macro_call_first_node(cx, expr) else { - return; - }; - if is_panic(cx, macro_call.def_id) { + if let Some(macro_call) = root_macro_call_first_node(cx, expr) { + if is_panic(cx, macro_call.def_id) { + if cx.tcx.hir().is_inside_const_context(expr.hir_id) + || self.allow_panic_in_tests && is_in_test(cx.tcx, expr.hir_id) + { + return; + } + + span_lint( + cx, + PANIC, + macro_call.span, + "`panic` should not be present in production code", + ); + return; + } + match cx.tcx.item_name(macro_call.def_id).as_str() { + "todo" => { + span_lint( + cx, + TODO, + macro_call.span, + "`todo` should not be present in production code", + ); + }, + "unimplemented" => { + span_lint( + cx, + UNIMPLEMENTED, + macro_call.span, + "`unimplemented` should not be present in production code", + ); + }, + "unreachable" => { + span_lint(cx, UNREACHABLE, macro_call.span, "usage of the `unreachable!` macro"); + }, + _ => {}, + } + } else if let ExprKind::Call(func, [_]) = expr.kind + && let ExprKind::Path(QPath::Resolved(None, expr_path)) = func.kind + && let Res::Def(DefKind::Fn, def_id) = expr_path.res + && match_def_path(cx, def_id, &paths::PANIC_ANY) + { if cx.tcx.hir().is_inside_const_context(expr.hir_id) || self.allow_panic_in_tests && is_in_test(cx.tcx, expr.hir_id) { @@ -108,32 +148,10 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented { span_lint( cx, PANIC, - macro_call.span, - "`panic` should not be present in production code", + expr.span, + "`panic_any` should not be present in production code", ); return; } - match cx.tcx.item_name(macro_call.def_id).as_str() { - "todo" => { - span_lint( - cx, - TODO, - macro_call.span, - "`todo` should not be present in production code", - ); - }, - "unimplemented" => { - span_lint( - cx, - UNIMPLEMENTED, - macro_call.span, - "`unimplemented` should not be present in production code", - ); - }, - "unreachable" => { - span_lint(cx, UNREACHABLE, macro_call.span, "usage of the `unreachable!` macro"); - }, - _ => {}, - } } } diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs index 5ca244f0141cb..75d8c09f2b08d 100644 --- a/clippy_lints/src/pass_by_ref_or_value.rs +++ b/clippy_lints/src/pass_by_ref_or_value.rs @@ -18,7 +18,7 @@ use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, RegionKind, TyCtxt}; use rustc_session::impl_lint_pass; use rustc_span::def_id::LocalDefId; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use rustc_target::spec::abi::Abi; declare_clippy_lint! { diff --git a/clippy_lints/src/pathbuf_init_then_push.rs b/clippy_lints/src/pathbuf_init_then_push.rs index d7fa48c1e38dd..1b9a5a4438297 100644 --- a/clippy_lints/src/pathbuf_init_then_push.rs +++ b/clippy_lints/src/pathbuf_init_then_push.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::path_to_local_id; -use clippy_utils::source::{snippet, SpanRangeExt}; +use clippy_utils::source::{SpanRangeExt, snippet}; use clippy_utils::ty::is_type_diagnostic_item; use rustc_ast::{LitKind, StrStyle}; use rustc_errors::Applicability; @@ -9,7 +9,7 @@ use rustc_hir::{BindingMode, Block, Expr, ExprKind, HirId, LetStmt, PatKind, QPa use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::impl_lint_pass; -use rustc_span::{sym, Span, Symbol}; +use rustc_span::{Span, Symbol, sym}; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/pattern_type_mismatch.rs b/clippy_lints/src/pattern_type_mismatch.rs index c1296b04387a0..42fbba8ef6dcd 100644 --- a/clippy_lints/src/pattern_type_mismatch.rs +++ b/clippy_lints/src/pattern_type_mismatch.rs @@ -1,13 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_then; use rustc_hir::{ - intravisit, Body, Expr, ExprKind, FnDecl, LetExpr, LocalSource, Mutability, Pat, PatKind, Stmt, StmtKind, + Body, Expr, ExprKind, FnDecl, LetExpr, LocalSource, Mutability, Pat, PatKind, Stmt, StmtKind, intravisit, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty; use rustc_session::declare_lint_pass; -use rustc_span::def_id::LocalDefId; use rustc_span::Span; +use rustc_span::def_id::LocalDefId; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/pointers_in_nomem_asm_block.rs b/clippy_lints/src/pointers_in_nomem_asm_block.rs new file mode 100644 index 0000000000000..385f634a15050 --- /dev/null +++ b/clippy_lints/src/pointers_in_nomem_asm_block.rs @@ -0,0 +1,88 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use rustc_ast::InlineAsmOptions; +use rustc_hir::{Expr, ExprKind, InlineAsm, InlineAsmOperand}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::declare_lint_pass; +use rustc_span::Span; + +declare_clippy_lint! { + /// ### What it does + /// Checks if any pointer is being passed to an asm! block with `nomem` option. + /// + /// ### Why is this bad? + /// `nomem` forbids any reads or writes to memory and passing a pointer suggests + /// that either of those will happen. + /// + /// ### Example + /// ```no_run + /// fn f(p: *mut u32) { + /// unsafe { core::arch::asm!("mov [{p}], 42", p = in(reg) p, options(nomem, nostack)); } + /// } + /// ``` + /// Use instead: + /// ```no_run + /// fn f(p: *mut u32) { + /// unsafe { core::arch::asm!("mov [{p}], 42", p = in(reg) p, options(nostack)); } + /// } + /// ``` + #[clippy::version = "1.81.0"] + pub POINTERS_IN_NOMEM_ASM_BLOCK, + suspicious, + "pointers in nomem asm block" +} + +declare_lint_pass!(PointersInNomemAsmBlock => [POINTERS_IN_NOMEM_ASM_BLOCK]); + +impl<'tcx> LateLintPass<'tcx> for PointersInNomemAsmBlock { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { + if let ExprKind::InlineAsm(asm) = &expr.kind { + check_asm(cx, asm); + } + } +} + +fn check_asm(cx: &LateContext<'_>, asm: &InlineAsm<'_>) { + if !asm.options.contains(InlineAsmOptions::NOMEM) { + return; + } + + let spans = asm + .operands + .iter() + .filter(|(op, _span)| has_in_operand_pointer(cx, op)) + .map(|(_op, span)| *span) + .collect::>(); + + if spans.is_empty() { + return; + } + + span_lint_and_then( + cx, + POINTERS_IN_NOMEM_ASM_BLOCK, + spans, + "passing pointers to nomem asm block", + additional_notes, + ); +} + +fn has_in_operand_pointer(cx: &LateContext<'_>, asm_op: &InlineAsmOperand<'_>) -> bool { + let asm_in_expr = match asm_op { + InlineAsmOperand::SymStatic { .. } + | InlineAsmOperand::Out { .. } + | InlineAsmOperand::Const { .. } + | InlineAsmOperand::SymFn { .. } + | InlineAsmOperand::Label { .. } => return false, + InlineAsmOperand::SplitInOut { in_expr, .. } => in_expr, + InlineAsmOperand::In { expr, .. } | InlineAsmOperand::InOut { expr, .. } => expr, + }; + + // This checks for raw ptrs, refs and function pointers - the last one + // also technically counts as reading memory. + cx.typeck_results().expr_ty(asm_in_expr).is_any_ptr() +} + +fn additional_notes(diag: &mut rustc_errors::Diag<'_, ()>) { + diag.note("`nomem` means that no memory write or read happens inside the asm! block"); + diag.note("if this is intentional and no pointers are read or written to, consider allowing the lint"); +} diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 125f694996c1c..807636bb642b2 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -1,13 +1,11 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::source::SpanRangeExt; -use clippy_utils::ty::expr_sig; use clippy_utils::visitors::contains_unsafe_block; use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, path_def_id, path_to_local}; use hir::LifetimeName; use rustc_errors::{Applicability, MultiSpan}; -use rustc_hir::def_id::DefId; use rustc_hir::hir_id::{HirId, HirIdMap}; -use rustc_hir::intravisit::{walk_expr, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{ self as hir, AnonConst, BinOpKind, BindingMode, Body, Expr, ExprKind, FnRetTy, FnSig, GenericArg, ImplItemKind, ItemKind, Lifetime, Mutability, Node, Param, PatKind, QPath, Safety, TraitFn, TraitItem, TraitItemKind, TyKind, @@ -19,7 +17,7 @@ use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, Binder, ClauseKind, ExistentialPredicate, List, PredicateKind, Ty}; use rustc_session::declare_lint_pass; use rustc_span::symbol::Symbol; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use rustc_target::spec::abi::Abi; use rustc_trait_selection::infer::InferCtxtExt as _; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; @@ -34,7 +32,7 @@ declare_clippy_lint! { /// with the appropriate `.to_owned()`/`to_string()` calls. /// /// ### Why is this bad? - /// Requiring the argument to be of the specific size + /// Requiring the argument to be of the specific type /// makes the function less useful for no benefit; slices in the form of `&[T]` /// or `&str` usually suffice and can be obtained from other types, too. /// @@ -323,7 +321,6 @@ struct PtrArg<'tcx> { idx: usize, emission_id: HirId, span: Span, - ty_did: DefId, ty_name: Symbol, method_renames: &'static [(&'static str, &'static str)], ref_prefix: RefPrefix, @@ -411,7 +408,6 @@ impl<'tcx> DerefTy<'tcx> { } } -#[expect(clippy::too_many_lines)] fn check_fn_args<'cx, 'tcx: 'cx>( cx: &'cx LateContext<'tcx>, fn_sig: ty::FnSig<'tcx>, @@ -514,7 +510,6 @@ fn check_fn_args<'cx, 'tcx: 'cx>( idx: i, emission_id, span: hir_ty.span, - ty_did: adt.did(), ty_name: name.ident.name, method_renames, ref_prefix: RefPrefix { lt: *lt, mutability }, @@ -610,65 +605,50 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &Body<'tcx>, args: &[ set_skip_flag(); } }, - Some((Node::Expr(e), child_id)) => match e.kind { - ExprKind::Call(f, expr_args) => { - let i = expr_args.iter().position(|arg| arg.hir_id == child_id).unwrap_or(0); - if expr_sig(self.cx, f).and_then(|sig| sig.input(i)).map_or(true, |ty| { - match *ty.skip_binder().peel_refs().kind() { - ty::Dynamic(preds, _, _) => !matches_preds(self.cx, args.deref_ty.ty(self.cx), preds), - ty::Param(_) => true, - ty::Adt(def, _) => def.did() == args.ty_did, - _ => false, - } - }) { - // Passed to a function taking the non-dereferenced type. - set_skip_flag(); - } - }, - ExprKind::MethodCall(name, self_arg, expr_args, _) => { - let i = iter::once(self_arg) - .chain(expr_args.iter()) - .position(|arg| arg.hir_id == child_id) - .unwrap_or(0); - if i == 0 { - // Check if the method can be renamed. - let name = name.ident.as_str(); - if let Some((_, replacement)) = args.method_renames.iter().find(|&&(x, _)| x == name) { - result.replacements.push(PtrArgReplacement { - expr_span: e.span, - self_span: self_arg.span, - replacement, - }); - return; - } + Some((Node::Expr(use_expr), child_id)) => { + if let ExprKind::Index(e, ..) = use_expr.kind + && e.hir_id == child_id + { + // Indexing works with both owned and its dereferenced type + return; + } + + if let ExprKind::MethodCall(name, receiver, ..) = use_expr.kind + && receiver.hir_id == child_id + { + let name = name.ident.as_str(); + + // Check if the method can be renamed. + if let Some((_, replacement)) = args.method_renames.iter().find(|&&(x, _)| x == name) { + result.replacements.push(PtrArgReplacement { + expr_span: use_expr.span, + self_span: receiver.span, + replacement, + }); + return; } - let Some(id) = self.cx.typeck_results().type_dependent_def_id(e.hir_id) else { - set_skip_flag(); + // Some methods exist on both `[T]` and `Vec`, such as `len`, where the receiver type + // doesn't coerce to a slice and our adjusted type check below isn't enough, + // but it would still be valid to call with a slice + if is_allowed_vec_method(self.cx, use_expr) { return; - }; - - match *self.cx.tcx.fn_sig(id).instantiate_identity().skip_binder().inputs()[i] - .peel_refs() - .kind() - { - ty::Dynamic(preds, _, _) if !matches_preds(self.cx, args.deref_ty.ty(self.cx), preds) => { - set_skip_flag(); - }, - ty::Param(_) => { - set_skip_flag(); - }, - // If the types match check for methods which exist on both types. e.g. `Vec::len` and - // `slice::len` - ty::Adt(def, _) if def.did() == args.ty_did && !is_allowed_vec_method(self.cx, e) => { - set_skip_flag(); - }, - _ => (), } - }, - // Indexing is fine for currently supported types. - ExprKind::Index(e, _, _) if e.hir_id == child_id => (), - _ => set_skip_flag(), + } + + let deref_ty = args.deref_ty.ty(self.cx); + let adjusted_ty = self.cx.typeck_results().expr_ty_adjusted(e).peel_refs(); + if adjusted_ty == deref_ty { + return; + } + + if let ty::Dynamic(preds, ..) = adjusted_ty.kind() + && matches_preds(self.cx, deref_ty, preds) + { + return; + } + + set_skip_flag(); }, _ => set_skip_flag(), } diff --git a/clippy_lints/src/pub_underscore_fields.rs b/clippy_lints/src/pub_underscore_fields.rs index 77b707567e4b4..db03657c9af43 100644 --- a/clippy_lints/src/pub_underscore_fields.rs +++ b/clippy_lints/src/pub_underscore_fields.rs @@ -1,5 +1,5 @@ -use clippy_config::types::PubUnderscoreFieldsBehaviour; use clippy_config::Conf; +use clippy_config::types::PubUnderscoreFieldsBehaviour; use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::is_path_lang_item; diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index e1e3ded2c795d..aa9a9001afb7b 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -1,8 +1,8 @@ use crate::manual_let_else::MANUAL_LET_ELSE; use crate::question_mark_used::QUESTION_MARK_USED; +use clippy_config::Conf; use clippy_config::msrvs::Msrv; use clippy_config::types::MatchLintBehaviour; -use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; @@ -12,8 +12,8 @@ use clippy_utils::{ span_contains_comment, }; use rustc_errors::Applicability; -use rustc_hir::def::Res; use rustc_hir::LangItem::{self, OptionNone, OptionSome, ResultErr, ResultOk}; +use rustc_hir::def::Res; use rustc_hir::{ BindingMode, Block, Body, ByRef, Expr, ExprKind, LetStmt, Mutability, Node, PatKind, PathSegment, QPath, Stmt, StmtKind, diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 81189fe517c00..21cd33672624f 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -1,8 +1,8 @@ -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::source::{snippet, snippet_with_applicability, SpanRangeExt}; +use clippy_utils::source::{SpanRangeExt, snippet, snippet_with_applicability}; use clippy_utils::sugg::Sugg; use clippy_utils::{get_parent_expr, higher, is_in_const_context, is_integer_const, path_to_local}; use rustc_ast::ast::RangeLimits; @@ -11,8 +11,8 @@ use rustc_hir::{BinOpKind, Expr, ExprKind, HirId}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::impl_lint_pass; -use rustc_span::source_map::Spanned; use rustc_span::Span; +use rustc_span::source_map::Spanned; use std::cmp::Ordering; declare_clippy_lint! { diff --git a/clippy_lints/src/rc_clone_in_vec_init.rs b/clippy_lints/src/rc_clone_in_vec_init.rs index d0b45b59526fc..e877f5d6ed432 100644 --- a/clippy_lints/src/rc_clone_in_vec_init.rs +++ b/clippy_lints/src/rc_clone_in_vec_init.rs @@ -8,7 +8,7 @@ use rustc_hir::{Expr, ExprKind, QPath, TyKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::declare_lint_pass; -use rustc_span::{sym, Span, Symbol}; +use rustc_span::{Span, Symbol, sym}; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/read_zero_byte_vec.rs b/clippy_lints/src/read_zero_byte_vec.rs index 7f4735c6a889b..6bd68dd4109d4 100644 --- a/clippy_lints/src/read_zero_byte_vec.rs +++ b/clippy_lints/src/read_zero_byte_vec.rs @@ -1,13 +1,13 @@ use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; use clippy_utils::get_enclosing_block; -use clippy_utils::higher::{get_vec_init_kind, VecInitKind}; +use clippy_utils::higher::{VecInitKind, get_vec_init_kind}; use clippy_utils::source::snippet; use hir::{Expr, ExprKind, HirId, LetStmt, PatKind, PathSegment, QPath, StmtKind}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::Res; -use rustc_hir::intravisit::{walk_expr, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index 4e24ddad83a56..b9e0106fc86b3 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -1,17 +1,17 @@ use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; -use clippy_utils::mir::{visit_local_usage, LocalUsage, PossibleBorrowerMap}; +use clippy_utils::fn_has_unsatisfiable_preds; +use clippy_utils::mir::{LocalUsage, PossibleBorrowerMap, visit_local_usage}; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::{has_drop, is_copy, is_type_diagnostic_item, is_type_lang_item, walk_ptrs_ty_depth}; -use clippy_utils::fn_has_unsatisfiable_preds; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; -use rustc_hir::{def_id, Body, FnDecl, LangItem}; +use rustc_hir::{Body, FnDecl, LangItem, def_id}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir; use rustc_middle::ty::{self, Ty}; use rustc_session::declare_lint_pass; use rustc_span::def_id::LocalDefId; -use rustc_span::{sym, BytePos, Span}; +use rustc_span::{BytePos, Span, sym}; macro_rules! unwrap_or_continue { ($x:expr) => { @@ -349,14 +349,10 @@ fn visit_clone_usage(cloned: mir::Local, clone: mir::Local, mir: &mir::Body<'_>, 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(), - }, - ) + )) = 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 { diff --git a/clippy_lints/src/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs index bad9b979203f9..6930a01d48be4 100644 --- a/clippy_lints/src/redundant_closure_call.rs +++ b/clippy_lints/src/redundant_closure_call.rs @@ -6,7 +6,7 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::intravisit::{Visitor as HirVisitor, Visitor}; use rustc_hir::{ - intravisit as hir_visit, ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, ExprKind, Node, + ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, ExprKind, Node, intravisit as hir_visit, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::nested_filter; diff --git a/clippy_lints/src/redundant_else.rs b/clippy_lints/src/redundant_else.rs index 3bdf13dbbea65..6a1d40334e755 100644 --- a/clippy_lints/src/redundant_else.rs +++ b/clippy_lints/src/redundant_else.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; use rustc_ast::ast::{Block, Expr, ExprKind, Stmt, StmtKind}; -use rustc_ast::visit::{walk_expr, Visitor}; +use rustc_ast::visit::{Visitor, walk_expr}; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::declare_lint_pass; diff --git a/clippy_lints/src/redundant_field_names.rs b/clippy_lints/src/redundant_field_names.rs index 0e637538615e1..d0dbff081f905 100644 --- a/clippy_lints/src/redundant_field_names.rs +++ b/clippy_lints/src/redundant_field_names.rs @@ -1,5 +1,5 @@ -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_sugg; use rustc_ast::ast::{Expr, ExprKind}; use rustc_errors::Applicability; diff --git a/clippy_lints/src/redundant_locals.rs b/clippy_lints/src/redundant_locals.rs index d94ca5bc7ec2c..4f46ca3c71505 100644 --- a/clippy_lints/src/redundant_locals.rs +++ b/clippy_lints/src/redundant_locals.rs @@ -9,8 +9,8 @@ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::UpvarCapture; use rustc_session::declare_lint_pass; -use rustc_span::symbol::Ident; use rustc_span::DesugaringKind; +use rustc_span::symbol::Ident; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/redundant_static_lifetimes.rs b/clippy_lints/src/redundant_static_lifetimes.rs index d6e741dd974b7..b27bb2e78afed 100644 --- a/clippy_lints/src/redundant_static_lifetimes.rs +++ b/clippy_lints/src/redundant_static_lifetimes.rs @@ -1,5 +1,5 @@ -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use rustc_ast::ast::{ConstItem, Item, ItemKind, StaticItem, Ty, TyKind}; diff --git a/clippy_lints/src/reference.rs b/clippy_lints/src/reference.rs index 2b4ef21fc48e1..4bff37216eda8 100644 --- a/clippy_lints/src/reference.rs +++ b/clippy_lints/src/reference.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{snippet_with_applicability, SpanRangeExt}; +use clippy_utils::source::{SpanRangeExt, snippet_with_applicability}; use rustc_ast::ast::{Expr, ExprKind, Mutability, UnOp}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; diff --git a/clippy_lints/src/regex.rs b/clippy_lints/src/regex.rs index f6ef02b7c2330..12cbdb854eff1 100644 --- a/clippy_lints/src/regex.rs +++ b/clippy_lints/src/regex.rs @@ -3,7 +3,7 @@ use std::fmt::Display; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::source::SpanRangeExt; -use clippy_utils::{def_path_def_ids, path_def_id, paths}; +use clippy_utils::{def_path_res_with_base, find_crates, path_def_id, paths}; use rustc_ast::ast::{LitKind, StrStyle}; use rustc_hir::def_id::DefIdMap; use rustc_hir::{BorrowKind, Expr, ExprKind}; @@ -75,11 +75,14 @@ impl<'tcx> LateLintPass<'tcx> for Regex { // We don't use `match_def_path` here because that relies on matching the exact path, which changed // between regex 1.8 and 1.9 // - // `def_path_def_ids` will resolve through re-exports but is relatively heavy, so we only perform - // the operation once and store the results - let mut resolve = |path, kind| { - for id in def_path_def_ids(cx.tcx, path) { - self.definitions.insert(id, kind); + // `def_path_res_with_base` will resolve through re-exports but is relatively heavy, so we only + // perform the operation once and store the results + let regex_crates = find_crates(cx.tcx, sym!(regex)); + let mut resolve = |path: &[&str], kind: RegexKind| { + for res in def_path_res_with_base(cx.tcx, regex_crates.clone(), &path[1..]) { + if let Some(id) = res.opt_def_id() { + self.definitions.insert(id, kind); + } } }; diff --git a/clippy_lints/src/repeat_vec_with_capacity.rs b/clippy_lints/src/repeat_vec_with_capacity.rs index 08de10f69b059..f54cafffb83c9 100644 --- a/clippy_lints/src/repeat_vec_with_capacity.rs +++ b/clippy_lints/src/repeat_vec_with_capacity.rs @@ -8,7 +8,7 @@ use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/reserve_after_initialization.rs b/clippy_lints/src/reserve_after_initialization.rs index caf3fb8707da3..6157adad059cd 100644 --- a/clippy_lints/src/reserve_after_initialization.rs +++ b/clippy_lints/src/reserve_after_initialization.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::higher::{get_vec_init_kind, VecInitKind}; +use clippy_utils::higher::{VecInitKind, get_vec_init_kind}; use clippy_utils::source::snippet; use clippy_utils::{is_from_proc_macro, path_to_local_id}; use rustc_errors::Applicability; diff --git a/clippy_lints/src/return_self_not_must_use.rs b/clippy_lints/src/return_self_not_must_use.rs index 5962e8be9594b..42d9cf2c88c16 100644 --- a/clippy_lints/src/return_self_not_must_use.rs +++ b/clippy_lints/src/return_self_not_must_use.rs @@ -7,7 +7,7 @@ use rustc_hir::{Body, FnDecl, OwnerId, TraitItem, TraitItemKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::declare_lint_pass; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 5ce091b7a7a30..3754fdddedfd1 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; -use clippy_utils::source::{snippet_with_context, SpanRangeExt}; +use clippy_utils::source::{SpanRangeExt, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; -use clippy_utils::visitors::{for_each_expr, Descend}; +use clippy_utils::visitors::{Descend, for_each_expr, for_each_unconsumed_temporary}; use clippy_utils::{ binary_expr_needs_parentheses, fn_def_id, is_from_proc_macro, is_inside_let_else, is_res_lang_ctor, path_res, path_to_local_id, span_contains_cfg, span_find_starting_semi, @@ -9,8 +9,8 @@ use clippy_utils::{ use core::ops::ControlFlow; use rustc_ast::NestedMetaItem; use rustc_errors::Applicability; -use rustc_hir::intravisit::FnKind; use rustc_hir::LangItem::ResultErr; +use rustc_hir::intravisit::FnKind; use rustc_hir::{ Block, Body, Expr, ExprKind, FnDecl, HirId, ItemKind, LangItem, MatchSource, Node, OwnerNode, PatKind, QPath, Stmt, StmtKind, @@ -21,7 +21,7 @@ use rustc_middle::ty::adjustment::Adjust; use rustc_middle::ty::{self, GenericArgKind, Ty}; use rustc_session::declare_lint_pass; use rustc_span::def_id::LocalDefId; -use rustc_span::{sym, BytePos, Pos, Span}; +use rustc_span::{BytePos, Pos, Span, sym}; use std::borrow::Cow; use std::fmt::Display; @@ -389,10 +389,24 @@ fn check_final_expr<'tcx>( } }; - let borrows = inner.map_or(false, |inner| last_statement_borrows(cx, inner)); - if borrows { - return; + if let Some(inner) = inner { + if for_each_unconsumed_temporary(cx, inner, |temporary_ty| { + if temporary_ty.has_significant_drop(cx.tcx, cx.param_env) + && temporary_ty + .walk() + .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(re) if !re.is_static())) + { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) + } + }) + .is_break() + { + return; + } } + if ret_span.from_expansion() { return; } diff --git a/clippy_lints/src/same_name_method.rs b/clippy_lints/src/same_name_method.rs index 508f3ae6def02..8d31641d4836c 100644 --- a/clippy_lints/src/same_name_method.rs +++ b/clippy_lints/src/same_name_method.rs @@ -5,8 +5,8 @@ use rustc_hir::{HirId, Impl, ItemKind, Node, Path, QPath, TraitRef, TyKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::AssocKind; use rustc_session::declare_lint_pass; -use rustc_span::symbol::Symbol; use rustc_span::Span; +use rustc_span::symbol::Symbol; use std::collections::{BTreeMap, BTreeSet}; declare_clippy_lint! { @@ -62,13 +62,10 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod { && let TyKind::Path(QPath::Resolved(_, Path { res, .. })) = self_ty.kind { if !map.contains_key(res) { - map.insert( - *res, - ExistingName { - impl_methods: BTreeMap::new(), - trait_methods: BTreeMap::new(), - }, - ); + map.insert(*res, ExistingName { + impl_methods: BTreeMap::new(), + trait_methods: BTreeMap::new(), + }); } let existing_name = map.get_mut(res).unwrap(); diff --git a/clippy_lints/src/set_contains_or_insert.rs b/clippy_lints/src/set_contains_or_insert.rs index e6fe76493974a..1185d67b1258b 100644 --- a/clippy_lints/src/set_contains_or_insert.rs +++ b/clippy_lints/src/set_contains_or_insert.rs @@ -3,12 +3,12 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::for_each_expr; -use clippy_utils::{higher, peel_hir_expr_while, SpanlessEq}; +use clippy_utils::{SpanlessEq, higher, peel_hir_expr_while}; use rustc_hir::{Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::symbol::Symbol; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does @@ -42,7 +42,7 @@ declare_clippy_lint! { /// println!("inserted {value:?}"); /// } /// ``` - #[clippy::version = "1.80.0"] + #[clippy::version = "1.81.0"] pub SET_CONTAINS_OR_INSERT, nursery, "call to `::contains` followed by `::insert`" diff --git a/clippy_lints/src/significant_drop_tightening.rs b/clippy_lints/src/significant_drop_tightening.rs index 979d6dc77aede..d1114cb29f7a3 100644 --- a/clippy_lints/src/significant_drop_tightening.rs +++ b/clippy_lints/src/significant_drop_tightening.rs @@ -4,13 +4,13 @@ use clippy_utils::{expr_or_init, get_attr, path_to_local, peel_hir_expr_unary}; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::intravisit::{walk_expr, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{self as hir, HirId}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty::{GenericArgKind, Ty}; use rustc_session::impl_lint_pass; use rustc_span::symbol::Ident; -use rustc_span::{sym, Span, DUMMY_SP}; +use rustc_span::{DUMMY_SP, Span, sym}; use std::borrow::Cow; use std::collections::hash_map::Entry; diff --git a/clippy_lints/src/single_component_path_imports.rs b/clippy_lints/src/single_component_path_imports.rs index acf44a9bb5ab4..c986c3e8aa6e8 100644 --- a/clippy_lints/src/single_component_path_imports.rs +++ b/clippy_lints/src/single_component_path_imports.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; use rustc_ast::node_id::{NodeId, NodeMap}; use rustc_ast::ptr::P; -use rustc_ast::visit::{walk_expr, Visitor}; +use rustc_ast::visit::{Visitor, walk_expr}; use rustc_ast::{Crate, Expr, ExprKind, Item, ItemKind, MacroDef, ModKind, Ty, TyKind, UseTreeKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; diff --git a/clippy_lints/src/slow_vector_initialization.rs b/clippy_lints/src/slow_vector_initialization.rs index 04c16281ec4be..5129bbf26655e 100644 --- a/clippy_lints/src/slow_vector_initialization.rs +++ b/clippy_lints/src/slow_vector_initialization.rs @@ -2,11 +2,10 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::matching_root_macro_call; use clippy_utils::sugg::Sugg; use clippy_utils::{ - get_enclosing_block, is_integer_literal, is_path_diagnostic_item, path_to_local, - path_to_local_id, SpanlessEq, + SpanlessEq, get_enclosing_block, is_integer_literal, is_path_diagnostic_item, path_to_local, path_to_local_id, }; use rustc_errors::Applicability; -use rustc_hir::intravisit::{walk_block, walk_expr, walk_stmt, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_stmt}; use rustc_hir::{BindingMode, Block, Expr, ExprKind, HirId, PatKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; diff --git a/clippy_lints/src/std_instead_of_core.rs b/clippy_lints/src/std_instead_of_core.rs index 44283a49e84b9..8dd9985879323 100644 --- a/clippy_lints/src/std_instead_of_core.rs +++ b/clippy_lints/src/std_instead_of_core.rs @@ -1,5 +1,5 @@ -use clippy_config::msrvs::Msrv; use clippy_config::Conf; +use clippy_config::msrvs::Msrv; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; use rustc_attr::{StabilityLevel, StableSince}; @@ -11,7 +11,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::impl_lint_pass; use rustc_span::symbol::kw; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/string_patterns.rs b/clippy_lints/src/string_patterns.rs index 7e211d64da141..ba2ddac2ec339 100644 --- a/clippy_lints/src/string_patterns.rs +++ b/clippy_lints/src/string_patterns.rs @@ -1,13 +1,13 @@ use std::ops::ControlFlow; -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::macros::matching_root_macro_call; use clippy_utils::path_to_local_id; use clippy_utils::source::{snippet, str_literal_to_char_literal}; -use clippy_utils::visitors::{for_each_expr, Descend}; +use clippy_utils::visitors::{Descend, for_each_expr}; use itertools::Itertools; use rustc_ast::{BinOpKind, LitKind}; use rustc_errors::Applicability; @@ -15,7 +15,7 @@ use rustc_hir::{Expr, ExprKind, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::impl_lint_pass; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does @@ -33,7 +33,7 @@ declare_clippy_lint! { /// ```no_run /// "Hello World!".trim_end_matches(['.', ',', '!', '?']); /// ``` - #[clippy::version = "1.80.0"] + #[clippy::version = "1.81.0"] pub MANUAL_PATTERN_CHAR_COMPARISON, style, "manual char comparison in string patterns" diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 6ca4ca000e9b4..1fb82b66ab85f 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -2,8 +2,8 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_the use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::ty::is_type_lang_item; use clippy_utils::{ - get_expr_use_or_unification_node, get_parent_expr, is_lint_allowed, is_path_diagnostic_item, method_calls, - peel_blocks, SpanlessEq, + SpanlessEq, get_expr_use_or_unification_node, get_parent_expr, is_lint_allowed, is_path_diagnostic_item, + method_calls, peel_blocks, }; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; diff --git a/clippy_lints/src/suspicious_operation_groupings.rs b/clippy_lints/src/suspicious_operation_groupings.rs index 1c1de805db0c6..be32dc0aab0b6 100644 --- a/clippy_lints/src/suspicious_operation_groupings.rs +++ b/clippy_lints/src/suspicious_operation_groupings.rs @@ -1,4 +1,4 @@ -use clippy_utils::ast_utils::{eq_id, is_useless_with_eq_exprs, IdentIter}; +use clippy_utils::ast_utils::{IdentIter, eq_id, is_useless_with_eq_exprs}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use core::ops::{Add, AddAssign}; @@ -7,9 +7,9 @@ use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::declare_lint_pass; +use rustc_span::Span; use rustc_span::source_map::Spanned; use rustc_span::symbol::Ident; -use rustc_span::Span; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/suspicious_trait_impl.rs b/clippy_lints/src/suspicious_trait_impl.rs index 744d6392e065c..e9779d437d432 100644 --- a/clippy_lints/src/suspicious_trait_impl.rs +++ b/clippy_lints/src/suspicious_trait_impl.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::visitors::for_each_expr_without_closures; -use clippy_utils::{binop_traits, trait_ref_of_method, BINOP_TRAITS, OP_ASSIGN_TRAITS}; +use clippy_utils::{BINOP_TRAITS, OP_ASSIGN_TRAITS, binop_traits, trait_ref_of_method}; use core::ops::ControlFlow; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index 197011cde3a1a..e05fa4095b8e3 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -6,7 +6,7 @@ use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{can_mut_borrow_both, eq_expr_value, is_in_const_context, std_or_core}; use itertools::Itertools; -use rustc_hir::intravisit::{walk_expr, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_expr}; use crate::FxHashSet; use rustc_errors::Applicability; @@ -17,7 +17,7 @@ use rustc_middle::ty; use rustc_session::declare_lint_pass; use rustc_span::source_map::Spanned; use rustc_span::symbol::Ident; -use rustc_span::{sym, Span, SyntaxContext}; +use rustc_span::{Span, SyntaxContext, sym}; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/swap_ptr_to_ref.rs b/clippy_lints/src/swap_ptr_to_ref.rs index 20e9608a15dc8..8c5cf93ab6e80 100644 --- a/clippy_lints/src/swap_ptr_to_ref.rs +++ b/clippy_lints/src/swap_ptr_to_ref.rs @@ -5,7 +5,7 @@ use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::{sym, Span, SyntaxContext}; +use rustc_span::{Span, SyntaxContext, sym}; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/tests_outside_test_module.rs b/clippy_lints/src/tests_outside_test_module.rs index 25d0a16e2ab45..3cd4fefffadf3 100644 --- a/clippy_lints/src/tests_outside_test_module.rs +++ b/clippy_lints/src/tests_outside_test_module.rs @@ -4,8 +4,8 @@ use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, FnDecl}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::def_id::LocalDefId; use rustc_span::Span; +use rustc_span::def_id::LocalDefId; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/to_digit_is_some.rs b/clippy_lints/src/to_digit_is_some.rs index dafe9e38818f5..4f96a566b63c2 100644 --- a/clippy_lints/src/to_digit_is_some.rs +++ b/clippy_lints/src/to_digit_is_some.rs @@ -56,11 +56,13 @@ impl<'tcx> LateLintPass<'tcx> for ToDigitIsSome { && let hir::ExprKind::Path(to_digits_path) = &to_digits_call.kind && let to_digits_call_res = cx.qpath_res(to_digits_path, to_digits_call.hir_id) && let Some(to_digits_def_id) = to_digits_call_res.opt_def_id() - && match_def_path( - cx, - to_digits_def_id, - &["core", "char", "methods", "", "to_digit"], - ) + && match_def_path(cx, to_digits_def_id, &[ + "core", + "char", + "methods", + "", + "to_digit", + ]) { Some((false, char_arg, radix_arg)) } else { diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index 2e87d36df3121..00277593622aa 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -1,8 +1,8 @@ -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; -use clippy_utils::source::{snippet, snippet_with_applicability, SpanRangeExt}; -use clippy_utils::{is_from_proc_macro, SpanlessEq, SpanlessHash}; +use clippy_utils::source::{SpanRangeExt, snippet, snippet_with_applicability}; +use clippy_utils::{SpanlessEq, SpanlessHash, is_from_proc_macro}; use core::hash::{Hash, Hasher}; use itertools::Itertools; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; diff --git a/clippy_lints/src/transmute/mod.rs b/clippy_lints/src/transmute/mod.rs index da7906b118353..25fec9f688ca3 100644 --- a/clippy_lints/src/transmute/mod.rs +++ b/clippy_lints/src/transmute/mod.rs @@ -19,8 +19,8 @@ mod useless_transmute; mod utils; mod wrong_transmute; -use clippy_config::msrvs::Msrv; use clippy_config::Conf; +use clippy_config::msrvs::Msrv; use clippy_utils::is_in_const_context; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; @@ -527,24 +527,44 @@ declare_clippy_lint! { /// Checks if transmute calls have all generics specified. /// /// ### Why is this bad? - /// If not set, some unexpected output type could be retrieved instead of the expected one, - /// potentially leading to invalid code. + /// If not, one or more unexpected types could be used during `transmute()`, potentially leading + /// to Undefined Behavior or other problems. + /// + /// This is particularly dangerous in case a seemingly innocent/unrelated change causes type + /// inference to result in a different type. For example, if `transmute()` is the tail + /// expression of an `if`-branch, and the `else`-branch type changes, the compiler may silently + /// infer a different type to be returned by `transmute()`. That is because the compiler is + /// free to change the inference of a type as long as that inference is technically correct, + /// regardless of the programmer's unknown expectation. /// - /// This is particularly dangerous in case a seemingly innocent/unrelated change can cause type - /// inference to start inferring a different type. E.g. the transmute is the tail expression of - /// an `if` branch, and a different branches type changes, causing the transmute to silently - /// have a different type, instead of a proper error. + /// Both type-parameters, the input- and the output-type, to any `transmute()` should + /// be given explicitly: Setting the input-type explicitly avoids confusion about what the + /// argument's type actually is. Setting the output-type explicitly avoids type-inference + /// to infer a technically correct yet unexpected type. /// /// ### Example /// ```no_run /// # unsafe { + /// // Avoid "naked" calls to `transmute()`! /// let x: i32 = std::mem::transmute([1u16, 2u16]); + /// + /// // `first_answers` is intended to transmute a slice of bool to a slice of u8. + /// // But the programmer forgot to index the first element of the outer slice, + /// // so we are actually transmuting from "pointers to slices" instead of + /// // transmuting from "a slice of bool", causing a nonsensical result. + /// let the_answers: &[&[bool]] = &[&[true, false, true]]; + /// let first_answers: &[u8] = std::mem::transmute(the_answers); /// # } /// ``` /// Use instead: /// ```no_run /// # unsafe { /// let x = std::mem::transmute::<[u16; 2], i32>([1u16, 2u16]); + /// + /// // The explicit type parameters on `transmute()` makes the intention clear, + /// // and cause a type-error if the actual types don't match our expectation. + /// let the_answers: &[&[bool]] = &[&[true, false, true]]; + /// let first_answers: &[u8] = std::mem::transmute::<&[bool], &[u8]>(the_answers[0]); /// # } /// ``` #[clippy::version = "1.79.0"] 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 ba8c7d6bfcb40..fca332dba401a 100644 --- a/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs +++ b/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs @@ -6,8 +6,8 @@ use rustc_errors::Applicability; use rustc_hir::{Expr, Node}; use rustc_hir_typeck::cast::check_cast; use rustc_lint::LateContext; -use rustc_middle::ty::cast::CastKind; use rustc_middle::ty::Ty; +use rustc_middle::ty::cast::CastKind; /// Checks for `transmutes_expressible_as_ptr_casts` lint. /// Returns `true` if it's triggered, otherwise returns `false`. diff --git a/clippy_lints/src/transmute/unsound_collection_transmute.rs b/clippy_lints/src/transmute/unsound_collection_transmute.rs index 35e9383076606..f46e95b0b8329 100644 --- a/clippy_lints/src/transmute/unsound_collection_transmute.rs +++ b/clippy_lints/src/transmute/unsound_collection_transmute.rs @@ -1,5 +1,5 @@ -use super::utils::is_layout_incompatible; use super::UNSOUND_COLLECTION_TRANSMUTE; +use super::utils::is_layout_incompatible; use clippy_utils::diagnostics::span_lint; use rustc_hir::Expr; use rustc_lint::LateContext; diff --git a/clippy_lints/src/tuple_array_conversions.rs b/clippy_lints/src/tuple_array_conversions.rs index 1d0de9327546f..3da8a449a7c58 100644 --- a/clippy_lints/src/tuple_array_conversions.rs +++ b/clippy_lints/src/tuple_array_conversions.rs @@ -1,5 +1,5 @@ -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::visitors::for_each_local_use_after_expr; use clippy_utils::{is_from_proc_macro, path_to_local}; diff --git a/clippy_lints/src/types/box_collection.rs b/clippy_lints/src/types/box_collection.rs index 9ac73394548c7..24fe4e08a5b44 100644 --- a/clippy_lints/src/types/box_collection.rs +++ b/clippy_lints/src/types/box_collection.rs @@ -3,7 +3,7 @@ use clippy_utils::{path_def_id, qpath_generic_tys}; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, QPath}; use rustc_lint::LateContext; -use rustc_span::{sym, Symbol}; +use rustc_span::{Symbol, sym}; use super::BOX_COLLECTION; diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index 3a14927802b48..363aea8be72e9 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -18,8 +18,8 @@ use rustc_hir::{ }; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; -use rustc_span::def_id::LocalDefId; use rustc_span::Span; +use rustc_span::def_id::LocalDefId; declare_clippy_lint! { /// ### What it does @@ -261,8 +261,59 @@ declare_clippy_lint! { /// ### Example /// ```no_run /// # use std::rc::Rc; - /// struct Foo { - /// inner: Rc>>>, + /// struct PointMatrixContainer { + /// matrix: Rc>>>, + /// } + /// + /// fn main() { + /// let point_matrix: Vec>> = vec![ + /// vec![ + /// Box::new((1, 2, 3, 4)), + /// Box::new((5, 6, 7, 8)), + /// ], + /// vec![ + /// Box::new((9, 10, 11, 12)), + /// ], + /// ]; + /// + /// let shared_point_matrix: Rc>>> = Rc::new(point_matrix); + /// + /// let container = PointMatrixContainer { + /// matrix: shared_point_matrix, + /// }; + /// + /// // ... + /// } + /// ``` + /// Use instead: + /// ### Example + /// ```no_run + /// # use std::rc::Rc; + /// type PointMatrix = Vec>>; + /// type SharedPointMatrix = Rc; + /// + /// struct PointMatrixContainer { + /// matrix: SharedPointMatrix, + /// } + /// + /// fn main() { + /// let point_matrix: PointMatrix = vec![ + /// vec![ + /// Box::new((1, 2, 3, 4)), + /// Box::new((5, 6, 7, 8)), + /// ], + /// vec![ + /// Box::new((9, 10, 11, 12)), + /// ], + /// ]; + /// + /// let shared_point_matrix: SharedPointMatrix = Rc::new(point_matrix); + /// + /// let container = PointMatrixContainer { + /// matrix: shared_point_matrix, + /// }; + /// + /// // ... /// } /// ``` #[clippy::version = "pre 1.29.0"] @@ -335,30 +386,22 @@ impl<'tcx> LateLintPass<'tcx> for Types { let is_exported = cx.effective_visibilities.is_exported(def_id); - self.check_fn_decl( - cx, - decl, - CheckTyContext { - is_in_trait_impl, - is_exported, - in_body: matches!(fn_kind, FnKind::Closure), - ..CheckTyContext::default() - }, - ); + self.check_fn_decl(cx, decl, CheckTyContext { + is_in_trait_impl, + is_exported, + in_body: matches!(fn_kind, FnKind::Closure), + ..CheckTyContext::default() + }); } fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { let is_exported = cx.effective_visibilities.is_exported(item.owner_id.def_id); match item.kind { - ItemKind::Static(ty, _, _) | ItemKind::Const(ty, _, _) => self.check_ty( - cx, - ty, - CheckTyContext { - is_exported, - ..CheckTyContext::default() - }, - ), + ItemKind::Static(ty, _, _) | ItemKind::Const(ty, _, _) => self.check_ty(cx, ty, CheckTyContext { + is_exported, + ..CheckTyContext::default() + }), // functions, enums, structs, impls and traits are covered _ => (), } @@ -376,14 +419,10 @@ impl<'tcx> LateLintPass<'tcx> for Types { false }; - self.check_ty( - cx, - ty, - CheckTyContext { - is_in_trait_impl, - ..CheckTyContext::default() - }, - ); + self.check_ty(cx, ty, CheckTyContext { + is_in_trait_impl, + ..CheckTyContext::default() + }); }, // Methods are covered by check_fn. // Type aliases are ignored because oftentimes it's impossible to @@ -399,14 +438,10 @@ impl<'tcx> LateLintPass<'tcx> for Types { let is_exported = cx.effective_visibilities.is_exported(field.def_id); - self.check_ty( - cx, - field.ty, - CheckTyContext { - is_exported, - ..CheckTyContext::default() - }, - ); + self.check_ty(cx, field.ty, CheckTyContext { + is_exported, + ..CheckTyContext::default() + }); } fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &TraitItem<'tcx>) { @@ -434,14 +469,10 @@ impl<'tcx> LateLintPass<'tcx> for Types { fn check_local(&mut self, cx: &LateContext<'tcx>, local: &LetStmt<'tcx>) { if let Some(ty) = local.ty { - self.check_ty( - cx, - ty, - CheckTyContext { - in_body: true, - ..CheckTyContext::default() - }, - ); + self.check_ty(cx, ty, CheckTyContext { + in_body: true, + ..CheckTyContext::default() + }); } } } diff --git a/clippy_lints/src/types/redundant_allocation.rs b/clippy_lints/src/types/redundant_allocation.rs index 0801eace4fcfd..1a656088b1747 100644 --- a/clippy_lints/src/types/redundant_allocation.rs +++ b/clippy_lints/src/types/redundant_allocation.rs @@ -9,7 +9,7 @@ use rustc_lint::LateContext; use rustc_middle::ty::TypeVisitableExt; use rustc_span::symbol::sym; -use super::{utils, REDUNDANT_ALLOCATION}; +use super::{REDUNDANT_ALLOCATION, utils}; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'tcx>, qpath: &QPath<'tcx>, def_id: DefId) -> bool { let mut applicability = Applicability::MaybeIncorrect; diff --git a/clippy_lints/src/types/type_complexity.rs b/clippy_lints/src/types/type_complexity.rs index 7fcfd5c8f350d..0b64fddb447b5 100644 --- a/clippy_lints/src/types/type_complexity.rs +++ b/clippy_lints/src/types/type_complexity.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint; use rustc_hir as hir; -use rustc_hir::intravisit::{walk_inf, walk_ty, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_inf, walk_ty}; use rustc_hir::{GenericParamKind, TyKind}; use rustc_lint::LateContext; use rustc_target::spec::abi::Abi; diff --git a/clippy_lints/src/types/vec_box.rs b/clippy_lints/src/types/vec_box.rs index 29996a6f783e1..230239335c65f 100644 --- a/clippy_lints/src/types/vec_box.rs +++ b/clippy_lints/src/types/vec_box.rs @@ -6,8 +6,8 @@ use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, GenericArg, LangItem, QPath, TyKind}; use rustc_hir_analysis::lower_ty; use rustc_lint::LateContext; -use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::TypeVisitableExt; +use rustc_middle::ty::layout::LayoutOf; use rustc_span::symbol::sym; use super::VEC_BOX; diff --git a/clippy_lints/src/unconditional_recursion.rs b/clippy_lints/src/unconditional_recursion.rs index 42100e1d755c7..3fc08e8192d4c 100644 --- a/clippy_lints/src/unconditional_recursion.rs +++ b/clippy_lints/src/unconditional_recursion.rs @@ -5,7 +5,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::intravisit::{walk_body, walk_expr, FnKind, Visitor}; +use rustc_hir::intravisit::{FnKind, Visitor, walk_body, walk_expr}; use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId, Item, ItemKind, Node, QPath, TyKind}; use rustc_hir_analysis::lower_ty; use rustc_lint::{LateContext, LateLintPass}; @@ -13,8 +13,8 @@ use rustc_middle::hir::map::Map; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, AssocKind, Ty, TyCtxt}; use rustc_session::impl_lint_pass; -use rustc_span::symbol::{kw, Ident}; -use rustc_span::{sym, Span}; +use rustc_span::symbol::{Ident, kw}; +use rustc_span::{Span, sym}; use rustc_trait_selection::error_reporting::traits::suggestions::ReturnsVisitor; use std::ops::ControlFlow; @@ -257,13 +257,10 @@ fn is_default_method_on_current_ty<'tcx>(tcx: TyCtxt<'tcx>, qpath: QPath<'tcx>, } if matches!( ty.kind, - TyKind::Path(QPath::Resolved( - _, - hir::Path { - res: Res::SelfTyAlias { .. }, - .. - }, - )) + TyKind::Path(QPath::Resolved(_, hir::Path { + res: Res::SelfTyAlias { .. }, + .. + },)) ) { return true; } diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs index f51c5f74d9930..44eb0a6c593da 100644 --- a/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -4,12 +4,12 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_lint_allowed; use clippy_utils::source::walk_span_to_context; -use clippy_utils::visitors::{for_each_expr, Descend}; +use clippy_utils::visitors::{Descend, for_each_expr}; use hir::HirId; use rustc_data_structures::sync::Lrc; use rustc_hir as hir; use rustc_hir::{Block, BlockCheckMode, ItemKind, Node, UnsafeSource}; -use rustc_lexer::{tokenize, TokenKind}; +use rustc_lexer::{TokenKind, tokenize}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::impl_lint_pass; diff --git a/clippy_lints/src/uninhabited_references.rs b/clippy_lints/src/uninhabited_references.rs index 88039372ebd2c..cfa565cf80379 100644 --- a/clippy_lints/src/uninhabited_references.rs +++ b/clippy_lints/src/uninhabited_references.rs @@ -5,8 +5,8 @@ use rustc_hir_analysis::lower_ty; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; use rustc_session::declare_lint_pass; -use rustc_span::def_id::LocalDefId; use rustc_span::Span; +use rustc_span::def_id::LocalDefId; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/uninit_vec.rs b/clippy_lints/src/uninit_vec.rs index 9ffcfcc0f50cb..93ed15777e015 100644 --- a/clippy_lints/src/uninit_vec.rs +++ b/clippy_lints/src/uninit_vec.rs @@ -1,13 +1,13 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; -use clippy_utils::higher::{get_vec_init_kind, VecInitKind}; +use clippy_utils::higher::{VecInitKind, get_vec_init_kind}; use clippy_utils::ty::{is_type_diagnostic_item, is_uninit_value_valid_for_ty}; -use clippy_utils::{is_integer_literal, is_lint_allowed, path_to_local_id, peel_hir_expr_while, SpanlessEq}; +use clippy_utils::{SpanlessEq, is_integer_literal, is_lint_allowed, path_to_local_id, peel_hir_expr_while}; use rustc_hir::{Block, Expr, ExprKind, HirId, PatKind, PathSegment, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty; use rustc_session::declare_lint_pass; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; // TODO: add `ReadBuf` (RFC 2930) in "How to fix" once it is available in std declare_clippy_lint! { diff --git a/clippy_lints/src/unit_return_expecting_ord.rs b/clippy_lints/src/unit_return_expecting_ord.rs index a8cc2f9796339..87478a120dd0b 100644 --- a/clippy_lints/src/unit_return_expecting_ord.rs +++ b/clippy_lints/src/unit_return_expecting_ord.rs @@ -5,7 +5,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_middle::ty::{ClauseKind, GenericPredicates, ProjectionPredicate, TraitPredicate}; use rustc_session::declare_lint_pass; -use rustc_span::{sym, BytePos, Span}; +use rustc_span::{BytePos, Span, sym}; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/unit_types/let_unit_value.rs b/clippy_lints/src/unit_types/let_unit_value.rs index 80b661a757c21..bf2d75ea1a963 100644 --- a/clippy_lints/src/unit_types/let_unit_value.rs +++ b/clippy_lints/src/unit_types/let_unit_value.rs @@ -4,7 +4,7 @@ use clippy_utils::visitors::{for_each_local_assignment, for_each_value_source, i use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::intravisit::{walk_body, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_body}; use rustc_hir::{Expr, ExprKind, HirId, HirIdSet, LetStmt, MatchSource, Node, PatKind, QPath, TyKind}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::{in_external_macro, is_from_async_await}; diff --git a/clippy_lints/src/unit_types/unit_arg.rs b/clippy_lints/src/unit_types/unit_arg.rs index 978d49f09c3a8..41b2ca5d268c2 100644 --- a/clippy_lints/src/unit_types/unit_arg.rs +++ b/clippy_lints/src/unit_types/unit_arg.rs @@ -1,11 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; -use clippy_utils::source::{indent_of, reindent_multiline, SourceText, SpanRangeExt}; +use clippy_utils::source::{SourceText, SpanRangeExt, indent_of, reindent_multiline}; use rustc_errors::Applicability; use rustc_hir::{Block, Expr, ExprKind, MatchSource, Node, StmtKind}; use rustc_lint::LateContext; -use super::{utils, UNIT_ARG}; +use super::{UNIT_ARG, utils}; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if expr.span.from_expansion() { diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs index 080efe983c2a8..937e35dea96d9 100644 --- a/clippy_lints/src/unnecessary_wraps.rs +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -4,15 +4,15 @@ use clippy_utils::source::snippet; use clippy_utils::visitors::find_all_ret_expressions; use clippy_utils::{contains_return, is_res_lang_ctor, path_res, return_ty}; use rustc_errors::Applicability; -use rustc_hir::intravisit::FnKind; use rustc_hir::LangItem::{OptionSome, ResultOk}; +use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, ExprKind, FnDecl, Impl, ItemKind, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::impl_lint_pass; +use rustc_span::Span; use rustc_span::def_id::LocalDefId; use rustc_span::symbol::sym; -use rustc_span::Span; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs index 98f0be3135dba..104be63bb1579 100644 --- a/clippy_lints/src/unnested_or_patterns.rs +++ b/clippy_lints/src/unnested_or_patterns.rs @@ -1,14 +1,14 @@ #![allow(clippy::wildcard_imports, clippy::enum_glob_use)] -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::ast_utils::{eq_field_pat, eq_id, eq_maybe_qself, eq_pat, eq_path}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::over; +use rustc_ast::PatKind::*; use rustc_ast::mut_visit::*; use rustc_ast::ptr::P; -use rustc_ast::PatKind::*; -use rustc_ast::{self as ast, Mutability, Pat, PatKind, DUMMY_NODE_ID}; +use rustc_ast::{self as ast, DUMMY_NODE_ID, Mutability, Pat, PatKind}; use rustc_ast_pretty::pprust; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; @@ -16,7 +16,7 @@ use rustc_session::impl_lint_pass; use rustc_span::DUMMY_SP; use std::cell::Cell; use std::mem; -use thin_vec::{thin_vec, ThinVec}; +use thin_vec::{ThinVec, thin_vec}; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/unsafe_removed_from_name.rs b/clippy_lints/src/unsafe_removed_from_name.rs index 309eaedac8d29..e70d2a2dafeea 100644 --- a/clippy_lints/src/unsafe_removed_from_name.rs +++ b/clippy_lints/src/unsafe_removed_from_name.rs @@ -2,8 +2,8 @@ use clippy_utils::diagnostics::span_lint; use rustc_ast::ast::{Item, ItemKind, UseTree, UseTreeKind}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::symbol::Ident; use rustc_span::Span; +use rustc_span::symbol::Ident; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/unused_async.rs b/clippy_lints/src/unused_async.rs index 738fba54fa83f..a1f08cf6623bc 100644 --- a/clippy_lints/src/unused_async.rs +++ b/clippy_lints/src/unused_async.rs @@ -1,13 +1,13 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::is_def_id_trait_method; use rustc_hir::def::DefKind; -use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, Visitor}; +use rustc_hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn}; use rustc_hir::{Body, Expr, ExprKind, FnDecl, Node, YieldSource}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; use rustc_session::impl_lint_pass; -use rustc_span::def_id::{LocalDefId, LocalDefIdSet}; use rustc_span::Span; +use rustc_span::def_id::{LocalDefId, LocalDefIdSet}; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/unused_io_amount.rs b/clippy_lints/src/unused_io_amount.rs index af7abd009d250..d2a21b11ef47d 100644 --- a/clippy_lints/src/unused_io_amount.rs +++ b/clippy_lints/src/unused_io_amount.rs @@ -5,7 +5,7 @@ use hir::{ExprKind, HirId, PatKind}; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/unused_peekable.rs b/clippy_lints/src/unused_peekable.rs index 86a811e17ca93..71aa57e0a14c2 100644 --- a/clippy_lints/src/unused_peekable.rs +++ b/clippy_lints/src/unused_peekable.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable}; use clippy_utils::{fn_def_id, is_trait_method, path_to_local_id, peel_ref_operators}; use rustc_ast::Mutability; -use rustc_hir::intravisit::{walk_expr, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{Block, Expr, ExprKind, HirId, LetStmt, Node, PatKind, PathSegment, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter::OnlyBodies; diff --git a/clippy_lints/src/unused_trait_names.rs b/clippy_lints/src/unused_trait_names.rs new file mode 100644 index 0000000000000..9fd6ebccd02f4 --- /dev/null +++ b/clippy_lints/src/unused_trait_names.rs @@ -0,0 +1,94 @@ +use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_from_proc_macro; +use clippy_utils::source::snippet_opt; +use rustc_errors::Applicability; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::{Item, ItemKind, UseKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext as _}; +use rustc_middle::lint::in_external_macro; +use rustc_middle::ty::Visibility; +use rustc_session::impl_lint_pass; +use rustc_span::symbol::kw; + +declare_clippy_lint! { + /// ### What it does + /// Checks for `use Trait` where the Trait is only used for its methods and not referenced by a path directly. + /// + /// ### Why is this bad? + /// Traits imported that aren't used directly can be imported anonymously with `use Trait as _`. + /// It is more explicit, avoids polluting the current scope with unused names and can be useful to show which imports are required for traits. + /// + /// ### Example + /// ```no_run + /// use std::fmt::Write; + /// + /// fn main() { + /// let mut s = String::new(); + /// let _ = write!(s, "hello, world!"); + /// println!("{s}"); + /// } + /// ``` + /// Use instead: + /// ```no_run + /// use std::fmt::Write as _; + /// + /// fn main() { + /// let mut s = String::new(); + /// let _ = write!(s, "hello, world!"); + /// println!("{s}"); + /// } + /// ``` + #[clippy::version = "1.83.0"] + pub UNUSED_TRAIT_NAMES, + restriction, + "use items that import a trait but only use it anonymously" +} + +pub struct UnusedTraitNames { + msrv: Msrv, +} + +impl UnusedTraitNames { + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } + } +} + +impl_lint_pass!(UnusedTraitNames => [UNUSED_TRAIT_NAMES]); + +impl<'tcx> LateLintPass<'tcx> for UnusedTraitNames { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + if self.msrv.meets(msrvs::UNDERSCORE_IMPORTS) + && !in_external_macro(cx.sess(), item.span) + && let ItemKind::Use(path, UseKind::Single) = item.kind + // Ignore imports that already use Underscore + && item.ident.name != kw::Underscore + // Only check traits + && let Some(Res::Def(DefKind::Trait, _)) = path.res.first() + && cx.tcx.maybe_unused_trait_imports(()).contains(&item.owner_id.def_id) + // Only check this import if it is visible to its module only (no pub, pub(crate), ...) + && let module = cx.tcx.parent_module_from_def_id(item.owner_id.def_id) + && cx.tcx.visibility(item.owner_id.def_id) == Visibility::Restricted(module.to_def_id()) + && let Some(last_segment) = path.segments.last() + && let Some(snip) = snippet_opt(cx, last_segment.ident.span) + && !is_from_proc_macro(cx, &last_segment.ident) + { + let complete_span = last_segment.ident.span.to(item.ident.span); + span_lint_and_sugg( + cx, + UNUSED_TRAIT_NAMES, + complete_span, + "importing trait that is only used anonymously", + "use", + format!("{snip} as _"), + Applicability::MachineApplicable, + ); + } + } + + extract_msrv_attr!(LateContext); +} diff --git a/clippy_lints/src/unused_unit.rs b/clippy_lints/src/unused_unit.rs index 65f431d338bdb..d5309aade7aac 100644 --- a/clippy_lints/src/unused_unit.rs +++ b/clippy_lints/src/unused_unit.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{position_before_rarrow, SpanRangeExt}; +use clippy_utils::source::{SpanRangeExt, position_before_rarrow}; use rustc_ast::visit::FnKind; -use rustc_ast::{ast, ClosureBinder}; +use rustc_ast::{ClosureBinder, ast}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::declare_lint_pass; diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index 0b4ea0752b6ba..596f0fd9c8b02 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -3,7 +3,7 @@ use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::usage::is_potentially_local_place; use clippy_utils::{higher, path_to_local}; use rustc_errors::Applicability; -use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, Visitor}; +use rustc_hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn}; use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, Node, PathSegment, UnOp}; use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceWithHirId}; use rustc_lint::{LateContext, LateLintPass}; @@ -13,7 +13,7 @@ use rustc_middle::mir::FakeReadCause; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::declare_lint_pass; use rustc_span::def_id::LocalDefId; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/unwrap_in_result.rs b/clippy_lints/src/unwrap_in_result.rs index f77badd97b7a3..9b9a2ffbbc807 100644 --- a/clippy_lints/src/unwrap_in_result.rs +++ b/clippy_lints/src/unwrap_in_result.rs @@ -7,7 +7,7 @@ use rustc_hir as hir; use rustc_hir::ImplItemKind; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index 5da48f4f7fbd7..e340b419bd07b 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -1,5 +1,5 @@ -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::is_from_proc_macro; use clippy_utils::ty::same_type_and_consts; @@ -7,7 +7,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::LocalDefId; -use rustc_hir::intravisit::{walk_inf, walk_ty, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_inf, walk_ty}; use rustc_hir::{ self as hir, Expr, ExprKind, FnRetTy, FnSig, GenericArgsParentheses, GenericParam, GenericParamKind, HirId, Impl, ImplItemKind, Item, ItemKind, Pat, PatKind, Path, QPath, Ty, TyKind, diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 2f7d54e73ed79..29a7949b34358 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -12,7 +12,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::{self, EarlyBinder, GenericArg, GenericArgsRef, Ty, TypeVisitableExt}; use rustc_session::impl_lint_pass; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; declare_clippy_lint! { @@ -217,12 +217,12 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { }, ExprKind::MethodCall(.., args, _) => { cx.typeck_results().type_dependent_def_id(parent.hir_id).map(|did| { - return ( + ( did, args, cx.typeck_results().node_args(parent.hir_id), MethodOrFunction::Method, - ); + ) }) }, _ => None, diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index 0cce45290cfe7..31f9d84f5e466 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -1,6 +1,6 @@ use clippy_utils::{get_attr, higher}; -use rustc_ast::ast::{LitFloatType, LitKind}; use rustc_ast::LitIntType; +use rustc_ast::ast::{LitFloatType, LitKind}; use rustc_data_structures::fx::FxHashMap; use rustc_hir::{ self as hir, ArrayLen, BindingMode, CaptureBy, Closure, ClosureKind, ConstArg, ConstArgKind, CoroutineKind, diff --git a/clippy_lints/src/utils/format_args_collector.rs b/clippy_lints/src/utils/format_args_collector.rs index f50ce6c99deac..8f314ce7a60ce 100644 --- a/clippy_lints/src/utils/format_args_collector.rs +++ b/clippy_lints/src/utils/format_args_collector.rs @@ -3,10 +3,10 @@ use clippy_utils::source::SpanRangeExt; use itertools::Itertools; use rustc_ast::{Crate, Expr, ExprKind, FormatArgs}; use rustc_data_structures::fx::FxHashMap; -use rustc_lexer::{tokenize, TokenKind}; +use rustc_lexer::{TokenKind, tokenize}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::impl_lint_pass; -use rustc_span::{hygiene, Span}; +use rustc_span::{Span, hygiene}; use std::iter::once; use std::mem; diff --git a/clippy_lints/src/utils/internal_lints/collapsible_calls.rs b/clippy_lints/src/utils/internal_lints/collapsible_calls.rs index f752968267553..d8f101a8614d8 100644 --- a/clippy_lints/src/utils/internal_lints/collapsible_calls.rs +++ b/clippy_lints/src/utils/internal_lints/collapsible_calls.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; -use clippy_utils::{is_expr_path_def_path, is_lint_allowed, peel_blocks_with_stmt, SpanlessEq}; +use clippy_utils::{SpanlessEq, is_expr_path_def_path, is_lint_allowed, peel_blocks_with_stmt}; use rustc_errors::Applicability; use rustc_hir::{Closure, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/utils/internal_lints/invalid_paths.rs b/clippy_lints/src/utils/internal_lints/invalid_paths.rs index 0ffcb433481e4..d444ad45087cf 100644 --- a/clippy_lints/src/utils/internal_lints/invalid_paths.rs +++ b/clippy_lints/src/utils/internal_lints/invalid_paths.rs @@ -2,11 +2,11 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::def_path_res; use clippy_utils::diagnostics::span_lint; use rustc_hir as hir; -use rustc_hir::def::DefKind; use rustc_hir::Item; +use rustc_hir::def::DefKind; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::fast_reject::SimplifiedType; use rustc_middle::ty::FloatTy; +use rustc_middle::ty::fast_reject::SimplifiedType; use rustc_session::declare_lint_pass; use rustc_span::symbol::Symbol; diff --git a/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs b/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs index 20526113d69e3..7c45a5b2f0971 100644 --- a/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs +++ b/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs @@ -12,7 +12,7 @@ use rustc_middle::hir::nested_filter; use rustc_session::impl_lint_pass; use rustc_span::source_map::Spanned; use rustc_span::symbol::Symbol; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use {rustc_ast as ast, rustc_hir as hir}; declare_clippy_lint! { diff --git a/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs b/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs index 41183700f09e8..76b0a52621be4 100644 --- a/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs +++ b/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs @@ -8,12 +8,12 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::{Expr, ExprKind, LetStmt, Mutability, Node}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::mir::interpret::{Allocation, GlobalAlloc}; use rustc_middle::mir::ConstValue; +use rustc_middle::mir::interpret::{Allocation, GlobalAlloc}; use rustc_middle::ty::{self, Ty}; use rustc_session::impl_lint_pass; -use rustc_span::symbol::Symbol; use rustc_span::Span; +use rustc_span::symbol::Symbol; use std::str; diff --git a/clippy_lints/src/vec.rs b/clippy_lints/src/vec.rs index d3e49bff422ca..ce4f41e854d5e 100644 --- a/clippy_lints/src/vec.rs +++ b/clippy_lints/src/vec.rs @@ -1,8 +1,8 @@ use std::collections::BTreeMap; use std::ops::ControlFlow; -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::source::SpanRangeExt; @@ -15,7 +15,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_middle::ty::layout::LayoutOf; use rustc_session::impl_lint_pass; -use rustc_span::{sym, DesugaringKind, Span}; +use rustc_span::{DesugaringKind, Span, sym}; #[expect(clippy::module_name_repetitions)] pub struct UselessVec { diff --git a/clippy_lints/src/vec_init_then_push.rs b/clippy_lints/src/vec_init_then_push.rs index a599415a2dd57..91dff5a95236e 100644 --- a/clippy_lints/src/vec_init_then_push.rs +++ b/clippy_lints/src/vec_init_then_push.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::higher::{get_vec_init_kind, VecInitKind}; +use clippy_utils::higher::{VecInitKind, get_vec_init_kind}; use clippy_utils::source::snippet; use clippy_utils::visitors::for_each_local_use_after_expr; use clippy_utils::{get_parent_expr, path_to_local_id}; diff --git a/clippy_lints/src/visibility.rs b/clippy_lints/src/visibility.rs index 7a85196cecaaa..2e5fc5834e24f 100644 --- a/clippy_lints/src/visibility.rs +++ b/clippy_lints/src/visibility.rs @@ -5,8 +5,8 @@ use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::declare_lint_pass; -use rustc_span::symbol::kw; use rustc_span::Span; +use rustc_span::symbol::kw; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index 7d9e58ad2f8d5..405310512dff3 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -10,7 +10,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty; use rustc_session::impl_lint_pass; use rustc_span::symbol::kw; -use rustc_span::{sym, BytePos}; +use rustc_span::{BytePos, sym}; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index ec5a5896fb2bf..1dd46ed5a8915 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -1,8 +1,8 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::is_in_test; -use clippy_utils::macros::{format_arg_removal_span, root_macro_call_first_node, FormatArgsStorage, MacroCall}; -use clippy_utils::source::{expand_past_previous_comma, SpanRangeExt}; +use clippy_utils::macros::{FormatArgsStorage, MacroCall, format_arg_removal_span, root_macro_call_first_node}; +use clippy_utils::source::{SpanRangeExt, expand_past_previous_comma}; use rustc_ast::token::LitKind; use rustc_ast::{ FormatArgPosition, FormatArgPositionKind, FormatArgs, FormatArgsPiece, FormatOptions, FormatPlaceholder, @@ -12,7 +12,7 @@ use rustc_errors::Applicability; use rustc_hir::{Expr, Impl, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; -use rustc_span::{sym, BytePos, Span}; +use rustc_span::{BytePos, Span, sym}; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/zombie_processes.rs b/clippy_lints/src/zombie_processes.rs new file mode 100644 index 0000000000000..ba2a80ee66b05 --- /dev/null +++ b/clippy_lints/src/zombie_processes.rs @@ -0,0 +1,334 @@ +use ControlFlow::{Break, Continue}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::{fn_def_id, get_enclosing_block, match_any_def_paths, match_def_path, path_to_local_id, paths}; +use rustc_ast::Mutability; +use rustc_errors::Applicability; +use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_local}; +use rustc_hir::{Expr, ExprKind, HirId, LetStmt, Node, PatKind, Stmt, StmtKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::declare_lint_pass; +use rustc_span::sym; +use std::ops::ControlFlow; + +declare_clippy_lint! { + /// ### What it does + /// Looks for code that spawns a process but never calls `wait()` on the child. + /// + /// ### Why is this bad? + /// As explained in the [standard library documentation](https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning), + /// calling `wait()` is necessary on Unix platforms to properly release all OS resources associated with the process. + /// Not doing so will effectively leak process IDs and/or other limited global resources, + /// which can eventually lead to resource exhaustion, so it's recommended to call `wait()` in long-running applications. + /// Such processes are called "zombie processes". + /// + /// ### Example + /// ```rust + /// use std::process::Command; + /// + /// let _child = Command::new("ls").spawn().expect("failed to execute child"); + /// ``` + /// Use instead: + /// ```rust + /// use std::process::Command; + /// + /// let mut child = Command::new("ls").spawn().expect("failed to execute child"); + /// child.wait().expect("failed to wait on child"); + /// ``` + #[clippy::version = "1.74.0"] + pub ZOMBIE_PROCESSES, + suspicious, + "not waiting on a spawned child process" +} +declare_lint_pass!(ZombieProcesses => [ZOMBIE_PROCESSES]); + +impl<'tcx> LateLintPass<'tcx> for ZombieProcesses { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if let ExprKind::Call(..) | ExprKind::MethodCall(..) = expr.kind + && let Some(child_adt) = cx.typeck_results().expr_ty(expr).ty_adt_def() + && match_def_path(cx, child_adt.did(), &paths::CHILD) + { + match cx.tcx.parent_hir_node(expr.hir_id) { + Node::LetStmt(local) + if let PatKind::Binding(_, local_id, ..) = local.pat.kind + && let Some(enclosing_block) = get_enclosing_block(cx, expr.hir_id) => + { + let mut vis = WaitFinder::WalkUpTo(cx, local_id); + + // If it does have a `wait()` call, we're done. Don't lint. + if let Break(BreakReason::WaitFound) = walk_block(&mut vis, enclosing_block) { + return; + } + + // Don't emit a suggestion since the binding is used later + check(cx, expr, false); + }, + Node::LetStmt(&LetStmt { pat, .. }) if let PatKind::Wild = pat.kind => { + // `let _ = child;`, also dropped immediately without `wait()`ing + check(cx, expr, true); + }, + Node::Stmt(&Stmt { + kind: StmtKind::Semi(_), + .. + }) => { + // Immediately dropped. E.g. `std::process::Command::new("echo").spawn().unwrap();` + check(cx, expr, true); + }, + _ => {}, + } + } + } +} + +enum BreakReason { + WaitFound, + EarlyReturn, +} + +/// A visitor responsible for finding a `wait()` call on a local variable. +/// +/// Conditional `wait()` calls are assumed to not call wait: +/// ```ignore +/// let mut c = Command::new("").spawn().unwrap(); +/// if true { +/// c.wait(); +/// } +/// ``` +/// +/// Note that this visitor does NOT explicitly look for `wait()` calls directly, but rather does the +/// inverse -- checking if all uses of the local are either: +/// - a field access (`child.{stderr,stdin,stdout}`) +/// - calling `id` or `kill` +/// - no use at all (e.g. `let _x = child;`) +/// - taking a shared reference (`&`), `wait()` can't go through that +/// +/// None of these are sufficient to prevent zombie processes. +/// Doing it like this means more FNs, but FNs are better than FPs. +/// +/// `return` expressions, conditional or not, short-circuit the visitor because +/// if a `wait()` call hadn't been found at that point, it might never reach one at a later point: +/// ```ignore +/// let mut c = Command::new("").spawn().unwrap(); +/// if true { +/// return; // Break(BreakReason::EarlyReturn) +/// } +/// c.wait(); // this might not be reachable +/// ``` +enum WaitFinder<'a, 'tcx> { + WalkUpTo(&'a LateContext<'tcx>, HirId), + Found(&'a LateContext<'tcx>, HirId), +} + +impl<'a, 'tcx> Visitor<'tcx> for WaitFinder<'a, 'tcx> { + type Result = ControlFlow; + + fn visit_local(&mut self, l: &'tcx LetStmt<'tcx>) -> Self::Result { + if let Self::WalkUpTo(cx, local_id) = *self + && let PatKind::Binding(_, pat_id, ..) = l.pat.kind + && local_id == pat_id + { + *self = Self::Found(cx, local_id); + } + + walk_local(self, l) + } + + fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) -> Self::Result { + let Self::Found(cx, local_id) = *self else { + return walk_expr(self, ex); + }; + + if path_to_local_id(ex, local_id) { + match cx.tcx.parent_hir_node(ex.hir_id) { + Node::Stmt(Stmt { + kind: StmtKind::Semi(_), + .. + }) => {}, + Node::Expr(expr) if let ExprKind::Field(..) = expr.kind => {}, + Node::Expr(expr) if let ExprKind::AddrOf(_, Mutability::Not, _) = expr.kind => {}, + Node::Expr(expr) + if let Some(fn_did) = fn_def_id(cx, expr) + && match_any_def_paths(cx, fn_did, &[&paths::CHILD_ID, &paths::CHILD_KILL]).is_some() => {}, + + // Conservatively assume that all other kinds of nodes call `.wait()` somehow. + _ => return Break(BreakReason::WaitFound), + } + } else { + match ex.kind { + ExprKind::Ret(..) => return Break(BreakReason::EarlyReturn), + ExprKind::If(cond, then, None) => { + walk_expr(self, cond)?; + + // A `wait()` call in an if expression with no else is not enough: + // + // let c = spawn(); + // if true { + // c.wait(); + // } + // + // This might not call wait(). However, early returns are propagated, + // because they might lead to a later wait() not being called. + if let Break(BreakReason::EarlyReturn) = walk_expr(self, then) { + return Break(BreakReason::EarlyReturn); + } + + return Continue(()); + }, + + ExprKind::If(cond, then, Some(else_)) => { + walk_expr(self, cond)?; + + #[expect(clippy::unnested_or_patterns)] + match (walk_expr(self, then), walk_expr(self, else_)) { + (Continue(()), Continue(())) + + // `wait()` in one branch but nothing in the other does not count + | (Continue(()), Break(BreakReason::WaitFound)) + | (Break(BreakReason::WaitFound), Continue(())) => {}, + + // `wait()` in both branches is ok + (Break(BreakReason::WaitFound), Break(BreakReason::WaitFound)) => { + return Break(BreakReason::WaitFound); + }, + + // Propagate early returns in either branch + (Break(BreakReason::EarlyReturn), _) | (_, Break(BreakReason::EarlyReturn)) => { + return Break(BreakReason::EarlyReturn); + }, + } + + return Continue(()); + }, + _ => {}, + } + } + + walk_expr(self, ex) + } +} + +/// This function has shared logic between the different kinds of nodes that can trigger the lint. +/// +/// In particular, `let = ;` requires some custom additional logic +/// such as checking that the binding is not used in certain ways, which isn't necessary for +/// `let _ = ;`. +/// +/// This checks if the program doesn't unconditionally exit after the spawn expression. +fn check<'tcx>(cx: &LateContext<'tcx>, spawn_expr: &'tcx Expr<'tcx>, emit_suggestion: bool) { + let Some(block) = get_enclosing_block(cx, spawn_expr.hir_id) else { + return; + }; + + let mut vis = ExitPointFinder { + cx, + state: ExitPointState::WalkUpTo(spawn_expr.hir_id), + }; + if let Break(ExitCallFound) = vis.visit_block(block) { + // Visitor found an unconditional `exit()` call, so don't lint. + return; + } + + span_lint_and_then( + cx, + ZOMBIE_PROCESSES, + spawn_expr.span, + "spawned process is never `wait()`ed on", + |diag| { + if emit_suggestion { + diag.span_suggestion( + spawn_expr.span.shrink_to_hi(), + "try", + ".wait()", + Applicability::MaybeIncorrect, + ); + } else { + diag.note("consider calling `.wait()`"); + } + + diag.note("not doing so might leave behind zombie processes") + .note("see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning"); + }, + ); +} + +/// Checks if the given expression exits the process. +fn is_exit_expression(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + fn_def_id(cx, expr).is_some_and(|fn_did| { + cx.tcx.is_diagnostic_item(sym::process_exit, fn_did) || match_def_path(cx, fn_did, &paths::ABORT) + }) +} + +#[derive(Debug)] +enum ExitPointState { + /// Still walking up to the expression that initiated the visitor. + WalkUpTo(HirId), + /// We're inside of a control flow construct (e.g. `if`, `match`, `loop`) + /// Within this, we shouldn't accept any `exit()` calls in here, but we can leave all of these + /// constructs later and still continue looking for an `exit()` call afterwards. Example: + /// ```ignore + /// Command::new("").spawn().unwrap(); + /// + /// if true { // depth=1 + /// if true { // depth=2 + /// match () { // depth=3 + /// () => loop { // depth=4 + /// + /// std::process::exit(); + /// ^^^^^^^^^^^^^^^^^^^^^ conditional exit call, ignored + /// + /// } // depth=3 + /// } // depth=2 + /// } // depth=1 + /// } // depth=0 + /// + /// std::process::exit(); + /// ^^^^^^^^^^^^^^^^^^^^^ this exit call is accepted because we're now unconditionally calling it + /// ``` + /// We can only get into this state from `NoExit`. + InControlFlow { depth: u32 }, + /// No exit call found yet, but looking for one. + NoExit, +} + +fn expr_enters_control_flow(expr: &Expr<'_>) -> bool { + matches!(expr.kind, ExprKind::If(..) | ExprKind::Match(..) | ExprKind::Loop(..)) +} + +struct ExitPointFinder<'a, 'tcx> { + state: ExitPointState, + cx: &'a LateContext<'tcx>, +} + +struct ExitCallFound; + +impl<'a, 'tcx> Visitor<'tcx> for ExitPointFinder<'a, 'tcx> { + type Result = ControlFlow; + + fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) -> Self::Result { + match self.state { + ExitPointState::WalkUpTo(id) if expr.hir_id == id => { + self.state = ExitPointState::NoExit; + walk_expr(self, expr) + }, + ExitPointState::NoExit if expr_enters_control_flow(expr) => { + self.state = ExitPointState::InControlFlow { depth: 1 }; + walk_expr(self, expr)?; + if let ExitPointState::InControlFlow { .. } = self.state { + self.state = ExitPointState::NoExit; + } + Continue(()) + }, + ExitPointState::NoExit if is_exit_expression(self.cx, expr) => Break(ExitCallFound), + ExitPointState::InControlFlow { ref mut depth } if expr_enters_control_flow(expr) => { + *depth += 1; + walk_expr(self, expr)?; + match self.state { + ExitPointState::InControlFlow { depth: 1 } => self.state = ExitPointState::NoExit, + ExitPointState::InControlFlow { ref mut depth } => *depth -= 1, + _ => {}, + } + Continue(()) + }, + _ => Continue(()), + } + } +} diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index 629ce9d04d432..fe30b10c435ed 100644 --- a/clippy_utils/Cargo.toml +++ b/clippy_utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_utils" -version = "0.1.82" +version = "0.1.83" edition = "2021" publish = false diff --git a/clippy_utils/src/ast_utils.rs b/clippy_utils/src/ast_utils.rs index de8bbb619f8e4..49323492e124d 100644 --- a/clippy_utils/src/ast_utils.rs +++ b/clippy_utils/src/ast_utils.rs @@ -242,13 +242,16 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool { } fn eq_coroutine_kind(a: Option, b: Option) -> bool { - match (a, b) { + matches!( + (a, b), (Some(CoroutineKind::Async { .. }), Some(CoroutineKind::Async { .. })) - | (Some(CoroutineKind::Gen { .. }), Some(CoroutineKind::Gen { .. })) - | (Some(CoroutineKind::AsyncGen { .. }), Some(CoroutineKind::AsyncGen { .. })) - | (None, None) => true, - _ => false, - } + | (Some(CoroutineKind::Gen { .. }), Some(CoroutineKind::Gen { .. })) + | ( + Some(CoroutineKind::AsyncGen { .. }), + Some(CoroutineKind::AsyncGen { .. }) + ) + | (None, None) + ) } pub fn eq_field(l: &ExprField, r: &ExprField) -> bool { diff --git a/clippy_utils/src/ast_utils/ident_iter.rs b/clippy_utils/src/ast_utils/ident_iter.rs index eefcbabd835d2..032cd3ed7399f 100644 --- a/clippy_utils/src/ast_utils/ident_iter.rs +++ b/clippy_utils/src/ast_utils/ident_iter.rs @@ -1,5 +1,5 @@ use core::iter::FusedIterator; -use rustc_ast::visit::{walk_attribute, walk_expr, Visitor}; +use rustc_ast::visit::{Visitor, walk_attribute, walk_expr}; use rustc_ast::{Attribute, Expr}; use rustc_span::symbol::Ident; diff --git a/clippy_utils/src/attrs.rs b/clippy_utils/src/attrs.rs index 42c8b218d14e8..edc9c6ccdff84 100644 --- a/clippy_utils/src/attrs.rs +++ b/clippy_utils/src/attrs.rs @@ -4,7 +4,7 @@ use rustc_lexer::TokenKind; use rustc_lint::LateContext; use rustc_middle::ty::{AdtDef, TyCtxt}; use rustc_session::Session; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use std::str::FromStr; use crate::source::SpanRangeExt; @@ -183,15 +183,15 @@ pub fn span_contains_cfg(cx: &LateContext<'_>, s: Span) -> bool { let mut iter = tokenize_with_text(src); // Search for the token sequence [`#`, `[`, `cfg`] - while iter.any(|(t, _)| matches!(t, TokenKind::Pound)) { - let mut iter = iter.by_ref().skip_while(|(t, _)| { + while iter.any(|(t, ..)| matches!(t, TokenKind::Pound)) { + let mut iter = iter.by_ref().skip_while(|(t, ..)| { matches!( t, TokenKind::Whitespace | TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } ) }); - if matches!(iter.next(), Some((TokenKind::OpenBracket, _))) - && matches!(iter.next(), Some((TokenKind::Ident, "cfg"))) + if matches!(iter.next(), Some((TokenKind::OpenBracket, ..))) + && matches!(iter.next(), Some((TokenKind::Ident, "cfg", _))) { return true; } diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs index 2bd6837d973bb..9143d292f670f 100644 --- a/clippy_utils/src/check_proc_macro.rs +++ b/clippy_utils/src/check_proc_macro.rs @@ -12,9 +12,9 @@ //! code was written, and check if the span contains that text. Note this will only work correctly //! if the span is not from a `macro_rules` based macro. +use rustc_ast::AttrStyle; use rustc_ast::ast::{AttrKind, Attribute, IntTy, LitIntType, LitKind, StrStyle, TraitObjectSyntax, UintTy}; use rustc_ast::token::CommentKind; -use rustc_ast::AttrStyle; use rustc_hir::intravisit::FnKind; use rustc_hir::{ Block, BlockCheckMode, Body, Closure, Destination, Expr, ExprKind, FieldDef, FnHeader, FnRetTy, HirId, Impl, @@ -24,7 +24,7 @@ use rustc_hir::{ use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty::TyCtxt; use rustc_session::Session; -use rustc_span::symbol::{kw, Ident}; +use rustc_span::symbol::{Ident, kw}; use rustc_span::{Span, Symbol}; use rustc_target::spec::abi::Abi; diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index 760d5bc95f7fb..bf47cf6d372d2 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -1,24 +1,24 @@ #![allow(clippy::float_cmp)] use crate::macros::HirNode; -use crate::source::{walk_span_to_context, SpanRangeExt}; +use crate::source::{SpanRangeExt, walk_span_to_context}; use crate::{clip, is_direct_expn_of, sext, unsext}; -use rustc_apfloat::ieee::{Half, Quad}; use rustc_apfloat::Float; +use rustc_apfloat::ieee::{Half, Quad}; use rustc_ast::ast::{self, LitFloatType, LitKind}; use rustc_data_structures::sync::Lrc; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{BinOp, BinOpKind, Block, ConstBlock, Expr, ExprKind, HirId, Item, ItemKind, Node, QPath, UnOp}; use rustc_lexer::tokenize; use rustc_lint::LateContext; -use rustc_middle::mir::interpret::{alloc_range, Scalar}; use rustc_middle::mir::ConstValue; +use rustc_middle::mir::interpret::{Scalar, alloc_range}; use rustc_middle::ty::{self, FloatTy, IntTy, ParamEnv, ScalarInt, Ty, TyCtxt, TypeckResults, UintTy}; use rustc_middle::{bug, mir, span_bug}; use rustc_span::def_id::DefId; use rustc_span::symbol::Ident; -use rustc_span::{sym, SyntaxContext}; +use rustc_span::{SyntaxContext, sym}; use rustc_target::abi::Size; use std::cell::Cell; use std::cmp::Ordering; @@ -581,7 +581,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { } fn constant_negate(&self, o: &Constant<'tcx>, ty: Ty<'_>) -> Option> { - use self::Constant::{Int, F32, F64}; + use self::Constant::{F32, F64, Int}; match *o { Int(value) => { let ty::Int(ity) = *ty.kind() else { return None }; diff --git a/clippy_utils/src/eager_or_lazy.rs b/clippy_utils/src/eager_or_lazy.rs index a6dd12a28c5bc..9420d84b9591d 100644 --- a/clippy_utils/src/eager_or_lazy.rs +++ b/clippy_utils/src/eager_or_lazy.rs @@ -14,12 +14,12 @@ use crate::ty::{all_predicates_of, is_copy}; use crate::visitors::is_const_evaluatable; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::intravisit::{walk_expr, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, QPath, UnOp}; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_middle::ty::adjustment::Adjust; -use rustc_span::{sym, Symbol}; +use rustc_span::{Symbol, sym}; use std::{cmp, ops}; #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index e09cc9edf3ac3..3175a9a1dd31f 100644 --- a/clippy_utils/src/higher.rs +++ b/clippy_utils/src/higher.rs @@ -3,14 +3,14 @@ #![deny(clippy::missing_docs_in_private_items)] use crate::consts::{ConstEvalCtxt, Constant}; -use crate::ty::is_type_diagnostic_item; use crate::is_expn_of; +use crate::ty::is_type_diagnostic_item; use rustc_ast::ast; use rustc_hir as hir; use rustc_hir::{Arm, Block, Expr, ExprKind, HirId, LoopSource, MatchSource, Node, Pat, QPath}; use rustc_lint::LateContext; -use rustc_span::{sym, symbol, Span}; +use rustc_span::{Span, sym, symbol}; /// The essential nodes of a desugared for loop as well as the entire span: /// `for pat in arg { body }` becomes `(pat, arg, body)`. Returns `(pat, arg, body, span)`. diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index f61ef9ac1b05e..76900379ac787 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -1,20 +1,20 @@ use crate::consts::ConstEvalCtxt; use crate::macros::macro_backtrace; -use crate::source::{walk_span_to_context, SpanRange, SpanRangeExt}; +use crate::source::{SpanRange, SpanRangeExt, walk_span_to_context}; use crate::tokenize_with_text; use rustc_ast::ast::InlineAsmTemplatePiece; use rustc_data_structures::fx::FxHasher; -use rustc_hir::def::Res; use rustc_hir::MatchSource::TryDesugar; +use rustc_hir::def::Res; use rustc_hir::{ ArrayLen, AssocItemConstraint, BinOpKind, BindingMode, Block, BodyId, Closure, ConstArg, ConstArgKind, Expr, ExprField, ExprKind, FnRetTy, GenericArg, GenericArgs, HirId, HirIdMap, InlineAsmOperand, LetExpr, Lifetime, LifetimeName, Pat, PatField, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, Ty, TyKind, }; -use rustc_lexer::{tokenize, TokenKind}; +use rustc_lexer::{TokenKind, tokenize}; use rustc_lint::LateContext; use rustc_middle::ty::TypeckResults; -use rustc_span::{sym, BytePos, ExpnKind, MacroKind, Symbol, SyntaxContext}; +use rustc_span::{BytePos, ExpnKind, MacroKind, Symbol, SyntaxContext, sym}; use std::hash::{Hash, Hasher}; use std::ops::Range; @@ -1194,8 +1194,8 @@ fn eq_span_tokens( && let Some(rsrc) = right.get_source_range(cx) && let Some(rsrc) = rsrc.as_str() { - let pred = |t: &(_, _)| pred(t.0); - let map = |(_, x)| x; + let pred = |&(token, ..): &(TokenKind, _, _)| pred(token); + let map = |(_, source, _)| source; let ltok = tokenize_with_text(lsrc).filter(pred).map(map); let rtok = tokenize_with_text(rsrc).filter(pred).map(map); diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 0d0d6219c5e05..2fee6473910b4 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -78,7 +78,7 @@ pub mod visitors; pub use self::attrs::*; pub use self::check_proc_macro::{is_from_proc_macro, is_span_if, is_span_match}; pub use self::hir_utils::{ - both, count_eq, eq_expr_value, hash_expr, hash_stmt, is_bool, over, HirEqInterExpr, SpanlessEq, SpanlessHash, + HirEqInterExpr, SpanlessEq, SpanlessHash, both, count_eq, eq_expr_value, hash_expr, hash_stmt, is_bool, over, }; use core::mem; @@ -94,20 +94,20 @@ use rustc_ast::ast::{self, LitKind, RangeLimits}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::packed::Pu128; use rustc_data_structures::unhash::UnhashMap; +use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LocalModDefId, LOCAL_CRATE}; +use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LOCAL_CRATE, LocalDefId, LocalModDefId}; use rustc_hir::definitions::{DefPath, DefPathData}; use rustc_hir::hir_id::{HirIdMap, HirIdSet}; -use rustc_hir::intravisit::{walk_expr, FnKind, Visitor}; -use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; +use rustc_hir::intravisit::{FnKind, Visitor, walk_expr}; use rustc_hir::{ - self as hir, def, Arm, ArrayLen, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstArgKind, - ConstContext, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArgs, HirId, Impl, ImplItem, - ImplItemKind, ImplItemRef, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, OwnerNode, - Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitItemRef, - TraitRef, TyKind, UnOp, + self as hir, Arm, ArrayLen, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstArgKind, ConstContext, + Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, + ImplItemRef, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, OwnerNode, Param, Pat, + PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitItemRef, TraitRef, + TyKind, UnOp, def, }; -use rustc_lexer::{tokenize, TokenKind}; +use rustc_lexer::{TokenKind, tokenize}; use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::place::PlaceBase; use rustc_middle::mir::Const; @@ -120,12 +120,12 @@ use rustc_middle::ty::{ }; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::SourceMap; -use rustc_span::symbol::{kw, Ident, Symbol}; -use rustc_span::{sym, Span}; +use rustc_span::symbol::{Ident, Symbol, kw}; +use rustc_span::{InnerSpan, Span, sym}; use rustc_target::abi::Integer; use visitors::Visitable; -use crate::consts::{mir_to_const, ConstEvalCtxt, Constant}; +use crate::consts::{ConstEvalCtxt, Constant, mir_to_const}; use crate::higher::Range; use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type}; use crate::visitors::for_each_expr_without_closures; @@ -263,9 +263,13 @@ pub fn is_res_lang_ctor(cx: &LateContext<'_>, res: Res, lang_item: LangItem) -> } } - /// Checks if `{ctor_call_id}(...)` is `{enum_item}::{variant_name}(...)`. -pub fn is_enum_variant_ctor(cx: &LateContext<'_>, enum_item: Symbol, variant_name: Symbol, ctor_call_id: DefId) -> bool { +pub fn is_enum_variant_ctor( + cx: &LateContext<'_>, + enum_item: Symbol, + variant_name: Symbol, + ctor_call_id: DefId, +) -> bool { let Some(enum_def_id) = cx.tcx.get_diagnostic_item(enum_item) else { return false; }; @@ -667,6 +671,17 @@ fn item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec, name: Symbol) -> Vec { + tcx.crates(()) + .iter() + .copied() + .filter(move |&num| tcx.crate_name(num) == name) + .map(CrateNum::as_def_id) + .map(|id| Res::Def(tcx.def_kind(id), id)) + .collect() +} + /// Resolves a def path like `std::vec::Vec`. /// /// Can return multiple resolutions when there are multiple versions of the same crate, e.g. @@ -677,15 +692,7 @@ fn item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec, path: &[&str]) -> Vec { - fn find_crates(tcx: TyCtxt<'_>, name: Symbol) -> impl Iterator + '_ { - tcx.crates(()) - .iter() - .copied() - .filter(move |&num| tcx.crate_name(num) == name) - .map(CrateNum::as_def_id) - } - - let (base, mut path) = match *path { + let (base, path) = match *path { [primitive] => { return vec![PrimTy::from_name(Symbol::intern(primitive)).map_or(Res::Err, Res::PrimTy)]; }, @@ -701,18 +708,25 @@ pub fn def_path_res(tcx: TyCtxt<'_>, path: &[&str]) -> Vec { None }; - let starts = find_primitive_impls(tcx, base) - .chain(find_crates(tcx, base_sym)) + let crates = find_primitive_impls(tcx, base) .chain(local_crate) - .map(|id| Res::Def(tcx.def_kind(id), id)); + .map(|id| Res::Def(tcx.def_kind(id), id)) + .chain(find_crates(tcx, base_sym)) + .collect(); - let mut resolutions: Vec = starts.collect(); + def_path_res_with_base(tcx, crates, path) +} +/// Resolves a def path like `vec::Vec` with the base `std`. +/// +/// This is lighter than [`def_path_res`], and should be called with [`find_crates`] looking up +/// items from the same crate repeatedly, although should still be used sparingly. +pub fn def_path_res_with_base(tcx: TyCtxt<'_>, mut base: Vec, mut path: &[&str]) -> Vec { while let [segment, rest @ ..] = path { path = rest; let segment = Symbol::intern(segment); - resolutions = resolutions + base = base .into_iter() .filter_map(|res| res.opt_def_id()) .flat_map(|def_id| { @@ -731,7 +745,7 @@ pub fn def_path_res(tcx: TyCtxt<'_>, path: &[&str]) -> Vec { .collect(); } - resolutions + base } /// Resolves a def path like `std::vec::Vec` to its [`DefId`]s, see [`def_path_res`]. @@ -2944,13 +2958,14 @@ pub fn expr_use_ctxt<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> ExprU } /// Tokenizes the input while keeping the text associated with each token. -pub fn tokenize_with_text(s: &str) -> impl Iterator { +pub fn tokenize_with_text(s: &str) -> impl Iterator { let mut pos = 0; tokenize(s).map(move |t| { let end = pos + t.len; let range = pos as usize..end as usize; + let inner = InnerSpan::new(range.start, range.end); pos = end; - (t.kind, s.get(range).unwrap_or_default()) + (t.kind, s.get(range).unwrap_or_default(), inner) }) } @@ -2974,8 +2989,8 @@ pub fn span_contains_comment(sm: &SourceMap, span: Span) -> bool { pub fn span_extract_comment(sm: &SourceMap, span: Span) -> String { let snippet = sm.span_to_snippet(span).unwrap_or_default(); let res = tokenize_with_text(&snippet) - .filter(|(t, _)| matches!(t, TokenKind::BlockComment { .. } | TokenKind::LineComment { .. })) - .map(|(_, s)| s) + .filter(|(t, ..)| matches!(t, TokenKind::BlockComment { .. } | TokenKind::LineComment { .. })) + .map(|(_, s, _)| s) .join("\n"); res } @@ -2995,7 +3010,7 @@ pub fn span_find_starting_semi(sm: &SourceMap, span: Span) -> Span { /// pat: Some(a) /// else_body: return None /// ``` - +/// /// And for this example: /// ```ignore /// let Some(FooBar { a, b }) = ex else { return None }; @@ -3005,7 +3020,7 @@ pub fn span_find_starting_semi(sm: &SourceMap, span: Span) -> Span { /// pat: Some(FooBar { a, b }) /// else_body: return None /// ``` - +/// /// We output `Some(a)` in the first instance, and `Some(FooBar { a, b })` in the second, because /// the question mark operator is applicable here. Callers have to check whether we are in a /// constant or not. diff --git a/clippy_utils/src/macros.rs b/clippy_utils/src/macros.rs index 1d7479bff82d9..9c4d19ac1f1dd 100644 --- a/clippy_utils/src/macros.rs +++ b/clippy_utils/src/macros.rs @@ -1,6 +1,6 @@ #![allow(clippy::similar_names)] // `expr` and `expn` -use crate::visitors::{for_each_expr_without_closures, Descend}; +use crate::visitors::{Descend, for_each_expr_without_closures}; use arrayvec::ArrayVec; use rustc_ast::{FormatArgs, FormatArgument, FormatPlaceholder}; @@ -10,7 +10,7 @@ use rustc_hir::{self as hir, Expr, ExprKind, HirId, Node, QPath}; use rustc_lint::LateContext; use rustc_span::def_id::DefId; use rustc_span::hygiene::{self, MacroKind, SyntaxContext}; -use rustc_span::{sym, BytePos, ExpnData, ExpnId, ExpnKind, Span, SpanData, Symbol}; +use rustc_span::{BytePos, ExpnData, ExpnId, ExpnKind, Span, SpanData, Symbol, sym}; use std::ops::ControlFlow; const FORMAT_MACRO_DIAG_ITEMS: &[Symbol] = &[ diff --git a/clippy_utils/src/mir/mod.rs b/clippy_utils/src/mir/mod.rs index 654fb564848e2..59bb5e35cda66 100644 --- a/clippy_utils/src/mir/mod.rs +++ b/clippy_utils/src/mir/mod.rs @@ -2,7 +2,7 @@ use rustc_hir::{Expr, HirId}; use rustc_index::bit_set::BitSet; use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::{ - traversal, BasicBlock, Body, InlineAsmOperand, Local, Location, Place, StatementKind, TerminatorKind, START_BLOCK, + BasicBlock, Body, InlineAsmOperand, Local, Location, Place, START_BLOCK, StatementKind, TerminatorKind, traversal, }; use rustc_middle::ty::TyCtxt; @@ -112,14 +112,10 @@ pub fn block_in_cycle(body: &Body<'_>, block: BasicBlock) -> bool { /// Convenience wrapper around `visit_local_usage`. pub fn used_exactly_once(mir: &Body<'_>, local: Local) -> Option { - visit_local_usage( - &[local], - mir, - Location { - block: START_BLOCK, - statement_index: 0, - }, - ) + visit_local_usage(&[local], mir, Location { + block: START_BLOCK, + statement_index: 0, + }) .map(|mut vec| { let LocalUsage { local_use_locs, .. } = vec.remove(0); let mut locations = local_use_locs diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index a30edc48fff26..11a98b02f337a 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -29,7 +29,11 @@ pub const SYM_MODULE: [&str; 3] = ["rustc_span", "symbol", "sym"]; pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"]; // Paths in `core`/`alloc`/`std`. This should be avoided and cleaned up by adding diagnostic items. -// ... none currently! +pub const ABORT: [&str; 3] = ["std", "process", "abort"]; +pub const CHILD: [&str; 3] = ["std", "process", "Child"]; +pub const CHILD_ID: [&str; 4] = ["std", "process", "Child", "id"]; +pub const CHILD_KILL: [&str; 4] = ["std", "process", "Child", "kill"]; +pub const PANIC_ANY: [&str; 3] = ["std", "panic", "panic_any"]; // Paths in clippy itself pub const MSRV: [&str; 3] = ["clippy_config", "msrvs", "Msrv"]; diff --git a/clippy_utils/src/ptr.rs b/clippy_utils/src/ptr.rs index 991ea428dc33b..273c1b0defab0 100644 --- a/clippy_utils/src/ptr.rs +++ b/clippy_utils/src/ptr.rs @@ -1,5 +1,5 @@ use crate::source::snippet; -use crate::visitors::{for_each_expr_without_closures, Descend}; +use crate::visitors::{Descend, for_each_expr_without_closures}; use crate::{path_to_local_id, strip_pat_refs}; use core::ops::ControlFlow; use rustc_hir::{Body, BodyId, ExprKind, HirId, PatKind}; diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index d9befb3c157a3..2ed20b7202cc3 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -18,8 +18,8 @@ use rustc_middle::mir::{ use rustc_middle::traits::{BuiltinImplSource, ImplSource, ObligationCause}; use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::{self, GenericArgKind, TraitRef, Ty, TyCtxt}; -use rustc_span::symbol::sym; use rustc_span::Span; +use rustc_span::symbol::sym; use rustc_trait_selection::traits::{ObligationCtxt, SelectionContext}; use std::borrow::Cow; diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index 482e1e0147b18..4ad7575e7201f 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -9,10 +9,10 @@ use rustc_hir::{BlockCheckMode, Expr, ExprKind, UnsafeSource}; use rustc_lint::{EarlyContext, LateContext}; use rustc_middle::ty::TyCtxt; use rustc_session::Session; -use rustc_span::source_map::{original_sp, SourceMap}; +use rustc_span::source_map::{SourceMap, original_sp}; use rustc_span::{ - hygiene, BytePos, FileNameDisplayPreference, Pos, SourceFile, SourceFileAndLine, Span, SpanData, SyntaxContext, - DUMMY_SP, + BytePos, DUMMY_SP, FileNameDisplayPreference, Pos, SourceFile, SourceFileAndLine, Span, SpanData, SyntaxContext, + hygiene, }; use std::borrow::Cow; use std::fmt; @@ -666,39 +666,6 @@ pub fn walk_span_to_context(span: Span, outer: SyntaxContext) -> Option { (outer_span.ctxt() == outer).then_some(outer_span) } -/// Removes block comments from the given `Vec` of lines. -/// -/// # Examples -/// -/// ```rust,ignore -/// without_block_comments(vec!["/*", "foo", "*/"]); -/// // => vec![] -/// -/// without_block_comments(vec!["bar", "/*", "foo", "*/"]); -/// // => vec!["bar"] -/// ``` -pub fn without_block_comments(lines: Vec<&str>) -> Vec<&str> { - let mut without = vec![]; - - let mut nest_level = 0; - - for line in lines { - if line.contains("/*") { - nest_level += 1; - continue; - } else if line.contains("*/") { - nest_level -= 1; - continue; - } - - if nest_level == 0 { - without.push(line); - } - } - - without -} - /// Trims the whitespace from the start and the end of the span. pub fn trim_span(sm: &SourceMap, span: Span) -> Span { let data = span.data(); @@ -758,15 +725,12 @@ pub fn str_literal_to_char_literal( &snip[1..(snip.len() - 1)] }; - let hint = format!( - "'{}'", - match ch { - "'" => "\\'", - r"\" => "\\\\", - "\\\"" => "\"", // no need to escape `"` in `'"'` - _ => ch, - } - ); + let hint = format!("'{}'", match ch { + "'" => "\\'", + r"\" => "\\\\", + "\\\"" => "\"", // no need to escape `"` in `'"'` + _ => ch, + }); Some(hint) } else { @@ -776,7 +740,7 @@ pub fn str_literal_to_char_literal( #[cfg(test)] mod test { - use super::{reindent_multiline, without_block_comments}; + use super::reindent_multiline; #[test] fn test_reindent_multiline_single_line() { @@ -844,29 +808,4 @@ mod test { z }".into(), true, Some(8))); } - - #[test] - fn test_without_block_comments_lines_without_block_comments() { - let result = without_block_comments(vec!["/*", "", "*/"]); - println!("result: {result:?}"); - assert!(result.is_empty()); - - let result = without_block_comments(vec!["", "/*", "", "*/", "#[crate_type = \"lib\"]", "/*", "", "*/", ""]); - assert_eq!(result, vec!["", "#[crate_type = \"lib\"]", ""]); - - let result = without_block_comments(vec!["/* rust", "", "*/"]); - assert!(result.is_empty()); - - let result = without_block_comments(vec!["/* one-line comment */"]); - assert!(result.is_empty()); - - let result = without_block_comments(vec!["/* nested", "/* multi-line", "comment", "*/", "test", "*/"]); - assert!(result.is_empty()); - - let result = without_block_comments(vec!["/* nested /* inline /* comment */ test */ */"]); - assert!(result.is_empty()); - - let result = without_block_comments(vec!["foo", "bar", "baz"]); - assert_eq!(result, vec!["foo", "bar", "baz"]); - } } diff --git a/clippy_utils/src/str_utils.rs b/clippy_utils/src/str_utils.rs index 421b25a77fe8b..1588ee452daea 100644 --- a/clippy_utils/src/str_utils.rs +++ b/clippy_utils/src/str_utils.rs @@ -370,9 +370,11 @@ mod test { assert_eq!(camel_case_split("AbcDef"), vec!["Abc", "Def"]); assert_eq!(camel_case_split("Abc"), vec!["Abc"]); assert_eq!(camel_case_split("abcDef"), vec!["abc", "Def"]); - assert_eq!( - camel_case_split("\u{f6}\u{f6}AabABcd"), - vec!["\u{f6}\u{f6}", "Aab", "A", "Bcd"] - ); + assert_eq!(camel_case_split("\u{f6}\u{f6}AabABcd"), vec![ + "\u{f6}\u{f6}", + "Aab", + "A", + "Bcd" + ]); } } diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index 0f86d89c980d7..3255c51d009ca 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -180,8 +180,10 @@ impl<'a> Sugg<'a> { ) -> Self { use rustc_ast::ast::RangeLimits; + let mut snippet = |span: Span| snippet_with_context(cx, span, ctxt, default, app).0; + match expr.kind { - _ if expr.span.ctxt() != ctxt => Sugg::NonParen(snippet_with_context(cx, expr.span, ctxt, default, app).0), + _ if expr.span.ctxt() != ctxt => Sugg::NonParen(snippet(expr.span)), ast::ExprKind::AddrOf(..) | ast::ExprKind::Closure { .. } | ast::ExprKind::If(..) @@ -224,46 +226,38 @@ impl<'a> Sugg<'a> { | ast::ExprKind::While(..) | ast::ExprKind::Await(..) | ast::ExprKind::Err(_) - | ast::ExprKind::Dummy => Sugg::NonParen(snippet_with_context(cx, expr.span, ctxt, default, app).0), + | ast::ExprKind::Dummy => Sugg::NonParen(snippet(expr.span)), ast::ExprKind::Range(ref lhs, ref rhs, RangeLimits::HalfOpen) => Sugg::BinOp( AssocOp::DotDot, - lhs.as_ref().map_or("".into(), |lhs| { - snippet_with_context(cx, lhs.span, ctxt, default, app).0 - }), - rhs.as_ref().map_or("".into(), |rhs| { - snippet_with_context(cx, rhs.span, ctxt, default, app).0 - }), + lhs.as_ref().map_or("".into(), |lhs| snippet(lhs.span)), + rhs.as_ref().map_or("".into(), |rhs| snippet(rhs.span)), ), ast::ExprKind::Range(ref lhs, ref rhs, RangeLimits::Closed) => Sugg::BinOp( AssocOp::DotDotEq, - lhs.as_ref().map_or("".into(), |lhs| { - snippet_with_context(cx, lhs.span, ctxt, default, app).0 - }), - rhs.as_ref().map_or("".into(), |rhs| { - snippet_with_context(cx, rhs.span, ctxt, default, app).0 - }), + lhs.as_ref().map_or("".into(), |lhs| snippet(lhs.span)), + rhs.as_ref().map_or("".into(), |rhs| snippet(rhs.span)), ), ast::ExprKind::Assign(ref lhs, ref rhs, _) => Sugg::BinOp( AssocOp::Assign, - snippet_with_context(cx, lhs.span, ctxt, default, app).0, - snippet_with_context(cx, rhs.span, ctxt, default, app).0, + snippet(lhs.span), + snippet(rhs.span), ), ast::ExprKind::AssignOp(op, ref lhs, ref rhs) => Sugg::BinOp( astbinop2assignop(op), - snippet_with_context(cx, lhs.span, ctxt, default, app).0, - snippet_with_context(cx, rhs.span, ctxt, default, app).0, + snippet(lhs.span), + snippet(rhs.span), ), ast::ExprKind::Binary(op, ref lhs, ref rhs) => Sugg::BinOp( AssocOp::from_ast_binop(op.node), - snippet_with_context(cx, lhs.span, ctxt, default, app).0, - snippet_with_context(cx, rhs.span, ctxt, default, app).0, + snippet(lhs.span), + snippet(rhs.span), ), ast::ExprKind::Cast(ref lhs, ref ty) | //FIXME(chenyukang), remove this after type ascription is removed from AST ast::ExprKind::Type(ref lhs, ref ty) => Sugg::BinOp( AssocOp::As, - snippet_with_context(cx, lhs.span, ctxt, default, app).0, - snippet_with_context(cx, ty.span, ctxt, default, app).0, + snippet(lhs.span), + snippet(ty.span), ), } } diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 585134209ca3a..775a98fe361d8 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -12,8 +12,8 @@ use rustc_hir::def_id::DefId; use rustc_hir::{Expr, FnDecl, LangItem, Safety, TyKind}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; -use rustc_middle::mir::interpret::Scalar; use rustc_middle::mir::ConstValue; +use rustc_middle::mir::interpret::Scalar; use rustc_middle::traits::EvaluationResult; use rustc_middle::ty::layout::ValidityRequirement; use rustc_middle::ty::{ @@ -22,7 +22,7 @@ use rustc_middle::ty::{ TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr, }; use rustc_span::symbol::Ident; -use rustc_span::{sym, Span, Symbol, DUMMY_SP}; +use rustc_span::{DUMMY_SP, Span, Symbol, sym}; use rustc_target::abi::VariantIdx; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt; @@ -606,10 +606,13 @@ fn is_uninit_value_valid_for_ty_fallback<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'t ty::Tuple(types) => types.iter().all(|ty| is_uninit_value_valid_for_ty(cx, ty)), // Unions are always fine right now. // This includes MaybeUninit, the main way people use uninitialized memory. - // For ADTs, we could look at all fields just like for tuples, but that's potentially - // exponential, so let's avoid doing that for now. Code doing that is sketchy enough to - // just use an `#[allow()]`. - ty::Adt(adt, _) => adt.is_union(), + ty::Adt(adt, _) if adt.is_union() => true, + // Types (e.g. `UnsafeCell>`) that recursively contain only types that can be uninit + // can themselves be uninit too. + // This purposefully ignores enums as they may have a discriminant that can't be uninit. + ty::Adt(adt, args) if adt.is_struct() => adt + .all_fields() + .all(|field| is_uninit_value_valid_for_ty(cx, field.ty(cx.tcx, args))), // For the rest, conservatively assume that they cannot be uninit. _ => false, } diff --git a/clippy_utils/src/ty/type_certainty/mod.rs b/clippy_utils/src/ty/type_certainty/mod.rs index 875ddec259ee8..e612e9c6cb606 100644 --- a/clippy_utils/src/ty/type_certainty/mod.rs +++ b/clippy_utils/src/ty/type_certainty/mod.rs @@ -14,14 +14,14 @@ use crate::def_path_res; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::intravisit::{walk_qpath, walk_ty, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_qpath, walk_ty}; use rustc_hir::{self as hir, Expr, ExprKind, GenericArgs, HirId, Node, PathSegment, QPath, TyKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, AdtDef, GenericArgKind, Ty}; use rustc_span::{Span, Symbol}; mod certainty; -use certainty::{join, meet, Certainty, Meet}; +use certainty::{Certainty, Meet, join, meet}; pub fn expr_type_is_certain(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { expr_type_certainty(cx, expr).is_certain() diff --git a/clippy_utils/src/usage.rs b/clippy_utils/src/usage.rs index fbf3d95365acb..1230b60c3a6b7 100644 --- a/clippy_utils/src/usage.rs +++ b/clippy_utils/src/usage.rs @@ -1,4 +1,4 @@ -use crate::visitors::{for_each_expr, for_each_expr_without_closures, Descend, Visitable}; +use crate::visitors::{Descend, Visitable, for_each_expr, for_each_expr_without_closures}; use crate::{self as utils, get_enclosing_loop_or_multi_call_closure}; use core::ops::ControlFlow; use hir::def::Res; diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs index e5b6d3965e930..6d9a85a1181c4 100644 --- a/clippy_utils/src/visitors.rs +++ b/clippy_utils/src/visitors.rs @@ -1,10 +1,10 @@ use crate::ty::needs_ordered_drop; use crate::{get_enclosing_block, path_to_local_id}; use core::ops::ControlFlow; -use rustc_ast::visit::{try_visit, VisitorResult}; +use rustc_ast::visit::{VisitorResult, try_visit}; 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::intravisit::{self, Visitor, walk_block, walk_expr}; use rustc_hir::{ AnonConst, Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, LetExpr, Pat, QPath, Safety, Stmt, UnOp, UnsafeSource, diff --git a/declare_clippy_lint/Cargo.toml b/declare_clippy_lint/Cargo.toml index 31270241b0b2e..67a1f7cc72c54 100644 --- a/declare_clippy_lint/Cargo.toml +++ b/declare_clippy_lint/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "declare_clippy_lint" -version = "0.1.82" +version = "0.1.83" edition = "2021" publish = false diff --git a/declare_clippy_lint/src/lib.rs b/declare_clippy_lint/src/lib.rs index 6aa24329b0659..fefc1a0a6c402 100644 --- a/declare_clippy_lint/src/lib.rs +++ b/declare_clippy_lint/src/lib.rs @@ -5,7 +5,7 @@ use proc_macro::TokenStream; use quote::{format_ident, quote}; use syn::parse::{Parse, ParseStream}; -use syn::{parse_macro_input, Attribute, Error, Expr, ExprLit, Ident, Lit, LitStr, Meta, Result, Token}; +use syn::{Attribute, Error, Expr, ExprLit, Ident, Lit, LitStr, Meta, Result, Token, parse_macro_input}; fn parse_attr(path: [&'static str; LEN], attr: &Attribute) -> Option { if let Meta::NameValue(name_value) = &attr.meta { @@ -140,15 +140,12 @@ pub fn declare_clippy_lint(input: TokenStream) -> TokenStream { let mut category = category.to_string(); - let level = format_ident!( - "{}", - match category.as_str() { - "correctness" => "Deny", - "style" | "suspicious" | "complexity" | "perf" => "Warn", - "pedantic" | "restriction" | "cargo" | "nursery" | "internal" => "Allow", - _ => panic!("unknown category {category}"), - }, - ); + let level = format_ident!("{}", match category.as_str() { + "correctness" => "Deny", + "style" | "suspicious" | "complexity" | "perf" => "Warn", + "pedantic" | "restriction" | "cargo" | "nursery" | "internal" => "Allow", + _ => panic!("unknown category {category}"), + },); let info_name = format_ident!("{name}_INFO"); diff --git a/lintcheck/src/driver.rs b/lintcheck/src/driver.rs index 2fda2b00f873b..87ab14ba0cf83 100644 --- a/lintcheck/src/driver.rs +++ b/lintcheck/src/driver.rs @@ -1,4 +1,4 @@ -use crate::recursive::{deserialize_line, serialize_line, DriverInfo}; +use crate::recursive::{DriverInfo, deserialize_line, serialize_line}; use std::io::{self, BufReader, Write}; use std::net::TcpStream; diff --git a/lintcheck/src/recursive.rs b/lintcheck/src/recursive.rs index 6a662970ae84d..57073f523648e 100644 --- a/lintcheck/src/recursive.rs +++ b/lintcheck/src/recursive.rs @@ -3,8 +3,8 @@ //! [`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::input::RecursiveOptions; use crate::ClippyWarning; +use crate::input::RecursiveOptions; use std::collections::HashSet; use std::io::{BufRead, BufReader, Read, Write}; diff --git a/rust-toolchain b/rust-toolchain index 0be2e81810ebe..b431599c224ef 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,4 +1,4 @@ [toolchain] -channel = "nightly-2024-08-23" +channel = "nightly-2024-09-22" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" diff --git a/src/driver.rs b/src/driver.rs index 0ac3f35b4465a..324f754e61556 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -15,9 +15,9 @@ extern crate rustc_session; extern crate rustc_span; use rustc_interface::interface; +use rustc_session::EarlyDiagCtxt; use rustc_session::config::ErrorOutputType; use rustc_session::parse::ParseSess; -use rustc_session::EarlyDiagCtxt; use rustc_span::symbol::Symbol; use std::env; @@ -268,8 +268,6 @@ pub fn main() { }, _ => Some(s.to_string()), }) - // FIXME: remove this line in 1.79 to only keep `--cfg clippy`. - .chain(vec!["--cfg".into(), r#"feature="cargo-clippy""#.into()]) .chain(vec!["--cfg".into(), "clippy".into()]) .collect::>(); diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 9754254cdd0df..af2aa51925777 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -2,26 +2,25 @@ #![warn(rust_2018_idioms, unused_lifetimes)] #![allow(unused_extern_crates)] -use cargo_metadata::diagnostic::{Applicability, Diagnostic}; use cargo_metadata::Message; +use cargo_metadata::diagnostic::{Applicability, Diagnostic}; use clippy_config::ClippyConfiguration; +use clippy_lints::LintInfo; use clippy_lints::declared_lints::LINTS; use clippy_lints::deprecated_lints::{DEPRECATED, DEPRECATED_VERSION, RENAMED}; -use clippy_lints::LintInfo; use serde::{Deserialize, Serialize}; use test_utils::IS_RUSTC_TEST_SUITE; -use ui_test::custom_flags::rustfix::RustfixMode; use ui_test::custom_flags::Flag; +use ui_test::custom_flags::rustfix::RustfixMode; use ui_test::spanned::Spanned; -use ui_test::test_result::TestRun; -use ui_test::{status_emitter, Args, CommandBuilder, Config, Match, OutputConflictHandling}; +use ui_test::{Args, CommandBuilder, Config, Match, OutputConflictHandling, status_emitter}; use std::collections::{BTreeMap, HashMap}; use std::env::{self, set_var, var_os}; use std::ffi::{OsStr, OsString}; use std::fmt::Write; use std::path::{Path, PathBuf}; -use std::sync::mpsc::{channel, Sender}; +use std::sync::mpsc::{Sender, channel}; use std::{fs, iter, thread}; // Test dependencies may need an `extern crate` here to ensure that they show up @@ -469,15 +468,14 @@ fn applicability_ord(applicability: &Applicability) -> u8 { impl Flag for DiagnosticCollector { fn post_test_action( &self, - _config: &ui_test::per_test_config::TestConfig<'_>, - _cmd: &mut std::process::Command, + _config: &ui_test::per_test_config::TestConfig, output: &std::process::Output, - _build_manager: &ui_test::build_manager::BuildManager<'_>, - ) -> Result, ui_test::Errored> { + _build_manager: &ui_test::build_manager::BuildManager, + ) -> Result<(), ui_test::Errored> { if !output.stderr.is_empty() { self.sender.send(output.stderr.clone()).unwrap(); } - Ok(Vec::new()) + Ok(()) } fn clone_inner(&self) -> Box { diff --git a/tests/config-metadata.rs b/tests/config-metadata.rs index 3e3711873baa5..628dfc8f758ac 100644 --- a/tests/config-metadata.rs +++ b/tests/config-metadata.rs @@ -1,6 +1,6 @@ #![feature(rustc_private)] -use clippy_config::{get_configuration_metadata, ClippyConfiguration}; +use clippy_config::{ClippyConfiguration, get_configuration_metadata}; use itertools::Itertools; use regex::Regex; use std::borrow::Cow; diff --git a/tests/dogfood.rs b/tests/dogfood.rs index 8d10d5e7161a6..858be389a9e6e 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -6,11 +6,9 @@ #![warn(rust_2018_idioms, unused_lifetimes)] use itertools::Itertools; -use std::fs::File; use std::io::{self, IsTerminal}; use std::path::PathBuf; use std::process::Command; -use std::time::SystemTime; use test_utils::IS_RUSTC_TEST_SUITE; use ui_test::Args; @@ -28,11 +26,7 @@ fn main() { println!("dogfood: test"); } } else if !args.skip.iter().any(|arg| arg == "dogfood") { - if args.filters.iter().any(|arg| arg == "collect_metadata") { - collect_metadata(); - } else { - dogfood(); - } + dogfood(); } } @@ -61,47 +55,6 @@ fn dogfood() { ); } -fn collect_metadata() { - assert!(cfg!(feature = "internal")); - - // Setup for validation - let metadata_output_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("util/gh-pages/lints.json"); - let start_time = SystemTime::now(); - - // Run collection as is - std::env::set_var("ENABLE_METADATA_COLLECTION", "1"); - assert!(run_clippy_for_package( - "clippy_lints", - &["-A", "unfulfilled_lint_expectations"] - )); - - // Check if cargo caching got in the way - if let Ok(file) = File::open(metadata_output_path) { - if let Ok(metadata) = file.metadata() { - if let Ok(last_modification) = metadata.modified() { - if last_modification > start_time { - // The output file has been modified. Most likely by a hungry - // metadata collection monster. So We'll return. - return; - } - } - } - } - - // Force cargo to invalidate the caches - filetime::set_file_mtime( - PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("clippy_lints/src/lib.rs"), - filetime::FileTime::now(), - ) - .unwrap(); - - // Running the collection again - assert!(run_clippy_for_package( - "clippy_lints", - &["-A", "unfulfilled_lint_expectations"] - )); -} - #[must_use] fn run_clippy_for_package(project: &str, args: &[&str]) -> bool { let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); diff --git a/tests/ui-internal/unnecessary_def_path.fixed b/tests/ui-internal/unnecessary_def_path.fixed index 3908411da827a..0a9948428341d 100644 --- a/tests/ui-internal/unnecessary_def_path.fixed +++ b/tests/ui-internal/unnecessary_def_path.fixed @@ -13,7 +13,7 @@ extern crate rustc_span; 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, + is_enum_variant_ctor, is_expr_path_def_path, is_path_diagnostic_item, is_res_lang_ctor, is_trait_method, match_def_path, match_trait_method, path_res, }; @@ -22,8 +22,8 @@ use rustc_hir::LangItem; #[allow(unused)] use rustc_span::sym; -use rustc_hir::def_id::DefId; use rustc_hir::Expr; +use rustc_hir::def_id::DefId; use rustc_lint::LateContext; use rustc_middle::ty::Ty; diff --git a/tests/ui-internal/unnecessary_def_path.rs b/tests/ui-internal/unnecessary_def_path.rs index 632e26215a4db..ba68de6c6d016 100644 --- a/tests/ui-internal/unnecessary_def_path.rs +++ b/tests/ui-internal/unnecessary_def_path.rs @@ -13,7 +13,7 @@ extern crate rustc_span; 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, + is_enum_variant_ctor, is_expr_path_def_path, is_path_diagnostic_item, is_res_lang_ctor, is_trait_method, match_def_path, match_trait_method, path_res, }; @@ -22,8 +22,8 @@ use rustc_hir::LangItem; #[allow(unused)] use rustc_span::sym; -use rustc_hir::def_id::DefId; use rustc_hir::Expr; +use rustc_hir::def_id::DefId; use rustc_lint::LateContext; use rustc_middle::ty::Ty; diff --git a/tests/ui-toml/disallowed_names_append/disallowed_names.rs b/tests/ui-toml/disallowed_names_append/disallowed_names.rs index a2e2b46c42693..61ae8de8e3357 100644 --- a/tests/ui-toml/disallowed_names_append/disallowed_names.rs +++ b/tests/ui-toml/disallowed_names_append/disallowed_names.rs @@ -1,4 +1,4 @@ -#[warn(clippy::disallowed_names)] +#![warn(clippy::disallowed_names)] fn main() { // `foo` is part of the default configuration diff --git a/tests/ui-toml/disallowed_names_replace/disallowed_names.rs b/tests/ui-toml/disallowed_names_replace/disallowed_names.rs index a2e2b46c42693..61ae8de8e3357 100644 --- a/tests/ui-toml/disallowed_names_replace/disallowed_names.rs +++ b/tests/ui-toml/disallowed_names_replace/disallowed_names.rs @@ -1,4 +1,4 @@ -#[warn(clippy::disallowed_names)] +#![warn(clippy::disallowed_names)] fn main() { // `foo` is part of the default configuration diff --git a/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.fixed b/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.fixed index 5f4f007cf5c7f..a6072111dc061 100644 --- a/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.fixed +++ b/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.fixed @@ -2,7 +2,7 @@ use std::alloc as colla; use std::option::Option as Maybe; -use std::process::{exit as goodbye, Child as Kid}; +use std::process::{Child as Kid, exit as goodbye}; use std::thread::sleep as thread_sleep; #[rustfmt::skip] use std::{ diff --git a/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.rs b/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.rs index f60058c862888..c2b61aab5b3c4 100644 --- a/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.rs +++ b/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.rs @@ -2,7 +2,7 @@ use std::alloc as colla; use std::option::Option as Maybe; -use std::process::{exit as wrong_exit, Child as Kid}; +use std::process::{Child as Kid, exit as wrong_exit}; use std::thread::sleep; #[rustfmt::skip] use std::{ diff --git a/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.stderr b/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.stderr index f66938c83fbf8..d3bb07ec47fd3 100644 --- a/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.stderr +++ b/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.stderr @@ -1,8 +1,8 @@ error: this import should be renamed - --> tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.rs:5:20 + --> tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.rs:5:34 | -LL | use std::process::{exit as wrong_exit, Child as Kid}; - | ^^^^^^^^^^^^^^^^^^ help: try: `exit as goodbye` +LL | use std::process::{Child as Kid, exit as wrong_exit}; + | ^^^^^^^^^^^^^^^^^^ help: try: `exit as goodbye` | = note: `-D clippy::missing-enforced-import-renames` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::missing_enforced_import_renames)]` diff --git a/tests/ui-toml/panic/panic.rs b/tests/ui-toml/panic/panic.rs index 618a37ddfc555..b6264c950e450 100644 --- a/tests/ui-toml/panic/panic.rs +++ b/tests/ui-toml/panic/panic.rs @@ -1,5 +1,6 @@ //@compile-flags: --test #![warn(clippy::panic)] +use std::panic::panic_any; fn main() { enum Enam { @@ -12,6 +13,10 @@ fn main() { } } +fn issue_13292() { + panic_any("should lint") +} + #[test] fn lonely_test() { enum Enam { diff --git a/tests/ui-toml/panic/panic.stderr b/tests/ui-toml/panic/panic.stderr index bf7503e086c9d..a034207d919fe 100644 --- a/tests/ui-toml/panic/panic.stderr +++ b/tests/ui-toml/panic/panic.stderr @@ -1,5 +1,5 @@ error: `panic` should not be present in production code - --> tests/ui-toml/panic/panic.rs:11:14 + --> tests/ui-toml/panic/panic.rs:12:14 | LL | _ => panic!(""), | ^^^^^^^^^^ @@ -7,5 +7,11 @@ LL | _ => panic!(""), = note: `-D clippy::panic` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::panic)]` -error: aborting due to 1 previous error +error: `panic_any` should not be present in production code + --> tests/ui-toml/panic/panic.rs:17:5 + | +LL | panic_any("should lint") + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors diff --git a/tests/ui/allow_attributes.fixed b/tests/ui/allow_attributes.fixed index 49ee3ee17c70f..058dbb77a3205 100644 --- a/tests/ui/allow_attributes.fixed +++ b/tests/ui/allow_attributes.fixed @@ -58,3 +58,10 @@ fn msrv_1_80() { #[allow(unused)] let x = 1; } + +#[deny(clippy::allow_attributes)] +fn deny_allow_attributes() -> Option { + let allow = None; + allow?; + Some(42) +} diff --git a/tests/ui/allow_attributes.rs b/tests/ui/allow_attributes.rs index 854acf8348dcb..6d94ce50e4c37 100644 --- a/tests/ui/allow_attributes.rs +++ b/tests/ui/allow_attributes.rs @@ -58,3 +58,10 @@ fn msrv_1_80() { #[allow(unused)] let x = 1; } + +#[deny(clippy::allow_attributes)] +fn deny_allow_attributes() -> Option { + let allow = None; + allow?; + Some(42) +} diff --git a/tests/ui/allow_attributes_without_reason.rs b/tests/ui/allow_attributes_without_reason.rs index 86f6b2c5742a9..334e7ddd9d233 100644 --- a/tests/ui/allow_attributes_without_reason.rs +++ b/tests/ui/allow_attributes_without_reason.rs @@ -15,7 +15,6 @@ use proc_macros::{external, with_span}; #[warn(deref_nullptr)] #[deny(deref_nullptr)] #[forbid(deref_nullptr)] - fn main() { external! { #[allow(dead_code)] diff --git a/tests/ui/allow_attributes_without_reason.stderr b/tests/ui/allow_attributes_without_reason.stderr index 9bc3ca0f2afd3..86d7845df0416 100644 --- a/tests/ui/allow_attributes_without_reason.stderr +++ b/tests/ui/allow_attributes_without_reason.stderr @@ -36,7 +36,7 @@ LL | #[expect(dead_code)] = help: try adding a reason at the end with `, reason = ".."` error: `allow` attribute without specifying a reason - --> tests/ui/allow_attributes_without_reason.rs:47:5 + --> tests/ui/allow_attributes_without_reason.rs:46:5 | LL | #[allow(unused)] | ^^^^^^^^^^^^^^^^ @@ -44,7 +44,7 @@ LL | #[allow(unused)] = help: try adding a reason at the end with `, reason = ".."` error: `allow` attribute without specifying a reason - --> tests/ui/allow_attributes_without_reason.rs:47:5 + --> tests/ui/allow_attributes_without_reason.rs:46:5 | LL | #[allow(unused)] | ^^^^^^^^^^^^^^^^ diff --git a/tests/ui/arithmetic_side_effects.rs b/tests/ui/arithmetic_side_effects.rs index 0838d064a5fa1..3f20407308516 100644 --- a/tests/ui/arithmetic_side_effects.rs +++ b/tests/ui/arithmetic_side_effects.rs @@ -2,7 +2,6 @@ #![feature(f128)] #![feature(f16)] - #![allow( clippy::assign_op_pattern, clippy::erasing_op, diff --git a/tests/ui/arithmetic_side_effects.stderr b/tests/ui/arithmetic_side_effects.stderr index 78914667bf305..78b1aca4b8a47 100644 --- a/tests/ui/arithmetic_side_effects.stderr +++ b/tests/ui/arithmetic_side_effects.stderr @@ -1,5 +1,5 @@ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:167:13 + --> tests/ui/arithmetic_side_effects.rs:166:13 | LL | let _ = 1f16 + 1f16; | ^^^^^^^^^^^ @@ -8,733 +8,733 @@ LL | let _ = 1f16 + 1f16; = help: to override `-D warnings` add `#[allow(clippy::arithmetic_side_effects)]` error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:170:13 + --> tests/ui/arithmetic_side_effects.rs:169:13 | LL | let _ = 1f128 + 1f128; | ^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:308:5 + --> tests/ui/arithmetic_side_effects.rs:307:5 | LL | _n += 1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:309:5 + --> tests/ui/arithmetic_side_effects.rs:308:5 | LL | _n += &1; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:310:5 + --> tests/ui/arithmetic_side_effects.rs:309:5 | LL | _n -= 1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:311:5 + --> tests/ui/arithmetic_side_effects.rs:310:5 | LL | _n -= &1; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:312:5 + --> tests/ui/arithmetic_side_effects.rs:311:5 | LL | _n /= 0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:313:5 + --> tests/ui/arithmetic_side_effects.rs:312:5 | LL | _n /= &0; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:314:5 + --> tests/ui/arithmetic_side_effects.rs:313:5 | LL | _n %= 0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:315:5 + --> tests/ui/arithmetic_side_effects.rs:314:5 | LL | _n %= &0; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:316:5 + --> tests/ui/arithmetic_side_effects.rs:315:5 | LL | _n *= 2; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:317:5 + --> tests/ui/arithmetic_side_effects.rs:316:5 | LL | _n *= &2; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:318:5 + --> tests/ui/arithmetic_side_effects.rs:317:5 | LL | _n += -1; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:319:5 + --> tests/ui/arithmetic_side_effects.rs:318:5 | LL | _n += &-1; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:320:5 + --> tests/ui/arithmetic_side_effects.rs:319:5 | LL | _n -= -1; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:321:5 + --> tests/ui/arithmetic_side_effects.rs:320:5 | LL | _n -= &-1; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:322:5 + --> tests/ui/arithmetic_side_effects.rs:321:5 | LL | _n /= -0; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:323:5 + --> tests/ui/arithmetic_side_effects.rs:322:5 | LL | _n /= &-0; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:324:5 + --> tests/ui/arithmetic_side_effects.rs:323:5 | LL | _n %= -0; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:325:5 + --> tests/ui/arithmetic_side_effects.rs:324:5 | LL | _n %= &-0; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:326:5 + --> tests/ui/arithmetic_side_effects.rs:325:5 | LL | _n *= -2; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:327:5 + --> tests/ui/arithmetic_side_effects.rs:326:5 | LL | _n *= &-2; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:328:5 + --> tests/ui/arithmetic_side_effects.rs:327:5 | LL | _custom += Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:329:5 + --> tests/ui/arithmetic_side_effects.rs:328:5 | LL | _custom += &Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:330:5 + --> tests/ui/arithmetic_side_effects.rs:329:5 | LL | _custom -= Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:331:5 + --> tests/ui/arithmetic_side_effects.rs:330:5 | LL | _custom -= &Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:332:5 + --> tests/ui/arithmetic_side_effects.rs:331:5 | LL | _custom /= Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:333:5 + --> tests/ui/arithmetic_side_effects.rs:332:5 | LL | _custom /= &Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:334:5 + --> tests/ui/arithmetic_side_effects.rs:333:5 | LL | _custom %= Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:335:5 + --> tests/ui/arithmetic_side_effects.rs:334:5 | LL | _custom %= &Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:336:5 + --> tests/ui/arithmetic_side_effects.rs:335:5 | LL | _custom *= Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:337:5 + --> tests/ui/arithmetic_side_effects.rs:336:5 | LL | _custom *= &Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:338:5 + --> tests/ui/arithmetic_side_effects.rs:337:5 | LL | _custom >>= Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:339:5 + --> tests/ui/arithmetic_side_effects.rs:338:5 | LL | _custom >>= &Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:340:5 + --> tests/ui/arithmetic_side_effects.rs:339:5 | LL | _custom <<= Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:341:5 + --> tests/ui/arithmetic_side_effects.rs:340:5 | LL | _custom <<= &Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:342:5 + --> tests/ui/arithmetic_side_effects.rs:341:5 | LL | _custom += -Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:343:5 + --> tests/ui/arithmetic_side_effects.rs:342:5 | LL | _custom += &-Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:344:5 + --> tests/ui/arithmetic_side_effects.rs:343:5 | LL | _custom -= -Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:345:5 + --> tests/ui/arithmetic_side_effects.rs:344:5 | LL | _custom -= &-Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:346:5 + --> tests/ui/arithmetic_side_effects.rs:345:5 | LL | _custom /= -Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:347:5 + --> tests/ui/arithmetic_side_effects.rs:346:5 | LL | _custom /= &-Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:348:5 + --> tests/ui/arithmetic_side_effects.rs:347:5 | LL | _custom %= -Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:349:5 + --> tests/ui/arithmetic_side_effects.rs:348:5 | LL | _custom %= &-Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:350:5 + --> tests/ui/arithmetic_side_effects.rs:349:5 | LL | _custom *= -Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:351:5 + --> tests/ui/arithmetic_side_effects.rs:350:5 | LL | _custom *= &-Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:352:5 + --> tests/ui/arithmetic_side_effects.rs:351:5 | LL | _custom >>= -Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:353:5 + --> tests/ui/arithmetic_side_effects.rs:352:5 | LL | _custom >>= &-Custom; | ^^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:354:5 + --> tests/ui/arithmetic_side_effects.rs:353:5 | LL | _custom <<= -Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:355:5 + --> tests/ui/arithmetic_side_effects.rs:354:5 | LL | _custom <<= &-Custom; | ^^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:358:10 + --> tests/ui/arithmetic_side_effects.rs:357:10 | LL | _n = _n + 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:359:10 + --> tests/ui/arithmetic_side_effects.rs:358:10 | LL | _n = _n + &1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:360:10 + --> tests/ui/arithmetic_side_effects.rs:359:10 | LL | _n = 1 + _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:361:10 + --> tests/ui/arithmetic_side_effects.rs:360:10 | LL | _n = &1 + _n; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:362:10 + --> tests/ui/arithmetic_side_effects.rs:361:10 | LL | _n = _n - 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:363:10 + --> tests/ui/arithmetic_side_effects.rs:362:10 | LL | _n = _n - &1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:364:10 + --> tests/ui/arithmetic_side_effects.rs:363:10 | LL | _n = 1 - _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:365:10 + --> tests/ui/arithmetic_side_effects.rs:364:10 | LL | _n = &1 - _n; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:366:10 + --> tests/ui/arithmetic_side_effects.rs:365:10 | LL | _n = _n / 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:367:10 + --> tests/ui/arithmetic_side_effects.rs:366:10 | LL | _n = _n / &0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:368:10 + --> tests/ui/arithmetic_side_effects.rs:367:10 | LL | _n = _n % 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:369:10 + --> tests/ui/arithmetic_side_effects.rs:368:10 | LL | _n = _n % &0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:370:10 + --> tests/ui/arithmetic_side_effects.rs:369:10 | LL | _n = _n * 2; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:371:10 + --> tests/ui/arithmetic_side_effects.rs:370:10 | LL | _n = _n * &2; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:372:10 + --> tests/ui/arithmetic_side_effects.rs:371:10 | LL | _n = 2 * _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:373:10 + --> tests/ui/arithmetic_side_effects.rs:372:10 | LL | _n = &2 * _n; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:374:10 + --> tests/ui/arithmetic_side_effects.rs:373:10 | LL | _n = 23 + &85; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:375:10 + --> tests/ui/arithmetic_side_effects.rs:374:10 | LL | _n = &23 + 85; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:376:10 + --> tests/ui/arithmetic_side_effects.rs:375:10 | LL | _n = &23 + &85; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:377:15 + --> tests/ui/arithmetic_side_effects.rs:376:15 | LL | _custom = _custom + _custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:378:15 + --> tests/ui/arithmetic_side_effects.rs:377:15 | LL | _custom = _custom + &_custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:379:15 + --> tests/ui/arithmetic_side_effects.rs:378:15 | LL | _custom = Custom + _custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:380:15 + --> tests/ui/arithmetic_side_effects.rs:379:15 | LL | _custom = &Custom + _custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:381:15 + --> tests/ui/arithmetic_side_effects.rs:380:15 | LL | _custom = _custom - Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:382:15 + --> tests/ui/arithmetic_side_effects.rs:381:15 | LL | _custom = _custom - &Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:383:15 + --> tests/ui/arithmetic_side_effects.rs:382:15 | LL | _custom = Custom - _custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:384:15 + --> tests/ui/arithmetic_side_effects.rs:383:15 | LL | _custom = &Custom - _custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:385:15 + --> tests/ui/arithmetic_side_effects.rs:384:15 | LL | _custom = _custom / Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:386:15 + --> tests/ui/arithmetic_side_effects.rs:385:15 | LL | _custom = _custom / &Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:387:15 + --> tests/ui/arithmetic_side_effects.rs:386:15 | LL | _custom = _custom % Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:388:15 + --> tests/ui/arithmetic_side_effects.rs:387:15 | LL | _custom = _custom % &Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:389:15 + --> tests/ui/arithmetic_side_effects.rs:388:15 | LL | _custom = _custom * Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:390:15 + --> tests/ui/arithmetic_side_effects.rs:389:15 | LL | _custom = _custom * &Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:391:15 + --> tests/ui/arithmetic_side_effects.rs:390:15 | LL | _custom = Custom * _custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:392:15 + --> tests/ui/arithmetic_side_effects.rs:391:15 | LL | _custom = &Custom * _custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:393:15 + --> tests/ui/arithmetic_side_effects.rs:392:15 | LL | _custom = Custom + &Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:394:15 + --> tests/ui/arithmetic_side_effects.rs:393:15 | LL | _custom = &Custom + Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:395:15 + --> tests/ui/arithmetic_side_effects.rs:394:15 | LL | _custom = &Custom + &Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:396:15 + --> tests/ui/arithmetic_side_effects.rs:395:15 | LL | _custom = _custom >> _custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:397:15 + --> tests/ui/arithmetic_side_effects.rs:396:15 | LL | _custom = _custom >> &_custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:398:15 + --> tests/ui/arithmetic_side_effects.rs:397:15 | LL | _custom = Custom << _custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:399:15 + --> tests/ui/arithmetic_side_effects.rs:398:15 | LL | _custom = &Custom << _custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:402:23 + --> tests/ui/arithmetic_side_effects.rs:401:23 | LL | _n.saturating_div(0); | ^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:403:21 + --> tests/ui/arithmetic_side_effects.rs:402:21 | LL | _n.wrapping_div(0); | ^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:404:21 + --> tests/ui/arithmetic_side_effects.rs:403:21 | LL | _n.wrapping_rem(0); | ^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:405:28 + --> tests/ui/arithmetic_side_effects.rs:404:28 | LL | _n.wrapping_rem_euclid(0); | ^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:407:23 + --> tests/ui/arithmetic_side_effects.rs:406:23 | LL | _n.saturating_div(_n); | ^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:408:21 + --> tests/ui/arithmetic_side_effects.rs:407:21 | LL | _n.wrapping_div(_n); | ^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:409:21 + --> tests/ui/arithmetic_side_effects.rs:408:21 | LL | _n.wrapping_rem(_n); | ^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:410:28 + --> tests/ui/arithmetic_side_effects.rs:409:28 | LL | _n.wrapping_rem_euclid(_n); | ^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:413:10 + --> tests/ui/arithmetic_side_effects.rs:412:10 | LL | _n = -_n; | ^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:414:10 + --> tests/ui/arithmetic_side_effects.rs:413:10 | LL | _n = -&_n; | ^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:415:15 + --> tests/ui/arithmetic_side_effects.rs:414:15 | LL | _custom = -_custom; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:416:15 + --> tests/ui/arithmetic_side_effects.rs:415:15 | LL | _custom = -&_custom; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:425:5 + --> tests/ui/arithmetic_side_effects.rs:424:5 | LL | 1 + i; | ^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:426:5 + --> tests/ui/arithmetic_side_effects.rs:425:5 | LL | i * 2; | ^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:427:5 + --> tests/ui/arithmetic_side_effects.rs:426:5 | LL | 1 % i / 2; | ^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:428:5 + --> tests/ui/arithmetic_side_effects.rs:427:5 | LL | i - 2 + 2 - i; | ^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:429:5 + --> tests/ui/arithmetic_side_effects.rs:428:5 | LL | -i; | ^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:440:5 + --> tests/ui/arithmetic_side_effects.rs:439:5 | LL | i += 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:441:5 + --> tests/ui/arithmetic_side_effects.rs:440:5 | LL | i -= 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:442:5 + --> tests/ui/arithmetic_side_effects.rs:441:5 | LL | i *= 2; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:444:5 + --> tests/ui/arithmetic_side_effects.rs:443:5 | LL | i /= 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:446:5 + --> tests/ui/arithmetic_side_effects.rs:445:5 | LL | i /= var1; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:447:5 + --> tests/ui/arithmetic_side_effects.rs:446:5 | LL | i /= var2; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:449:5 + --> tests/ui/arithmetic_side_effects.rs:448:5 | LL | i %= 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:451:5 + --> tests/ui/arithmetic_side_effects.rs:450:5 | LL | i %= var1; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:452:5 + --> tests/ui/arithmetic_side_effects.rs:451:5 | LL | i %= var2; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:462:5 + --> tests/ui/arithmetic_side_effects.rs:461:5 | LL | 10 / a | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:516:9 + --> tests/ui/arithmetic_side_effects.rs:515:9 | LL | x / maybe_zero | ^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:520:9 + --> tests/ui/arithmetic_side_effects.rs:519:9 | LL | x % maybe_zero | ^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:531:5 + --> tests/ui/arithmetic_side_effects.rs:530:5 | LL | one.add_assign(1); | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:535:5 + --> tests/ui/arithmetic_side_effects.rs:534:5 | LL | one.sub_assign(1); | ^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/asm_syntax_not_x86.rs b/tests/ui/asm_syntax_not_x86.rs index 33cea4806814b..a7d29cc239e53 100644 --- a/tests/ui/asm_syntax_not_x86.rs +++ b/tests/ui/asm_syntax_not_x86.rs @@ -1,5 +1,4 @@ -//@ignore-target-i686 -//@ignore-target-x86 +//@ignore-target: i686 x86 //@needs-asm-support #[warn(clippy::inline_asm_x86_intel_syntax)] diff --git a/tests/ui/asm_syntax_x86.rs b/tests/ui/asm_syntax_x86.rs index 835943b43894c..5ceaceb7527b3 100644 --- a/tests/ui/asm_syntax_x86.rs +++ b/tests/ui/asm_syntax_x86.rs @@ -1,6 +1,4 @@ -//@revisions: i686 x86_64 -//@[i686] only-target-i686 -//@[x86_64] only-target-x86_64 +//@only-target: i686 x86_64 #[warn(clippy::inline_asm_x86_intel_syntax)] mod warn_intel { diff --git a/tests/ui/asm_syntax_x86.stderr b/tests/ui/asm_syntax_x86.stderr new file mode 100644 index 0000000000000..1911ef66e2391 --- /dev/null +++ b/tests/ui/asm_syntax_x86.stderr @@ -0,0 +1,70 @@ +error: Intel x86 assembly syntax used + --> tests/ui/asm_syntax_x86.rs:8:9 + | +LL | asm!(""); + | ^^^^^^^^ + | + = help: use AT&T x86 assembly syntax + = note: `-D clippy::inline-asm-x86-intel-syntax` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::inline_asm_x86_intel_syntax)]` + +error: Intel x86 assembly syntax used + --> tests/ui/asm_syntax_x86.rs:10:9 + | +LL | asm!("", options()); + | ^^^^^^^^^^^^^^^^^^^ + | + = help: use AT&T x86 assembly syntax + +error: Intel x86 assembly syntax used + --> tests/ui/asm_syntax_x86.rs:12:9 + | +LL | asm!("", options(nostack)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use AT&T x86 assembly syntax + +error: Intel x86 assembly syntax used + --> tests/ui/asm_syntax_x86.rs:18:5 + | +LL | global_asm!(""); + | ^^^^^^^^^^^^^^^ + | + = help: use AT&T x86 assembly syntax + +error: Intel x86 assembly syntax used + --> tests/ui/asm_syntax_x86.rs:20:5 + | +LL | global_asm!("", options()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use AT&T x86 assembly syntax + +error: AT&T x86 assembly syntax used + --> tests/ui/asm_syntax_x86.rs:33:9 + | +LL | asm!("", options(att_syntax)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use Intel x86 assembly syntax + = note: `-D clippy::inline-asm-x86-att-syntax` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::inline_asm_x86_att_syntax)]` + +error: AT&T x86 assembly syntax used + --> tests/ui/asm_syntax_x86.rs:35:9 + | +LL | asm!("", options(nostack, att_syntax)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use Intel x86 assembly syntax + +error: AT&T x86 assembly syntax used + --> tests/ui/asm_syntax_x86.rs:41:5 + | +LL | global_asm!("", options(att_syntax)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use Intel x86 assembly syntax + +error: aborting due to 8 previous errors + diff --git a/tests/ui/auxiliary/external_item.rs b/tests/ui/auxiliary/external_item.rs new file mode 100644 index 0000000000000..ca4bc369e4494 --- /dev/null +++ b/tests/ui/auxiliary/external_item.rs @@ -0,0 +1,7 @@ +pub struct _ExternalStruct {} + +impl _ExternalStruct { + pub fn _foo(self) {} +} + +pub fn _exernal_foo() {} diff --git a/tests/ui/auxiliary/proc_macro_attr.rs b/tests/ui/auxiliary/proc_macro_attr.rs index f6fdebaf25274..e72d6b6ceadfd 100644 --- a/tests/ui/auxiliary/proc_macro_attr.rs +++ b/tests/ui/auxiliary/proc_macro_attr.rs @@ -11,8 +11,8 @@ use quote::{quote, quote_spanned}; use syn::spanned::Spanned; use syn::token::Star; use syn::{ - parse_macro_input, parse_quote, FnArg, ImplItem, ItemFn, ItemImpl, ItemStruct, ItemTrait, Lifetime, Pat, PatIdent, - PatType, Signature, TraitItem, Type, Visibility, + FnArg, ImplItem, ItemFn, ItemImpl, ItemStruct, ItemTrait, Lifetime, Pat, PatIdent, PatType, Signature, TraitItem, + Type, Visibility, parse_macro_input, parse_quote, }; #[proc_macro_attribute] diff --git a/tests/ui/auxiliary/proc_macro_derive.rs b/tests/ui/auxiliary/proc_macro_derive.rs index 4c3df47226909..bd90042c1da82 100644 --- a/tests/ui/auxiliary/proc_macro_derive.rs +++ b/tests/ui/auxiliary/proc_macro_derive.rs @@ -5,7 +5,7 @@ extern crate proc_macro; -use proc_macro::{quote, Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree}; +use proc_macro::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree, quote}; #[proc_macro_derive(DeriveSomething)] pub fn derive(_: TokenStream) -> TokenStream { diff --git a/tests/ui/auxiliary/proc_macro_suspicious_else_formatting.rs b/tests/ui/auxiliary/proc_macro_suspicious_else_formatting.rs index 79e8eff3aa101..3d6f164d35833 100644 --- a/tests/ui/auxiliary/proc_macro_suspicious_else_formatting.rs +++ b/tests/ui/auxiliary/proc_macro_suspicious_else_formatting.rs @@ -1,5 +1,5 @@ extern crate proc_macro; -use proc_macro::{token_stream, Delimiter, Group, Ident, Span, TokenStream, TokenTree}; +use proc_macro::{Delimiter, Group, Ident, Span, TokenStream, TokenTree, token_stream}; fn read_ident(iter: &mut token_stream::IntoIter) -> Ident { match iter.next() { diff --git a/tests/ui/auxiliary/proc_macros.rs b/tests/ui/auxiliary/proc_macros.rs index ed7412f7c4076..1a2a4ec231143 100644 --- a/tests/ui/auxiliary/proc_macros.rs +++ b/tests/ui/auxiliary/proc_macros.rs @@ -5,9 +5,9 @@ extern crate proc_macro; use core::mem; -use proc_macro::token_stream::IntoIter; use proc_macro::Delimiter::{self, Brace, Parenthesis}; use proc_macro::Spacing::{self, Alone, Joint}; +use proc_macro::token_stream::IntoIter; use proc_macro::{Group, Ident, Literal, Punct, Span, TokenStream, TokenTree as TT}; use syn::spanned::Spanned; diff --git a/tests/ui/borrow_interior_mutable_const/others.rs b/tests/ui/borrow_interior_mutable_const/others.rs index 452d1b198133e..de220505c3e08 100644 --- a/tests/ui/borrow_interior_mutable_const/others.rs +++ b/tests/ui/borrow_interior_mutable_const/others.rs @@ -5,8 +5,8 @@ use std::borrow::Cow; use std::cell::{Cell, UnsafeCell}; use std::fmt::Display; -use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Once; +use std::sync::atomic::{AtomicUsize, Ordering}; const ATOMIC: AtomicUsize = AtomicUsize::new(5); const CELL: Cell = Cell::new(6); diff --git a/tests/ui/cast_alignment.rs b/tests/ui/cast_alignment.rs index 98ef5e36f9488..72f5d4268cc1b 100644 --- a/tests/ui/cast_alignment.rs +++ b/tests/ui/cast_alignment.rs @@ -2,16 +2,16 @@ #![feature(rustc_private)] #![feature(core_intrinsics)] -extern crate libc; - -#[warn(clippy::cast_ptr_alignment)] -#[allow( +#![warn(clippy::cast_ptr_alignment)] +#![allow( clippy::no_effect, clippy::unnecessary_operation, clippy::cast_lossless, clippy::borrow_as_ptr )] +extern crate libc; + fn main() { /* These should be warned against */ diff --git a/tests/ui/cmp_owned/without_suggestion.rs b/tests/ui/cmp_owned/without_suggestion.rs index ec45d635c1727..913aab7274716 100644 --- a/tests/ui/cmp_owned/without_suggestion.rs +++ b/tests/ui/cmp_owned/without_suggestion.rs @@ -1,5 +1,5 @@ -#[allow(clippy::unnecessary_operation)] -#[allow(clippy::implicit_clone)] +#![allow(clippy::unnecessary_operation)] +#![allow(clippy::implicit_clone)] fn main() { let x = &Baz; diff --git a/tests/ui/collapsible_else_if.fixed b/tests/ui/collapsible_else_if.fixed index 3b410b2f17b80..c2d76146c641d 100644 --- a/tests/ui/collapsible_else_if.fixed +++ b/tests/ui/collapsible_else_if.fixed @@ -1,9 +1,7 @@ #![allow(clippy::assertions_on_constants, clippy::equatable_if_let, clippy::needless_if)] +#![warn(clippy::collapsible_if, clippy::collapsible_else_if)] #[rustfmt::skip] -#[warn(clippy::collapsible_if)] -#[warn(clippy::collapsible_else_if)] - fn main() { let x = "hello"; let y = "world"; @@ -76,7 +74,6 @@ fn main() { } #[rustfmt::skip] -#[allow(dead_code)] fn issue_7318() { if true { println!("I've been resolved!") }else if false {} diff --git a/tests/ui/collapsible_else_if.rs b/tests/ui/collapsible_else_if.rs index 772ef6f9fc606..3579e46cd4479 100644 --- a/tests/ui/collapsible_else_if.rs +++ b/tests/ui/collapsible_else_if.rs @@ -1,9 +1,7 @@ #![allow(clippy::assertions_on_constants, clippy::equatable_if_let, clippy::needless_if)] +#![warn(clippy::collapsible_if, clippy::collapsible_else_if)] #[rustfmt::skip] -#[warn(clippy::collapsible_if)] -#[warn(clippy::collapsible_else_if)] - fn main() { let x = "hello"; let y = "world"; @@ -90,7 +88,6 @@ fn main() { } #[rustfmt::skip] -#[allow(dead_code)] fn issue_7318() { if true { println!("I've been resolved!") }else{ diff --git a/tests/ui/collapsible_else_if.stderr b/tests/ui/collapsible_else_if.stderr index dc19d90b4d132..395c2dcf68dc0 100644 --- a/tests/ui/collapsible_else_if.stderr +++ b/tests/ui/collapsible_else_if.stderr @@ -1,5 +1,5 @@ error: this `else { if .. }` block can be collapsed - --> tests/ui/collapsible_else_if.rs:13:12 + --> tests/ui/collapsible_else_if.rs:11:12 | LL | } else { | ____________^ @@ -19,7 +19,7 @@ LL + } | error: this `else { if .. }` block can be collapsed - --> tests/ui/collapsible_else_if.rs:21:12 + --> tests/ui/collapsible_else_if.rs:19:12 | LL | } else { | ____________^ @@ -37,7 +37,7 @@ LL + } | error: this `else { if .. }` block can be collapsed - --> tests/ui/collapsible_else_if.rs:29:12 + --> tests/ui/collapsible_else_if.rs:27:12 | LL | } else { | ____________^ @@ -60,7 +60,7 @@ LL + } | error: this `else { if .. }` block can be collapsed - --> tests/ui/collapsible_else_if.rs:40:12 + --> tests/ui/collapsible_else_if.rs:38:12 | LL | } else { | ____________^ @@ -83,7 +83,7 @@ LL + } | error: this `else { if .. }` block can be collapsed - --> tests/ui/collapsible_else_if.rs:51:12 + --> tests/ui/collapsible_else_if.rs:49:12 | LL | } else { | ____________^ @@ -106,7 +106,7 @@ LL + } | error: this `else { if .. }` block can be collapsed - --> tests/ui/collapsible_else_if.rs:62:12 + --> tests/ui/collapsible_else_if.rs:60:12 | LL | } else { | ____________^ @@ -129,7 +129,7 @@ LL + } | error: this `else { if .. }` block can be collapsed - --> tests/ui/collapsible_else_if.rs:73:12 + --> tests/ui/collapsible_else_if.rs:71:12 | LL | } else { | ____________^ @@ -152,7 +152,7 @@ LL + } | error: this `else { if .. }` block can be collapsed - --> tests/ui/collapsible_else_if.rs:96:10 + --> tests/ui/collapsible_else_if.rs:93:10 | LL | }else{ | __________^ diff --git a/tests/ui/comparison_to_empty.fixed b/tests/ui/comparison_to_empty.fixed index e102b13a76185..a2a3dd9086d4c 100644 --- a/tests/ui/comparison_to_empty.fixed +++ b/tests/ui/comparison_to_empty.fixed @@ -33,4 +33,12 @@ fn main() { if let [0] = &*s && s == [0] {} + + // Also lint the `PartialEq` methods + let s = String::new(); + let _ = s.is_empty(); + let _ = !s.is_empty(); + let v = vec![0]; + let _ = v.is_empty(); + let _ = !v.is_empty(); } diff --git a/tests/ui/comparison_to_empty.rs b/tests/ui/comparison_to_empty.rs index 69a6c967d3821..7c5689a4bbec9 100644 --- a/tests/ui/comparison_to_empty.rs +++ b/tests/ui/comparison_to_empty.rs @@ -33,4 +33,12 @@ fn main() { if let [0] = &*s && s == [0] {} + + // Also lint the `PartialEq` methods + let s = String::new(); + let _ = s.eq(""); + let _ = s.ne(""); + let v = vec![0]; + let _ = v.eq(&[]); + let _ = v.ne(&[]); } diff --git a/tests/ui/comparison_to_empty.stderr b/tests/ui/comparison_to_empty.stderr index 6b027459ed347..2ee0efc7dbb19 100644 --- a/tests/ui/comparison_to_empty.stderr +++ b/tests/ui/comparison_to_empty.stderr @@ -55,5 +55,29 @@ error: comparison to empty slice LL | && s == [] | ^^^^^^^ help: using `is_empty` is clearer and more explicit: `s.is_empty()` -error: aborting due to 9 previous errors +error: comparison to empty slice + --> tests/ui/comparison_to_empty.rs:39:13 + | +LL | let _ = s.eq(""); + | ^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s.is_empty()` + +error: comparison to empty slice + --> tests/ui/comparison_to_empty.rs:40:13 + | +LL | let _ = s.ne(""); + | ^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!s.is_empty()` + +error: comparison to empty slice + --> tests/ui/comparison_to_empty.rs:42:13 + | +LL | let _ = v.eq(&[]); + | ^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `v.is_empty()` + +error: comparison to empty slice + --> tests/ui/comparison_to_empty.rs:43:13 + | +LL | let _ = v.ne(&[]); + | ^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!v.is_empty()` + +error: aborting due to 13 previous errors diff --git a/tests/ui/crashes/associated-constant-ice.rs b/tests/ui/crashes/associated-constant-ice.rs index 948deba3ea6e3..fec16671eeb32 100644 --- a/tests/ui/crashes/associated-constant-ice.rs +++ b/tests/ui/crashes/associated-constant-ice.rs @@ -1,4 +1,4 @@ -/// Test for https://github.com/rust-lang/rust-clippy/issues/1698 +// Test for https://github.com/rust-lang/rust-clippy/issues/1698 pub trait Trait { const CONSTANT: u8; diff --git a/tests/ui/crashes/cc_seme.rs b/tests/ui/crashes/cc_seme.rs index 98588be9cf829..98897d6d7aaf9 100644 --- a/tests/ui/crashes/cc_seme.rs +++ b/tests/ui/crashes/cc_seme.rs @@ -1,6 +1,4 @@ -#[allow(dead_code)] - -/// Test for https://github.com/rust-lang/rust-clippy/issues/478 +// Test for https://github.com/rust-lang/rust-clippy/issues/478 enum Baz { One, diff --git a/tests/ui/crashes/ice-11230.rs b/tests/ui/crashes/ice-11230.rs index 5761882273e00..94044e9435ed4 100644 --- a/tests/ui/crashes/ice-11230.rs +++ b/tests/ui/crashes/ice-11230.rs @@ -1,4 +1,4 @@ -/// Test for https://github.com/rust-lang/rust-clippy/issues/11230 +// Test for https://github.com/rust-lang/rust-clippy/issues/11230 fn main() { const A: &[for<'a> fn(&'a ())] = &[]; diff --git a/tests/ui/crashes/ice-1588.rs b/tests/ui/crashes/ice-1588.rs index b0a3d11bce463..9ec093721c17e 100644 --- a/tests/ui/crashes/ice-1588.rs +++ b/tests/ui/crashes/ice-1588.rs @@ -1,6 +1,6 @@ #![allow(clippy::all)] -/// Test for https://github.com/rust-lang/rust-clippy/issues/1588 +// Test for https://github.com/rust-lang/rust-clippy/issues/1588 fn main() { match 1 { diff --git a/tests/ui/crashes/ice-1969.rs b/tests/ui/crashes/ice-1969.rs index 96a8fe6c24d56..eb901c7672948 100644 --- a/tests/ui/crashes/ice-1969.rs +++ b/tests/ui/crashes/ice-1969.rs @@ -1,6 +1,6 @@ #![allow(clippy::all)] -/// Test for https://github.com/rust-lang/rust-clippy/issues/1969 +// Test for https://github.com/rust-lang/rust-clippy/issues/1969 fn main() {} diff --git a/tests/ui/crashes/ice-2499.rs b/tests/ui/crashes/ice-2499.rs index 45b3b1869dde6..732f331ad145e 100644 --- a/tests/ui/crashes/ice-2499.rs +++ b/tests/ui/crashes/ice-2499.rs @@ -1,8 +1,8 @@ #![allow(dead_code, clippy::char_lit_as_u8, clippy::needless_bool)] -/// Should not trigger an ICE in `SpanlessHash` / `consts::constant` -/// -/// Issue: https://github.com/rust-lang/rust-clippy/issues/2499 +// Should not trigger an ICE in `SpanlessHash` / `consts::constant` +// +// Issue: https://github.com/rust-lang/rust-clippy/issues/2499 fn f(s: &[u8]) -> bool { let t = s[0] as char; diff --git a/tests/ui/crashes/ice-2594.rs b/tests/ui/crashes/ice-2594.rs index 3f3986b6fc692..dddf860bd1785 100644 --- a/tests/ui/crashes/ice-2594.rs +++ b/tests/ui/crashes/ice-2594.rs @@ -3,7 +3,6 @@ /// Should not trigger an ICE in `SpanlessHash` / `consts::constant` /// /// Issue: https://github.com/rust-lang/rust-clippy/issues/2594 - fn spanless_hash_ice() { let txt = "something"; let empty_header: [u8; 1] = [1; 1]; diff --git a/tests/ui/crashes/ice-2727.rs b/tests/ui/crashes/ice-2727.rs index 56024abc8f58d..59fb9b04b86d0 100644 --- a/tests/ui/crashes/ice-2727.rs +++ b/tests/ui/crashes/ice-2727.rs @@ -1,4 +1,4 @@ -/// Test for https://github.com/rust-lang/rust-clippy/issues/2727 +// Test for https://github.com/rust-lang/rust-clippy/issues/2727 pub fn f(new: fn()) { new(); diff --git a/tests/ui/crashes/ice-2760.rs b/tests/ui/crashes/ice-2760.rs index 61ef24804986e..5f7d91abf9959 100644 --- a/tests/ui/crashes/ice-2760.rs +++ b/tests/ui/crashes/ice-2760.rs @@ -5,10 +5,10 @@ dead_code )] -/// This should not compile-fail with: -/// -/// error[E0277]: the trait bound `T: Foo` is not satisfied -// See rust-lang/rust-clippy#2760. +// This should not compile-fail with: +// +// error[E0277]: the trait bound `T: Foo` is not satisfied +// See https://github.com/rust-lang/rust-clippy/issues/2760 trait Foo { type Bar; diff --git a/tests/ui/crashes/ice-2862.rs b/tests/ui/crashes/ice-2862.rs index 8326e3663b054..2573b571f55cd 100644 --- a/tests/ui/crashes/ice-2862.rs +++ b/tests/ui/crashes/ice-2862.rs @@ -1,4 +1,4 @@ -/// Test for https://github.com/rust-lang/rust-clippy/issues/2862 +// Test for https://github.com/rust-lang/rust-clippy/issues/2862 pub trait FooMap { fn map B>(&self, f: F) -> B; diff --git a/tests/ui/crashes/ice-2865.rs b/tests/ui/crashes/ice-2865.rs index c629813960162..28363707acca7 100644 --- a/tests/ui/crashes/ice-2865.rs +++ b/tests/ui/crashes/ice-2865.rs @@ -1,6 +1,6 @@ #![allow(dead_code, clippy::extra_unused_lifetimes)] -/// Test for https://github.com/rust-lang/rust-clippy/issues/2865 +// Test for https://github.com/rust-lang/rust-clippy/issues/2865 struct Ice { size: String, diff --git a/tests/ui/crashes/ice-3151.rs b/tests/ui/crashes/ice-3151.rs index 268ba86fc7aa8..f88a26cb48592 100644 --- a/tests/ui/crashes/ice-3151.rs +++ b/tests/ui/crashes/ice-3151.rs @@ -1,4 +1,4 @@ -/// Test for https://github.com/rust-lang/rust-clippy/issues/3151 +// Test for https://github.com/rust-lang/rust-clippy/issues/3151 #[derive(Clone)] pub struct HashMap { diff --git a/tests/ui/crashes/ice-3462.rs b/tests/ui/crashes/ice-3462.rs index 21cd9d337cdd0..ccd617e305dae 100644 --- a/tests/ui/crashes/ice-3462.rs +++ b/tests/ui/crashes/ice-3462.rs @@ -2,7 +2,7 @@ #![allow(clippy::disallowed_names, clippy::equatable_if_let, clippy::needless_if)] #![allow(unused)] -/// Test for https://github.com/rust-lang/rust-clippy/issues/3462 +// Test for https://github.com/rust-lang/rust-clippy/issues/3462 enum Foo { Bar, diff --git a/tests/ui/crashes/ice-3747.rs b/tests/ui/crashes/ice-3747.rs index cdf018cbc88d8..44b1d7ed1b2e8 100644 --- a/tests/ui/crashes/ice-3747.rs +++ b/tests/ui/crashes/ice-3747.rs @@ -1,4 +1,4 @@ -/// Test for https://github.com/rust-lang/rust-clippy/issues/3747 +// Test for https://github.com/rust-lang/rust-clippy/issues/3747 macro_rules! a { ( $pub:tt $($attr:tt)* ) => { diff --git a/tests/ui/crashes/ice-3969.rs b/tests/ui/crashes/ice-3969.rs index d5676cbd91d19..ac09ce08753af 100644 --- a/tests/ui/crashes/ice-3969.rs +++ b/tests/ui/crashes/ice-3969.rs @@ -1,7 +1,7 @@ // https://github.com/rust-lang/rust-clippy/issues/3969 // used to crash: error: internal compiler error: // src/librustc_traits/normalize_erasing_regions.rs:43: could not fully normalize `::Item test from rustc ./ui/trivial-bounds/trivial-bounds-inconsistent.rs +// std::iter::Iterator>::Item` test from rustc ./ui/trivial-bounds/trivial-bounds-inconsistent.rs // Check that tautalogically false bounds are accepted, and are used // in type inference. @@ -18,7 +18,7 @@ struct Dst { struct TwoStrs(str, str) where str: Sized; -//~^ ERROR: trait bound str: std::marker::Sized does not depend on any type or lifetim +//~^ ERROR: trait bound str: std::marker::Sized does not depend on any type or lifetime //~| NOTE: `-D trivial-bounds` implied by `-D warnings` fn unsized_local() diff --git a/tests/ui/crashes/ice-6251.rs b/tests/ui/crashes/ice-6251.rs index a81137a646558..73e919b6dd2eb 100644 --- a/tests/ui/crashes/ice-6251.rs +++ b/tests/ui/crashes/ice-6251.rs @@ -1,5 +1,5 @@ // originally from glacier/fixed/77329.rs -// assertion failed: `(left == right) ; different DefIds +// assertion failed: `(left == right)` ; different DefIds //@no-rustfix fn bug() -> impl Iterator { std::iter::empty() diff --git a/tests/ui/crashes/ice-700.rs b/tests/ui/crashes/ice-700.rs index 0cbceedbd6bdb..5e004b94330ea 100644 --- a/tests/ui/crashes/ice-700.rs +++ b/tests/ui/crashes/ice-700.rs @@ -1,6 +1,6 @@ #![deny(clippy::all)] -/// Test for https://github.com/rust-lang/rust-clippy/issues/700 +// Test for https://github.com/rust-lang/rust-clippy/issues/700 fn core() {} diff --git a/tests/ui/crashes/ice-7410.rs b/tests/ui/crashes/ice-7410.rs index a2683b3ce3402..ccf6d7ff94f23 100644 --- a/tests/ui/crashes/ice-7410.rs +++ b/tests/ui/crashes/ice-7410.rs @@ -1,6 +1,5 @@ //@compile-flags: -Clink-arg=-nostartfiles -//@ignore-target-apple -//@ignore-target-windows +//@ignore-target: apple windows #![feature(lang_items, start, libc)] #![no_std] diff --git a/tests/ui/crashes/ice_exact_size.rs b/tests/ui/crashes/ice_exact_size.rs index 30e4b11ec0bd4..c0671eaff1450 100644 --- a/tests/ui/crashes/ice_exact_size.rs +++ b/tests/ui/crashes/ice_exact_size.rs @@ -1,6 +1,6 @@ #![deny(clippy::all)] -/// Test for https://github.com/rust-lang/rust-clippy/issues/1336 +// Test for https://github.com/rust-lang/rust-clippy/issues/1336 #[allow(dead_code)] struct Foo; diff --git a/tests/ui/crashes/if_same_then_else.rs b/tests/ui/crashes/if_same_then_else.rs index 2f913292995ea..a900fe5e6bc66 100644 --- a/tests/ui/crashes/if_same_then_else.rs +++ b/tests/ui/crashes/if_same_then_else.rs @@ -1,7 +1,7 @@ #![allow(clippy::comparison_chain)] #![deny(clippy::if_same_then_else)] -/// Test for https://github.com/rust-lang/rust-clippy/issues/2426 +// Test for https://github.com/rust-lang/rust-clippy/issues/2426 fn main() {} diff --git a/tests/ui/crashes/inherent_impl.rs b/tests/ui/crashes/inherent_impl.rs index aeb27b5ba8c2f..800a5a383f626 100644 --- a/tests/ui/crashes/inherent_impl.rs +++ b/tests/ui/crashes/inherent_impl.rs @@ -1,6 +1,6 @@ #![deny(clippy::multiple_inherent_impl)] -/// Test for https://github.com/rust-lang/rust-clippy/issues/4578 +// Test for https://github.com/rust-lang/rust-clippy/issues/4578 macro_rules! impl_foo { ($struct:ident) => { diff --git a/tests/ui/crashes/issue-825.rs b/tests/ui/crashes/issue-825.rs index 05696e3d7d56f..e8b455a0ec67b 100644 --- a/tests/ui/crashes/issue-825.rs +++ b/tests/ui/crashes/issue-825.rs @@ -1,6 +1,6 @@ #![allow(warnings)] -/// Test for https://github.com/rust-lang/rust-clippy/issues/825 +// Test for https://github.com/rust-lang/rust-clippy/issues/825 // this should compile in a reasonable amount of time fn rust_type_id(name: &str) { diff --git a/tests/ui/crashes/match_same_arms_const.rs b/tests/ui/crashes/match_same_arms_const.rs index 94c939665e616..626179c001557 100644 --- a/tests/ui/crashes/match_same_arms_const.rs +++ b/tests/ui/crashes/match_same_arms_const.rs @@ -1,6 +1,6 @@ #![deny(clippy::match_same_arms)] -/// Test for https://github.com/rust-lang/rust-clippy/issues/2427 +// Test for https://github.com/rust-lang/rust-clippy/issues/2427 const PRICE_OF_SWEETS: u32 = 5; const PRICE_OF_KINDNESS: u32 = 0; diff --git a/tests/ui/crashes/needless_pass_by_value-w-late-bound.stderr b/tests/ui/crashes/needless_pass_by_value-w-late-bound.stderr index 90076d4338a47..2847957000692 100644 --- a/tests/ui/crashes/needless_pass_by_value-w-late-bound.stderr +++ b/tests/ui/crashes/needless_pass_by_value-w-late-bound.stderr @@ -4,7 +4,7 @@ error: this argument is passed by value, but not consumed in the function body LL | fn test(x: Foo<'_>) {} | ^^^^^^^ help: consider taking a reference instead: `&Foo<'_>` | -help: consider marking this type as `Copy` +help: or consider marking this type as `Copy` --> tests/ui/crashes/needless_pass_by_value-w-late-bound.rs:5:1 | LL | struct Foo<'a>(&'a [(); 100]); diff --git a/tests/ui/crashes/returns.rs b/tests/ui/crashes/returns.rs index 8021ed4607dde..91cdb5306c895 100644 --- a/tests/ui/crashes/returns.rs +++ b/tests/ui/crashes/returns.rs @@ -1,4 +1,4 @@ -/// Test for https://github.com/rust-lang/rust-clippy/issues/1346 +// Test for https://github.com/rust-lang/rust-clippy/issues/1346 #[deny(warnings)] fn cfg_return() -> i32 { diff --git a/tests/ui/crate_level_checks/entrypoint_recursion.rs b/tests/ui/crate_level_checks/entrypoint_recursion.rs index aa76688d80101..5d853d97bc35b 100644 --- a/tests/ui/crate_level_checks/entrypoint_recursion.rs +++ b/tests/ui/crate_level_checks/entrypoint_recursion.rs @@ -1,4 +1,4 @@ -//@ignore-target-apple +//@ignore-target: apple #![feature(rustc_attrs)] diff --git a/tests/ui/crate_level_checks/no_std_main_recursion.rs b/tests/ui/crate_level_checks/no_std_main_recursion.rs index 32eba9695920c..9e5b2a4890348 100644 --- a/tests/ui/crate_level_checks/no_std_main_recursion.rs +++ b/tests/ui/crate_level_checks/no_std_main_recursion.rs @@ -1,5 +1,5 @@ //@compile-flags: -Clink-arg=-nostartfiles -//@ignore-target-apple +//@ignore-target: apple #![feature(lang_items, start, libc)] #![no_std] diff --git a/tests/ui/declare_interior_mutable_const/others.rs b/tests/ui/declare_interior_mutable_const/others.rs index 9dafad8b784b9..0dccf18c49303 100644 --- a/tests/ui/declare_interior_mutable_const/others.rs +++ b/tests/ui/declare_interior_mutable_const/others.rs @@ -4,8 +4,8 @@ use std::borrow::Cow; use std::cell::Cell; use std::fmt::Display; use std::ptr; -use std::sync::atomic::AtomicUsize; use std::sync::Once; +use std::sync::atomic::AtomicUsize; const ATOMIC: AtomicUsize = AtomicUsize::new(5); //~ ERROR: interior mutable const CELL: Cell = Cell::new(6); //~ ERROR: interior mutable diff --git a/tests/ui/def_id_nocore.rs b/tests/ui/def_id_nocore.rs index 190d636ebf34a..c9650312db878 100644 --- a/tests/ui/def_id_nocore.rs +++ b/tests/ui/def_id_nocore.rs @@ -1,4 +1,4 @@ -//@ignore-target-apple +//@ignore-target: apple #![feature(no_core, lang_items, start)] #![no_core] diff --git a/tests/ui/diverging_sub_expression.rs b/tests/ui/diverging_sub_expression.rs index e0acf050949a1..1abba60fd34a8 100644 --- a/tests/ui/diverging_sub_expression.rs +++ b/tests/ui/diverging_sub_expression.rs @@ -67,3 +67,9 @@ fn foobar() { }; } } + +#[allow(unused)] +fn ignore_todo() { + let x: u32 = todo!(); + println!("{x}"); +} diff --git a/tests/ui/doc/doc_lazy_blank_line.fixed b/tests/ui/doc/doc_lazy_blank_line.fixed deleted file mode 100644 index 1aaa26afe7f26..0000000000000 --- a/tests/ui/doc/doc_lazy_blank_line.fixed +++ /dev/null @@ -1,47 +0,0 @@ -// https://github.com/rust-lang/rust-clippy/issues/12917 -#![warn(clippy::doc_lazy_continuation)] - -/// This is a constant. -/// -/// The meaning of which should not be explained. -pub const A: i32 = 42; - -/// This is another constant, no longer used. -/// -/// This block of documentation has a long -/// explanation and derivation to explain -/// why it is what it is, and how it's used. -/// -/// It is left here for historical reasons, and -/// for reference. -/// -/// Reasons it's great: -/// - First reason -/// - Second reason -/// -//pub const B: i32 = 1337; - -/// This is yet another constant. -/// -/// This has a similar fate as `B`. -/// -/// Reasons it's useful: -/// 1. First reason -/// 2. Second reason -/// -//pub const C: i32 = 8008; - -/// This is still in use. -pub const D: i32 = 20; - -/// > blockquote code path -/// - -/// bottom text -pub const E: i32 = 20; - -/// > blockquote code path -/// -#[repr(C)] -/// bottom text -pub struct Foo(i32); diff --git a/tests/ui/doc/doc_lazy_blank_line.rs b/tests/ui/doc/doc_lazy_blank_line.rs deleted file mode 100644 index e1ab8fc838926..0000000000000 --- a/tests/ui/doc/doc_lazy_blank_line.rs +++ /dev/null @@ -1,43 +0,0 @@ -// https://github.com/rust-lang/rust-clippy/issues/12917 -#![warn(clippy::doc_lazy_continuation)] - -/// This is a constant. -/// -/// The meaning of which should not be explained. -pub const A: i32 = 42; - -/// This is another constant, no longer used. -/// -/// This block of documentation has a long -/// explanation and derivation to explain -/// why it is what it is, and how it's used. -/// -/// It is left here for historical reasons, and -/// for reference. -/// -/// Reasons it's great: -/// - First reason -/// - Second reason -//pub const B: i32 = 1337; - -/// This is yet another constant. -/// -/// This has a similar fate as `B`. -/// -/// Reasons it's useful: -/// 1. First reason -/// 2. Second reason -//pub const C: i32 = 8008; - -/// This is still in use. -pub const D: i32 = 20; - -/// > blockquote code path - -/// bottom text -pub const E: i32 = 20; - -/// > blockquote code path -#[repr(C)] -/// bottom text -pub struct Foo(i32); diff --git a/tests/ui/doc/doc_lazy_blank_line.stderr b/tests/ui/doc/doc_lazy_blank_line.stderr deleted file mode 100644 index 854906a747418..0000000000000 --- a/tests/ui/doc/doc_lazy_blank_line.stderr +++ /dev/null @@ -1,56 +0,0 @@ -error: doc list item without indentation - --> tests/ui/doc/doc_lazy_blank_line.rs:23:5 - | -LL | /// This is yet another constant. - | ^ - | - = help: if this is intended to be part of the list, indent 3 spaces - = note: `-D clippy::doc-lazy-continuation` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::doc_lazy_continuation)]` -help: if this should be its own paragraph, add a blank doc comment line - | -LL ~ /// - Second reason -LL + /// - | - -error: doc list item without indentation - --> tests/ui/doc/doc_lazy_blank_line.rs:32:5 - | -LL | /// This is still in use. - | ^ - | - = help: if this is intended to be part of the list, indent 4 spaces -help: if this should be its own paragraph, add a blank doc comment line - | -LL ~ /// 2. Second reason -LL + /// - | - -error: doc quote line without `>` marker - --> tests/ui/doc/doc_lazy_blank_line.rs:37:5 - | -LL | /// bottom text - | ^ - | - = help: if this not intended to be a quote at all, escape it with `\>` -help: if this should be its own paragraph, add a blank doc comment line - | -LL ~ /// > blockquote code path -LL + /// - | - -error: doc quote line without `>` marker - --> tests/ui/doc/doc_lazy_blank_line.rs:42:5 - | -LL | /// bottom text - | ^ - | - = help: if this not intended to be a quote at all, escape it with `\>` -help: if this should be its own paragraph, add a blank doc comment line - | -LL ~ /// > blockquote code path -LL + /// - | - -error: aborting due to 4 previous errors - diff --git a/tests/ui/doc/doc_lazy_list.fixed b/tests/ui/doc/doc_lazy_list.fixed index ea59ae4c01c96..da537518a2b53 100644 --- a/tests/ui/doc/doc_lazy_list.fixed +++ b/tests/ui/doc/doc_lazy_list.fixed @@ -7,9 +7,8 @@ fn one() {} /// 1. first line /// lazy list continuations don't make warnings with this lint -/// //~^ ERROR: doc list item without indentation -/// because they don't have the +/// because they don't have the //~^ ERROR: doc list item without indentation fn two() {} @@ -20,9 +19,8 @@ fn three() {} /// - first line /// lazy list continuations don't make warnings with this lint -/// //~^ ERROR: doc list item without indentation -/// because they don't have the +/// because they don't have the //~^ ERROR: doc list item without indentation fn four() {} @@ -33,9 +31,8 @@ fn five() {} /// - - first line /// this will warn on the lazy continuation -/// //~^ ERROR: doc list item without indentation -/// and so should this +/// and so should this //~^ ERROR: doc list item without indentation fn six() {} diff --git a/tests/ui/doc/doc_lazy_list.stderr b/tests/ui/doc/doc_lazy_list.stderr index 52aa74df8948c..b38f43b7555f1 100644 --- a/tests/ui/doc/doc_lazy_list.stderr +++ b/tests/ui/doc/doc_lazy_list.stderr @@ -30,12 +30,11 @@ error: doc list item without indentation LL | /// because they don't have the | ^ | - = help: if this is intended to be part of the list, indent 3 spaces -help: if this should be its own paragraph, add a blank doc comment line - | -LL ~ /// lazy list continuations don't make warnings with this lint -LL + /// + = help: if this is supposed to be its own paragraph, add a blank line +help: indent this line | +LL | /// because they don't have the + | +++ error: doc list item without indentation --> tests/ui/doc/doc_lazy_list.rs:16:5 @@ -67,12 +66,11 @@ error: doc list item without indentation LL | /// because they don't have the | ^ | - = help: if this is intended to be part of the list, indent 4 spaces -help: if this should be its own paragraph, add a blank doc comment line - | -LL ~ /// lazy list continuations don't make warnings with this lint -LL + /// + = help: if this is supposed to be its own paragraph, add a blank line +help: indent this line | +LL | /// because they don't have the + | ++++ error: doc list item without indentation --> tests/ui/doc/doc_lazy_list.rs:28:5 @@ -104,12 +102,11 @@ error: doc list item without indentation LL | /// and so should this | ^^^^ | - = help: if this is intended to be part of the list, indent 2 spaces -help: if this should be its own paragraph, add a blank doc comment line - | -LL ~ /// this will warn on the lazy continuation -LL + /// + = help: if this is supposed to be its own paragraph, add a blank line +help: indent this line | +LL | /// and so should this + | ++ error: doc list item without indentation --> tests/ui/doc/doc_lazy_list.rs:56:5 diff --git a/tests/ui/duplicate_underscore_argument.rs b/tests/ui/duplicate_underscore_argument.rs index 118f6e4a34c0a..a725538436cb1 100644 --- a/tests/ui/duplicate_underscore_argument.rs +++ b/tests/ui/duplicate_underscore_argument.rs @@ -1,5 +1,4 @@ #![warn(clippy::duplicate_underscore_argument)] -#[allow(dead_code, unused)] fn join_the_dark_side(darth: i32, _darth: i32) {} //~^ ERROR: `darth` already exists, having another argument having almost the same name ma diff --git a/tests/ui/duplicate_underscore_argument.stderr b/tests/ui/duplicate_underscore_argument.stderr index 40a24b823d115..74979b157882a 100644 --- a/tests/ui/duplicate_underscore_argument.stderr +++ b/tests/ui/duplicate_underscore_argument.stderr @@ -1,5 +1,5 @@ error: `darth` already exists, having another argument having almost the same name makes code comprehension and documentation more difficult - --> tests/ui/duplicate_underscore_argument.rs:4:23 + --> tests/ui/duplicate_underscore_argument.rs:3:23 | LL | fn join_the_dark_side(darth: i32, _darth: i32) {} | ^^^^^ diff --git a/tests/ui/duplicated_attributes.rs b/tests/ui/duplicated_attributes.rs index 97cf4a69682d7..874f5d22075c1 100644 --- a/tests/ui/duplicated_attributes.rs +++ b/tests/ui/duplicated_attributes.rs @@ -27,4 +27,8 @@ trait Abc {} #[proc_macro_attr::duplicated_attr()] // Should not warn! fn babar() {} +#[allow(missing_docs, reason = "library for internal use only")] +#[allow(exported_private_dependencies, reason = "library for internal use only")] +fn duplicate_reason() {} + fn main() {} diff --git a/tests/ui/empty_line_after/doc_comments.1.fixed b/tests/ui/empty_line_after/doc_comments.1.fixed new file mode 100644 index 0000000000000..fd6a94b6a80c8 --- /dev/null +++ b/tests/ui/empty_line_after/doc_comments.1.fixed @@ -0,0 +1,135 @@ +#![warn(clippy::empty_line_after_outer_attr, clippy::empty_line_after_doc_comments)] + +//~vvv empty_line_after_doc_comments +/// Meant to be an +/// inner doc comment +/// for the crate +fn first_in_crate() {} + +mod m { + //~vvv empty_line_after_doc_comments + /// Meant to be an + /// inner doc comment + /// for the module + fn first_in_module() {} +} + +mod some_mod { + //! This doc comment should *NOT* produce a warning + + mod some_inner_mod { + fn some_noop() {} + } + + //~v empty_line_after_doc_comments + /// # Indented + /// Blank line + fn indented() {} +} + +//~v empty_line_after_doc_comments +/// This should produce a warning +fn with_doc_and_newline() {} + +// This should *NOT* produce a warning +#[crate_type = "lib"] +/// some comment +fn with_no_newline_and_comment() {} + +//~v empty_line_after_doc_comments +/// This doc comment should produce a warning +/** This is also a doc comment and is part of the warning + */ +#[allow(non_camel_case_types)] +#[allow(missing_docs)] +#[allow(dead_code)] +fn three_attributes() {} + +mod misattributed { + //~v empty_line_after_doc_comments + /// docs for `old_code` + // fn old_code() {} + fn new_code() {} + + //~vv empty_line_after_doc_comments + /// Docs + /// for OldA + // struct OldA; + /// Docs + /// for OldB + // struct OldB; + /// Docs + /// for Multiple + #[allow(dead_code)] + struct Multiple; +} + +mod block_comments { + //~v empty_line_after_doc_comments + /** + * Meant to be inner doc comment + */ + fn first_in_module() {} + + //~v empty_line_after_doc_comments + /** + * Docs for `old_code` + */ + /* fn old_code() {} */ + /** + * Docs for `new_code` + */ + fn new_code() {} + + //~v empty_line_after_doc_comments + /// Docs for `old_code2` + /* fn old_code2() {} */ + /// Docs for `new_code2` + fn new_code2() {} +} + +// This should *NOT* produce a warning +#[doc = " +Returns the escaped value of the textual representation of + +"] +pub fn function() -> bool { + true +} + +// This should *NOT* produce a warning +#[derive(Clone, Copy)] +pub enum FooFighter { + Bar1, + + Bar2, + + Bar3, + + Bar4, +} + +/// Should not lint +// some line comment +/// gaps without an empty line +struct LineComment; + +/// This should *NOT* produce a warning because the empty line is inside a block comment +/* + +*/ +pub struct EmptyInBlockComment; + +/// This should *NOT* produce a warning +/* test */ +pub struct BlockComment; + +/// Ignore the empty line inside a cfg_attr'd out attribute +#[cfg_attr(any(), multiline( + foo = 1 + + bar = 2 +))] +fn empty_line_in_cfg_attr() {} + +fn main() {} diff --git a/tests/ui/empty_line_after/doc_comments.2.fixed b/tests/ui/empty_line_after/doc_comments.2.fixed new file mode 100644 index 0000000000000..7a57dcd92332b --- /dev/null +++ b/tests/ui/empty_line_after/doc_comments.2.fixed @@ -0,0 +1,144 @@ +#![warn(clippy::empty_line_after_outer_attr, clippy::empty_line_after_doc_comments)] + +//~vvv empty_line_after_doc_comments +//! Meant to be an +//! inner doc comment +//! for the crate + +fn first_in_crate() {} + +mod m { + //~vvv empty_line_after_doc_comments + //! Meant to be an + //! inner doc comment + //! for the module + + fn first_in_module() {} +} + +mod some_mod { + //! This doc comment should *NOT* produce a warning + + mod some_inner_mod { + fn some_noop() {} + } + + //~v empty_line_after_doc_comments + /// # Indented + /// + /// Blank line + fn indented() {} +} + +//~v empty_line_after_doc_comments +/// This should produce a warning +fn with_doc_and_newline() {} + +// This should *NOT* produce a warning +#[crate_type = "lib"] +/// some comment +fn with_no_newline_and_comment() {} + +//~v empty_line_after_doc_comments +/// This doc comment should produce a warning +/** This is also a doc comment and is part of the warning + */ +#[allow(non_camel_case_types)] +#[allow(missing_docs)] +#[allow(dead_code)] +fn three_attributes() {} + +mod misattributed { + //~v empty_line_after_doc_comments + // /// docs for `old_code` + // fn old_code() {} + + fn new_code() {} + + //~vv empty_line_after_doc_comments + // /// Docs + // /// for OldA + // struct OldA; + + // /// Docs + // /// for OldB + // struct OldB; + + /// Docs + /// for Multiple + #[allow(dead_code)] + struct Multiple; +} + +mod block_comments { + //~v empty_line_after_doc_comments + /*! + * Meant to be inner doc comment + */ + + fn first_in_module() {} + + //~v empty_line_after_doc_comments + /* + * Docs for `old_code` + */ + /* fn old_code() {} */ + + /** + * Docs for `new_code` + */ + fn new_code() {} + + //~v empty_line_after_doc_comments + // /// Docs for `old_code2` + /* fn old_code2() {} */ + + /// Docs for `new_code2` + fn new_code2() {} +} + +// This should *NOT* produce a warning +#[doc = " +Returns the escaped value of the textual representation of + +"] +pub fn function() -> bool { + true +} + +// This should *NOT* produce a warning +#[derive(Clone, Copy)] +pub enum FooFighter { + Bar1, + + Bar2, + + Bar3, + + Bar4, +} + +/// Should not lint +// some line comment +/// gaps without an empty line +struct LineComment; + +/// This should *NOT* produce a warning because the empty line is inside a block comment +/* + +*/ +pub struct EmptyInBlockComment; + +/// This should *NOT* produce a warning +/* test */ +pub struct BlockComment; + +/// Ignore the empty line inside a cfg_attr'd out attribute +#[cfg_attr(any(), multiline( + foo = 1 + + bar = 2 +))] +fn empty_line_in_cfg_attr() {} + +fn main() {} diff --git a/tests/ui/empty_line_after/doc_comments.rs b/tests/ui/empty_line_after/doc_comments.rs new file mode 100644 index 0000000000000..1da761a5c3d52 --- /dev/null +++ b/tests/ui/empty_line_after/doc_comments.rs @@ -0,0 +1,147 @@ +#![warn(clippy::empty_line_after_outer_attr, clippy::empty_line_after_doc_comments)] + +//~vvv empty_line_after_doc_comments +/// Meant to be an +/// inner doc comment +/// for the crate + +fn first_in_crate() {} + +mod m { + //~vvv empty_line_after_doc_comments + /// Meant to be an + /// inner doc comment + /// for the module + + fn first_in_module() {} +} + +mod some_mod { + //! This doc comment should *NOT* produce a warning + + mod some_inner_mod { + fn some_noop() {} + } + + //~v empty_line_after_doc_comments + /// # Indented + + /// Blank line + fn indented() {} +} + +//~v empty_line_after_doc_comments +/// This should produce a warning + +fn with_doc_and_newline() {} + +// This should *NOT* produce a warning +#[crate_type = "lib"] +/// some comment +fn with_no_newline_and_comment() {} + +//~v empty_line_after_doc_comments +/// This doc comment should produce a warning + +/** This is also a doc comment and is part of the warning + */ + +#[allow(non_camel_case_types)] +#[allow(missing_docs)] +#[allow(dead_code)] +fn three_attributes() {} + +mod misattributed { + //~v empty_line_after_doc_comments + /// docs for `old_code` + // fn old_code() {} + + fn new_code() {} + + //~vv empty_line_after_doc_comments + /// Docs + /// for OldA + // struct OldA; + + /// Docs + /// for OldB + // struct OldB; + + /// Docs + /// for Multiple + #[allow(dead_code)] + struct Multiple; +} + +mod block_comments { + //~v empty_line_after_doc_comments + /** + * Meant to be inner doc comment + */ + + fn first_in_module() {} + + //~v empty_line_after_doc_comments + /** + * Docs for `old_code` + */ + /* fn old_code() {} */ + + /** + * Docs for `new_code` + */ + fn new_code() {} + + //~v empty_line_after_doc_comments + /// Docs for `old_code2` + /* fn old_code2() {} */ + + /// Docs for `new_code2` + fn new_code2() {} +} + +// This should *NOT* produce a warning +#[doc = " +Returns the escaped value of the textual representation of + +"] +pub fn function() -> bool { + true +} + +// This should *NOT* produce a warning +#[derive(Clone, Copy)] +pub enum FooFighter { + Bar1, + + Bar2, + + Bar3, + + Bar4, +} + +/// Should not lint +// some line comment +/// gaps without an empty line +struct LineComment; + +/// This should *NOT* produce a warning because the empty line is inside a block comment +/* + +*/ +pub struct EmptyInBlockComment; + +/// This should *NOT* produce a warning +/* test */ +pub struct BlockComment; + +/// Ignore the empty line inside a cfg_attr'd out attribute +#[cfg_attr(any(), multiline( + foo = 1 + + bar = 2 +))] +fn empty_line_in_cfg_attr() {} + +fn main() {} diff --git a/tests/ui/empty_line_after/doc_comments.stderr b/tests/ui/empty_line_after/doc_comments.stderr new file mode 100644 index 0000000000000..c238b4c9a17f2 --- /dev/null +++ b/tests/ui/empty_line_after/doc_comments.stderr @@ -0,0 +1,176 @@ +error: empty line after doc comment + --> tests/ui/empty_line_after/doc_comments.rs:6:1 + | +LL | / /// for the crate +LL | | + | |_ +LL | fn first_in_crate() {} + | ------------------- the comment documents this function + | + = note: `-D clippy::empty-line-after-doc-comments` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::empty_line_after_doc_comments)]` + = help: if the empty line is unintentional remove it +help: if the comment should document the crate use an inner doc comment + | +LL ~ //! Meant to be an +LL ~ //! inner doc comment +LL ~ //! for the crate + | + +error: empty line after doc comment + --> tests/ui/empty_line_after/doc_comments.rs:14:5 + | +LL | / /// for the module +LL | | + | |_ +LL | fn first_in_module() {} + | -------------------- the comment documents this function + | + = help: if the empty line is unintentional remove it +help: if the comment should document the parent module use an inner doc comment + | +LL ~ //! Meant to be an +LL ~ //! inner doc comment +LL ~ //! for the module + | + +error: empty line after doc comment + --> tests/ui/empty_line_after/doc_comments.rs:27:5 + | +LL | / /// # Indented +LL | | + | |_ +LL | /// Blank line +LL | fn indented() {} + | ------------- the comment documents this function + | + = help: if the empty line is unintentional remove it +help: if the documentation should include the empty line include it in the comment + | +LL | /// + | + +error: empty line after doc comment + --> tests/ui/empty_line_after/doc_comments.rs:34:1 + | +LL | / /// This should produce a warning +LL | | + | |_ +LL | fn with_doc_and_newline() {} + | ------------------------- the comment documents this function + | + = help: if the empty line is unintentional remove it + +error: empty lines after doc comment + --> tests/ui/empty_line_after/doc_comments.rs:44:1 + | +LL | / /// This doc comment should produce a warning +LL | | +LL | | /** This is also a doc comment and is part of the warning +LL | | */ +LL | | + | |_ +... +LL | fn three_attributes() {} + | --------------------- the comment documents this function + | + = help: if the empty lines are unintentional remove them + +error: empty line after doc comment + --> tests/ui/empty_line_after/doc_comments.rs:56:5 + | +LL | / /// docs for `old_code` +LL | | // fn old_code() {} +LL | | + | |_ +LL | fn new_code() {} + | ------------- the comment documents this function + | + = help: if the empty line is unintentional remove it +help: if the doc comment should not document `new_code` comment it out + | +LL | // /// docs for `old_code` + | ++ + +error: empty lines after doc comment + --> tests/ui/empty_line_after/doc_comments.rs:63:5 + | +LL | / /// for OldA +LL | | // struct OldA; +LL | | +LL | | /// Docs +LL | | /// for OldB +LL | | // struct OldB; +LL | | + | |_ +... +LL | struct Multiple; + | --------------- the comment documents this struct + | + = help: if the empty lines are unintentional remove them +help: if the doc comment should not document `Multiple` comment it out + | +LL ~ // /// Docs +LL ~ // /// for OldA +LL | // struct OldA; +LL | +LL ~ // /// Docs +LL ~ // /// for OldB + | + +error: empty line after doc comment + --> tests/ui/empty_line_after/doc_comments.rs:78:5 + | +LL | / /** +LL | | * Meant to be inner doc comment +LL | | */ +LL | | + | |_ +LL | fn first_in_module() {} + | -------------------- the comment documents this function + | + = help: if the empty line is unintentional remove it +help: if the comment should document the parent module use an inner doc comment + | +LL | /*! + | ~ + +error: empty line after doc comment + --> tests/ui/empty_line_after/doc_comments.rs:85:5 + | +LL | / /** +LL | | * Docs for `old_code` +LL | | */ +LL | | /* fn old_code() {} */ +LL | | + | |_ +... +LL | fn new_code() {} + | ------------- the comment documents this function + | + = help: if the empty line is unintentional remove it +help: if the doc comment should not document `new_code` comment it out + | +LL - /** +LL + /* + | + +error: empty line after doc comment + --> tests/ui/empty_line_after/doc_comments.rs:96:5 + | +LL | / /// Docs for `old_code2` +LL | | /* fn old_code2() {} */ +LL | | + | |_ +LL | /// Docs for `new_code2` +LL | fn new_code2() {} + | -------------- the comment documents this function + | + = help: if the empty line is unintentional remove it +help: if the doc comment should not document `new_code2` comment it out + | +LL | // /// Docs for `old_code2` + | ++ + +error: aborting due to 10 previous errors + diff --git a/tests/ui/empty_line_after/outer_attribute.1.fixed b/tests/ui/empty_line_after/outer_attribute.1.fixed new file mode 100644 index 0000000000000..36d80a2c95bf6 --- /dev/null +++ b/tests/ui/empty_line_after/outer_attribute.1.fixed @@ -0,0 +1,108 @@ +//@aux-build:../auxiliary/proc_macro_attr.rs +#![warn(clippy::empty_line_after_outer_attr, clippy::empty_line_after_doc_comments)] + +//~v empty_line_after_outer_attr +#[crate_type = "lib"] +fn first_in_crate() {} + +#[macro_use] +extern crate proc_macro_attr; + +//~v empty_line_after_outer_attr +#[inline] +/// some comment +fn with_one_newline_and_comment() {} + +#[inline] +/// some comment +fn with_no_newline_and_comment() {} + +//~v empty_line_after_outer_attr +#[inline] +fn with_one_newline() {} + +#[rustfmt::skip] +mod two_lines { + //~v empty_line_after_outer_attr + #[crate_type = "lib"] + fn with_two_newlines() {} +} + +//~v empty_line_after_outer_attr +#[doc = "doc attributes should be considered attributes"] +enum Baz { + One, + Two, +} + +//~v empty_line_after_outer_attr +#[repr(C)] +struct Foo { + one: isize, + two: isize, +} + +//~v empty_line_after_outer_attr +#[allow(dead_code)] +mod foo {} + +//~v empty_line_after_outer_attr +#[inline] +// Still lint cases where the empty line does not immediately follow the attribute +fn comment_before_empty_line() {} + +//~v empty_line_after_outer_attr +#[allow(unused)] +// This comment is isolated +pub fn isolated_comment() {} + +#[doc = " +Returns the escaped value of the textual representation of + +"] +pub fn function() -> bool { + true +} + +#[derive(Clone, Copy)] +pub enum FooFighter { + Bar1, + + Bar2, + + Bar3, + + Bar4, +} + +#[crate_type = "lib"] +/* + +*/ +pub struct EmptyLineInBlockComment; + +#[crate_type = "lib"] +/* test */ +pub struct BlockComment; + +// See https://github.com/rust-lang/rust-clippy/issues/5567 +#[rustfmt::skip] +#[fake_async_trait] +pub trait Bazz { + fn foo() -> Vec { + let _i = ""; + + + + vec![] + } +} + +#[derive(Clone, Copy)] +#[dummy(string = "first line + +second line +")] +pub struct Args; + +fn main() {} diff --git a/tests/ui/empty_line_after/outer_attribute.2.fixed b/tests/ui/empty_line_after/outer_attribute.2.fixed new file mode 100644 index 0000000000000..0e8e4129e858c --- /dev/null +++ b/tests/ui/empty_line_after/outer_attribute.2.fixed @@ -0,0 +1,111 @@ +//@aux-build:../auxiliary/proc_macro_attr.rs +#![warn(clippy::empty_line_after_outer_attr, clippy::empty_line_after_doc_comments)] + +//~v empty_line_after_outer_attr +#![crate_type = "lib"] + +fn first_in_crate() {} + +#[macro_use] +extern crate proc_macro_attr; + +//~v empty_line_after_outer_attr +#[inline] +/// some comment +fn with_one_newline_and_comment() {} + +#[inline] +/// some comment +fn with_no_newline_and_comment() {} + +//~v empty_line_after_outer_attr +#[inline] +fn with_one_newline() {} + +#[rustfmt::skip] +mod two_lines { + //~v empty_line_after_outer_attr + #![crate_type = "lib"] + + + fn with_two_newlines() {} +} + +//~v empty_line_after_outer_attr +#[doc = "doc attributes should be considered attributes"] +enum Baz { + One, + Two, +} + +//~v empty_line_after_outer_attr +#[repr(C)] +struct Foo { + one: isize, + two: isize, +} + +//~v empty_line_after_outer_attr +#[allow(dead_code)] +mod foo {} + +//~v empty_line_after_outer_attr +#[inline] +// Still lint cases where the empty line does not immediately follow the attribute +fn comment_before_empty_line() {} + +//~v empty_line_after_outer_attr +#[allow(unused)] +// This comment is isolated +pub fn isolated_comment() {} + +#[doc = " +Returns the escaped value of the textual representation of + +"] +pub fn function() -> bool { + true +} + +#[derive(Clone, Copy)] +pub enum FooFighter { + Bar1, + + Bar2, + + Bar3, + + Bar4, +} + +#[crate_type = "lib"] +/* + +*/ +pub struct EmptyLineInBlockComment; + +#[crate_type = "lib"] +/* test */ +pub struct BlockComment; + +// See https://github.com/rust-lang/rust-clippy/issues/5567 +#[rustfmt::skip] +#[fake_async_trait] +pub trait Bazz { + fn foo() -> Vec { + let _i = ""; + + + + vec![] + } +} + +#[derive(Clone, Copy)] +#[dummy(string = "first line + +second line +")] +pub struct Args; + +fn main() {} diff --git a/tests/ui/empty_line_after/outer_attribute.rs b/tests/ui/empty_line_after/outer_attribute.rs new file mode 100644 index 0000000000000..1295088ac00ed --- /dev/null +++ b/tests/ui/empty_line_after/outer_attribute.rs @@ -0,0 +1,119 @@ +//@aux-build:../auxiliary/proc_macro_attr.rs +#![warn(clippy::empty_line_after_outer_attr, clippy::empty_line_after_doc_comments)] + +//~v empty_line_after_outer_attr +#[crate_type = "lib"] + +fn first_in_crate() {} + +#[macro_use] +extern crate proc_macro_attr; + +//~v empty_line_after_outer_attr +#[inline] + +/// some comment +fn with_one_newline_and_comment() {} + +#[inline] +/// some comment +fn with_no_newline_and_comment() {} + +//~v empty_line_after_outer_attr +#[inline] + +fn with_one_newline() {} + +#[rustfmt::skip] +mod two_lines { + //~v empty_line_after_outer_attr + #[crate_type = "lib"] + + + fn with_two_newlines() {} +} + +//~v empty_line_after_outer_attr +#[doc = "doc attributes should be considered attributes"] + +enum Baz { + One, + Two, +} + +//~v empty_line_after_outer_attr +#[repr(C)] + +struct Foo { + one: isize, + two: isize, +} + +//~v empty_line_after_outer_attr +#[allow(dead_code)] + +mod foo {} + +//~v empty_line_after_outer_attr +#[inline] +// Still lint cases where the empty line does not immediately follow the attribute + +fn comment_before_empty_line() {} + +//~v empty_line_after_outer_attr +#[allow(unused)] + +// This comment is isolated + +pub fn isolated_comment() {} + +#[doc = " +Returns the escaped value of the textual representation of + +"] +pub fn function() -> bool { + true +} + +#[derive(Clone, Copy)] +pub enum FooFighter { + Bar1, + + Bar2, + + Bar3, + + Bar4, +} + +#[crate_type = "lib"] +/* + +*/ +pub struct EmptyLineInBlockComment; + +#[crate_type = "lib"] +/* test */ +pub struct BlockComment; + +// See https://github.com/rust-lang/rust-clippy/issues/5567 +#[rustfmt::skip] +#[fake_async_trait] +pub trait Bazz { + fn foo() -> Vec { + let _i = ""; + + + + vec![] + } +} + +#[derive(Clone, Copy)] +#[dummy(string = "first line + +second line +")] +pub struct Args; + +fn main() {} diff --git a/tests/ui/empty_line_after/outer_attribute.stderr b/tests/ui/empty_line_after/outer_attribute.stderr new file mode 100644 index 0000000000000..958b40424a928 --- /dev/null +++ b/tests/ui/empty_line_after/outer_attribute.stderr @@ -0,0 +1,116 @@ +error: empty line after outer attribute + --> tests/ui/empty_line_after/outer_attribute.rs:5:1 + | +LL | / #[crate_type = "lib"] +LL | | + | |_ +LL | fn first_in_crate() {} + | ------------------- the attribute applies to this function + | + = note: `-D clippy::empty-line-after-outer-attr` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::empty_line_after_outer_attr)]` + = help: if the empty line is unintentional remove it +help: if the attribute should apply to the crate use an inner attribute + | +LL | #![crate_type = "lib"] + | + + +error: empty line after outer attribute + --> tests/ui/empty_line_after/outer_attribute.rs:13:1 + | +LL | / #[inline] +LL | | + | |_ +LL | /// some comment +LL | fn with_one_newline_and_comment() {} + | --------------------------------- the attribute applies to this function + | + = help: if the empty line is unintentional remove it + +error: empty line after outer attribute + --> tests/ui/empty_line_after/outer_attribute.rs:23:1 + | +LL | / #[inline] +LL | | + | |_ +LL | fn with_one_newline() {} + | --------------------- the attribute applies to this function + | + = help: if the empty line is unintentional remove it + +error: empty lines after outer attribute + --> tests/ui/empty_line_after/outer_attribute.rs:30:5 + | +LL | / #[crate_type = "lib"] +LL | | +LL | | + | |_ +LL | fn with_two_newlines() {} + | ---------------------- the attribute applies to this function + | + = help: if the empty lines are unintentional remove them +help: if the attribute should apply to the parent module use an inner attribute + | +LL | #![crate_type = "lib"] + | + + +error: empty line after outer attribute + --> tests/ui/empty_line_after/outer_attribute.rs:37:1 + | +LL | / #[doc = "doc attributes should be considered attributes"] +LL | | + | |_ +LL | enum Baz { + | -------- the attribute applies to this enum + | + = help: if the empty line is unintentional remove it + +error: empty line after outer attribute + --> tests/ui/empty_line_after/outer_attribute.rs:45:1 + | +LL | / #[repr(C)] +LL | | + | |_ +LL | struct Foo { + | ---------- the attribute applies to this struct + | + = help: if the empty line is unintentional remove it + +error: empty line after outer attribute + --> tests/ui/empty_line_after/outer_attribute.rs:53:1 + | +LL | / #[allow(dead_code)] +LL | | + | |_ +LL | mod foo {} + | ------- the attribute applies to this module + | + = help: if the empty line is unintentional remove it + +error: empty line after outer attribute + --> tests/ui/empty_line_after/outer_attribute.rs:58:1 + | +LL | / #[inline] +LL | | // Still lint cases where the empty line does not immediately follow the attribute +LL | | + | |_ +LL | fn comment_before_empty_line() {} + | ------------------------------ the attribute applies to this function + | + = help: if the empty line is unintentional remove it + +error: empty lines after outer attribute + --> tests/ui/empty_line_after/outer_attribute.rs:64:1 + | +LL | / #[allow(unused)] +LL | | +LL | | // This comment is isolated +LL | | + | |_ +LL | pub fn isolated_comment() {} + | ------------------------- the attribute applies to this function + | + = help: if the empty lines are unintentional remove them + +error: aborting due to 9 previous errors + diff --git a/tests/ui/empty_line_after_doc_comments.rs b/tests/ui/empty_line_after_doc_comments.rs deleted file mode 100644 index dd78491749c77..0000000000000 --- a/tests/ui/empty_line_after_doc_comments.rs +++ /dev/null @@ -1,132 +0,0 @@ -//@aux-build:proc_macro_attr.rs -#![warn(clippy::empty_line_after_doc_comments)] -#![allow(clippy::assertions_on_constants, clippy::duplicated_attributes)] -#![feature(custom_inner_attributes)] -#![rustfmt::skip] - -#[macro_use] -extern crate proc_macro_attr; - -mod some_mod { - //! This doc comment should *NOT* produce a warning - - mod some_inner_mod { - fn some_noop() {} - } -} - -/// This should produce a warning - -fn with_doc_and_newline() { assert!(true)} - -// This should *NOT* produce a warning -#[crate_type = "lib"] - -/// some comment -fn with_one_newline_and_comment() { assert!(true) } - -// This should *NOT* produce a warning -#[crate_type = "lib"] -/// some comment -fn with_no_newline_and_comment() { assert!(true) } - - -// This should *NOT* produce a warning -#[crate_type = "lib"] - -fn with_one_newline() { assert!(true) } - -// This should *NOT* produce a warning -#[crate_type = "lib"] - - -fn with_two_newlines() { assert!(true) } - - -// This should *NOT* produce a warning -#[crate_type = "lib"] - -enum Baz { - One, - Two -} - -// This should *NOT* produce a warning -#[crate_type = "lib"] - -struct Foo { - one: isize, - two: isize -} - -// This should *NOT* produce a warning -#[crate_type = "lib"] - -mod foo { -} - -/// This doc comment should produce a warning - -/** This is also a doc comment and should produce a warning - */ - -// This should *NOT* produce a warning -#[allow(non_camel_case_types)] -#[allow(missing_docs)] -#[allow(missing_docs)] -fn three_attributes() { assert!(true) } - -// This should *NOT* produce a warning -#[doc = " -Returns the escaped value of the textual representation of - -"] -pub fn function() -> bool { - true -} - -// This should *NOT* produce a warning -#[derive(Clone, Copy)] -pub enum FooFighter { - Bar1, - - Bar2, - - Bar3, - - Bar4 -} - -// This should *NOT* produce a warning because the empty line is inside a block comment -#[crate_type = "lib"] -/* - -*/ -pub struct S; - -// This should *NOT* produce a warning -#[crate_type = "lib"] -/* test */ -pub struct T; - -// This should *NOT* produce a warning -// See https://github.com/rust-lang/rust-clippy/issues/5567 -#[fake_async_trait] -pub trait Bazz { - fn foo() -> Vec { - let _i = ""; - - - - vec![] - } -} - -#[derive(Clone, Copy)] -#[dummy(string = "first line - -second line -")] -pub struct Args; - -fn main() {} diff --git a/tests/ui/empty_line_after_doc_comments.stderr b/tests/ui/empty_line_after_doc_comments.stderr deleted file mode 100644 index 889ccf6ba19dd..0000000000000 --- a/tests/ui/empty_line_after_doc_comments.stderr +++ /dev/null @@ -1,37 +0,0 @@ -error: found an empty line after a doc comment. Perhaps you need to use `//!` to make a comment on a module, remove the empty line, or make a regular comment with `//`? - --> tests/ui/empty_line_after_doc_comments.rs:18:1 - | -LL | / /// This should produce a warning -LL | | -LL | | fn with_doc_and_newline() { assert!(true)} - | |_ - | - = note: `-D clippy::empty-line-after-doc-comments` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::empty_line_after_doc_comments)]` - -error: found an empty line after a doc comment. Perhaps you need to use `//!` to make a comment on a module, remove the empty line, or make a regular comment with `//`? - --> tests/ui/empty_line_after_doc_comments.rs:68:1 - | -LL | / /// This doc comment should produce a warning -LL | | -LL | | /** This is also a doc comment and should produce a warning -LL | | */ -... | -LL | | #[allow(missing_docs)] -LL | | fn three_attributes() { assert!(true) } - | |_ - -error: found an empty line after a doc comment. Perhaps you need to use `//!` to make a comment on a module, remove the empty line, or make a regular comment with `//`? - --> tests/ui/empty_line_after_doc_comments.rs:70:1 - | -LL | / /** This is also a doc comment and should produce a warning -LL | | */ -LL | | -LL | | // This should *NOT* produce a warning -... | -LL | | #[allow(missing_docs)] -LL | | fn three_attributes() { assert!(true) } - | |_ - -error: aborting due to 3 previous errors - diff --git a/tests/ui/empty_line_after_outer_attribute.rs b/tests/ui/empty_line_after_outer_attribute.rs deleted file mode 100644 index f147cf2cd5d1e..0000000000000 --- a/tests/ui/empty_line_after_outer_attribute.rs +++ /dev/null @@ -1,120 +0,0 @@ -//@aux-build:proc_macro_attr.rs -#![warn(clippy::empty_line_after_outer_attr)] -#![allow(clippy::assertions_on_constants, clippy::duplicated_attributes)] -#![feature(custom_inner_attributes)] -#![rustfmt::skip] - -#[macro_use] -extern crate proc_macro_attr; - -// This should produce a warning -#[crate_type = "lib"] - -/// some comment -fn with_one_newline_and_comment() { assert!(true) } - -// This should not produce a warning -#[crate_type = "lib"] -/// some comment -fn with_no_newline_and_comment() { assert!(true) } - - -// This should produce a warning -#[crate_type = "lib"] - -fn with_one_newline() { assert!(true) } - -// This should produce a warning, too -#[crate_type = "lib"] - - -fn with_two_newlines() { assert!(true) } - - -// This should produce a warning -#[crate_type = "lib"] - -enum Baz { - One, - Two -} - -// This should produce a warning -#[crate_type = "lib"] - -struct Foo { - one: isize, - two: isize -} - -// This should produce a warning -#[crate_type = "lib"] - -mod foo { -} - -/// This doc comment should not produce a warning - -/** This is also a doc comment and should not produce a warning - */ - -// This should not produce a warning -#[allow(non_camel_case_types)] -#[allow(missing_docs)] -#[allow(missing_docs)] -fn three_attributes() { assert!(true) } - -// This should not produce a warning -#[doc = " -Returns the escaped value of the textual representation of - -"] -pub fn function() -> bool { - true -} - -// This should not produce a warning -#[derive(Clone, Copy)] -pub enum FooFighter { - Bar1, - - Bar2, - - Bar3, - - Bar4 -} - -// This should not produce a warning because the empty line is inside a block comment -#[crate_type = "lib"] -/* - -*/ -pub struct S; - -// This should not produce a warning -#[crate_type = "lib"] -/* test */ -pub struct T; - -// This should not produce a warning -// See https://github.com/rust-lang/rust-clippy/issues/5567 -#[fake_async_trait] -pub trait Bazz { - fn foo() -> Vec { - let _i = ""; - - - - vec![] - } -} - -#[derive(Clone, Copy)] -#[dummy(string = "first line - -second line -")] -pub struct Args; - -fn main() {} diff --git a/tests/ui/empty_line_after_outer_attribute.stderr b/tests/ui/empty_line_after_outer_attribute.stderr deleted file mode 100644 index b43e6e30da220..0000000000000 --- a/tests/ui/empty_line_after_outer_attribute.stderr +++ /dev/null @@ -1,54 +0,0 @@ -error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> tests/ui/empty_line_after_outer_attribute.rs:11:1 - | -LL | / #[crate_type = "lib"] -LL | | -LL | | /// some comment -LL | | fn with_one_newline_and_comment() { assert!(true) } - | |_ - | - = note: `-D clippy::empty-line-after-outer-attr` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::empty_line_after_outer_attr)]` - -error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> tests/ui/empty_line_after_outer_attribute.rs:23:1 - | -LL | / #[crate_type = "lib"] -LL | | -LL | | fn with_one_newline() { assert!(true) } - | |_ - -error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> tests/ui/empty_line_after_outer_attribute.rs:28:1 - | -LL | / #[crate_type = "lib"] -... | -LL | | fn with_two_newlines() { assert!(true) } - | |_ - -error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> tests/ui/empty_line_after_outer_attribute.rs:35:1 - | -LL | / #[crate_type = "lib"] -LL | | -LL | | enum Baz { - | |_ - -error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> tests/ui/empty_line_after_outer_attribute.rs:43:1 - | -LL | / #[crate_type = "lib"] -LL | | -LL | | struct Foo { - | |_ - -error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> tests/ui/empty_line_after_outer_attribute.rs:51:1 - | -LL | / #[crate_type = "lib"] -LL | | -LL | | mod foo { - | |_ - -error: aborting due to 6 previous errors - diff --git a/tests/ui/empty_loop_no_std.rs b/tests/ui/empty_loop_no_std.rs index 5fe32351ed450..1bb895bda75d7 100644 --- a/tests/ui/empty_loop_no_std.rs +++ b/tests/ui/empty_loop_no_std.rs @@ -1,5 +1,5 @@ //@compile-flags: -Clink-arg=-nostartfiles -//@ignore-target-apple +//@ignore-target: apple #![warn(clippy::empty_loop)] #![feature(lang_items, start, libc)] diff --git a/tests/ui/enum_clike_unportable_variant.rs b/tests/ui/enum_clike_unportable_variant.rs index c50404c5047bb..37849179d6a0b 100644 --- a/tests/ui/enum_clike_unportable_variant.rs +++ b/tests/ui/enum_clike_unportable_variant.rs @@ -1,4 +1,4 @@ -//@ignore-32bit +//@ignore-bitwidth: 32 #![warn(clippy::enum_clike_unportable_variant)] #![allow(unused, non_upper_case_globals)] diff --git a/tests/ui/exit1.rs b/tests/ui/exit1.rs index a89f6dd4ca0eb..36b3c42fd9958 100644 --- a/tests/ui/exit1.rs +++ b/tests/ui/exit1.rs @@ -1,4 +1,4 @@ -#[warn(clippy::exit)] +#![warn(clippy::exit)] fn not_main() { if true { diff --git a/tests/ui/exit2.rs b/tests/ui/exit2.rs index d5ff93fb9ccb8..9bbb7b073a4b0 100644 --- a/tests/ui/exit2.rs +++ b/tests/ui/exit2.rs @@ -1,4 +1,4 @@ -#[warn(clippy::exit)] +#![warn(clippy::exit)] fn also_not_main() { std::process::exit(3); diff --git a/tests/ui/exit3.rs b/tests/ui/exit3.rs index 9dc0e1015a4f7..cab908aafd33d 100644 --- a/tests/ui/exit3.rs +++ b/tests/ui/exit3.rs @@ -1,4 +1,4 @@ -#[warn(clippy::exit)] +#![warn(clippy::exit)] fn main() { if true { diff --git a/tests/ui/expect_fun_call.fixed b/tests/ui/expect_fun_call.fixed index 6ac3c43ad2982..8f800c71941a1 100644 --- a/tests/ui/expect_fun_call.fixed +++ b/tests/ui/expect_fun_call.fixed @@ -5,8 +5,6 @@ clippy::unnecessary_literal_unwrap )] -/// Checks implementation of the `EXPECT_FUN_CALL` lint - macro_rules! one { () => { 1 diff --git a/tests/ui/expect_fun_call.rs b/tests/ui/expect_fun_call.rs index 22ea2db504fa4..b5cfafb2993e5 100644 --- a/tests/ui/expect_fun_call.rs +++ b/tests/ui/expect_fun_call.rs @@ -5,8 +5,6 @@ clippy::unnecessary_literal_unwrap )] -/// Checks implementation of the `EXPECT_FUN_CALL` lint - macro_rules! one { () => { 1 diff --git a/tests/ui/expect_fun_call.stderr b/tests/ui/expect_fun_call.stderr index b41904d04fa4c..bae853ac5c19a 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 - --> tests/ui/expect_fun_call.rs:37:26 + --> tests/ui/expect_fun_call.rs:35:26 | LL | with_none_and_format.expect(&format!("Error {}: fake error", error_code)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| panic!("Error {}: fake error", error_code))` @@ -8,85 +8,85 @@ LL | with_none_and_format.expect(&format!("Error {}: fake error", error_code = help: to override `-D warnings` add `#[allow(clippy::expect_fun_call)]` error: use of `expect` followed by a function call - --> tests/ui/expect_fun_call.rs:40:26 + --> tests/ui/expect_fun_call.rs:38:26 | LL | with_none_and_as_str.expect(format!("Error {}: fake error", error_code).as_str()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| panic!("Error {}: fake error", error_code))` error: use of `expect` followed by a function call - --> tests/ui/expect_fun_call.rs:43:37 + --> tests/ui/expect_fun_call.rs:41:37 | LL | with_none_and_format_with_macro.expect(format!("Error {}: fake error", one!()).as_str()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| panic!("Error {}: fake error", one!()))` error: use of `expect` followed by a function call - --> tests/ui/expect_fun_call.rs:53:25 + --> tests/ui/expect_fun_call.rs:51:25 | LL | with_err_and_format.expect(&format!("Error {}: fake error", error_code)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|_| panic!("Error {}: fake error", error_code))` error: use of `expect` followed by a function call - --> tests/ui/expect_fun_call.rs:56:25 + --> tests/ui/expect_fun_call.rs:54:25 | LL | with_err_and_as_str.expect(format!("Error {}: fake error", error_code).as_str()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|_| panic!("Error {}: fake error", error_code))` error: use of `expect` followed by a function call - --> tests/ui/expect_fun_call.rs:68:17 + --> tests/ui/expect_fun_call.rs:66:17 | LL | Some("foo").expect(format!("{} {}", 1, 2).as_ref()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| panic!("{} {}", 1, 2))` error: use of `expect` followed by a function call - --> tests/ui/expect_fun_call.rs:89:21 + --> tests/ui/expect_fun_call.rs:87:21 | LL | Some("foo").expect(&get_string()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| { panic!("{}", get_string()) })` error: use of `expect` followed by a function call - --> tests/ui/expect_fun_call.rs:90:21 + --> tests/ui/expect_fun_call.rs:88:21 | LL | Some("foo").expect(get_string().as_ref()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| { panic!("{}", get_string()) })` error: use of `expect` followed by a function call - --> tests/ui/expect_fun_call.rs:91:21 + --> tests/ui/expect_fun_call.rs:89:21 | LL | Some("foo").expect(get_string().as_str()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| { panic!("{}", get_string()) })` error: use of `expect` followed by a function call - --> tests/ui/expect_fun_call.rs:93:21 + --> tests/ui/expect_fun_call.rs:91:21 | LL | Some("foo").expect(get_static_str()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| { panic!("{}", get_static_str()) })` error: use of `expect` followed by a function call - --> tests/ui/expect_fun_call.rs:94:21 + --> tests/ui/expect_fun_call.rs:92:21 | LL | Some("foo").expect(get_non_static_str(&0)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| { panic!("{}", get_non_static_str(&0).to_string()) })` error: use of `expect` followed by a function call - --> tests/ui/expect_fun_call.rs:98:16 + --> tests/ui/expect_fun_call.rs:96:16 | LL | Some(true).expect(&format!("key {}, {}", 1, 2)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| panic!("key {}, {}", 1, 2))` error: use of `expect` followed by a function call - --> tests/ui/expect_fun_call.rs:104:17 + --> tests/ui/expect_fun_call.rs:102:17 | LL | opt_ref.expect(&format!("{:?}", opt_ref)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| panic!("{:?}", opt_ref))` error: use of `expect` followed by a function call - --> tests/ui/expect_fun_call.rs:108:20 + --> tests/ui/expect_fun_call.rs:106:20 | LL | format_capture.expect(&format!("{error_code}")); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| panic!("{error_code}"))` error: use of `expect` followed by a function call - --> tests/ui/expect_fun_call.rs:111:30 + --> tests/ui/expect_fun_call.rs:109:30 | LL | format_capture_and_value.expect(&format!("{error_code}, {}", 1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| panic!("{error_code}, {}", 1))` diff --git a/tests/ui/field_reassign_with_default.rs b/tests/ui/field_reassign_with_default.rs index 620cffb4f04da..2a432751952d0 100644 --- a/tests/ui/field_reassign_with_default.rs +++ b/tests/ui/field_reassign_with_default.rs @@ -192,8 +192,8 @@ struct WrapperMulti { } mod issue6312 { - use std::sync::atomic::AtomicBool; use std::sync::Arc; + use std::sync::atomic::AtomicBool; // do not lint: type implements `Drop` but not all fields are `Copy` #[derive(Clone, Default)] diff --git a/tests/ui/floating_point_arithmetic_nostd.rs b/tests/ui/floating_point_arithmetic_nostd.rs index 47c113d61c041..8ea75fae89b65 100644 --- a/tests/ui/floating_point_arithmetic_nostd.rs +++ b/tests/ui/floating_point_arithmetic_nostd.rs @@ -4,7 +4,7 @@ #![no_std] // The following should not lint, as the suggested methods `{f16,f32,f64,f128}.mul_add()` -// and ``{f16,f32,f64,f128}::abs()` are not available in no_std +// and `{f16,f32,f64,f128}::abs()` are not available in no_std pub fn mul_add() { let a: f64 = 1234.567; diff --git a/tests/ui/format_args.fixed b/tests/ui/format_args.fixed index 4d812f6bf1dce..20d1e3d905042 100644 --- a/tests/ui/format_args.fixed +++ b/tests/ui/format_args.fixed @@ -8,7 +8,7 @@ clippy::uninlined_format_args )] -use std::io::{stdout, Write}; +use std::io::{Write, stdout}; use std::ops::Deref; use std::panic::Location; diff --git a/tests/ui/format_args.rs b/tests/ui/format_args.rs index d242623feb62a..18ab223db78a1 100644 --- a/tests/ui/format_args.rs +++ b/tests/ui/format_args.rs @@ -8,7 +8,7 @@ clippy::uninlined_format_args )] -use std::io::{stdout, Write}; +use std::io::{Write, stdout}; use std::ops::Deref; use std::panic::Location; diff --git a/tests/ui/format_args_unfixable.rs b/tests/ui/format_args_unfixable.rs index b7492e38b256b..f04715f4f0182 100644 --- a/tests/ui/format_args_unfixable.rs +++ b/tests/ui/format_args_unfixable.rs @@ -2,7 +2,7 @@ #![allow(unused)] #![allow(clippy::assertions_on_constants, clippy::eq_op, clippy::uninlined_format_args)] -use std::io::{stdout, Error, ErrorKind, Write}; +use std::io::{Error, ErrorKind, Write, stdout}; use std::ops::Deref; use std::panic::Location; diff --git a/tests/ui/if_then_some_else_none.fixed b/tests/ui/if_then_some_else_none.fixed index ad13372a68b7e..1f47dddcbc40a 100644 --- a/tests/ui/if_then_some_else_none.fixed +++ b/tests/ui/if_then_some_else_none.fixed @@ -113,6 +113,10 @@ fn issue11394(b: bool, v: Result<(), ()>) -> Result<(), ()> { Ok(()) } +fn issue13407(s: &str) -> Option { + (s == "1").then(|| true) +} + const fn issue12103(x: u32) -> Option { // Should not issue an error in `const` context if x > 42 { Some(150) } else { None } diff --git a/tests/ui/if_then_some_else_none.rs b/tests/ui/if_then_some_else_none.rs index 73edbb7da2a87..499f008fb876d 100644 --- a/tests/ui/if_then_some_else_none.rs +++ b/tests/ui/if_then_some_else_none.rs @@ -131,6 +131,10 @@ fn issue11394(b: bool, v: Result<(), ()>) -> Result<(), ()> { Ok(()) } +fn issue13407(s: &str) -> Option { + if s == "1" { Some(true) } else { None } +} + const fn issue12103(x: u32) -> Option { // Should not issue an error in `const` context if x > 42 { Some(150) } else { None } diff --git a/tests/ui/if_then_some_else_none.stderr b/tests/ui/if_then_some_else_none.stderr index aed01e026cbe7..e7bc66b3ee881 100644 --- a/tests/ui/if_then_some_else_none.stderr +++ b/tests/ui/if_then_some_else_none.stderr @@ -52,5 +52,11 @@ LL | | None LL | | }; | |_____^ help: try: `foo().then(|| { println!("true!"); 150 })` -error: aborting due to 5 previous errors +error: this could be simplified with `bool::then` + --> tests/ui/if_then_some_else_none.rs:135:5 + | +LL | if s == "1" { Some(true) } else { None } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(s == "1").then(|| true)` + +error: aborting due to 6 previous errors diff --git a/tests/ui/ignored_unit_patterns.fixed b/tests/ui/ignored_unit_patterns.fixed index 118f0b4889529..fde4043730986 100644 --- a/tests/ui/ignored_unit_patterns.fixed +++ b/tests/ui/ignored_unit_patterns.fixed @@ -21,15 +21,12 @@ fn main() { let _ = foo().map_err(|()| todo!()); //~^ ERROR: matching over `()` is more explicit - println!( - "{:?}", - match foo() { - Ok(()) => {}, - //~^ ERROR: matching over `()` is more explicit - Err(()) => {}, - //~^ ERROR: matching over `()` is more explicit - } - ); + println!("{:?}", match foo() { + Ok(()) => {}, + //~^ ERROR: matching over `()` is more explicit + Err(()) => {}, + //~^ ERROR: matching over `()` is more explicit + }); } // ignored_unit_patterns in derive macro should be ok diff --git a/tests/ui/ignored_unit_patterns.rs b/tests/ui/ignored_unit_patterns.rs index 92feb9e6c2814..528844d76e051 100644 --- a/tests/ui/ignored_unit_patterns.rs +++ b/tests/ui/ignored_unit_patterns.rs @@ -21,15 +21,12 @@ fn main() { let _ = foo().map_err(|_| todo!()); //~^ ERROR: matching over `()` is more explicit - println!( - "{:?}", - match foo() { - Ok(_) => {}, - //~^ ERROR: matching over `()` is more explicit - Err(_) => {}, - //~^ ERROR: matching over `()` is more explicit - } - ); + println!("{:?}", match foo() { + Ok(_) => {}, + //~^ ERROR: matching over `()` is more explicit + Err(_) => {}, + //~^ ERROR: matching over `()` is more explicit + }); } // ignored_unit_patterns in derive macro should be ok diff --git a/tests/ui/ignored_unit_patterns.stderr b/tests/ui/ignored_unit_patterns.stderr index 00a254e39192c..54ff4454d6bdf 100644 --- a/tests/ui/ignored_unit_patterns.stderr +++ b/tests/ui/ignored_unit_patterns.stderr @@ -26,31 +26,31 @@ LL | let _ = foo().map_err(|_| todo!()); | ^ help: use `()` instead of `_`: `()` error: matching over `()` is more explicit - --> tests/ui/ignored_unit_patterns.rs:27:16 + --> tests/ui/ignored_unit_patterns.rs:25:12 | -LL | Ok(_) => {}, - | ^ help: use `()` instead of `_`: `()` +LL | Ok(_) => {}, + | ^ help: use `()` instead of `_`: `()` error: matching over `()` is more explicit - --> tests/ui/ignored_unit_patterns.rs:29:17 + --> tests/ui/ignored_unit_patterns.rs:27:13 | -LL | Err(_) => {}, - | ^ help: use `()` instead of `_`: `()` +LL | Err(_) => {}, + | ^ help: use `()` instead of `_`: `()` error: matching over `()` is more explicit - --> tests/ui/ignored_unit_patterns.rs:41:9 + --> tests/ui/ignored_unit_patterns.rs:38:9 | LL | let _ = foo().unwrap(); | ^ help: use `()` instead of `_`: `()` error: matching over `()` is more explicit - --> tests/ui/ignored_unit_patterns.rs:50:13 + --> tests/ui/ignored_unit_patterns.rs:47:13 | LL | (1, _) => unimplemented!(), | ^ help: use `()` instead of `_`: `()` error: matching over `()` is more explicit - --> tests/ui/ignored_unit_patterns.rs:57:13 + --> tests/ui/ignored_unit_patterns.rs:54:13 | LL | for (x, _) in v { | ^ help: use `()` instead of `_`: `()` diff --git a/tests/ui/implicit_saturating_sub.fixed b/tests/ui/implicit_saturating_sub.fixed index 27f679797dd9e..81cc149491442 100644 --- a/tests/ui/implicit_saturating_sub.fixed +++ b/tests/ui/implicit_saturating_sub.fixed @@ -184,18 +184,18 @@ fn main() { let mut m = Mock; let mut u_32 = 3000; let a = 200; - let mut _b = 8; + let mut b = 8; if m != 0 { m -= 1; } if a > 0 { - _b -= 1; + b -= 1; } if 0 > a { - _b -= 1; + b -= 1; } if u_32 > 0 { @@ -214,4 +214,11 @@ fn main() { } else if u_32 > 0 { u_32 -= 1; } + + let result = if a < b { + println!("we shouldn't remove this"); + 0 + } else { + a - b + }; } diff --git a/tests/ui/implicit_saturating_sub.rs b/tests/ui/implicit_saturating_sub.rs index 5d7b95d2c652f..f73396ebd27e2 100644 --- a/tests/ui/implicit_saturating_sub.rs +++ b/tests/ui/implicit_saturating_sub.rs @@ -230,18 +230,18 @@ fn main() { let mut m = Mock; let mut u_32 = 3000; let a = 200; - let mut _b = 8; + let mut b = 8; if m != 0 { m -= 1; } if a > 0 { - _b -= 1; + b -= 1; } if 0 > a { - _b -= 1; + b -= 1; } if u_32 > 0 { @@ -260,4 +260,11 @@ fn main() { } else if u_32 > 0 { u_32 -= 1; } + + let result = if a < b { + println!("we shouldn't remove this"); + 0 + } else { + a - b + }; } diff --git a/tests/ui/incompatible_msrv.rs b/tests/ui/incompatible_msrv.rs index 8ef1f554bc35d..c23df223d5088 100644 --- a/tests/ui/incompatible_msrv.rs +++ b/tests/ui/incompatible_msrv.rs @@ -2,8 +2,8 @@ #![feature(custom_inner_attributes)] #![clippy::msrv = "1.3.0"] -use std::collections::hash_map::Entry; use std::collections::HashMap; +use std::collections::hash_map::Entry; use std::future::Future; use std::thread::sleep; use std::time::Duration; diff --git a/tests/ui/legacy_numeric_constants.fixed b/tests/ui/legacy_numeric_constants.fixed index a6ef8f8c119a5..3a2294ef4c59b 100644 --- a/tests/ui/legacy_numeric_constants.fixed +++ b/tests/ui/legacy_numeric_constants.fixed @@ -22,8 +22,8 @@ macro_rules! b { }; } -use std::u32::MAX; use std::u8::MIN; +use std::u32::MAX; use std::{f64, u32}; #[warn(clippy::legacy_numeric_constants)] @@ -99,8 +99,8 @@ fn allow() { ::std::primitive::u8::MIN; ::std::u8::MIN; ::std::primitive::u8::min_value(); - use std::u64; use std::u8::MIN; + use std::u64; } #[warn(clippy::legacy_numeric_constants)] diff --git a/tests/ui/legacy_numeric_constants.rs b/tests/ui/legacy_numeric_constants.rs index cd633545372cd..6cb3e694ea145 100644 --- a/tests/ui/legacy_numeric_constants.rs +++ b/tests/ui/legacy_numeric_constants.rs @@ -22,8 +22,8 @@ macro_rules! b { }; } -use std::u32::MAX; use std::u8::MIN; +use std::u32::MAX; use std::{f64, u32}; #[warn(clippy::legacy_numeric_constants)] @@ -99,8 +99,8 @@ fn allow() { ::std::primitive::u8::MIN; ::std::u8::MIN; ::std::primitive::u8::min_value(); - use std::u64; use std::u8::MIN; + use std::u64; } #[warn(clippy::legacy_numeric_constants)] diff --git a/tests/ui/macro_use_imports.fixed b/tests/ui/macro_use_imports.fixed index 38ed5a957e73e..28844fab74793 100644 --- a/tests/ui/macro_use_imports.fixed +++ b/tests/ui/macro_use_imports.fixed @@ -2,7 +2,7 @@ //@aux-build:macro_use_helper.rs //@aux-build:proc_macro_derive.rs -//@ignore-32bit +//@ignore-bitwidth: 32 #![allow(unused_imports, unreachable_code, unused_variables, dead_code, unused_attributes)] #![allow(clippy::single_component_path_imports)] diff --git a/tests/ui/macro_use_imports.rs b/tests/ui/macro_use_imports.rs index ae6cc16ed2769..5381f29598987 100644 --- a/tests/ui/macro_use_imports.rs +++ b/tests/ui/macro_use_imports.rs @@ -2,7 +2,7 @@ //@aux-build:macro_use_helper.rs //@aux-build:proc_macro_derive.rs -//@ignore-32bit +//@ignore-bitwidth: 32 #![allow(unused_imports, unreachable_code, unused_variables, dead_code, unused_attributes)] #![allow(clippy::single_component_path_imports)] diff --git a/tests/ui/macro_use_imports_expect.rs b/tests/ui/macro_use_imports_expect.rs index df6d5b9fbabf2..60cce1d24a289 100644 --- a/tests/ui/macro_use_imports_expect.rs +++ b/tests/ui/macro_use_imports_expect.rs @@ -1,7 +1,7 @@ //@aux-build:macro_rules.rs //@aux-build:macro_use_helper.rs //@aux-build:proc_macro_derive.rs -//@ignore-32bit +//@ignore-bitwidth: 32 #![allow(unused_imports, unreachable_code, unused_variables, dead_code, unused_attributes)] #![allow(clippy::single_component_path_imports)] diff --git a/tests/ui/manual_arithmetic_check-2.rs b/tests/ui/manual_arithmetic_check-2.rs new file mode 100644 index 0000000000000..e97e3bdfef769 --- /dev/null +++ b/tests/ui/manual_arithmetic_check-2.rs @@ -0,0 +1,35 @@ +//@no-rustfix +#![warn(clippy::implicit_saturating_sub)] +#![allow(arithmetic_overflow)] + +fn main() { + let a = 12u32; + let b = 13u32; + + let result = if a > b { b - a } else { 0 }; + //~^ ERROR: inverted arithmetic check before subtraction + let result = if b < a { b - a } else { 0 }; + //~^ ERROR: inverted arithmetic check before subtraction + + let result = if a > b { 0 } else { a - b }; + //~^ ERROR: inverted arithmetic check before subtraction + let result = if a >= b { 0 } else { a - b }; + //~^ ERROR: inverted arithmetic check before subtraction + let result = if b < a { 0 } else { a - b }; + //~^ ERROR: inverted arithmetic check before subtraction + let result = if b <= a { 0 } else { a - b }; + //~^ ERROR: inverted arithmetic check before subtraction + + let af = 12f32; + let bf = 13f32; + // Should not lint! + let result = if bf < af { 0. } else { af - bf }; + + // Should not lint! + let result = if a < b { + println!("we shouldn't remove this"); + 0 + } else { + a - b + }; +} diff --git a/tests/ui/manual_arithmetic_check-2.stderr b/tests/ui/manual_arithmetic_check-2.stderr new file mode 100644 index 0000000000000..4121aa7464f05 --- /dev/null +++ b/tests/ui/manual_arithmetic_check-2.stderr @@ -0,0 +1,75 @@ +error: inverted arithmetic check before subtraction + --> tests/ui/manual_arithmetic_check-2.rs:9:23 + | +LL | let result = if a > b { b - a } else { 0 }; + | ^ ----- help: try replacing it with: `a - b` + | +note: this subtraction underflows when `b < a` + --> tests/ui/manual_arithmetic_check-2.rs:9:29 + | +LL | let result = if a > b { b - a } else { 0 }; + | ^^^^^ + = note: `#[deny(clippy::inverted_saturating_sub)]` on by default + +error: inverted arithmetic check before subtraction + --> tests/ui/manual_arithmetic_check-2.rs:11:23 + | +LL | let result = if b < a { b - a } else { 0 }; + | ^ ----- help: try replacing it with: `a - b` + | +note: this subtraction underflows when `b < a` + --> tests/ui/manual_arithmetic_check-2.rs:11:29 + | +LL | let result = if b < a { b - a } else { 0 }; + | ^^^^^ + +error: inverted arithmetic check before subtraction + --> tests/ui/manual_arithmetic_check-2.rs:14:23 + | +LL | let result = if a > b { 0 } else { a - b }; + | ^ ----- help: try replacing it with: `b - a` + | +note: this subtraction underflows when `a < b` + --> tests/ui/manual_arithmetic_check-2.rs:14:40 + | +LL | let result = if a > b { 0 } else { a - b }; + | ^^^^^ + +error: inverted arithmetic check before subtraction + --> tests/ui/manual_arithmetic_check-2.rs:16:23 + | +LL | let result = if a >= b { 0 } else { a - b }; + | ^^ ----- help: try replacing it with: `b - a` + | +note: this subtraction underflows when `a < b` + --> tests/ui/manual_arithmetic_check-2.rs:16:41 + | +LL | let result = if a >= b { 0 } else { a - b }; + | ^^^^^ + +error: inverted arithmetic check before subtraction + --> tests/ui/manual_arithmetic_check-2.rs:18:23 + | +LL | let result = if b < a { 0 } else { a - b }; + | ^ ----- help: try replacing it with: `b - a` + | +note: this subtraction underflows when `a < b` + --> tests/ui/manual_arithmetic_check-2.rs:18:40 + | +LL | let result = if b < a { 0 } else { a - b }; + | ^^^^^ + +error: inverted arithmetic check before subtraction + --> tests/ui/manual_arithmetic_check-2.rs:20:23 + | +LL | let result = if b <= a { 0 } else { a - b }; + | ^^ ----- help: try replacing it with: `b - a` + | +note: this subtraction underflows when `a < b` + --> tests/ui/manual_arithmetic_check-2.rs:20:41 + | +LL | let result = if b <= a { 0 } else { a - b }; + | ^^^^^ + +error: aborting due to 6 previous errors + diff --git a/tests/ui/manual_arithmetic_check.fixed b/tests/ui/manual_arithmetic_check.fixed new file mode 100644 index 0000000000000..29ecbb9ad2adb --- /dev/null +++ b/tests/ui/manual_arithmetic_check.fixed @@ -0,0 +1,24 @@ +#![warn(clippy::implicit_saturating_sub, clippy::inverted_saturating_sub)] +#![allow(clippy::if_same_then_else)] + +fn main() { + let a = 12u32; + let b = 13u32; + let c = 8u32; + + let result = a.saturating_sub(b); + //~^ ERROR: manual arithmetic check found + let result = a.saturating_sub(b); + //~^ ERROR: manual arithmetic check found + + let result = a.saturating_sub(b); + //~^ ERROR: manual arithmetic check found + let result = a.saturating_sub(b); + //~^ ERROR: manual arithmetic check found + + // Should not warn! + let result = if a > b { a - b } else { a - c }; + + // Just to check it won't break clippy. + let result = if b > a { 0 } else { 0 }; +} diff --git a/tests/ui/manual_arithmetic_check.rs b/tests/ui/manual_arithmetic_check.rs new file mode 100644 index 0000000000000..69554c6b61caa --- /dev/null +++ b/tests/ui/manual_arithmetic_check.rs @@ -0,0 +1,24 @@ +#![warn(clippy::implicit_saturating_sub, clippy::inverted_saturating_sub)] +#![allow(clippy::if_same_then_else)] + +fn main() { + let a = 12u32; + let b = 13u32; + let c = 8u32; + + let result = if a > b { a - b } else { 0 }; + //~^ ERROR: manual arithmetic check found + let result = if b < a { a - b } else { 0 }; + //~^ ERROR: manual arithmetic check found + + let result = if a < b { 0 } else { a - b }; + //~^ ERROR: manual arithmetic check found + let result = if b > a { 0 } else { a - b }; + //~^ ERROR: manual arithmetic check found + + // Should not warn! + let result = if a > b { a - b } else { a - c }; + + // Just to check it won't break clippy. + let result = if b > a { 0 } else { 0 }; +} diff --git a/tests/ui/manual_arithmetic_check.stderr b/tests/ui/manual_arithmetic_check.stderr new file mode 100644 index 0000000000000..b0cf73cd9151d --- /dev/null +++ b/tests/ui/manual_arithmetic_check.stderr @@ -0,0 +1,29 @@ +error: manual arithmetic check found + --> tests/ui/manual_arithmetic_check.rs:9:18 + | +LL | let result = if a > b { a - b } else { 0 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `a.saturating_sub(b)` + | + = note: `-D clippy::implicit-saturating-sub` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::implicit_saturating_sub)]` + +error: manual arithmetic check found + --> tests/ui/manual_arithmetic_check.rs:11:18 + | +LL | let result = if b < a { a - b } else { 0 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `a.saturating_sub(b)` + +error: manual arithmetic check found + --> tests/ui/manual_arithmetic_check.rs:14:18 + | +LL | let result = if a < b { 0 } else { a - b }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `a.saturating_sub(b)` + +error: manual arithmetic check found + --> tests/ui/manual_arithmetic_check.rs:16:18 + | +LL | let result = if b > a { 0 } else { a - b }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `a.saturating_sub(b)` + +error: aborting due to 4 previous errors + diff --git a/tests/ui/manual_div_ceil.fixed b/tests/ui/manual_div_ceil.fixed new file mode 100644 index 0000000000000..e7801f7376aaf --- /dev/null +++ b/tests/ui/manual_div_ceil.fixed @@ -0,0 +1,30 @@ +#![warn(clippy::manual_div_ceil)] + +fn main() { + let x = 7_u32; + let y = 4_u32; + let z = 11_u32; + + // Lint + let _ = x.div_ceil(y); //~ ERROR: manually reimplementing `div_ceil` + let _ = x.div_ceil(y); //~ ERROR: manually reimplementing `div_ceil` + let _ = x.div_ceil(y); //~ ERROR: manually reimplementing `div_ceil` + + let _ = 7_u32.div_ceil(4); //~ ERROR: manually reimplementing `div_ceil` + let _ = (7_i32 as u32).div_ceil(4); //~ ERROR: manually reimplementing `div_ceil` + + // No lint + let _ = (x + (y - 2)) / y; + let _ = (x + (y + 1)) / y; + + let _ = (x + (y - 1)) / z; + + let x_i = 7_i32; + let y_i = 4_i32; + let z_i = 11_i32; + + // No lint because `int_roundings` feature is not enabled. + let _ = (z as i32 + (y_i - 1)) / y_i; + let _ = (7_u32 as i32 + (y_i - 1)) / y_i; + let _ = (7_u32 as i32 + (4 - 1)) / 4; +} diff --git a/tests/ui/manual_div_ceil.rs b/tests/ui/manual_div_ceil.rs new file mode 100644 index 0000000000000..2de74c7eaa881 --- /dev/null +++ b/tests/ui/manual_div_ceil.rs @@ -0,0 +1,30 @@ +#![warn(clippy::manual_div_ceil)] + +fn main() { + let x = 7_u32; + let y = 4_u32; + let z = 11_u32; + + // Lint + let _ = (x + (y - 1)) / y; //~ ERROR: manually reimplementing `div_ceil` + let _ = ((y - 1) + x) / y; //~ ERROR: manually reimplementing `div_ceil` + let _ = (x + y - 1) / y; //~ ERROR: manually reimplementing `div_ceil` + + let _ = (7_u32 + (4 - 1)) / 4; //~ ERROR: manually reimplementing `div_ceil` + let _ = (7_i32 as u32 + (4 - 1)) / 4; //~ ERROR: manually reimplementing `div_ceil` + + // No lint + let _ = (x + (y - 2)) / y; + let _ = (x + (y + 1)) / y; + + let _ = (x + (y - 1)) / z; + + let x_i = 7_i32; + let y_i = 4_i32; + let z_i = 11_i32; + + // No lint because `int_roundings` feature is not enabled. + let _ = (z as i32 + (y_i - 1)) / y_i; + let _ = (7_u32 as i32 + (y_i - 1)) / y_i; + let _ = (7_u32 as i32 + (4 - 1)) / 4; +} diff --git a/tests/ui/manual_div_ceil.stderr b/tests/ui/manual_div_ceil.stderr new file mode 100644 index 0000000000000..dc652dff405f2 --- /dev/null +++ b/tests/ui/manual_div_ceil.stderr @@ -0,0 +1,35 @@ +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:9:13 + | +LL | let _ = (x + (y - 1)) / y; + | ^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(y)` + | + = note: `-D clippy::manual-div-ceil` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::manual_div_ceil)]` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:10:13 + | +LL | let _ = ((y - 1) + x) / y; + | ^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(y)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:11:13 + | +LL | let _ = (x + y - 1) / y; + | ^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(y)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:13:13 + | +LL | let _ = (7_u32 + (4 - 1)) / 4; + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `7_u32.div_ceil(4)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:14:13 + | +LL | let _ = (7_i32 as u32 + (4 - 1)) / 4; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `(7_i32 as u32).div_ceil(4)` + +error: aborting due to 5 previous errors + diff --git a/tests/ui/manual_div_ceil_with_feature.fixed b/tests/ui/manual_div_ceil_with_feature.fixed new file mode 100644 index 0000000000000..a1d678c66898c --- /dev/null +++ b/tests/ui/manual_div_ceil_with_feature.fixed @@ -0,0 +1,25 @@ +#![warn(clippy::manual_div_ceil)] +#![feature(int_roundings)] + +fn main() { + let x = 7_i32; + let y = 4_i32; + let z = 3_i32; + let z_u: u32 = 11; + + // Lint. + let _ = x.div_ceil(y); + let _ = x.div_ceil(y); + let _ = x.div_ceil(y); + + let _ = 7_i32.div_ceil(4); + let _ = (7_i32 as u32).div_ceil(4); + let _ = (7_u32 as i32).div_ceil(4); + let _ = z_u.div_ceil(4); + + // No lint. + let _ = (x + (y - 2)) / y; + let _ = (x + (y + 1)) / y; + + let _ = (x + (y - 1)) / z; +} diff --git a/tests/ui/manual_div_ceil_with_feature.rs b/tests/ui/manual_div_ceil_with_feature.rs new file mode 100644 index 0000000000000..58cb1dbe34d1c --- /dev/null +++ b/tests/ui/manual_div_ceil_with_feature.rs @@ -0,0 +1,25 @@ +#![warn(clippy::manual_div_ceil)] +#![feature(int_roundings)] + +fn main() { + let x = 7_i32; + let y = 4_i32; + let z = 3_i32; + let z_u: u32 = 11; + + // Lint. + let _ = (x + (y - 1)) / y; + let _ = ((y - 1) + x) / y; + let _ = (x + y - 1) / y; + + let _ = (7_i32 + (4 - 1)) / 4; + let _ = (7_i32 as u32 + (4 - 1)) / 4; + let _ = (7_u32 as i32 + (4 - 1)) / 4; + let _ = (z_u + (4 - 1)) / 4; + + // No lint. + let _ = (x + (y - 2)) / y; + let _ = (x + (y + 1)) / y; + + let _ = (x + (y - 1)) / z; +} diff --git a/tests/ui/manual_div_ceil_with_feature.stderr b/tests/ui/manual_div_ceil_with_feature.stderr new file mode 100644 index 0000000000000..361ef9bd9f42a --- /dev/null +++ b/tests/ui/manual_div_ceil_with_feature.stderr @@ -0,0 +1,47 @@ +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:11:13 + | +LL | let _ = (x + (y - 1)) / y; + | ^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(y)` + | + = note: `-D clippy::manual-div-ceil` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::manual_div_ceil)]` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:12:13 + | +LL | let _ = ((y - 1) + x) / y; + | ^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(y)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:13:13 + | +LL | let _ = (x + y - 1) / y; + | ^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(y)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:15:13 + | +LL | let _ = (7_i32 + (4 - 1)) / 4; + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `7_i32.div_ceil(4)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:16:13 + | +LL | let _ = (7_i32 as u32 + (4 - 1)) / 4; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `(7_i32 as u32).div_ceil(4)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:17:13 + | +LL | let _ = (7_u32 as i32 + (4 - 1)) / 4; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `(7_u32 as i32).div_ceil(4)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:18:13 + | +LL | let _ = (z_u + (4 - 1)) / 4; + | ^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `z_u.div_ceil(4)` + +error: aborting due to 7 previous errors + diff --git a/tests/ui/manual_is_power_of_two.fixed b/tests/ui/manual_is_power_of_two.fixed new file mode 100644 index 0000000000000..dda5fe0ec3e77 --- /dev/null +++ b/tests/ui/manual_is_power_of_two.fixed @@ -0,0 +1,20 @@ +#![warn(clippy::manual_is_power_of_two)] + +fn main() { + let a = 16_u64; + + let _ = a.is_power_of_two(); + let _ = a.is_power_of_two(); + + // Test different orders of expression + let _ = a.is_power_of_two(); + let _ = a.is_power_of_two(); + let _ = a.is_power_of_two(); + let _ = a.is_power_of_two(); + + let b = 4_i64; + + // is_power_of_two only works for unsigned integers + let _ = b.count_ones() == 1; + let _ = b & (b - 1) == 0; +} diff --git a/tests/ui/manual_is_power_of_two.rs b/tests/ui/manual_is_power_of_two.rs new file mode 100644 index 0000000000000..a1d3a95c4102b --- /dev/null +++ b/tests/ui/manual_is_power_of_two.rs @@ -0,0 +1,20 @@ +#![warn(clippy::manual_is_power_of_two)] + +fn main() { + let a = 16_u64; + + let _ = a.count_ones() == 1; + let _ = a & (a - 1) == 0; + + // Test different orders of expression + let _ = 1 == a.count_ones(); + let _ = (a - 1) & a == 0; + let _ = 0 == a & (a - 1); + let _ = 0 == (a - 1) & a; + + let b = 4_i64; + + // is_power_of_two only works for unsigned integers + let _ = b.count_ones() == 1; + let _ = b & (b - 1) == 0; +} diff --git a/tests/ui/manual_is_power_of_two.stderr b/tests/ui/manual_is_power_of_two.stderr new file mode 100644 index 0000000000000..3cfc6297abf27 --- /dev/null +++ b/tests/ui/manual_is_power_of_two.stderr @@ -0,0 +1,41 @@ +error: manually reimplementing `is_power_of_two` + --> tests/ui/manual_is_power_of_two.rs:6:13 + | +LL | let _ = a.count_ones() == 1; + | ^^^^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `a.is_power_of_two()` + | + = note: `-D clippy::manual-is-power-of-two` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::manual_is_power_of_two)]` + +error: manually reimplementing `is_power_of_two` + --> tests/ui/manual_is_power_of_two.rs:7:13 + | +LL | let _ = a & (a - 1) == 0; + | ^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `a.is_power_of_two()` + +error: manually reimplementing `is_power_of_two` + --> tests/ui/manual_is_power_of_two.rs:10:13 + | +LL | let _ = 1 == a.count_ones(); + | ^^^^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `a.is_power_of_two()` + +error: manually reimplementing `is_power_of_two` + --> tests/ui/manual_is_power_of_two.rs:11:13 + | +LL | let _ = (a - 1) & a == 0; + | ^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `a.is_power_of_two()` + +error: manually reimplementing `is_power_of_two` + --> tests/ui/manual_is_power_of_two.rs:12:13 + | +LL | let _ = 0 == a & (a - 1); + | ^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `a.is_power_of_two()` + +error: manually reimplementing `is_power_of_two` + --> tests/ui/manual_is_power_of_two.rs:13:13 + | +LL | let _ = 0 == (a - 1) & a; + | ^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `a.is_power_of_two()` + +error: aborting due to 6 previous errors + diff --git a/tests/ui/manual_non_exhaustive_enum.rs b/tests/ui/manual_non_exhaustive_enum.rs index 31c3cc8013722..ffe2bb9246737 100644 --- a/tests/ui/manual_non_exhaustive_enum.rs +++ b/tests/ui/manual_non_exhaustive_enum.rs @@ -1,8 +1,8 @@ #![warn(clippy::manual_non_exhaustive)] #![allow(unused)] //@no-rustfix -enum E { - //~^ ERROR: this seems like a manual implementation of the non-exhaustive pattern +pub enum E { + //~^ manual_non_exhaustive A, B, #[doc(hidden)] @@ -11,7 +11,7 @@ enum E { // if the user explicitly marks as nonexhaustive we shouldn't warn them #[non_exhaustive] -enum Ep { +pub enum Ep { A, B, #[doc(hidden)] @@ -19,14 +19,14 @@ enum Ep { } // marker variant does not have doc hidden attribute, should be ignored -enum NoDocHidden { +pub enum NoDocHidden { A, B, _C, } // name of variant with doc hidden does not start with underscore -enum NoUnderscore { +pub enum NoUnderscore { A, B, #[doc(hidden)] @@ -34,7 +34,7 @@ enum NoUnderscore { } // variant with doc hidden is not unit, should be ignored -enum NotUnit { +pub enum NotUnit { A, B, #[doc(hidden)] @@ -42,13 +42,13 @@ enum NotUnit { } // variant with doc hidden is the only one, should be ignored -enum OnlyMarker { +pub enum OnlyMarker { #[doc(hidden)] _A, } // variant with multiple markers, should be ignored -enum MultipleMarkers { +pub enum MultipleMarkers { A, #[doc(hidden)] _B, @@ -58,13 +58,13 @@ enum MultipleMarkers { // already non_exhaustive and no markers, should be ignored #[non_exhaustive] -enum NonExhaustive { +pub enum NonExhaustive { A, B, } // marked is used, don't lint -enum UsedHidden { +pub enum UsedHidden { #[doc(hidden)] _A, B, @@ -77,11 +77,9 @@ fn foo(x: &mut UsedHidden) { } #[expect(clippy::manual_non_exhaustive)] -enum ExpectLint { +pub enum ExpectLint { A, B, #[doc(hidden)] _C, } - -fn main() {} diff --git a/tests/ui/manual_non_exhaustive_enum.stderr b/tests/ui/manual_non_exhaustive_enum.stderr index dc669568dd2d4..0a9ac157f8502 100644 --- a/tests/ui/manual_non_exhaustive_enum.stderr +++ b/tests/ui/manual_non_exhaustive_enum.stderr @@ -1,11 +1,7 @@ error: this seems like a manual implementation of the non-exhaustive pattern --> tests/ui/manual_non_exhaustive_enum.rs:4:1 | -LL | enum E { - | ^----- - | | - | _help: add the attribute: `#[non_exhaustive] enum E` - | | +LL | / pub enum E { LL | | LL | | A, LL | | B, @@ -21,15 +17,16 @@ LL | _C, | ^^ = note: `-D clippy::manual-non-exhaustive` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::manual_non_exhaustive)]` +help: use the `#[non_exhaustive]` attribute instead + | +LL + #[non_exhaustive] +LL | pub enum E { + | error: this seems like a manual implementation of the non-exhaustive pattern --> tests/ui/manual_non_exhaustive_enum.rs:29:1 | -LL | enum NoUnderscore { - | ^---------------- - | | - | _help: add the attribute: `#[non_exhaustive] enum NoUnderscore` - | | +LL | / pub enum NoUnderscore { LL | | A, LL | | B, LL | | #[doc(hidden)] @@ -42,6 +39,11 @@ help: remove this variant | LL | C, | ^ +help: use the `#[non_exhaustive]` attribute instead + | +LL + #[non_exhaustive] +LL | pub enum NoUnderscore { + | error: aborting due to 2 previous errors diff --git a/tests/ui/manual_non_exhaustive_struct.rs b/tests/ui/manual_non_exhaustive_struct.rs index 4b2803ccc4a79..31dd50281fa1d 100644 --- a/tests/ui/manual_non_exhaustive_struct.rs +++ b/tests/ui/manual_non_exhaustive_struct.rs @@ -1,9 +1,9 @@ #![warn(clippy::manual_non_exhaustive)] #![allow(unused)] //@no-rustfix -mod structs { - struct S { - //~^ ERROR: this seems like a manual implementation of the non-exhaustive pattern +pub mod structs { + pub struct S { + //~^ manual_non_exhaustive pub a: i32, pub b: i32, _c: (), @@ -11,68 +11,76 @@ mod structs { // user forgot to remove the private field #[non_exhaustive] - struct Sp { - //~^ ERROR: this seems like a manual implementation of the non-exhaustive pattern + pub struct Sp { + //~^ manual_non_exhaustive pub a: i32, pub b: i32, _c: (), } // some other fields are private, should be ignored - struct PrivateFields { + pub struct PrivateFields { a: i32, pub b: i32, _c: (), } - // private field name does not start with underscore, should be ignored - struct NoUnderscore { + pub struct NoUnderscore { + //~^ manual_non_exhaustive pub a: i32, pub b: i32, c: (), } // private field is not unit type, should be ignored - struct NotUnit { + pub struct NotUnit { pub a: i32, pub b: i32, _c: i32, } // private field is the only field, should be ignored - struct OnlyMarker { + pub struct OnlyMarker { _a: (), } // already non exhaustive and no private fields, should be ignored #[non_exhaustive] - struct NonExhaustive { + pub struct NonExhaustive { pub a: i32, pub b: i32, } } -mod tuple_structs { - struct T(pub i32, pub i32, ()); - //~^ ERROR: this seems like a manual implementation of the non-exhaustive pattern +pub mod tuple_structs { + pub struct T(pub i32, pub i32, ()); + //~^ manual_non_exhaustive // user forgot to remove the private field #[non_exhaustive] - struct Tp(pub i32, pub i32, ()); - //~^ ERROR: this seems like a manual implementation of the non-exhaustive pattern + pub struct Tp(pub i32, pub i32, ()); + //~^ manual_non_exhaustive // some other fields are private, should be ignored - struct PrivateFields(pub i32, i32, ()); + pub struct PrivateFields(pub i32, i32, ()); // private field is not unit type, should be ignored - struct NotUnit(pub i32, pub i32, i32); + pub struct NotUnit(pub i32, pub i32, i32); // private field is the only field, should be ignored - struct OnlyMarker(()); + pub struct OnlyMarker(()); // already non exhaustive and no private fields, should be ignored #[non_exhaustive] - struct NonExhaustive(pub i32, pub i32); + pub struct NonExhaustive(pub i32, pub i32); } -fn main() {} +mod private { + // Don't lint structs that are not actually public as `#[non_exhaustive]` only applies to + // external crates. The manual pattern can still be used to get module local non exhaustiveness + pub struct NotPublic { + pub a: i32, + pub b: i32, + _c: (), + } +} diff --git a/tests/ui/manual_non_exhaustive_struct.stderr b/tests/ui/manual_non_exhaustive_struct.stderr index 1cab812988a34..81de1e4f27190 100644 --- a/tests/ui/manual_non_exhaustive_struct.stderr +++ b/tests/ui/manual_non_exhaustive_struct.stderr @@ -1,11 +1,7 @@ error: this seems like a manual implementation of the non-exhaustive pattern --> tests/ui/manual_non_exhaustive_struct.rs:5:5 | -LL | struct S { - | ^------- - | | - | _____help: add the attribute: `#[non_exhaustive] struct S` - | | +LL | / pub struct S { LL | | LL | | pub a: i32, LL | | pub b: i32, @@ -20,11 +16,16 @@ LL | _c: (), | ^^^^^^ = note: `-D clippy::manual-non-exhaustive` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::manual_non_exhaustive)]` +help: use the `#[non_exhaustive]` attribute instead + | +LL ~ #[non_exhaustive] +LL ~ pub struct S { + | error: this seems like a manual implementation of the non-exhaustive pattern --> tests/ui/manual_non_exhaustive_struct.rs:14:5 | -LL | / struct Sp { +LL | / pub struct Sp { LL | | LL | | pub a: i32, LL | | pub b: i32, @@ -32,6 +33,11 @@ LL | | _c: (), LL | | } | |_____^ | +note: the struct is already non-exhaustive + --> tests/ui/manual_non_exhaustive_struct.rs:13:5 + | +LL | #[non_exhaustive] + | ^^^^^^^^^^^^^^^^^ help: remove this field --> tests/ui/manual_non_exhaustive_struct.rs:18:9 | @@ -39,13 +45,10 @@ LL | _c: (), | ^^^^^^ error: this seems like a manual implementation of the non-exhaustive pattern - --> tests/ui/manual_non_exhaustive_struct.rs:29:5 + --> tests/ui/manual_non_exhaustive_struct.rs:28:5 | -LL | struct NoUnderscore { - | ^------------------ - | | - | _____help: add the attribute: `#[non_exhaustive] struct NoUnderscore` - | | +LL | / pub struct NoUnderscore { +LL | | LL | | pub a: i32, LL | | pub b: i32, LL | | c: (), @@ -57,32 +60,45 @@ help: remove this field | LL | c: (), | ^^^^^ +help: use the `#[non_exhaustive]` attribute instead + | +LL ~ #[non_exhaustive] +LL ~ pub struct NoUnderscore { + | error: this seems like a manual implementation of the non-exhaustive pattern --> tests/ui/manual_non_exhaustive_struct.rs:56:5 | -LL | struct T(pub i32, pub i32, ()); - | --------^^^^^^^^^^^^^^^^^^^^^^^ - | | - | help: add the attribute: `#[non_exhaustive] struct T` +LL | pub struct T(pub i32, pub i32, ()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove this field - --> tests/ui/manual_non_exhaustive_struct.rs:56:32 + --> tests/ui/manual_non_exhaustive_struct.rs:56:36 + | +LL | pub struct T(pub i32, pub i32, ()); + | ^^ +help: use the `#[non_exhaustive]` attribute instead + | +LL ~ #[non_exhaustive] +LL ~ pub struct T(pub i32, pub i32, ()); | -LL | struct T(pub i32, pub i32, ()); - | ^^ error: this seems like a manual implementation of the non-exhaustive pattern --> tests/ui/manual_non_exhaustive_struct.rs:61:5 | -LL | struct Tp(pub i32, pub i32, ()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | pub struct Tp(pub i32, pub i32, ()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the struct is already non-exhaustive + --> tests/ui/manual_non_exhaustive_struct.rs:60:5 | +LL | #[non_exhaustive] + | ^^^^^^^^^^^^^^^^^ help: remove this field - --> tests/ui/manual_non_exhaustive_struct.rs:61:33 + --> tests/ui/manual_non_exhaustive_struct.rs:61:37 | -LL | struct Tp(pub i32, pub i32, ()); - | ^^ +LL | pub struct Tp(pub i32, pub i32, ()); + | ^^ error: aborting due to 5 previous errors diff --git a/tests/ui/manual_range_patterns.fixed b/tests/ui/manual_range_patterns.fixed index f1b99637afdba..60467bf9e8848 100644 --- a/tests/ui/manual_range_patterns.fixed +++ b/tests/ui/manual_range_patterns.fixed @@ -45,4 +45,12 @@ fn main() { }; } mac!(f); + + #[rustfmt::skip] + let _ = match f { + | 2..=15 => 4, + | 241..=254 => 5, + | 255 => 6, + | _ => 7, + }; } diff --git a/tests/ui/manual_range_patterns.rs b/tests/ui/manual_range_patterns.rs index 869ffbe80b973..9cd803334499d 100644 --- a/tests/ui/manual_range_patterns.rs +++ b/tests/ui/manual_range_patterns.rs @@ -45,4 +45,12 @@ fn main() { }; } mac!(f); + + #[rustfmt::skip] + let _ = match f { + | 2..=15 => 4, + | 241..=254 => 5, + | 255 => 6, + | _ => 7, + }; } diff --git a/tests/ui/manual_saturating_arithmetic.fixed b/tests/ui/manual_saturating_arithmetic.fixed index 41a32df32ee40..528a65790c4c8 100644 --- a/tests/ui/manual_saturating_arithmetic.fixed +++ b/tests/ui/manual_saturating_arithmetic.fixed @@ -1,6 +1,6 @@ #![allow(clippy::legacy_numeric_constants, unused_imports)] -use std::{i128, i32, u128, u32}; +use std::{i32, i128, u32, u128}; fn main() { let _ = 1u32.saturating_add(1); diff --git a/tests/ui/manual_saturating_arithmetic.rs b/tests/ui/manual_saturating_arithmetic.rs index 3a6b32d806904..55f3720dfcc06 100644 --- a/tests/ui/manual_saturating_arithmetic.rs +++ b/tests/ui/manual_saturating_arithmetic.rs @@ -1,6 +1,6 @@ #![allow(clippy::legacy_numeric_constants, unused_imports)] -use std::{i128, i32, u128, u32}; +use std::{i32, i128, u32, u128}; fn main() { let _ = 1u32.checked_add(1).unwrap_or(u32::max_value()); diff --git a/tests/ui/match_overlapping_arm.rs b/tests/ui/match_overlapping_arm.rs index 4457ae73da2f0..a056fdeaa5d0c 100644 --- a/tests/ui/match_overlapping_arm.rs +++ b/tests/ui/match_overlapping_arm.rs @@ -2,8 +2,6 @@ #![allow(clippy::redundant_pattern_matching)] #![allow(clippy::if_same_then_else, clippy::equatable_if_let, clippy::needless_if)] -/// Tests for match_overlapping_arm - fn overlapping() { const FOO: u64 = 2; diff --git a/tests/ui/match_overlapping_arm.stderr b/tests/ui/match_overlapping_arm.stderr index 65092ffbb5558..a60a09a079907 100644 --- a/tests/ui/match_overlapping_arm.stderr +++ b/tests/ui/match_overlapping_arm.stderr @@ -1,11 +1,11 @@ error: some ranges overlap - --> tests/ui/match_overlapping_arm.rs:11:9 + --> tests/ui/match_overlapping_arm.rs:9:9 | LL | 0..=10 => println!("0..=10"), | ^^^^^^ | note: overlaps with this - --> tests/ui/match_overlapping_arm.rs:13:9 + --> tests/ui/match_overlapping_arm.rs:11:9 | LL | 0..=11 => println!("0..=11"), | ^^^^^^ @@ -13,85 +13,85 @@ LL | 0..=11 => println!("0..=11"), = help: to override `-D warnings` add `#[allow(clippy::match_overlapping_arm)]` error: some ranges overlap - --> tests/ui/match_overlapping_arm.rs:18:9 + --> tests/ui/match_overlapping_arm.rs:16:9 | LL | 0..=5 => println!("0..=5"), | ^^^^^ | note: overlaps with this - --> tests/ui/match_overlapping_arm.rs:21:9 + --> tests/ui/match_overlapping_arm.rs:19:9 | LL | FOO..=11 => println!("FOO..=11"), | ^^^^^^^^ error: some ranges overlap - --> tests/ui/match_overlapping_arm.rs:56:9 + --> tests/ui/match_overlapping_arm.rs:54:9 | LL | 0..11 => println!("0..11"), | ^^^^^ | note: overlaps with this - --> tests/ui/match_overlapping_arm.rs:58:9 + --> tests/ui/match_overlapping_arm.rs:56:9 | LL | 0..=11 => println!("0..=11"), | ^^^^^^ error: some ranges overlap - --> tests/ui/match_overlapping_arm.rs:82:9 + --> tests/ui/match_overlapping_arm.rs:80:9 | LL | 0..=10 => println!("0..=10"), | ^^^^^^ | note: overlaps with this - --> tests/ui/match_overlapping_arm.rs:81:9 + --> tests/ui/match_overlapping_arm.rs:79:9 | LL | 5..14 => println!("5..14"), | ^^^^^ error: some ranges overlap - --> tests/ui/match_overlapping_arm.rs:88:9 + --> tests/ui/match_overlapping_arm.rs:86:9 | LL | 0..7 => println!("0..7"), | ^^^^ | note: overlaps with this - --> tests/ui/match_overlapping_arm.rs:90:9 + --> tests/ui/match_overlapping_arm.rs:88:9 | LL | 0..=10 => println!("0..=10"), | ^^^^^^ error: some ranges overlap - --> tests/ui/match_overlapping_arm.rs:101:9 + --> tests/ui/match_overlapping_arm.rs:99:9 | LL | ..=23 => println!("..=23"), | ^^^^^ | note: overlaps with this - --> tests/ui/match_overlapping_arm.rs:103:9 + --> tests/ui/match_overlapping_arm.rs:101:9 | LL | ..26 => println!("..26"), | ^^^^ error: some ranges overlap - --> tests/ui/match_overlapping_arm.rs:111:9 + --> tests/ui/match_overlapping_arm.rs:109:9 | LL | 21..=30 => (), | ^^^^^^^ | note: overlaps with this - --> tests/ui/match_overlapping_arm.rs:113:9 + --> tests/ui/match_overlapping_arm.rs:111:9 | LL | 21..=40 => (), | ^^^^^^^ error: some ranges overlap - --> tests/ui/match_overlapping_arm.rs:126:9 + --> tests/ui/match_overlapping_arm.rs:124:9 | LL | 0..=0x0000_0000_0000_00ff => (), | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: overlaps with this - --> tests/ui/match_overlapping_arm.rs:128:9 + --> tests/ui/match_overlapping_arm.rs:126:9 | LL | 0..=0x0000_0000_0000_ffff => (), | ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/mixed_attributes_style.rs b/tests/ui/mixed_attributes_style.rs index 1a646c2652236..93ab9392cf8bd 100644 --- a/tests/ui/mixed_attributes_style.rs +++ b/tests/ui/mixed_attributes_style.rs @@ -82,7 +82,8 @@ mod issue_12530 { #![allow(dead_code)] } } - /// Nested mod //~ ERROR: item has both inner and outer attributes + /// Nested mod + //~^ ERROR: item has both inner and outer attributes #[allow(unused)] mod nest_mod_2 { #![allow(unused)] diff --git a/tests/ui/mixed_attributes_style.stderr b/tests/ui/mixed_attributes_style.stderr index a1d3fc430f6cf..abdc2df23cf12 100644 --- a/tests/ui/mixed_attributes_style.stderr +++ b/tests/ui/mixed_attributes_style.stderr @@ -46,13 +46,14 @@ error: item has both inner and outer attributes --> tests/ui/mixed_attributes_style.rs:85:5 | LL | / /// Nested mod +LL | | LL | | #[allow(unused)] LL | | mod nest_mod_2 { LL | | #![allow(unused)] | |_________________________^ error: item has both inner and outer attributes - --> tests/ui/mixed_attributes_style.rs:90:9 + --> tests/ui/mixed_attributes_style.rs:91:9 | LL | / #[allow(dead_code)] LL | | mod inner_mod { diff --git a/tests/ui/must_use_candidates.fixed b/tests/ui/must_use_candidates.fixed index adb266ffbc8dd..2459af6068845 100644 --- a/tests/ui/must_use_candidates.fixed +++ b/tests/ui/must_use_candidates.fixed @@ -7,8 +7,8 @@ )] #![warn(clippy::must_use_candidate)] use std::rc::Rc; -use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; +use std::sync::atomic::{AtomicBool, Ordering}; pub struct MyAtomic(AtomicBool); pub struct MyPure; diff --git a/tests/ui/must_use_candidates.rs b/tests/ui/must_use_candidates.rs index 49bb16af788f1..5f9b2449552a9 100644 --- a/tests/ui/must_use_candidates.rs +++ b/tests/ui/must_use_candidates.rs @@ -7,8 +7,8 @@ )] #![warn(clippy::must_use_candidate)] use std::rc::Rc; -use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; +use std::sync::atomic::{AtomicBool, Ordering}; pub struct MyAtomic(AtomicBool); pub struct MyPure; diff --git a/tests/ui/mut_key.rs b/tests/ui/mut_key.rs index 81d8732b3b210..43ff705c47750 100644 --- a/tests/ui/mut_key.rs +++ b/tests/ui/mut_key.rs @@ -2,9 +2,9 @@ use std::cell::Cell; use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; use std::hash::{Hash, Hasher}; use std::rc::Rc; +use std::sync::Arc; use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering::Relaxed; -use std::sync::Arc; struct Key(AtomicUsize); diff --git a/tests/ui/needless_pass_by_value.rs b/tests/ui/needless_pass_by_value.rs index 14cba5a7eb520..9408b8c948fed 100644 --- a/tests/ui/needless_pass_by_value.rs +++ b/tests/ui/needless_pass_by_value.rs @@ -84,7 +84,7 @@ trait Serialize {} impl<'a, T> Serialize for &'a T where T: Serialize {} impl Serialize for i32 {} -fn test_blanket_ref(_foo: T, _serializable: S) {} +fn test_blanket_ref(vals: T, serializable: S) {} //~^ ERROR: this argument is passed by value, but not consumed in the function body fn issue_2114(s: String, t: String, u: Vec, v: Vec) { @@ -116,7 +116,7 @@ impl S { ) { } - fn baz(&self, _u: U, _s: Self) {} + fn baz(&self, uu: U, ss: Self) {} //~^ ERROR: this argument is passed by value, but not consumed in the function body //~| ERROR: this argument is passed by value, but not consumed in the function body } @@ -162,13 +162,13 @@ fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) { // The following 3 lines should not cause an ICE. See #2831 trait Bar<'a, A> {} impl<'b, T> Bar<'b, T> for T {} -fn some_fun<'b, S: Bar<'b, ()>>(_item: S) {} +fn some_fun<'b, S: Bar<'b, ()>>(items: S) {} //~^ ERROR: this argument is passed by value, but not consumed in the function body // Also this should not cause an ICE. See #2831 trait Club<'a, A> {} impl Club<'static, T> for T {} -fn more_fun(_item: impl Club<'static, i32>) {} +fn more_fun(items: impl Club<'static, i32>) {} //~^ ERROR: this argument is passed by value, but not consumed in the function body fn is_sync(_: T) @@ -177,6 +177,10 @@ where { } +struct Obj(String); + +fn prefix_test(_unused_with_prefix: Obj) {} + fn main() { // This should not cause an ICE either // https://github.com/rust-lang/rust-clippy/issues/3144 diff --git a/tests/ui/needless_pass_by_value.stderr b/tests/ui/needless_pass_by_value.stderr index 827a200ba6819..46ef8f3e8da43 100644 --- a/tests/ui/needless_pass_by_value.stderr +++ b/tests/ui/needless_pass_by_value.stderr @@ -46,7 +46,7 @@ LL | fn test_destructure(x: Wrapper, y: Wrapper, z: Wrapper) { error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:87:49 | -LL | fn test_blanket_ref(_foo: T, _serializable: S) {} +LL | fn test_blanket_ref(vals: T, serializable: S) {} | ^ help: consider taking a reference instead: `&T` error: this argument is passed by value, but not consumed in the function body @@ -106,13 +106,13 @@ LL | t: String, error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:119:23 | -LL | fn baz(&self, _u: U, _s: Self) {} +LL | fn baz(&self, uu: U, ss: Self) {} | ^ help: consider taking a reference instead: `&U` error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:119:30 | -LL | fn baz(&self, _u: U, _s: Self) {} +LL | fn baz(&self, uu: U, ss: Self) {} | ^^^^ help: consider taking a reference instead: `&Self` error: this argument is passed by value, but not consumed in the function body @@ -121,7 +121,7 @@ error: this argument is passed by value, but not consumed in the function body LL | fn bar_copy(x: u32, y: CopyWrapper) { | ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper` | -help: consider marking this type as `Copy` +help: or consider marking this type as `Copy` --> tests/ui/needless_pass_by_value.rs:141:1 | LL | struct CopyWrapper(u32); @@ -133,7 +133,7 @@ error: this argument is passed by value, but not consumed in the function body 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` +help: or consider marking this type as `Copy` --> tests/ui/needless_pass_by_value.rs:141:1 | LL | struct CopyWrapper(u32); @@ -145,7 +145,7 @@ error: this argument is passed by value, but not consumed in the function body 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` +help: or consider marking this type as `Copy` --> tests/ui/needless_pass_by_value.rs:141:1 | LL | struct CopyWrapper(u32); @@ -157,7 +157,7 @@ error: this argument is passed by value, but not consumed in the function body 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` +help: or consider marking this type as `Copy` --> tests/ui/needless_pass_by_value.rs:141:1 | LL | struct CopyWrapper(u32); @@ -166,13 +166,13 @@ LL | struct CopyWrapper(u32); error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:165:40 | -LL | fn some_fun<'b, S: Bar<'b, ()>>(_item: S) {} +LL | fn some_fun<'b, S: Bar<'b, ()>>(items: S) {} | ^ help: consider taking a reference instead: `&S` error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:171:20 | -LL | fn more_fun(_item: impl Club<'static, i32>) {} +LL | fn more_fun(items: impl Club<'static, i32>) {} | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider taking a reference instead: `&impl Club<'static, i32>` error: aborting due to 22 previous errors diff --git a/tests/ui/needless_return.fixed b/tests/ui/needless_return.fixed index fc4129e1db84f..c5c570690b450 100644 --- a/tests/ui/needless_return.fixed +++ b/tests/ui/needless_return.fixed @@ -355,4 +355,8 @@ fn conjunctive_blocks() -> String { ({ "a".to_string() } + "b" + { "c" }) } +fn issue12907() -> String { + "".split("").next().unwrap().to_string() +} + fn main() {} diff --git a/tests/ui/needless_return.rs b/tests/ui/needless_return.rs index 61c7a02008f08..738611391dfdf 100644 --- a/tests/ui/needless_return.rs +++ b/tests/ui/needless_return.rs @@ -365,4 +365,8 @@ fn conjunctive_blocks() -> String { return { "a".to_string() } + "b" + { "c" }; } +fn issue12907() -> String { + return "".split("").next().unwrap().to_string(); +} + fn main() {} diff --git a/tests/ui/needless_return.stderr b/tests/ui/needless_return.stderr index ea9c230eafd22..da0fa220d8c4a 100644 --- a/tests/ui/needless_return.stderr +++ b/tests/ui/needless_return.stderr @@ -665,5 +665,17 @@ LL - return { "a".to_string() } + "b" + { "c" }; LL + ({ "a".to_string() } + "b" + { "c" }) | -error: aborting due to 53 previous errors +error: unneeded `return` statement + --> tests/ui/needless_return.rs:369:5 + | +LL | return "".split("").next().unwrap().to_string(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove `return` + | +LL - return "".split("").next().unwrap().to_string(); +LL + "".split("").next().unwrap().to_string() + | + +error: aborting due to 54 previous errors diff --git a/tests/ui/non_octal_unix_permissions.fixed b/tests/ui/non_octal_unix_permissions.fixed index 237f5f5b97a4f..f68d5e30d27e7 100644 --- a/tests/ui/non_octal_unix_permissions.fixed +++ b/tests/ui/non_octal_unix_permissions.fixed @@ -1,4 +1,4 @@ -//@ignore-target-windows +//@ignore-target: windows #![warn(clippy::non_octal_unix_permissions)] use std::fs::{DirBuilder, File, OpenOptions, Permissions}; diff --git a/tests/ui/non_octal_unix_permissions.rs b/tests/ui/non_octal_unix_permissions.rs index c8da5dbcec282..647c3769d2c3f 100644 --- a/tests/ui/non_octal_unix_permissions.rs +++ b/tests/ui/non_octal_unix_permissions.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows +//@ignore-target: windows #![warn(clippy::non_octal_unix_permissions)] use std::fs::{DirBuilder, File, OpenOptions, Permissions}; diff --git a/tests/ui/non_zero_suggestions.fixed b/tests/ui/non_zero_suggestions.fixed new file mode 100644 index 0000000000000..e67c992fdde01 --- /dev/null +++ b/tests/ui/non_zero_suggestions.fixed @@ -0,0 +1,65 @@ +#![warn(clippy::non_zero_suggestions)] +use std::num::{NonZeroI8, NonZeroI16, NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroUsize}; + +fn main() { + /// Positive test cases (lint should trigger) + // U32 -> U64 + let x: u64 = 100; + let y = NonZeroU32::new(10).unwrap(); + let r1 = x / NonZeroU64::from(y); + //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion + + let r2 = x % NonZeroU64::from(y); + //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion + + // U16 -> U32 + let a: u32 = 50; + let b = NonZeroU16::new(5).unwrap(); + let r3 = a / NonZeroU32::from(b); + //~^ ERROR: consider using `NonZeroU32::from()` for more efficient and type-safe conversion + + let x = NonZeroU64::from(NonZeroU32::new(5).unwrap()); + //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion + + /// Negative test cases (lint should not trigger) + // Left hand side expressions should not be triggered + let c: u32 = 50; + let d = NonZeroU16::new(5).unwrap(); + let r4 = u32::from(b.get()) / a; + + // Should not trigger for any other operand other than `/` and `%` + let r5 = a + u32::from(b.get()); + let r6 = a - u32::from(b.get()); + + // Same size types + let e: u32 = 200; + let f = NonZeroU32::new(20).unwrap(); + let r7 = e / f.get(); + + // Smaller to larger, but not NonZero + let g: u64 = 1000; + let h: u32 = 50; + let r8 = g / u64::from(h); + + // Using From correctly + let k: u64 = 300; + let l = NonZeroU32::new(15).unwrap(); + let r9 = k / NonZeroU64::from(l); +} + +// Additional function to test the lint in a different context +fn divide_numbers(x: u64, y: NonZeroU32) -> u64 { + x / NonZeroU64::from(y) + //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion +} + +struct Calculator { + value: u64, +} + +impl Calculator { + fn divide(&self, divisor: NonZeroU32) -> u64 { + self.value / NonZeroU64::from(divisor) + //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion + } +} diff --git a/tests/ui/non_zero_suggestions.rs b/tests/ui/non_zero_suggestions.rs new file mode 100644 index 0000000000000..de82371a8f294 --- /dev/null +++ b/tests/ui/non_zero_suggestions.rs @@ -0,0 +1,65 @@ +#![warn(clippy::non_zero_suggestions)] +use std::num::{NonZeroI8, NonZeroI16, NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroUsize}; + +fn main() { + /// Positive test cases (lint should trigger) + // U32 -> U64 + let x: u64 = 100; + let y = NonZeroU32::new(10).unwrap(); + let r1 = x / u64::from(y.get()); + //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion + + let r2 = x % u64::from(y.get()); + //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion + + // U16 -> U32 + let a: u32 = 50; + let b = NonZeroU16::new(5).unwrap(); + let r3 = a / u32::from(b.get()); + //~^ ERROR: consider using `NonZeroU32::from()` for more efficient and type-safe conversion + + let x = u64::from(NonZeroU32::new(5).unwrap().get()); + //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion + + /// Negative test cases (lint should not trigger) + // Left hand side expressions should not be triggered + let c: u32 = 50; + let d = NonZeroU16::new(5).unwrap(); + let r4 = u32::from(b.get()) / a; + + // Should not trigger for any other operand other than `/` and `%` + let r5 = a + u32::from(b.get()); + let r6 = a - u32::from(b.get()); + + // Same size types + let e: u32 = 200; + let f = NonZeroU32::new(20).unwrap(); + let r7 = e / f.get(); + + // Smaller to larger, but not NonZero + let g: u64 = 1000; + let h: u32 = 50; + let r8 = g / u64::from(h); + + // Using From correctly + let k: u64 = 300; + let l = NonZeroU32::new(15).unwrap(); + let r9 = k / NonZeroU64::from(l); +} + +// Additional function to test the lint in a different context +fn divide_numbers(x: u64, y: NonZeroU32) -> u64 { + x / u64::from(y.get()) + //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion +} + +struct Calculator { + value: u64, +} + +impl Calculator { + fn divide(&self, divisor: NonZeroU32) -> u64 { + self.value / u64::from(divisor.get()) + //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion + } +} diff --git a/tests/ui/non_zero_suggestions.stderr b/tests/ui/non_zero_suggestions.stderr new file mode 100644 index 0000000000000..7a57f7983be74 --- /dev/null +++ b/tests/ui/non_zero_suggestions.stderr @@ -0,0 +1,41 @@ +error: consider using `NonZeroU64::from()` for more efficient and type-safe conversion + --> tests/ui/non_zero_suggestions.rs:9:18 + | +LL | let r1 = x / u64::from(y.get()); + | ^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU64::from(y)` + | + = note: `-D clippy::non-zero-suggestions` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::non_zero_suggestions)]` + +error: consider using `NonZeroU64::from()` for more efficient and type-safe conversion + --> tests/ui/non_zero_suggestions.rs:12:18 + | +LL | let r2 = x % u64::from(y.get()); + | ^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU64::from(y)` + +error: consider using `NonZeroU32::from()` for more efficient and type-safe conversion + --> tests/ui/non_zero_suggestions.rs:18:18 + | +LL | let r3 = a / u32::from(b.get()); + | ^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU32::from(b)` + +error: consider using `NonZeroU64::from()` for more efficient and type-safe conversion + --> tests/ui/non_zero_suggestions.rs:21:13 + | +LL | let x = u64::from(NonZeroU32::new(5).unwrap().get()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU64::from(NonZeroU32::new(5).unwrap())` + +error: consider using `NonZeroU64::from()` for more efficient and type-safe conversion + --> tests/ui/non_zero_suggestions.rs:52:9 + | +LL | x / u64::from(y.get()) + | ^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU64::from(y)` + +error: consider using `NonZeroU64::from()` for more efficient and type-safe conversion + --> tests/ui/non_zero_suggestions.rs:62:22 + | +LL | self.value / u64::from(divisor.get()) + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU64::from(divisor)` + +error: aborting due to 6 previous errors + diff --git a/tests/ui/non_zero_suggestions_unfixable.rs b/tests/ui/non_zero_suggestions_unfixable.rs new file mode 100644 index 0000000000000..193c710e589ff --- /dev/null +++ b/tests/ui/non_zero_suggestions_unfixable.rs @@ -0,0 +1,23 @@ +#![warn(clippy::non_zero_suggestions)] +//@no-rustfix +use std::num::{NonZeroI8, NonZeroI16, NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroUsize}; + +fn main() { + let x: u64 = u64::from(NonZeroU32::new(5).unwrap().get()); + //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion + + let n = NonZeroU32::new(20).unwrap(); + let y = u64::from(n.get()); + //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion + some_fn_that_only_takes_u64(y); + + let m = NonZeroU32::try_from(1).unwrap(); + let _z: NonZeroU64 = m.into(); +} + +fn return_non_zero(x: u64, y: NonZeroU32) -> u64 { + u64::from(y.get()) + //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion +} + +fn some_fn_that_only_takes_u64(_: u64) {} diff --git a/tests/ui/non_zero_suggestions_unfixable.stderr b/tests/ui/non_zero_suggestions_unfixable.stderr new file mode 100644 index 0000000000000..787179f2a2d64 --- /dev/null +++ b/tests/ui/non_zero_suggestions_unfixable.stderr @@ -0,0 +1,23 @@ +error: consider using `NonZeroU64::from()` for more efficient and type-safe conversion + --> tests/ui/non_zero_suggestions_unfixable.rs:6:18 + | +LL | let x: u64 = u64::from(NonZeroU32::new(5).unwrap().get()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU64::from(NonZeroU32::new(5).unwrap())` + | + = note: `-D clippy::non-zero-suggestions` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::non_zero_suggestions)]` + +error: consider using `NonZeroU64::from()` for more efficient and type-safe conversion + --> tests/ui/non_zero_suggestions_unfixable.rs:10:13 + | +LL | let y = u64::from(n.get()); + | ^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU64::from(n)` + +error: consider using `NonZeroU64::from()` for more efficient and type-safe conversion + --> tests/ui/non_zero_suggestions_unfixable.rs:19:5 + | +LL | u64::from(y.get()) + | ^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU64::from(y)` + +error: aborting due to 3 previous errors + diff --git a/tests/ui/panic_in_result_fn.rs b/tests/ui/panic_in_result_fn.rs index aaaf9a109acbb..e2375aa996f7d 100644 --- a/tests/ui/panic_in_result_fn.rs +++ b/tests/ui/panic_in_result_fn.rs @@ -71,6 +71,15 @@ fn function_result_with_custom_todo() -> Result // should not emit Ok(true) } +fn issue_13381() -> Result<(), String> { + const { + if N == 0 { + panic!(); + } + } + Ok(()) +} + fn main() -> Result<(), String> { todo!("finish main method"); Ok(()) diff --git a/tests/ui/pointers_in_nomem_asm_block.rs b/tests/ui/pointers_in_nomem_asm_block.rs new file mode 100644 index 0000000000000..b5abcbb3474c2 --- /dev/null +++ b/tests/ui/pointers_in_nomem_asm_block.rs @@ -0,0 +1,33 @@ +//@ needs-asm-support +#![warn(clippy::pointers_in_nomem_asm_block)] +#![crate_type = "lib"] +#![no_std] + +use core::arch::asm; + +unsafe fn nomem_bad(p: &i32) { + asm!( + "asdf {p1}, {p2}, {p3}", + p1 = in(reg) p, + //~^ ERROR: passing pointers to nomem asm block + p2 = in(reg) p as *const _ as usize, + p3 = in(reg) p, + options(nomem, nostack, preserves_flags) + ); +} + +unsafe fn nomem_good(p: &i32) { + asm!("asdf {p}", p = in(reg) p, options(readonly, nostack, preserves_flags)); + let p = p as *const i32 as usize; + asm!("asdf {p}", p = in(reg) p, options(nomem, nostack, preserves_flags)); +} + +unsafe fn nomem_bad2(p: &mut i32) { + asm!("asdf {p}", p = in(reg) p, options(nomem, nostack, preserves_flags)); + //~^ ERROR: passing pointers to nomem asm block +} + +unsafe fn nomem_fn(p: extern "C" fn()) { + asm!("call {p}", p = in(reg) p, options(nomem)); + //~^ ERROR: passing pointers to nomem asm block +} diff --git a/tests/ui/pointers_in_nomem_asm_block.stderr b/tests/ui/pointers_in_nomem_asm_block.stderr new file mode 100644 index 0000000000000..cabeb37344f2b --- /dev/null +++ b/tests/ui/pointers_in_nomem_asm_block.stderr @@ -0,0 +1,34 @@ +error: passing pointers to nomem asm block + --> tests/ui/pointers_in_nomem_asm_block.rs:11:9 + | +LL | p1 = in(reg) p, + | ^^^^^^^^^^^^^^ +... +LL | p3 = in(reg) p, + | ^^^^^^^^^^^^^^ + | + = note: `nomem` means that no memory write or read happens inside the asm! block + = note: if this is intentional and no pointers are read or written to, consider allowing the lint + = note: `-D clippy::pointers-in-nomem-asm-block` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::pointers_in_nomem_asm_block)]` + +error: passing pointers to nomem asm block + --> tests/ui/pointers_in_nomem_asm_block.rs:26:22 + | +LL | asm!("asdf {p}", p = in(reg) p, options(nomem, nostack, preserves_flags)); + | ^^^^^^^^^^^^^ + | + = note: `nomem` means that no memory write or read happens inside the asm! block + = note: if this is intentional and no pointers are read or written to, consider allowing the lint + +error: passing pointers to nomem asm block + --> tests/ui/pointers_in_nomem_asm_block.rs:31:22 + | +LL | asm!("call {p}", p = in(reg) p, options(nomem)); + | ^^^^^^^^^^^^^ + | + = note: `nomem` means that no memory write or read happens inside the asm! block + = note: if this is intentional and no pointers are read or written to, consider allowing the lint + +error: aborting due to 3 previous errors + diff --git a/tests/ui/ptr_arg.rs b/tests/ui/ptr_arg.rs index e6ef626812128..e8b42d3b913b0 100644 --- a/tests/ui/ptr_arg.rs +++ b/tests/ui/ptr_arg.rs @@ -309,3 +309,25 @@ mod issue_11181 { extern "C" fn allowed(_v: &Vec) {} } } + +mod issue_13308 { + use std::ops::Deref; + + fn repro(source: &str, destination: &mut String) { + source.clone_into(destination); + } + fn repro2(source: &str, destination: &mut String) { + ToOwned::clone_into(source, destination); + } + + fn h1(_: &::Target) {} + fn h2(_: T, _: &T::Target) {} + + // Other cases that are still ok to lint and ideally shouldn't regress + fn good(v1: &String, v2: &String) { + //~^ ERROR: writing `&String` instead of `&str` + //~^^ ERROR: writing `&String` instead of `&str` + h1(v1); + h2(String::new(), v2); + } +} diff --git a/tests/ui/ptr_arg.stderr b/tests/ui/ptr_arg.stderr index 4246453e64ca7..1a6b3aa1f8d41 100644 --- a/tests/ui/ptr_arg.stderr +++ b/tests/ui/ptr_arg.stderr @@ -221,5 +221,17 @@ error: using a reference to `Cow` is not recommended LL | fn cow_bad_ret_ty_2<'a, 'b>(input: &'a Cow<'a, str>) -> &'b str { | ^^^^^^^^^^^^^^^^ help: change this to: `&str` -error: aborting due to 25 previous errors +error: writing `&String` instead of `&str` involves a new object where a slice will do + --> tests/ui/ptr_arg.rs:327:17 + | +LL | fn good(v1: &String, v2: &String) { + | ^^^^^^^ help: change this to: `&str` + +error: writing `&String` instead of `&str` involves a new object where a slice will do + --> tests/ui/ptr_arg.rs:327:30 + | +LL | fn good(v1: &String, v2: &String) { + | ^^^^^^^ help: change this to: `&str` + +error: aborting due to 27 previous errors diff --git a/tests/ui/ptr_cast_constness.fixed b/tests/ui/ptr_cast_constness.fixed index 21ac42196e1bb..9a5272c7adc36 100644 --- a/tests/ui/ptr_cast_constness.fixed +++ b/tests/ui/ptr_cast_constness.fixed @@ -68,3 +68,20 @@ fn _msrv_1_65() { let _ = ptr.cast_mut(); let _ = mut_ptr.cast_const(); } + +#[inline_macros] +fn null_pointers() { + use std::ptr; + let _ = std::ptr::null_mut::(); + let _ = std::ptr::null::(); + let _ = std::ptr::null_mut::(); + let _ = std::ptr::null::(); + + // Make sure the lint is triggered inside a macro + let _ = inline!(std::ptr::null_mut::()); + let _ = inline!(std::ptr::null_mut::()); + + // Do not lint inside macros from external crates + let _ = external!(ptr::null::() as *mut u32); + let _ = external!(ptr::null::().cast_mut()); +} diff --git a/tests/ui/ptr_cast_constness.rs b/tests/ui/ptr_cast_constness.rs index 5ce590b2b7e47..43ab5f5ba7ccc 100644 --- a/tests/ui/ptr_cast_constness.rs +++ b/tests/ui/ptr_cast_constness.rs @@ -68,3 +68,20 @@ fn _msrv_1_65() { let _ = ptr as *mut u32; let _ = mut_ptr as *const u32; } + +#[inline_macros] +fn null_pointers() { + use std::ptr; + let _ = ptr::null::() as *mut String; + let _ = ptr::null_mut::() as *const u32; + let _ = ptr::null::().cast_mut(); + let _ = ptr::null_mut::().cast_const(); + + // Make sure the lint is triggered inside a macro + let _ = inline!(ptr::null::() as *mut u32); + let _ = inline!(ptr::null::().cast_mut()); + + // Do not lint inside macros from external crates + let _ = external!(ptr::null::() as *mut u32); + let _ = external!(ptr::null::().cast_mut()); +} diff --git a/tests/ui/ptr_cast_constness.stderr b/tests/ui/ptr_cast_constness.stderr index 2c52ebd3464d6..a693793a4ae96 100644 --- a/tests/ui/ptr_cast_constness.stderr +++ b/tests/ui/ptr_cast_constness.stderr @@ -43,5 +43,45 @@ error: `as` casting between raw pointers while changing only its constness LL | let _ = mut_ptr as *const u32; | ^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast_const`, a safer alternative: `mut_ptr.cast_const()` -error: aborting due to 7 previous errors +error: `as` casting to make a const null pointer into a mutable null pointer + --> tests/ui/ptr_cast_constness.rs:75:13 + | +LL | let _ = ptr::null::() as *mut String; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null_mut()` directly instead: `std::ptr::null_mut::()` + +error: `as` casting to make a mutable null pointer into a const null pointer + --> tests/ui/ptr_cast_constness.rs:76:13 + | +LL | let _ = ptr::null_mut::() as *const u32; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null()` directly instead: `std::ptr::null::()` + +error: changing constness of a null pointer + --> tests/ui/ptr_cast_constness.rs:77:13 + | +LL | let _ = ptr::null::().cast_mut(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null_mut()` directly instead: `std::ptr::null_mut::()` + +error: changing constness of a null pointer + --> tests/ui/ptr_cast_constness.rs:78:13 + | +LL | let _ = ptr::null_mut::().cast_const(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null()` directly instead: `std::ptr::null::()` + +error: `as` casting to make a const null pointer into a mutable null pointer + --> tests/ui/ptr_cast_constness.rs:81:21 + | +LL | let _ = inline!(ptr::null::() as *mut u32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null_mut()` directly instead: `std::ptr::null_mut::()` + | + = note: this error originates in the macro `__inline_mac_fn_null_pointers` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: changing constness of a null pointer + --> tests/ui/ptr_cast_constness.rs:82:21 + | +LL | let _ = inline!(ptr::null::().cast_mut()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null_mut()` directly instead: `std::ptr::null_mut::()` + | + = note: this error originates in the macro `__inline_mac_fn_null_pointers` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 13 previous errors diff --git a/tests/ui/result_large_err.rs b/tests/ui/result_large_err.rs index b25348bf99617..9c39f023da2da 100644 --- a/tests/ui/result_large_err.rs +++ b/tests/ui/result_large_err.rs @@ -1,4 +1,4 @@ -//@ignore-32bit +//@ignore-bitwidth: 32 #![warn(clippy::result_large_err)] #![allow(clippy::large_enum_variant)] diff --git a/tests/ui/single_call_fn.rs b/tests/ui/single_call_fn.rs index 55e7508a9573a..a0597664da55c 100644 --- a/tests/ui/single_call_fn.rs +++ b/tests/ui/single_call_fn.rs @@ -1,4 +1,4 @@ -//@ignore-32bit +//@ignore-bitwidth: 32 //@aux-build:proc_macros.rs #![allow(clippy::redundant_closure_call, unused)] #![warn(clippy::single_call_fn)] diff --git a/tests/ui/single_match.fixed b/tests/ui/single_match.fixed index 5249c4408899a..4016b2699d6b4 100644 --- a/tests/ui/single_match.fixed +++ b/tests/ui/single_match.fixed @@ -39,8 +39,8 @@ enum Foo { Bar, Baz(u8), } -use std::borrow::Cow; use Foo::*; +use std::borrow::Cow; fn single_match_know_enum() { let x = Some(1u8); @@ -296,3 +296,27 @@ fn issue11365() { if let Some(A | B) = &Some(A) { println!() } } + +#[derive(Eq, PartialEq)] +pub struct Data([u8; 4]); + +const DATA: Data = Data([1, 2, 3, 4]); +const CONST_I32: i32 = 1; + +fn irrefutable_match() { + println!(); + + println!(); + + let i = 0; + { + let a = 1; + let b = 2; + } + + + + + + println!() +} diff --git a/tests/ui/single_match.rs b/tests/ui/single_match.rs index 882098a56e780..75edaa606053d 100644 --- a/tests/ui/single_match.rs +++ b/tests/ui/single_match.rs @@ -51,8 +51,8 @@ enum Foo { Bar, Baz(u8), } -use std::borrow::Cow; use Foo::*; +use std::borrow::Cow; fn single_match_know_enum() { let x = Some(1u8); @@ -360,3 +360,45 @@ fn issue11365() { None | Some(_) => {}, } } + +#[derive(Eq, PartialEq)] +pub struct Data([u8; 4]); + +const DATA: Data = Data([1, 2, 3, 4]); +const CONST_I32: i32 = 1; + +fn irrefutable_match() { + match DATA { + DATA => println!(), + _ => {}, + } + + match CONST_I32 { + CONST_I32 => println!(), + _ => {}, + } + + let i = 0; + match i { + i => { + let a = 1; + let b = 2; + }, + _ => {}, + } + + match i { + i => {}, + _ => {}, + } + + match i { + i => (), + _ => (), + } + + match CONST_I32 { + CONST_I32 => println!(), + _ => {}, + } +} diff --git a/tests/ui/single_match.stderr b/tests/ui/single_match.stderr index ceb2a193bf7b1..9240b09c50a9f 100644 --- a/tests/ui/single_match.stderr +++ b/tests/ui/single_match.stderr @@ -216,5 +216,70 @@ LL | | None | Some(_) => {}, LL | | } | |_____^ help: try: `if let Some(A | B) = &Some(A) { println!() }` -error: aborting due to 20 previous errors +error: this pattern is irrefutable, `match` is useless + --> tests/ui/single_match.rs:371:5 + | +LL | / match DATA { +LL | | DATA => println!(), +LL | | _ => {}, +LL | | } + | |_____^ help: try: `println!();` + +error: this pattern is irrefutable, `match` is useless + --> tests/ui/single_match.rs:376:5 + | +LL | / match CONST_I32 { +LL | | CONST_I32 => println!(), +LL | | _ => {}, +LL | | } + | |_____^ help: try: `println!();` + +error: this pattern is irrefutable, `match` is useless + --> tests/ui/single_match.rs:382:5 + | +LL | / match i { +LL | | i => { +LL | | let a = 1; +LL | | let b = 2; +LL | | }, +LL | | _ => {}, +LL | | } + | |_____^ + | +help: try + | +LL ~ { +LL + let a = 1; +LL + let b = 2; +LL + } + | + +error: this pattern is irrefutable, `match` is useless + --> tests/ui/single_match.rs:390:5 + | +LL | / match i { +LL | | i => {}, +LL | | _ => {}, +LL | | } + | |_____^ help: `match` expression can be removed + +error: this pattern is irrefutable, `match` is useless + --> tests/ui/single_match.rs:395:5 + | +LL | / match i { +LL | | i => (), +LL | | _ => (), +LL | | } + | |_____^ help: `match` expression can be removed + +error: this pattern is irrefutable, `match` is useless + --> tests/ui/single_match.rs:400:5 + | +LL | / match CONST_I32 { +LL | | CONST_I32 => println!(), +LL | | _ => {}, +LL | | } + | |_____^ help: try: `println!()` + +error: aborting due to 26 previous errors diff --git a/tests/ui/single_match_else.fixed b/tests/ui/single_match_else.fixed index 163be16ad8be7..c2ca746976bd1 100644 --- a/tests/ui/single_match_else.fixed +++ b/tests/ui/single_match_else.fixed @@ -171,3 +171,7 @@ fn issue_10808(bar: Option) { }, } } + +fn irrefutable_match() -> Option<&'static ExprNode> { + Some(&NODE) +} diff --git a/tests/ui/single_match_else.rs b/tests/ui/single_match_else.rs index 3f1fd2b31832f..2d9e877ee0fee 100644 --- a/tests/ui/single_match_else.rs +++ b/tests/ui/single_match_else.rs @@ -199,3 +199,13 @@ fn issue_10808(bar: Option) { }, } } + +fn irrefutable_match() -> Option<&'static ExprNode> { + match ExprNode::Butterflies { + ExprNode::Butterflies => Some(&NODE), + _ => { + let x = 5; + None + }, + } +} diff --git a/tests/ui/single_match_else.stderr b/tests/ui/single_match_else.stderr index 61c348260d05e..a2801751a430a 100644 --- a/tests/ui/single_match_else.stderr +++ b/tests/ui/single_match_else.stderr @@ -197,5 +197,17 @@ LL + println!("None"); LL + } | -error: aborting due to 9 previous errors +error: this pattern is irrefutable, `match` is useless + --> tests/ui/single_match_else.rs:204:5 + | +LL | / match ExprNode::Butterflies { +LL | | ExprNode::Butterflies => Some(&NODE), +LL | | _ => { +LL | | let x = 5; +LL | | None +LL | | }, +LL | | } + | |_____^ help: try: `Some(&NODE)` + +error: aborting due to 10 previous errors diff --git a/tests/ui/string_slice.rs b/tests/ui/string_slice.rs index 1d1911aaa1d91..dc519493a4dd3 100644 --- a/tests/ui/string_slice.rs +++ b/tests/ui/string_slice.rs @@ -1,7 +1,7 @@ -use std::borrow::Cow; +#![warn(clippy::string_slice)] +#![allow(clippy::no_effect)] -#[warn(clippy::string_slice)] -#[allow(clippy::no_effect)] +use std::borrow::Cow; fn main() { &"Ölkanne"[1..]; diff --git a/tests/ui/suspicious_command_arg_space.fixed b/tests/ui/suspicious_command_arg_space.fixed index 5d7b1e0c17f25..704d6ea1bb837 100644 --- a/tests/ui/suspicious_command_arg_space.fixed +++ b/tests/ui/suspicious_command_arg_space.fixed @@ -1,3 +1,4 @@ +#![allow(clippy::zombie_processes)] fn main() { // Things it should warn about: std::process::Command::new("echo").args(["-n", "hello"]).spawn().unwrap(); diff --git a/tests/ui/suspicious_command_arg_space.rs b/tests/ui/suspicious_command_arg_space.rs index 8abd9803a0c6f..2a2a7557381c4 100644 --- a/tests/ui/suspicious_command_arg_space.rs +++ b/tests/ui/suspicious_command_arg_space.rs @@ -1,3 +1,4 @@ +#![allow(clippy::zombie_processes)] fn main() { // Things it should warn about: std::process::Command::new("echo").arg("-n hello").spawn().unwrap(); diff --git a/tests/ui/suspicious_command_arg_space.stderr b/tests/ui/suspicious_command_arg_space.stderr index d2517b66b5661..6fd07d07d7be6 100644 --- a/tests/ui/suspicious_command_arg_space.stderr +++ b/tests/ui/suspicious_command_arg_space.stderr @@ -1,5 +1,5 @@ error: single argument that looks like it should be multiple arguments - --> tests/ui/suspicious_command_arg_space.rs:3:44 + --> tests/ui/suspicious_command_arg_space.rs:4:44 | LL | std::process::Command::new("echo").arg("-n hello").spawn().unwrap(); | ^^^^^^^^^^ @@ -12,7 +12,7 @@ LL | std::process::Command::new("echo").args(["-n", "hello"]).spawn().unwrap | ~~~~ ~~~~~~~~~~~~~~~ error: single argument that looks like it should be multiple arguments - --> tests/ui/suspicious_command_arg_space.rs:6:43 + --> tests/ui/suspicious_command_arg_space.rs:7:43 | LL | std::process::Command::new("cat").arg("--number file").spawn().unwrap(); | ^^^^^^^^^^^^^^^ diff --git a/tests/ui/suspicious_to_owned.rs b/tests/ui/suspicious_to_owned.rs index f32b07d45f635..794c2e7174afe 100644 --- a/tests/ui/suspicious_to_owned.rs +++ b/tests/ui/suspicious_to_owned.rs @@ -3,7 +3,7 @@ #![warn(clippy::implicit_clone)] #![allow(clippy::redundant_clone)] use std::borrow::Cow; -use std::ffi::{c_char, CStr}; +use std::ffi::{CStr, c_char}; fn main() { let moo = "Moooo"; diff --git a/tests/ui/tabs_in_doc_comments.fixed b/tests/ui/tabs_in_doc_comments.fixed index 26cc5c27e88cc..3536c1746df36 100644 --- a/tests/ui/tabs_in_doc_comments.fixed +++ b/tests/ui/tabs_in_doc_comments.fixed @@ -1,5 +1,4 @@ #![warn(clippy::tabs_in_doc_comments)] -#[allow(dead_code)] /// /// Struct to hold two strings: diff --git a/tests/ui/tabs_in_doc_comments.rs b/tests/ui/tabs_in_doc_comments.rs index 14b06966ecc1a..033a685066e1a 100644 --- a/tests/ui/tabs_in_doc_comments.rs +++ b/tests/ui/tabs_in_doc_comments.rs @@ -1,5 +1,4 @@ #![warn(clippy::tabs_in_doc_comments)] -#[allow(dead_code)] /// /// Struct to hold two strings: diff --git a/tests/ui/tabs_in_doc_comments.stderr b/tests/ui/tabs_in_doc_comments.stderr index aef6c39145263..f8d30b728e58c 100644 --- a/tests/ui/tabs_in_doc_comments.stderr +++ b/tests/ui/tabs_in_doc_comments.stderr @@ -1,5 +1,5 @@ error: using tabs in doc comments is not recommended - --> tests/ui/tabs_in_doc_comments.rs:6:5 + --> tests/ui/tabs_in_doc_comments.rs:5:5 | LL | /// - first one | ^^^^ help: consider using four spaces per tab @@ -8,43 +8,43 @@ LL | /// - first one = help: to override `-D warnings` add `#[allow(clippy::tabs_in_doc_comments)]` error: using tabs in doc comments is not recommended - --> tests/ui/tabs_in_doc_comments.rs:6:13 + --> tests/ui/tabs_in_doc_comments.rs:5:13 | LL | /// - first one | ^^^^^^^^ help: consider using four spaces per tab error: using tabs in doc comments is not recommended - --> tests/ui/tabs_in_doc_comments.rs:7:5 + --> tests/ui/tabs_in_doc_comments.rs:6:5 | LL | /// - second one | ^^^^ help: consider using four spaces per tab error: using tabs in doc comments is not recommended - --> tests/ui/tabs_in_doc_comments.rs:7:14 + --> tests/ui/tabs_in_doc_comments.rs:6:14 | LL | /// - second one | ^^^^ help: consider using four spaces per tab error: using tabs in doc comments is not recommended - --> tests/ui/tabs_in_doc_comments.rs:10:9 + --> tests/ui/tabs_in_doc_comments.rs:9:9 | LL | /// - First String: | ^^^^ help: consider using four spaces per tab error: using tabs in doc comments is not recommended - --> tests/ui/tabs_in_doc_comments.rs:11:9 + --> tests/ui/tabs_in_doc_comments.rs:10:9 | LL | /// - needs to be inside here | ^^^^^^^^ help: consider using four spaces per tab error: using tabs in doc comments is not recommended - --> tests/ui/tabs_in_doc_comments.rs:14:9 + --> tests/ui/tabs_in_doc_comments.rs:13:9 | LL | /// - Second String: | ^^^^ help: consider using four spaces per tab error: using tabs in doc comments is not recommended - --> tests/ui/tabs_in_doc_comments.rs:15:9 + --> tests/ui/tabs_in_doc_comments.rs:14:9 | LL | /// - needs to be inside here | ^^^^^^^^ help: consider using four spaces per tab diff --git a/tests/ui/too_long_first_doc_paragraph.rs b/tests/ui/too_long_first_doc_paragraph.rs index 1042249c5b7bd..7d0a37cde46db 100644 --- a/tests/ui/too_long_first_doc_paragraph.rs +++ b/tests/ui/too_long_first_doc_paragraph.rs @@ -2,6 +2,16 @@ #![warn(clippy::too_long_first_doc_paragraph)] +pub mod foo { + + // in foo.rs + //! A very short summary. + //! A much longer explanation that goes into a lot more detail about + //! how the thing works, possibly with doclinks and so one, + //! and probably spanning a many rows. Blablabla, it needs to be over + //! 200 characters so I needed to write something longeeeeeeer. +} + /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc turpis nunc, lacinia /// a dolor in, pellentesque aliquet enim. Cras nec maximus sem. Mauris arcu libero, /// gravida non lacinia at, rhoncus eu lacus. diff --git a/tests/ui/too_long_first_doc_paragraph.stderr b/tests/ui/too_long_first_doc_paragraph.stderr index 7f48e5cf884e6..39926647f5437 100644 --- a/tests/ui/too_long_first_doc_paragraph.stderr +++ b/tests/ui/too_long_first_doc_paragraph.stderr @@ -1,16 +1,32 @@ error: first doc comment paragraph is too long - --> tests/ui/too_long_first_doc_paragraph.rs:5:1 + --> tests/ui/too_long_first_doc_paragraph.rs:8:5 + | +LL | / //! A very short summary. +LL | | //! A much longer explanation that goes into a lot more detail about +LL | | //! how the thing works, possibly with doclinks and so one, +LL | | //! and probably spanning a many rows. Blablabla, it needs to be over +LL | | //! 200 characters so I needed to write something longeeeeeeer. + | |____^ + | + = note: `-D clippy::too-long-first-doc-paragraph` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::too_long_first_doc_paragraph)]` +help: add an empty line + | +LL ~ //! A very short summary. +LL + //! +LL ~ //! A much longer explanation that goes into a lot more detail about + | + +error: first doc comment paragraph is too long + --> tests/ui/too_long_first_doc_paragraph.rs:15:1 | LL | / /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc turpis nunc, lacinia LL | | /// a dolor in, pellentesque aliquet enim. Cras nec maximus sem. Mauris arcu libero, LL | | /// gravida non lacinia at, rhoncus eu lacus. | |_ - | - = note: `-D clippy::too-long-first-doc-paragraph` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::too_long_first_doc_paragraph)]` error: first doc comment paragraph is too long - --> tests/ui/too_long_first_doc_paragraph.rs:26:1 + --> tests/ui/too_long_first_doc_paragraph.rs:36:1 | LL | / /// Lorem LL | | /// ipsum dolor sit amet, consectetur adipiscing elit. Nunc turpis nunc, lacinia @@ -18,5 +34,5 @@ LL | | /// a dolor in, pellentesque aliquet enim. Cras nec maximus sem. Mauris a LL | | /// gravida non lacinia at, rhoncus eu lacus. | |_ -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors diff --git a/tests/ui/transmute_32bit.rs b/tests/ui/transmute_32bit.rs index 8e1316ca39d4f..e162bbb2d9296 100644 --- a/tests/ui/transmute_32bit.rs +++ b/tests/ui/transmute_32bit.rs @@ -1,4 +1,4 @@ -//@ignore-64bit +//@ignore-bitwidth: 64 #[warn(clippy::wrong_transmute)] fn main() { diff --git a/tests/ui/transmute_64bit.rs b/tests/ui/transmute_64bit.rs index 767cc503a39df..fd0ad74bcfa0a 100644 --- a/tests/ui/transmute_64bit.rs +++ b/tests/ui/transmute_64bit.rs @@ -1,4 +1,4 @@ -//@ignore-32bit +//@ignore-bitwidth: 32 #[warn(clippy::wrong_transmute)] fn main() { diff --git a/tests/ui/transmute_collection.rs b/tests/ui/transmute_collection.rs index e30b34a5d7d6f..6748b66e0eb6c 100644 --- a/tests/ui/transmute_collection.rs +++ b/tests/ui/transmute_collection.rs @@ -2,7 +2,7 @@ #![allow(clippy::missing_transmute_annotations)] use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, VecDeque}; -use std::mem::{transmute, MaybeUninit}; +use std::mem::{MaybeUninit, transmute}; fn main() { unsafe { diff --git a/tests/ui/transmute_undefined_repr.rs b/tests/ui/transmute_undefined_repr.rs index dd4bac7f1ed72..5b16d71f1142a 100644 --- a/tests/ui/transmute_undefined_repr.rs +++ b/tests/ui/transmute_undefined_repr.rs @@ -8,7 +8,7 @@ use core::any::TypeId; use core::ffi::c_void; -use core::mem::{size_of, transmute, MaybeUninit}; +use core::mem::{MaybeUninit, size_of, transmute}; use core::ptr::NonNull; fn value() -> T { diff --git a/tests/ui/uninit_vec.rs b/tests/ui/uninit_vec.rs index 0cc77a8775d57..464f88140bdba 100644 --- a/tests/ui/uninit_vec.rs +++ b/tests/ui/uninit_vec.rs @@ -150,4 +150,36 @@ fn main() { vec.set_len(10); } } + + fn nested_union() { + let mut vec: Vec>> = Vec::with_capacity(1); + unsafe { + vec.set_len(1); + } + } + + struct Recursive(*const Recursive, MaybeUninit); + fn recursive_union() { + // Make sure we don't stack overflow on recursive types. + // The pointer acts as the base case because it can't be uninit regardless of its pointee. + + let mut vec: Vec> = Vec::with_capacity(1); + //~^ uninit_vec + unsafe { + vec.set_len(1); + } + } + + #[repr(u8)] + enum Enum { + Variant(T), + } + fn union_in_enum() { + // Enums can have a discriminant that can't be uninit, so this should still warn + let mut vec: Vec> = Vec::with_capacity(1); + //~^ uninit_vec + unsafe { + vec.set_len(1); + } + } } diff --git a/tests/ui/uninit_vec.stderr b/tests/ui/uninit_vec.stderr index e8b77d653f089..e7c81cf792f23 100644 --- a/tests/ui/uninit_vec.stderr +++ b/tests/ui/uninit_vec.stderr @@ -125,5 +125,27 @@ LL | vec.set_len(10); | = help: initialize the buffer or wrap the content in `MaybeUninit` -error: aborting due to 12 previous errors +error: calling `set_len()` immediately after reserving a buffer creates uninitialized values + --> tests/ui/uninit_vec.rs:166:9 + | +LL | let mut vec: Vec> = Vec::with_capacity(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | vec.set_len(1); + | ^^^^^^^^^^^^^^ + | + = help: initialize the buffer or wrap the content in `MaybeUninit` + +error: calling `set_len()` immediately after reserving a buffer creates uninitialized values + --> tests/ui/uninit_vec.rs:179:9 + | +LL | let mut vec: Vec> = Vec::with_capacity(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | vec.set_len(1); + | ^^^^^^^^^^^^^^ + | + = help: initialize the buffer or wrap the content in `MaybeUninit` + +error: aborting due to 14 previous errors diff --git a/tests/ui/unnecessary_first_then_check.fixed b/tests/ui/unnecessary_first_then_check.fixed new file mode 100644 index 0000000000000..7202e1bbd179c --- /dev/null +++ b/tests/ui/unnecessary_first_then_check.fixed @@ -0,0 +1,22 @@ +#![warn(clippy::unnecessary_first_then_check)] +#![allow(clippy::useless_vec, clippy::const_is_empty)] + +fn main() { + let s = [1, 2, 3]; + let _: bool = !s.is_empty(); + let _: bool = s.is_empty(); + + let v = vec![1, 2, 3]; + let _: bool = !v.is_empty(); + + let n = [[1, 2, 3], [4, 5, 6]]; + let _: bool = !n[0].is_empty(); + let _: bool = n[0].is_empty(); + + struct Foo { + bar: &'static [i32], + } + let f = [Foo { bar: &[] }]; + let _: bool = !f[0].bar.is_empty(); + let _: bool = f[0].bar.is_empty(); +} diff --git a/tests/ui/unnecessary_first_then_check.rs b/tests/ui/unnecessary_first_then_check.rs new file mode 100644 index 0000000000000..762b959992883 --- /dev/null +++ b/tests/ui/unnecessary_first_then_check.rs @@ -0,0 +1,22 @@ +#![warn(clippy::unnecessary_first_then_check)] +#![allow(clippy::useless_vec, clippy::const_is_empty)] + +fn main() { + let s = [1, 2, 3]; + let _: bool = s.first().is_some(); + let _: bool = s.first().is_none(); + + let v = vec![1, 2, 3]; + let _: bool = v.first().is_some(); + + let n = [[1, 2, 3], [4, 5, 6]]; + let _: bool = n[0].first().is_some(); + let _: bool = n[0].first().is_none(); + + struct Foo { + bar: &'static [i32], + } + let f = [Foo { bar: &[] }]; + let _: bool = f[0].bar.first().is_some(); + let _: bool = f[0].bar.first().is_none(); +} diff --git a/tests/ui/unnecessary_first_then_check.stderr b/tests/ui/unnecessary_first_then_check.stderr new file mode 100644 index 0000000000000..bbaf7e68edabb --- /dev/null +++ b/tests/ui/unnecessary_first_then_check.stderr @@ -0,0 +1,47 @@ +error: unnecessary use of `first().is_some()` to check if slice is not empty + --> tests/ui/unnecessary_first_then_check.rs:6:19 + | +LL | let _: bool = s.first().is_some(); + | ^^^^^^^^^^^^^^^^^^^ help: replace this with: `!s.is_empty()` + | + = note: `-D clippy::unnecessary-first-then-check` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_first_then_check)]` + +error: unnecessary use of `first().is_none()` to check if slice is empty + --> tests/ui/unnecessary_first_then_check.rs:7:21 + | +LL | let _: bool = s.first().is_none(); + | ^^^^^^^^^^^^^^^^^ help: replace this with: `is_empty()` + +error: unnecessary use of `first().is_some()` to check if slice is not empty + --> tests/ui/unnecessary_first_then_check.rs:10:19 + | +LL | let _: bool = v.first().is_some(); + | ^^^^^^^^^^^^^^^^^^^ help: replace this with: `!v.is_empty()` + +error: unnecessary use of `first().is_some()` to check if slice is not empty + --> tests/ui/unnecessary_first_then_check.rs:13:19 + | +LL | let _: bool = n[0].first().is_some(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: replace this with: `!n[0].is_empty()` + +error: unnecessary use of `first().is_none()` to check if slice is empty + --> tests/ui/unnecessary_first_then_check.rs:14:24 + | +LL | let _: bool = n[0].first().is_none(); + | ^^^^^^^^^^^^^^^^^ help: replace this with: `is_empty()` + +error: unnecessary use of `first().is_some()` to check if slice is not empty + --> tests/ui/unnecessary_first_then_check.rs:20:19 + | +LL | let _: bool = f[0].bar.first().is_some(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace this with: `!f[0].bar.is_empty()` + +error: unnecessary use of `first().is_none()` to check if slice is empty + --> tests/ui/unnecessary_first_then_check.rs:21:28 + | +LL | let _: bool = f[0].bar.first().is_none(); + | ^^^^^^^^^^^^^^^^^ help: replace this with: `is_empty()` + +error: aborting due to 7 previous errors + diff --git a/tests/ui/unnecessary_min_or_max.fixed b/tests/ui/unnecessary_min_or_max.fixed index 392f6dd1fee43..1f3e131516ce6 100644 --- a/tests/ui/unnecessary_min_or_max.fixed +++ b/tests/ui/unnecessary_min_or_max.fixed @@ -65,3 +65,32 @@ fn random_u32() -> u32 { // random number generator 0 } + +struct Issue13191 { + min: u16, + max: u16, +} + +impl Issue13191 { + fn new() -> Self { + Self { min: 0, max: 0 } + } + + fn min(mut self, value: u16) -> Self { + self.min = value; + self + } + + fn max(mut self, value: u16) -> Self { + self.max = value; + self + } +} + +fn issue_13191() { + // should not fixed + Issue13191::new().min(0); + + // should not fixed + Issue13191::new().max(0); +} diff --git a/tests/ui/unnecessary_min_or_max.rs b/tests/ui/unnecessary_min_or_max.rs index b03755e6d23d1..58356b9d49e77 100644 --- a/tests/ui/unnecessary_min_or_max.rs +++ b/tests/ui/unnecessary_min_or_max.rs @@ -65,3 +65,32 @@ fn random_u32() -> u32 { // random number generator 0 } + +struct Issue13191 { + min: u16, + max: u16, +} + +impl Issue13191 { + fn new() -> Self { + Self { min: 0, max: 0 } + } + + fn min(mut self, value: u16) -> Self { + self.min = value; + self + } + + fn max(mut self, value: u16) -> Self { + self.max = value; + self + } +} + +fn issue_13191() { + // should not fixed + Issue13191::new().min(0); + + // should not fixed + Issue13191::new().max(0); +} diff --git a/tests/ui/unsafe_removed_from_name.rs b/tests/ui/unsafe_removed_from_name.rs index e0e0ded140fc0..e9e6c8312f5eb 100644 --- a/tests/ui/unsafe_removed_from_name.rs +++ b/tests/ui/unsafe_removed_from_name.rs @@ -7,7 +7,7 @@ use std::cell::UnsafeCell as TotallySafeCell; //~| NOTE: `-D clippy::unsafe-removed-from-name` implied by `-D warnings` use std::cell::UnsafeCell as TotallySafeCellAgain; -//~^ ERROR: removed `unsafe` from the name of `UnsafeCell` in use as `TotallySafeCellAgain +//~^ ERROR: removed `unsafe` from the name of `UnsafeCell` in use as `TotallySafeCellAgain` // Shouldn't error use std::cell::RefCell as ProbablyNotUnsafe; diff --git a/tests/ui/unused_trait_names.fixed b/tests/ui/unused_trait_names.fixed new file mode 100644 index 0000000000000..7dfd0db65aa1b --- /dev/null +++ b/tests/ui/unused_trait_names.fixed @@ -0,0 +1,286 @@ +//@aux-build:proc_macros.rs + +#![allow(unused)] +#![warn(clippy::unused_trait_names)] +#![feature(decl_macro)] + +extern crate proc_macros; + +fn main() {} + +fn bad() { + use std::any::Any as _; + + println!("{:?}", "foo".type_id()); +} + +fn good() { + use std::any::Any as _; + + println!("{:?}", "foo".type_id()); +} + +fn used_good() { + use std::any::Any; + + println!("{:?}", Any::type_id("foo")); + println!("{:?}", "foo".type_id()); +} + +fn multi_bad() { + use std::any::{self, Any as _, TypeId}; + + println!("{:?}", "foo".type_id()); +} + +fn multi_good() { + use std::any::{self, Any as _, TypeId}; + + println!("{:?}", "foo".type_id()); +} + +fn renamed_bad() { + use std::any::Any as _; + + println!("{:?}", "foo".type_id()); +} + +fn multi_renamed_bad() { + use std::any::{Any as _, TypeId as MyTypeId}; + + println!("{:?}", "foo".type_id()); +} + +mod pub_good { + pub use std::any::Any; + + fn foo() { + println!("{:?}", "foo".type_id()); + } +} + +mod used_mod_good { + use std::any::Any; + + fn foo() { + println!("{:?}", Any::type_id("foo")); + } +} + +mod mod_import_bad { + fn mod_import_bad() { + use std::any::Any as _; + + println!("{:?}", "foo".type_id()); + } +} + +mod nested_mod_used_good1 { + use std::any::Any; + + mod foo { + fn foo() { + super::Any::type_id("foo"); + } + } +} + +mod nested_mod_used_good2 { + use std::any::Any; + + mod foo { + use super::Any; + + fn foo() { + Any::type_id("foo"); + } + } +} + +mod nested_mod_used_good3 { + use std::any::Any; + + mod foo { + use crate::nested_mod_used_good3::Any; + + fn foo() { + println!("{:?}", Any::type_id("foo")); + } + } +} + +mod nested_mod_used_bad { + use std::any::Any as _; + + fn bar() { + println!("{:?}", "foo".type_id()); + } + + mod foo { + use std::any::Any; + + fn foo() { + println!("{:?}", Any::type_id("foo")); + } + } +} + +// More complex example where `use std::any::Any;` should be anonymised but `use std::any::Any as +// MyAny;` should not as it is used by a sub module. Even though if you removed `use std::any::Any;` +// the code would still compile. +mod nested_mod_used_bad1 { + use std::any::Any as _; + + use std::any::Any as MyAny; + + fn baz() { + println!("{:?}", "baz".type_id()); + } + + mod foo { + use crate::nested_mod_used_bad1::MyAny; + + fn foo() { + println!("{:?}", MyAny::type_id("foo")); + } + } +} + +// Example of nested import with an unused import to try and trick it +mod nested_mod_used_good5 { + use std::any::Any; + + mod foo { + use std::any::Any; + + fn baz() { + println!("{:?}", "baz".type_id()); + } + + mod bar { + use crate::nested_mod_used_good5::foo::Any; + + fn foo() { + println!("{:?}", Any::type_id("foo")); + } + } + } +} + +mod simple_trait { + pub trait MyTrait { + fn do_things(&self); + } + + pub struct MyStruct; + + impl MyTrait for MyStruct { + fn do_things(&self) {} + } +} + +// Underscore imports were stabilized in 1.33 +#[clippy::msrv = "1.32"] +fn msrv_1_32() { + use simple_trait::{MyStruct, MyTrait}; + MyStruct.do_things(); +} + +#[clippy::msrv = "1.33"] +fn msrv_1_33() { + use simple_trait::{MyStruct, MyTrait as _}; + MyStruct.do_things(); +} + +mod lint_inside_macro_expansion_bad { + macro_rules! foo { + () => { + use std::any::Any as _; + fn bar() { + "bar".type_id(); + } + }; + } + + foo!(); +} + +mod macro_and_trait_same_name { + pub macro Foo() {} + pub trait Foo { + fn bar(&self); + } + impl Foo for () { + fn bar(&self) {} + } +} + +fn call_macro_and_trait_good() { + // importing trait and macro but only using macro by path won't allow us to change this to + // `use macro_and_trait_same_name::Foo as _;` + use macro_and_trait_same_name::Foo; + Foo!(); + ().bar(); +} + +proc_macros::external!( + fn ignore_inside_external_proc_macro() { + use std::any::Any; + "foo".type_id(); + } +); + +proc_macros::with_span!( + span + + fn ignore_inside_with_span_proc_macro() { + use std::any::Any; + "foo".type_id(); + } +); + +// This should warn the import is unused but should not trigger unused_trait_names +#[warn(unused)] +mod unused_import { + +} + +#[allow(clippy::unused_trait_names)] +fn allow_lint_fn() { + use std::any::Any; + + "foo".type_id(); +} + +#[allow(clippy::unused_trait_names)] +mod allow_lint_mod { + use std::any::Any; + + fn foo() { + "foo".type_id(); + } +} + +mod allow_lint_import { + #[allow(clippy::unused_trait_names)] + use std::any::Any; + + fn foo() { + "foo".type_id(); + } +} + +// Limitation: Suggests `use std::any::Any as _::{self};` which looks weird +// fn use_trait_self_good() { +// use std::any::Any::{self}; +// "foo".type_id(); +// } + +// Limitation: Suggests `use std::any::{Any as _, Any as _};` +// mod repeated_renamed { +// use std::any::{Any, Any as MyAny}; + +// fn foo() { +// "foo".type_id(); +// } +// } diff --git a/tests/ui/unused_trait_names.rs b/tests/ui/unused_trait_names.rs new file mode 100644 index 0000000000000..ce44eedbc79c2 --- /dev/null +++ b/tests/ui/unused_trait_names.rs @@ -0,0 +1,286 @@ +//@aux-build:proc_macros.rs + +#![allow(unused)] +#![warn(clippy::unused_trait_names)] +#![feature(decl_macro)] + +extern crate proc_macros; + +fn main() {} + +fn bad() { + use std::any::Any; + + println!("{:?}", "foo".type_id()); +} + +fn good() { + use std::any::Any as _; + + println!("{:?}", "foo".type_id()); +} + +fn used_good() { + use std::any::Any; + + println!("{:?}", Any::type_id("foo")); + println!("{:?}", "foo".type_id()); +} + +fn multi_bad() { + use std::any::{self, Any, TypeId}; + + println!("{:?}", "foo".type_id()); +} + +fn multi_good() { + use std::any::{self, Any as _, TypeId}; + + println!("{:?}", "foo".type_id()); +} + +fn renamed_bad() { + use std::any::Any as MyAny; + + println!("{:?}", "foo".type_id()); +} + +fn multi_renamed_bad() { + use std::any::{Any as MyAny, TypeId as MyTypeId}; + + println!("{:?}", "foo".type_id()); +} + +mod pub_good { + pub use std::any::Any; + + fn foo() { + println!("{:?}", "foo".type_id()); + } +} + +mod used_mod_good { + use std::any::Any; + + fn foo() { + println!("{:?}", Any::type_id("foo")); + } +} + +mod mod_import_bad { + fn mod_import_bad() { + use std::any::Any; + + println!("{:?}", "foo".type_id()); + } +} + +mod nested_mod_used_good1 { + use std::any::Any; + + mod foo { + fn foo() { + super::Any::type_id("foo"); + } + } +} + +mod nested_mod_used_good2 { + use std::any::Any; + + mod foo { + use super::Any; + + fn foo() { + Any::type_id("foo"); + } + } +} + +mod nested_mod_used_good3 { + use std::any::Any; + + mod foo { + use crate::nested_mod_used_good3::Any; + + fn foo() { + println!("{:?}", Any::type_id("foo")); + } + } +} + +mod nested_mod_used_bad { + use std::any::Any; + + fn bar() { + println!("{:?}", "foo".type_id()); + } + + mod foo { + use std::any::Any; + + fn foo() { + println!("{:?}", Any::type_id("foo")); + } + } +} + +// More complex example where `use std::any::Any;` should be anonymised but `use std::any::Any as +// MyAny;` should not as it is used by a sub module. Even though if you removed `use std::any::Any;` +// the code would still compile. +mod nested_mod_used_bad1 { + use std::any::Any; + + use std::any::Any as MyAny; + + fn baz() { + println!("{:?}", "baz".type_id()); + } + + mod foo { + use crate::nested_mod_used_bad1::MyAny; + + fn foo() { + println!("{:?}", MyAny::type_id("foo")); + } + } +} + +// Example of nested import with an unused import to try and trick it +mod nested_mod_used_good5 { + use std::any::Any; + + mod foo { + use std::any::Any; + + fn baz() { + println!("{:?}", "baz".type_id()); + } + + mod bar { + use crate::nested_mod_used_good5::foo::Any; + + fn foo() { + println!("{:?}", Any::type_id("foo")); + } + } + } +} + +mod simple_trait { + pub trait MyTrait { + fn do_things(&self); + } + + pub struct MyStruct; + + impl MyTrait for MyStruct { + fn do_things(&self) {} + } +} + +// Underscore imports were stabilized in 1.33 +#[clippy::msrv = "1.32"] +fn msrv_1_32() { + use simple_trait::{MyStruct, MyTrait}; + MyStruct.do_things(); +} + +#[clippy::msrv = "1.33"] +fn msrv_1_33() { + use simple_trait::{MyStruct, MyTrait}; + MyStruct.do_things(); +} + +mod lint_inside_macro_expansion_bad { + macro_rules! foo { + () => { + use std::any::Any; + fn bar() { + "bar".type_id(); + } + }; + } + + foo!(); +} + +mod macro_and_trait_same_name { + pub macro Foo() {} + pub trait Foo { + fn bar(&self); + } + impl Foo for () { + fn bar(&self) {} + } +} + +fn call_macro_and_trait_good() { + // importing trait and macro but only using macro by path won't allow us to change this to + // `use macro_and_trait_same_name::Foo as _;` + use macro_and_trait_same_name::Foo; + Foo!(); + ().bar(); +} + +proc_macros::external!( + fn ignore_inside_external_proc_macro() { + use std::any::Any; + "foo".type_id(); + } +); + +proc_macros::with_span!( + span + + fn ignore_inside_with_span_proc_macro() { + use std::any::Any; + "foo".type_id(); + } +); + +// This should warn the import is unused but should not trigger unused_trait_names +#[warn(unused)] +mod unused_import { + use std::any::Any; +} + +#[allow(clippy::unused_trait_names)] +fn allow_lint_fn() { + use std::any::Any; + + "foo".type_id(); +} + +#[allow(clippy::unused_trait_names)] +mod allow_lint_mod { + use std::any::Any; + + fn foo() { + "foo".type_id(); + } +} + +mod allow_lint_import { + #[allow(clippy::unused_trait_names)] + use std::any::Any; + + fn foo() { + "foo".type_id(); + } +} + +// Limitation: Suggests `use std::any::Any as _::{self};` which looks weird +// fn use_trait_self_good() { +// use std::any::Any::{self}; +// "foo".type_id(); +// } + +// Limitation: Suggests `use std::any::{Any as _, Any as _};` +// mod repeated_renamed { +// use std::any::{Any, Any as MyAny}; + +// fn foo() { +// "foo".type_id(); +// } +// } diff --git a/tests/ui/unused_trait_names.stderr b/tests/ui/unused_trait_names.stderr new file mode 100644 index 0000000000000..f59d8f58a170a --- /dev/null +++ b/tests/ui/unused_trait_names.stderr @@ -0,0 +1,73 @@ +error: unused import: `std::any::Any` + --> tests/ui/unused_trait_names.rs:245:9 + | +LL | use std::any::Any; + | ^^^^^^^^^^^^^ + | + = note: `-D unused-imports` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(unused_imports)]` + +error: importing trait that is only used anonymously + --> tests/ui/unused_trait_names.rs:12:19 + | +LL | use std::any::Any; + | ^^^ help: use: `Any as _` + | + = note: `-D clippy::unused-trait-names` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unused_trait_names)]` + +error: importing trait that is only used anonymously + --> tests/ui/unused_trait_names.rs:31:26 + | +LL | use std::any::{self, Any, TypeId}; + | ^^^ help: use: `Any as _` + +error: importing trait that is only used anonymously + --> tests/ui/unused_trait_names.rs:43:19 + | +LL | use std::any::Any as MyAny; + | ^^^^^^^^^^^^ help: use: `Any as _` + +error: importing trait that is only used anonymously + --> tests/ui/unused_trait_names.rs:49:20 + | +LL | use std::any::{Any as MyAny, TypeId as MyTypeId}; + | ^^^^^^^^^^^^ help: use: `Any as _` + +error: importing trait that is only used anonymously + --> tests/ui/unused_trait_names.rs:72:23 + | +LL | use std::any::Any; + | ^^^ help: use: `Any as _` + +error: importing trait that is only used anonymously + --> tests/ui/unused_trait_names.rs:113:19 + | +LL | use std::any::Any; + | ^^^ help: use: `Any as _` + +error: importing trait that is only used anonymously + --> tests/ui/unused_trait_names.rs:132:19 + | +LL | use std::any::Any; + | ^^^ help: use: `Any as _` + +error: importing trait that is only used anonymously + --> tests/ui/unused_trait_names.rs:191:34 + | +LL | use simple_trait::{MyStruct, MyTrait}; + | ^^^^^^^ help: use: `MyTrait as _` + +error: importing trait that is only used anonymously + --> tests/ui/unused_trait_names.rs:198:27 + | +LL | use std::any::Any; + | ^^^ help: use: `Any as _` +... +LL | foo!(); + | ------ in this macro invocation + | + = note: this error originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 10 previous errors + diff --git a/tests/ui/used_underscore_binding.stderr b/tests/ui/used_underscore_binding.stderr index 556e1792b3e68..f9e8013d3ad57 100644 --- a/tests/ui/used_underscore_binding.stderr +++ b/tests/ui/used_underscore_binding.stderr @@ -1,10 +1,10 @@ -error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used +error: used underscore-prefixed binding --> tests/ui/used_underscore_binding.rs:23:5 | LL | _foo + 1 | ^^^^ | -note: `_foo` is defined here +note: binding is defined here --> tests/ui/used_underscore_binding.rs:22:22 | LL | fn prefix_underscore(_foo: u32) -> u32 { @@ -12,61 +12,61 @@ LL | fn prefix_underscore(_foo: u32) -> u32 { = note: `-D clippy::used-underscore-binding` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::used_underscore_binding)]` -error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used +error: used underscore-prefixed binding --> tests/ui/used_underscore_binding.rs:28:20 | LL | println!("{}", _foo); | ^^^^ | -note: `_foo` is defined here +note: binding is defined here --> tests/ui/used_underscore_binding.rs:27:24 | LL | fn in_macro_or_desugar(_foo: u32) { | ^^^^ -error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used +error: used underscore-prefixed binding --> tests/ui/used_underscore_binding.rs:29:16 | LL | assert_eq!(_foo, _foo); | ^^^^ | -note: `_foo` is defined here +note: binding is defined here --> tests/ui/used_underscore_binding.rs:27:24 | LL | fn in_macro_or_desugar(_foo: u32) { | ^^^^ -error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used +error: used underscore-prefixed binding --> tests/ui/used_underscore_binding.rs:29:22 | LL | assert_eq!(_foo, _foo); | ^^^^ | -note: `_foo` is defined here +note: binding is defined here --> tests/ui/used_underscore_binding.rs:27:24 | LL | fn in_macro_or_desugar(_foo: u32) { | ^^^^ -error: used binding `_underscore_field` which is prefixed with an underscore. A leading underscore signals that a binding will not be used +error: used underscore-prefixed binding --> tests/ui/used_underscore_binding.rs:42:5 | LL | s._underscore_field += 1; | ^^^^^^^^^^^^^^^^^^^ | -note: `_underscore_field` is defined here +note: binding is defined here --> tests/ui/used_underscore_binding.rs:36:5 | LL | _underscore_field: u32, | ^^^^^^^^^^^^^^^^^^^^^^ -error: used binding `_i` which is prefixed with an underscore. A leading underscore signals that a binding will not be used +error: used underscore-prefixed binding --> tests/ui/used_underscore_binding.rs:103:16 | LL | uses_i(_i); | ^^ | -note: `_i` is defined here +note: binding is defined here --> tests/ui/used_underscore_binding.rs:102:13 | LL | let _i = 5; diff --git a/tests/ui/used_underscore_items.rs b/tests/ui/used_underscore_items.rs new file mode 100644 index 0000000000000..223016a5c9667 --- /dev/null +++ b/tests/ui/used_underscore_items.rs @@ -0,0 +1,63 @@ +//@aux-build:external_item.rs +#![allow(unused)] +#![warn(clippy::used_underscore_items)] + +extern crate external_item; + +// should not lint macro +macro_rules! macro_wrap_func { + () => { + fn _marco_foo() {} + }; +} + +macro_wrap_func!(); + +struct _FooStruct {} + +impl _FooStruct { + fn _method_call(self) {} +} + +fn _foo1() {} + +fn _foo2() -> i32 { + 0 +} + +mod a { + pub mod b { + pub mod c { + pub fn _foo3() {} + + pub struct _FooStruct2 {} + + impl _FooStruct2 { + pub fn _method_call(self) {} + } + } + } +} + +fn main() { + _foo1(); + let _ = _foo2(); + a::b::c::_foo3(); + let _ = &_FooStruct {}; + let _ = _FooStruct {}; + + let foo_struct = _FooStruct {}; + foo_struct._method_call(); + + let foo_struct2 = a::b::c::_FooStruct2 {}; + foo_struct2._method_call(); +} + +// should not lint exteranl crate. +// user cannot control how others name their items +fn external_item_call() { + let foo_struct3 = external_item::_ExternalStruct {}; + foo_struct3._foo(); + + external_item::_exernal_foo(); +} diff --git a/tests/ui/used_underscore_items.stderr b/tests/ui/used_underscore_items.stderr new file mode 100644 index 0000000000000..93ac3a6fec6be --- /dev/null +++ b/tests/ui/used_underscore_items.stderr @@ -0,0 +1,112 @@ +error: used underscore-prefixed item + --> tests/ui/used_underscore_items.rs:43:5 + | +LL | _foo1(); + | ^^^^^^^ + | +note: item is defined here + --> tests/ui/used_underscore_items.rs:22:1 + | +LL | fn _foo1() {} + | ^^^^^^^^^^ + = note: `-D clippy::used-underscore-items` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::used_underscore_items)]` + +error: used underscore-prefixed item + --> tests/ui/used_underscore_items.rs:44:13 + | +LL | let _ = _foo2(); + | ^^^^^^^ + | +note: item is defined here + --> tests/ui/used_underscore_items.rs:24:1 + | +LL | fn _foo2() -> i32 { + | ^^^^^^^^^^^^^^^^^ + +error: used underscore-prefixed item + --> tests/ui/used_underscore_items.rs:45:5 + | +LL | a::b::c::_foo3(); + | ^^^^^^^^^^^^^^^^ + | +note: item is defined here + --> tests/ui/used_underscore_items.rs:31:13 + | +LL | pub fn _foo3() {} + | ^^^^^^^^^^^^^^ + +error: used underscore-prefixed item + --> tests/ui/used_underscore_items.rs:46:14 + | +LL | let _ = &_FooStruct {}; + | ^^^^^^^^^^^^^ + | +note: item is defined here + --> tests/ui/used_underscore_items.rs:16:1 + | +LL | struct _FooStruct {} + | ^^^^^^^^^^^^^^^^^ + +error: used underscore-prefixed item + --> tests/ui/used_underscore_items.rs:47:13 + | +LL | let _ = _FooStruct {}; + | ^^^^^^^^^^^^^ + | +note: item is defined here + --> tests/ui/used_underscore_items.rs:16:1 + | +LL | struct _FooStruct {} + | ^^^^^^^^^^^^^^^^^ + +error: used underscore-prefixed item + --> tests/ui/used_underscore_items.rs:49:22 + | +LL | let foo_struct = _FooStruct {}; + | ^^^^^^^^^^^^^ + | +note: item is defined here + --> tests/ui/used_underscore_items.rs:16:1 + | +LL | struct _FooStruct {} + | ^^^^^^^^^^^^^^^^^ + +error: used underscore-prefixed item + --> tests/ui/used_underscore_items.rs:50:5 + | +LL | foo_struct._method_call(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: item is defined here + --> tests/ui/used_underscore_items.rs:19:5 + | +LL | fn _method_call(self) {} + | ^^^^^^^^^^^^^^^^^^^^^ + +error: used underscore-prefixed item + --> tests/ui/used_underscore_items.rs:52:23 + | +LL | let foo_struct2 = a::b::c::_FooStruct2 {}; + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +note: item is defined here + --> tests/ui/used_underscore_items.rs:33:13 + | +LL | pub struct _FooStruct2 {} + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: used underscore-prefixed item + --> tests/ui/used_underscore_items.rs:53:5 + | +LL | foo_struct2._method_call(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: item is defined here + --> tests/ui/used_underscore_items.rs:36:17 + | +LL | pub fn _method_call(self) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 9 previous errors + diff --git a/tests/ui/zombie_processes.rs b/tests/ui/zombie_processes.rs new file mode 100644 index 0000000000000..a2abc7fc3a179 --- /dev/null +++ b/tests/ui/zombie_processes.rs @@ -0,0 +1,138 @@ +#![warn(clippy::zombie_processes)] +#![allow(clippy::if_same_then_else, clippy::ifs_same_cond)] + +use std::process::{Child, Command}; + +fn main() { + { + // Check that #[expect] works + #[expect(clippy::zombie_processes)] + let mut x = Command::new("").spawn().unwrap(); + } + + { + let mut x = Command::new("").spawn().unwrap(); + //~^ ERROR: spawned process is never `wait()`ed on + x.kill(); + x.id(); + } + { + let mut x = Command::new("").spawn().unwrap(); + x.wait().unwrap(); // OK + } + { + let x = Command::new("").spawn().unwrap(); + x.wait_with_output().unwrap(); // OK + } + { + let mut x = Command::new("").spawn().unwrap(); + x.try_wait().unwrap(); // OK + } + { + let mut x = Command::new("").spawn().unwrap(); + let mut r = &mut x; + r.wait().unwrap(); // OK, not calling `.wait()` directly on `x` but through `r` -> `x` + } + { + let mut x = Command::new("").spawn().unwrap(); + process_child(x); // OK, other function might call `.wait()` so assume it does + } + { + let mut x = Command::new("").spawn().unwrap(); + //~^ ERROR: spawned process is never `wait()`ed on + let v = &x; + // (allow shared refs is fine because one cannot call `.wait()` through that) + } + + // https://github.com/rust-lang/rust-clippy/pull/11476#issuecomment-1718456033 + // Unconditionally exiting the process in various ways (should not lint) + { + let mut x = Command::new("").spawn().unwrap(); + std::process::exit(0); + } + { + let mut x = Command::new("").spawn().unwrap(); + std::process::abort(); // same as above, but abort instead of exit + } + { + let mut x = Command::new("").spawn().unwrap(); + if true { /* nothing */ } + std::process::abort(); // still unconditionally exits + } + + // Conditionally exiting + // It should assume that it might not exit and still lint + { + let mut x = Command::new("").spawn().unwrap(); + //~^ ERROR: spawned process is never `wait()`ed on + if true { + std::process::exit(0); + } + } + { + let mut x = Command::new("").spawn().unwrap(); + //~^ ERROR: spawned process is never `wait()`ed on + if true { + while false {} + // Calling `exit()` after leaving a while loop should still be linted. + std::process::exit(0); + } + } + + { + let mut x = { Command::new("").spawn().unwrap() }; + x.wait().unwrap(); + } + + { + struct S { + c: Child, + } + + let mut s = S { + c: Command::new("").spawn().unwrap(), + }; + s.c.wait().unwrap(); + } + + { + let mut x = Command::new("").spawn().unwrap(); + //~^ ERROR: spawned process is never `wait()`ed on + if true { + return; + } + x.wait().unwrap(); + } + + { + let mut x = Command::new("").spawn().unwrap(); + //~^ ERROR: spawned process is never `wait()`ed on + if true { + x.wait().unwrap(); + } + } + + { + let mut x = Command::new("").spawn().unwrap(); + if true { + x.wait().unwrap(); + } else if true { + x.wait().unwrap(); + } else { + x.wait().unwrap(); + } + } + + { + let mut x = Command::new("").spawn().unwrap(); + if true { + x.wait().unwrap(); + return; + } + x.wait().unwrap(); + } +} + +fn process_child(c: Child) { + todo!() +} diff --git a/tests/ui/zombie_processes.stderr b/tests/ui/zombie_processes.stderr new file mode 100644 index 0000000000000..eec821a4c8f18 --- /dev/null +++ b/tests/ui/zombie_processes.stderr @@ -0,0 +1,64 @@ +error: spawned process is never `wait()`ed on + --> tests/ui/zombie_processes.rs:14:21 + | +LL | let mut x = Command::new("").spawn().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: consider calling `.wait()` + = note: not doing so might leave behind zombie processes + = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning + = note: `-D clippy::zombie-processes` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::zombie_processes)]` + +error: spawned process is never `wait()`ed on + --> tests/ui/zombie_processes.rs:41:21 + | +LL | let mut x = Command::new("").spawn().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: consider calling `.wait()` + = note: not doing so might leave behind zombie processes + = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning + +error: spawned process is never `wait()`ed on + --> tests/ui/zombie_processes.rs:66:21 + | +LL | let mut x = Command::new("").spawn().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: consider calling `.wait()` + = note: not doing so might leave behind zombie processes + = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning + +error: spawned process is never `wait()`ed on + --> tests/ui/zombie_processes.rs:73:21 + | +LL | let mut x = Command::new("").spawn().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: consider calling `.wait()` + = note: not doing so might leave behind zombie processes + = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning + +error: spawned process is never `wait()`ed on + --> tests/ui/zombie_processes.rs:99:21 + | +LL | let mut x = Command::new("").spawn().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: consider calling `.wait()` + = note: not doing so might leave behind zombie processes + = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning + +error: spawned process is never `wait()`ed on + --> tests/ui/zombie_processes.rs:108:21 + | +LL | let mut x = Command::new("").spawn().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: consider calling `.wait()` + = note: not doing so might leave behind zombie processes + = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning + +error: aborting due to 6 previous errors + diff --git a/tests/ui/zombie_processes_fixable.fixed b/tests/ui/zombie_processes_fixable.fixed new file mode 100644 index 0000000000000..6045262f519a7 --- /dev/null +++ b/tests/ui/zombie_processes_fixable.fixed @@ -0,0 +1,26 @@ +#![warn(clippy::zombie_processes)] +#![allow(clippy::needless_return)] + +use std::process::{Child, Command}; + +fn main() { + let _ = Command::new("").spawn().unwrap().wait(); + //~^ ERROR: spawned process is never `wait()`ed on + Command::new("").spawn().unwrap().wait(); + //~^ ERROR: spawned process is never `wait()`ed on + spawn_proc().wait(); + //~^ ERROR: spawned process is never `wait()`ed on + spawn_proc().wait().unwrap(); // OK +} + +fn not_main() { + Command::new("").spawn().unwrap().wait(); +} + +fn spawn_proc() -> Child { + Command::new("").spawn().unwrap() +} + +fn spawn_proc_2() -> Child { + return Command::new("").spawn().unwrap(); +} diff --git a/tests/ui/zombie_processes_fixable.rs b/tests/ui/zombie_processes_fixable.rs new file mode 100644 index 0000000000000..e1ecb771641e2 --- /dev/null +++ b/tests/ui/zombie_processes_fixable.rs @@ -0,0 +1,26 @@ +#![warn(clippy::zombie_processes)] +#![allow(clippy::needless_return)] + +use std::process::{Child, Command}; + +fn main() { + let _ = Command::new("").spawn().unwrap(); + //~^ ERROR: spawned process is never `wait()`ed on + Command::new("").spawn().unwrap(); + //~^ ERROR: spawned process is never `wait()`ed on + spawn_proc(); + //~^ ERROR: spawned process is never `wait()`ed on + spawn_proc().wait().unwrap(); // OK +} + +fn not_main() { + Command::new("").spawn().unwrap(); +} + +fn spawn_proc() -> Child { + Command::new("").spawn().unwrap() +} + +fn spawn_proc_2() -> Child { + return Command::new("").spawn().unwrap(); +} diff --git a/tests/ui/zombie_processes_fixable.stderr b/tests/ui/zombie_processes_fixable.stderr new file mode 100644 index 0000000000000..e1c40472c325e --- /dev/null +++ b/tests/ui/zombie_processes_fixable.stderr @@ -0,0 +1,40 @@ +error: spawned process is never `wait()`ed on + --> tests/ui/zombie_processes_fixable.rs:7:13 + | +LL | let _ = Command::new("").spawn().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- help: try: `.wait()` + | + = note: not doing so might leave behind zombie processes + = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning + = note: `-D clippy::zombie-processes` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::zombie_processes)]` + +error: spawned process is never `wait()`ed on + --> tests/ui/zombie_processes_fixable.rs:9:5 + | +LL | Command::new("").spawn().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- help: try: `.wait()` + | + = note: not doing so might leave behind zombie processes + = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning + +error: spawned process is never `wait()`ed on + --> tests/ui/zombie_processes_fixable.rs:11:5 + | +LL | spawn_proc(); + | ^^^^^^^^^^^^- help: try: `.wait()` + | + = note: not doing so might leave behind zombie processes + = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning + +error: spawned process is never `wait()`ed on + --> tests/ui/zombie_processes_fixable.rs:17:5 + | +LL | Command::new("").spawn().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- help: try: `.wait()` + | + = note: not doing so might leave behind zombie processes + = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning + +error: aborting due to 4 previous errors + diff --git a/triagebot.toml b/triagebot.toml index 99b3560a06427..dcf00e4e384b0 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -20,7 +20,6 @@ new_pr = true [assign] contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md" users_on_vacation = [ - "flip1995", "matthiaskrgr", "giraffate", ] diff --git a/util/gh-pages/versions.html b/util/gh-pages/versions.html index 31ce881932952..d38fe39a46403 100644 --- a/util/gh-pages/versions.html +++ b/util/gh-pages/versions.html @@ -1,4 +1,5 @@ + @@ -7,26 +8,15 @@ Clippy lints documentation - -
+
-
- - - -
- - - - - - - + .github-corner:hover .octo-arm { + animation: octocat-wave 560ms ease-in-out; + } + @keyframes octocat-wave { + 0%, + 100% { + transform: rotate(0); + } + 20%, + 60% { + transform: rotate(-25deg); + } + 40%, + 80% { + transform: rotate(10deg); + } + } + @media (max-width: 500px) { + .github-corner:hover .octo-arm { + animation: none; + } + .github-corner .octo-arm { + animation: octocat-wave 560ms ease-in-out; + } + } + + diff --git a/util/versions.py b/util/versions.py index c041fc606a8f3..7a21a840a6d9a 100755 --- a/util/versions.py +++ b/util/versions.py @@ -1,22 +1,22 @@ #!/usr/bin/env python +from string import Template +import argparse import json -import logging as log import os import sys -log.basicConfig(level=log.INFO, format="%(levelname)s: %(message)s") - - def key(v): if v == "master": - return float("inf") - if v == "stable": return sys.maxsize - if v == "beta": + if v == "stable": return sys.maxsize - 1 + if v == "beta": + return sys.maxsize - 2 if v == "pre-1.29.0": return -1 + if not v.startswith("rust-"): + return None v = v.replace("rust-", "") @@ -26,26 +26,27 @@ def key(v): return s - def main(): - if len(sys.argv) < 2: - log.error("specify output directory") - return + parser = argparse.ArgumentParser() + parser.add_argument("input", help="path to the versions.html template", type=argparse.FileType("r")) + parser.add_argument("outdir", help="path to write the output HTML") + args = parser.parse_args() - outdir = sys.argv[1] versions = [ dir - for dir in os.listdir(outdir) - if not dir.startswith(".") - and not dir.startswith("v") - and os.path.isdir(os.path.join(outdir, dir)) + for dir in os.listdir(args.outdir) + if key(dir) is not None ] - versions.sort(key=key) + versions.sort(key=key, reverse=True) + links = [f'{version}' for version in versions] - with open(os.path.join(outdir, "versions.json"), "w") as fp: - json.dump(versions, fp, indent=2) - log.info("wrote JSON for great justice") + template = Template(args.input.read()) + html = template.substitute(list="\n".join(links)) + path = os.path.join(args.outdir, "index.html") + with open(path, "w") as out: + out.write(html) + print(f"wrote HTML to {path}") if __name__ == "__main__": main() From 4108c6e5a0d0f6839f7586ae9681fb7ca4fc2f49 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Tue, 24 Sep 2024 11:58:48 +0200 Subject: [PATCH 010/120] Hotfix: remove profile from clippy Cargo.toml --- Cargo.toml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ddc27179ef2f2..cf810798d8cc1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -67,10 +67,3 @@ harness = false [[test]] name = "dogfood" harness = false - -# quine-mc_cluskey makes up a significant part of the runtime in dogfood -# due to the number of conditions in the clippy_lints crate -# and enabling optimizations for that specific dependency helps a bit -# without increasing total build times. -[profile.dev.package.quine-mc_cluskey] -opt-level = 3 From f203e1ede5d46b0281b2b83250d761350e85deda Mon Sep 17 00:00:00 2001 From: Liam Germain Date: Tue, 24 Sep 2024 09:52:11 -0400 Subject: [PATCH 011/120] Fix false positive invalid null usage warning for `ptr::slice_from_raw_parts_*` functions --- clippy_lints/src/ptr.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 807636bb642b2..d523ec2edff6d 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -277,8 +277,6 @@ fn check_invalid_ptr_usage<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { | sym::ptr_read_unaligned | sym::ptr_read_volatile | sym::ptr_replace - | sym::ptr_slice_from_raw_parts - | sym::ptr_slice_from_raw_parts_mut | sym::ptr_write | sym::ptr_write_bytes | sym::ptr_write_unaligned From f8969853ebcf591b20d38ca3ff3b72537be8f581 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 24 Sep 2024 10:11:23 -0400 Subject: [PATCH 012/120] Fix tools --- clippy_lints/src/derive.rs | 1 - clippy_lints/src/inherent_impl.rs | 4 +--- clippy_lints/src/len_zero.rs | 3 +-- clippy_lints/src/methods/or_fun_call.rs | 1 - clippy_utils/src/lib.rs | 8 ++------ clippy_utils/src/ty.rs | 2 +- 6 files changed, 5 insertions(+), 14 deletions(-) diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 636698e96f6d1..649c480fb92a1 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -386,7 +386,6 @@ fn check_unsafe_derive_deserialize<'tcx>( .tcx .inherent_impls(def.did()) .into_iter() - .flatten() .map(|imp_did| cx.tcx.hir().expect_item(imp_did.expect_local())) .any(|imp| has_unsafe(cx, imp)) { diff --git a/clippy_lints/src/inherent_impl.rs b/clippy_lints/src/inherent_impl.rs index d39f910f9936d..309d2dfb28b87 100644 --- a/clippy_lints/src/inherent_impl.rs +++ b/clippy_lints/src/inherent_impl.rs @@ -51,9 +51,7 @@ impl<'tcx> LateLintPass<'tcx> for MultipleInherentImpl { // List of spans to lint. (lint_span, first_span) let mut lint_spans = Vec::new(); - let Ok(impls) = cx.tcx.crate_inherent_impls(()) else { - return; - }; + let (impls, _) = cx.tcx.crate_inherent_impls(()); for (&id, impl_ids) in &impls.inherent_impls { if impl_ids.len() < 2 diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 0bc7fc2dd9d1e..16f86b18e2e55 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -448,7 +448,6 @@ fn check_for_is_empty( .tcx .inherent_impls(impl_ty) .into_iter() - .flatten() .flat_map(|&id| cx.tcx.associated_items(id).filter_by_name_unhygienic(is_empty)) .find(|item| item.kind == AssocKind::Fn); @@ -616,7 +615,7 @@ fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { /// Checks the inherent impl's items for an `is_empty(self)` method. fn has_is_empty_impl(cx: &LateContext<'_>, id: DefId) -> bool { let is_empty = sym!(is_empty); - cx.tcx.inherent_impls(id).into_iter().flatten().any(|imp| { + cx.tcx.inherent_impls(id).into_iter().any(|imp| { cx.tcx .associated_items(*imp) .filter_by_name_unhygienic(is_empty) diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index e4326cb958e5f..af71799f0a731 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -78,7 +78,6 @@ pub(super) fn check<'tcx>( cx.tcx .inherent_impls(adt_def.did()) .into_iter() - .flatten() .flat_map(|impl_id| cx.tcx.associated_items(impl_id).filter_by_name_unhygienic(sugg)) .find_map(|assoc| { if assoc.fn_has_self_parameter diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 0d0d6219c5e05..aed0bc565d8e9 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -589,14 +589,11 @@ fn find_primitive_impls<'tcx>(tcx: TyCtxt<'tcx>, name: &str) -> impl Iterator SimplifiedType::Float(FloatTy::F32), "f64" => SimplifiedType::Float(FloatTy::F64), _ => { - return Result::<&[_], rustc_errors::ErrorGuaranteed>::Ok(&[]) - .into_iter() - .flatten() - .copied(); + return [].iter().copied(); }, }; - tcx.incoherent_impls(ty).into_iter().flatten().copied() + tcx.incoherent_impls(ty).into_iter().copied() } fn non_local_item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec { @@ -721,7 +718,6 @@ pub fn def_path_res(tcx: TyCtxt<'_>, path: &[&str]) -> Vec { let inherent_impl_children = tcx .inherent_impls(def_id) .into_iter() - .flatten() .flat_map(|&impl_def_id| item_children_by_name(tcx, impl_def_id, segment)); let direct_children = item_children_by_name(tcx, def_id, segment); diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 585134209ca3a..22aaaa1b73419 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -1316,7 +1316,7 @@ pub fn deref_chain<'cx, 'tcx>(cx: &'cx LateContext<'tcx>, ty: Ty<'tcx>) -> impl /// If you need this, you should wrap this call in `clippy_utils::ty::deref_chain().any(...)`. pub fn get_adt_inherent_method<'a>(cx: &'a LateContext<'_>, ty: Ty<'_>, method_name: Symbol) -> Option<&'a AssocItem> { if let Some(ty_did) = ty.ty_adt_def().map(AdtDef::did) { - cx.tcx.inherent_impls(ty_did).into_iter().flatten().find_map(|&did| { + cx.tcx.inherent_impls(ty_did).into_iter().find_map(|&did| { cx.tcx .associated_items(did) .filter_by_name_unhygienic(method_name) From 11363c7bf275a98d87cfa3ec3b0501deb6804147 Mon Sep 17 00:00:00 2001 From: ljgermain Date: Tue, 24 Sep 2024 11:22:26 -0400 Subject: [PATCH 013/120] Fix failing UI test --- tests/ui/invalid_null_ptr_usage.fixed | 5 ---- tests/ui/invalid_null_ptr_usage.rs | 5 ---- tests/ui/invalid_null_ptr_usage.stderr | 36 +++++++------------------- 3 files changed, 9 insertions(+), 37 deletions(-) diff --git a/tests/ui/invalid_null_ptr_usage.fixed b/tests/ui/invalid_null_ptr_usage.fixed index eeddc2349a133..c8f1858b4f8bd 100644 --- a/tests/ui/invalid_null_ptr_usage.fixed +++ b/tests/ui/invalid_null_ptr_usage.fixed @@ -25,11 +25,6 @@ fn main() { let _a: A = std::ptr::replace(core::ptr::NonNull::dangling().as_ptr(), A); - let _slice: *const [usize] = std::ptr::slice_from_raw_parts(core::ptr::NonNull::dangling().as_ptr(), 0); - let _slice: *const [usize] = std::ptr::slice_from_raw_parts(core::ptr::NonNull::dangling().as_ptr(), 0); - - let _slice: *const [usize] = std::ptr::slice_from_raw_parts_mut(core::ptr::NonNull::dangling().as_ptr(), 0); - std::ptr::swap::(core::ptr::NonNull::dangling().as_ptr(), &mut A); std::ptr::swap::(&mut A, core::ptr::NonNull::dangling().as_ptr()); diff --git a/tests/ui/invalid_null_ptr_usage.rs b/tests/ui/invalid_null_ptr_usage.rs index 8569b77408458..5f3fff8e90d81 100644 --- a/tests/ui/invalid_null_ptr_usage.rs +++ b/tests/ui/invalid_null_ptr_usage.rs @@ -25,11 +25,6 @@ fn main() { let _a: A = std::ptr::replace(std::ptr::null_mut(), A); - let _slice: *const [usize] = std::ptr::slice_from_raw_parts(std::ptr::null(), 0); - let _slice: *const [usize] = std::ptr::slice_from_raw_parts(std::ptr::null_mut(), 0); - - let _slice: *const [usize] = std::ptr::slice_from_raw_parts_mut(std::ptr::null_mut(), 0); - std::ptr::swap::(std::ptr::null_mut(), &mut A); std::ptr::swap::(&mut A, std::ptr::null_mut()); diff --git a/tests/ui/invalid_null_ptr_usage.stderr b/tests/ui/invalid_null_ptr_usage.stderr index 54d79ba1aeb4f..ca7b8c133a5ed 100644 --- a/tests/ui/invalid_null_ptr_usage.stderr +++ b/tests/ui/invalid_null_ptr_usage.stderr @@ -85,70 +85,52 @@ LL | let _a: A = std::ptr::replace(std::ptr::null_mut(), A); | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:28:69 - | -LL | let _slice: *const [usize] = std::ptr::slice_from_raw_parts(std::ptr::null(), 0); - | ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:29:69 - | -LL | let _slice: *const [usize] = std::ptr::slice_from_raw_parts(std::ptr::null_mut(), 0); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:31:73 - | -LL | let _slice: *const [usize] = std::ptr::slice_from_raw_parts_mut(std::ptr::null_mut(), 0); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:33:29 + --> tests/ui/invalid_null_ptr_usage.rs:28:29 | LL | std::ptr::swap::(std::ptr::null_mut(), &mut A); | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:34:37 + --> tests/ui/invalid_null_ptr_usage.rs:29:37 | LL | std::ptr::swap::(&mut A, std::ptr::null_mut()); | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:36:44 + --> tests/ui/invalid_null_ptr_usage.rs:31:44 | LL | std::ptr::swap_nonoverlapping::(std::ptr::null_mut(), &mut A, 0); | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:37:52 + --> tests/ui/invalid_null_ptr_usage.rs:32:52 | LL | std::ptr::swap_nonoverlapping::(&mut A, std::ptr::null_mut(), 0); | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:39:25 + --> tests/ui/invalid_null_ptr_usage.rs:34:25 | LL | std::ptr::write(std::ptr::null_mut(), A); | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:41:35 + --> tests/ui/invalid_null_ptr_usage.rs:36:35 | LL | std::ptr::write_unaligned(std::ptr::null_mut(), A); | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:43:34 + --> tests/ui/invalid_null_ptr_usage.rs:38:34 | LL | std::ptr::write_volatile(std::ptr::null_mut(), A); | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:45:40 + --> tests/ui/invalid_null_ptr_usage.rs:40:40 | LL | std::ptr::write_bytes::(std::ptr::null_mut(), 42, 0); | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` -error: aborting due to 25 previous errors +error: aborting due to 22 previous errors From 6c2e82496f523372e18f311eb61b6e53bafc0da8 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Tue, 24 Sep 2024 18:13:17 +0200 Subject: [PATCH 014/120] Remove unused import from Clippy versions.py file --- util/versions.py | 1 - 1 file changed, 1 deletion(-) diff --git a/util/versions.py b/util/versions.py index 7a21a840a6d9a..fee0d292df16f 100755 --- a/util/versions.py +++ b/util/versions.py @@ -2,7 +2,6 @@ from string import Template import argparse -import json import os import sys From 0791efaaace3927fb6cc716d127561b2028dfc71 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Tue, 24 Sep 2024 18:59:58 +0200 Subject: [PATCH 015/120] Use std_or_core to determine the correct prefix Replace hand-crafted tests by the a call to the `std_or_core()` utility function. --- clippy_lints/src/casts/borrow_as_ptr.rs | 6 +++--- clippy_lints/src/casts/ref_as_ptr.rs | 6 +++--- clippy_lints/src/loops/missing_spin_loop.rs | 10 +++------- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/clippy_lints/src/casts/borrow_as_ptr.rs b/clippy_lints/src/casts/borrow_as_ptr.rs index b7256dd2eae9d..4dd51dcbc9a20 100644 --- a/clippy_lints/src/casts/borrow_as_ptr.rs +++ b/clippy_lints/src/casts/borrow_as_ptr.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_no_std_crate; use clippy_utils::source::snippet_with_context; +use clippy_utils::std_or_core; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Ty, TyKind}; use rustc_lint::LateContext; @@ -16,8 +16,8 @@ pub(super) fn check<'tcx>( ) { if matches!(cast_to.kind, TyKind::Ptr(_)) && let ExprKind::AddrOf(BorrowKind::Ref, mutability, e) = cast_expr.kind + && let Some(std_or_core) = std_or_core(cx) { - let core_or_std = if is_no_std_crate(cx) { "core" } else { "std" }; let macro_name = match mutability { Mutability::Not => "addr_of", Mutability::Mut => "addr_of_mut", @@ -40,7 +40,7 @@ pub(super) fn check<'tcx>( expr.span, "borrow as raw pointer", "try", - format!("{core_or_std}::ptr::{macro_name}!({snip})"), + format!("{std_or_core}::ptr::{macro_name}!({snip})"), Applicability::MachineApplicable, ); } diff --git a/clippy_lints/src/casts/ref_as_ptr.rs b/clippy_lints/src/casts/ref_as_ptr.rs index dfa240ccec623..f699bba20ed01 100644 --- a/clippy_lints/src/casts/ref_as_ptr.rs +++ b/clippy_lints/src/casts/ref_as_ptr.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; -use clippy_utils::{ExprUseNode, expr_use_ctxt, is_no_std_crate}; +use clippy_utils::{ExprUseNode, expr_use_ctxt, std_or_core}; use rustc_errors::Applicability; use rustc_hir::{Expr, Mutability, Ty, TyKind}; use rustc_lint::LateContext; @@ -25,8 +25,8 @@ pub(super) fn check<'tcx>( && let use_cx = expr_use_ctxt(cx, expr) // TODO: only block the lint if `cast_expr` is a temporary && !matches!(use_cx.use_node(cx), ExprUseNode::LetStmt(_) | ExprUseNode::ConstStatic(_)) + && let Some(std_or_core) = std_or_core(cx) { - let core_or_std = if is_no_std_crate(cx) { "core" } else { "std" }; let fn_name = match to_mutbl { Mutability::Not => "from_ref", Mutability::Mut => "from_mut", @@ -56,7 +56,7 @@ pub(super) fn check<'tcx>( expr.span, "reference as raw pointer", "try", - format!("{core_or_std}::ptr::{fn_name}{turbofish}({cast_expr_sugg})"), + format!("{std_or_core}::ptr::{fn_name}{turbofish}({cast_expr_sugg})"), app, ); } diff --git a/clippy_lints/src/loops/missing_spin_loop.rs b/clippy_lints/src/loops/missing_spin_loop.rs index e405829b2f4ba..a9944d64ce2dd 100644 --- a/clippy_lints/src/loops/missing_spin_loop.rs +++ b/clippy_lints/src/loops/missing_spin_loop.rs @@ -1,6 +1,6 @@ use super::MISSING_SPIN_LOOP; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_no_std_crate; +use clippy_utils::std_or_core; use rustc_errors::Applicability; use rustc_hir::{Block, Expr, ExprKind}; use rustc_lint::LateContext; @@ -41,6 +41,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, body: &' && [sym::load, sym::compare_exchange, sym::compare_exchange_weak].contains(&method.ident.name) && let ty::Adt(def, _args) = cx.typeck_results().expr_ty(callee).kind() && cx.tcx.is_diagnostic_item(sym::AtomicBool, def.did()) + && let Some(std_or_core) = std_or_core(cx) { span_lint_and_sugg( cx, @@ -48,12 +49,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, body: &' body.span, "busy-waiting loop should at least have a spin loop hint", "try", - (if is_no_std_crate(cx) { - "{ core::hint::spin_loop() }" - } else { - "{ std::hint::spin_loop() }" - }) - .into(), + format!("{{ {std_or_core}::hint::spin_loop() }}"), Applicability::MachineApplicable, ); } From 31bbe2cee3acb720e4d60d5829db2c292a447c14 Mon Sep 17 00:00:00 2001 From: ljgermain Date: Tue, 24 Sep 2024 15:17:52 -0400 Subject: [PATCH 016/120] Apply review suggestions --- clippy_lints/src/ptr.rs | 6 ++++++ tests/ui/invalid_null_ptr_usage.fixed | 2 ++ tests/ui/invalid_null_ptr_usage.rs | 2 ++ tests/ui/invalid_null_ptr_usage.stderr | 16 ++++++++-------- 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index d523ec2edff6d..bb8a9b6fca85b 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -271,6 +271,12 @@ fn check_invalid_ptr_usage<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { && let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id() && let Some(name) = cx.tcx.get_diagnostic_name(fun_def_id) { + // TODO: `ptr_slice_from_raw_parts` and its mutable variant should probably still be linted + // conditionally based on how the return value is used, but not universally like the other + // functions since there are valid uses for null slice pointers. + // + // See: https://github.com/rust-lang/rust-clippy/pull/13452/files#r1773772034 + // `arg` positions where null would cause U.B. let arg_indices: &[_] = match name { sym::ptr_read diff --git a/tests/ui/invalid_null_ptr_usage.fixed b/tests/ui/invalid_null_ptr_usage.fixed index c8f1858b4f8bd..092e875a2554a 100644 --- a/tests/ui/invalid_null_ptr_usage.fixed +++ b/tests/ui/invalid_null_ptr_usage.fixed @@ -24,6 +24,8 @@ fn main() { let _a: A = std::ptr::read_volatile(core::ptr::NonNull::dangling().as_ptr()); let _a: A = std::ptr::replace(core::ptr::NonNull::dangling().as_ptr(), A); + let _slice: *const [usize] = std::ptr::slice_from_raw_parts(std::ptr::null_mut(), 0); // shouldn't lint + let _slice: *const [usize] = std::ptr::slice_from_raw_parts_mut(std::ptr::null_mut(), 0); std::ptr::swap::(core::ptr::NonNull::dangling().as_ptr(), &mut A); std::ptr::swap::(&mut A, core::ptr::NonNull::dangling().as_ptr()); diff --git a/tests/ui/invalid_null_ptr_usage.rs b/tests/ui/invalid_null_ptr_usage.rs index 5f3fff8e90d81..480b6642a3e3a 100644 --- a/tests/ui/invalid_null_ptr_usage.rs +++ b/tests/ui/invalid_null_ptr_usage.rs @@ -24,6 +24,8 @@ fn main() { let _a: A = std::ptr::read_volatile(std::ptr::null_mut()); let _a: A = std::ptr::replace(std::ptr::null_mut(), A); + let _slice: *const [usize] = std::ptr::slice_from_raw_parts(std::ptr::null_mut(), 0); // shouldn't lint + let _slice: *const [usize] = std::ptr::slice_from_raw_parts_mut(std::ptr::null_mut(), 0); std::ptr::swap::(std::ptr::null_mut(), &mut A); std::ptr::swap::(&mut A, std::ptr::null_mut()); diff --git a/tests/ui/invalid_null_ptr_usage.stderr b/tests/ui/invalid_null_ptr_usage.stderr index ca7b8c133a5ed..a0be2c0ad75c6 100644 --- a/tests/ui/invalid_null_ptr_usage.stderr +++ b/tests/ui/invalid_null_ptr_usage.stderr @@ -85,49 +85,49 @@ LL | let _a: A = std::ptr::replace(std::ptr::null_mut(), A); | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:28:29 + --> tests/ui/invalid_null_ptr_usage.rs:30:29 | LL | std::ptr::swap::(std::ptr::null_mut(), &mut A); | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:29:37 + --> tests/ui/invalid_null_ptr_usage.rs:31:37 | LL | std::ptr::swap::(&mut A, std::ptr::null_mut()); | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:31:44 + --> tests/ui/invalid_null_ptr_usage.rs:33:44 | LL | std::ptr::swap_nonoverlapping::(std::ptr::null_mut(), &mut A, 0); | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:32:52 + --> tests/ui/invalid_null_ptr_usage.rs:34:52 | LL | std::ptr::swap_nonoverlapping::(&mut A, std::ptr::null_mut(), 0); | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:34:25 + --> tests/ui/invalid_null_ptr_usage.rs:36:25 | LL | std::ptr::write(std::ptr::null_mut(), A); | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:36:35 + --> tests/ui/invalid_null_ptr_usage.rs:38:35 | LL | std::ptr::write_unaligned(std::ptr::null_mut(), A); | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:38:34 + --> tests/ui/invalid_null_ptr_usage.rs:40:34 | LL | std::ptr::write_volatile(std::ptr::null_mut(), A); | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:40:40 + --> tests/ui/invalid_null_ptr_usage.rs:42:40 | LL | std::ptr::write_bytes::(std::ptr::null_mut(), 42, 0); | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` From d802a7a3c79e96be1f77c7ce3ecc7f8c650000f7 Mon Sep 17 00:00:00 2001 From: Lukas Markeffsky <@> Date: Sun, 15 Sep 2024 16:47:42 +0200 Subject: [PATCH 017/120] unify dyn* coercions with other pointer coercions --- clippy_utils/src/qualify_min_const_fn.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index d9befb3c157a3..6434c2f42cfb9 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -154,7 +154,7 @@ fn check_rvalue<'tcx>( Rvalue::Cast(CastKind::PointerExposeProvenance, _, _) => { Err((span, "casting pointers to ints is unstable in const fn".into())) }, - Rvalue::Cast(CastKind::DynStar, _, _) => { + Rvalue::Cast(CastKind::PointerCoercion(PointerCoercion::DynStar), _, _) => { // FIXME(dyn-star) unimplemented!() }, From 08a8e68d2e9e4b637c2f0344314f6e836405aa77 Mon Sep 17 00:00:00 2001 From: Lukas Markeffsky <@> Date: Sun, 15 Sep 2024 19:35:06 +0200 Subject: [PATCH 018/120] be even more precise about "cast" vs "coercion" --- clippy_utils/src/qualify_min_const_fn.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index 6434c2f42cfb9..ce1895b7fb19b 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -123,7 +123,7 @@ fn check_rvalue<'tcx>( | CastKind::FloatToFloat | CastKind::FnPtrToPtr | CastKind::PtrToPtr - | CastKind::PointerCoercion(PointerCoercion::MutToConstPointer | PointerCoercion::ArrayToPointer), + | CastKind::PointerCoercion(PointerCoercion::MutToConstPointer | PointerCoercion::ArrayToPointer, _), operand, _, ) => check_operand(tcx, operand, span, body, msrv), @@ -131,12 +131,12 @@ fn check_rvalue<'tcx>( CastKind::PointerCoercion( PointerCoercion::UnsafeFnPointer | PointerCoercion::ClosureFnPointer(_) - | PointerCoercion::ReifyFnPointer, + | PointerCoercion::ReifyFnPointer, _ ), _, _, ) => Err((span, "function pointer casts are not allowed in const fn".into())), - Rvalue::Cast(CastKind::PointerCoercion(PointerCoercion::Unsize), op, cast_ty) => { + Rvalue::Cast(CastKind::PointerCoercion(PointerCoercion::Unsize, _), op, cast_ty) => { let Some(pointee_ty) = cast_ty.builtin_deref(true) else { // We cannot allow this for now. return Err((span, "unsizing casts are only allowed for references right now".into())); @@ -154,7 +154,7 @@ fn check_rvalue<'tcx>( Rvalue::Cast(CastKind::PointerExposeProvenance, _, _) => { Err((span, "casting pointers to ints is unstable in const fn".into())) }, - Rvalue::Cast(CastKind::PointerCoercion(PointerCoercion::DynStar), _, _) => { + Rvalue::Cast(CastKind::PointerCoercion(PointerCoercion::DynStar, _), _, _) => { // FIXME(dyn-star) unimplemented!() }, From 701ccd3eadcf507a4e035668b45856c6f875fa83 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 16 Sep 2024 09:11:30 +1000 Subject: [PATCH 019/120] Remove `HirCollector::sess`. It's redundant w.r.t. `HirCollector::tcx`. This removes the unnecessary `'a` lifetime. --- src/librustdoc/doctest.rs | 1 - src/librustdoc/doctest/rust.rs | 17 +++++++---------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 3ee6b24ac92cb..9ef203b4f1258 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -186,7 +186,6 @@ pub(crate) fn run( let mut collector = CreateRunnableDocTests::new(options, opts); let hir_collector = HirCollector::new( - &compiler.sess, tcx.hir(), ErrorCodes::from(compiler.sess.opts.unstable_features.is_nightly_build()), enable_per_target_ignores, diff --git a/src/librustdoc/doctest/rust.rs b/src/librustdoc/doctest/rust.rs index cc85a73430b37..7739790c310a7 100644 --- a/src/librustdoc/doctest/rust.rs +++ b/src/librustdoc/doctest/rust.rs @@ -10,7 +10,6 @@ use rustc_middle::hir::map::Map; use rustc_middle::hir::nested_filter; use rustc_middle::ty::TyCtxt; use rustc_resolve::rustdoc::span_of_fragments; -use rustc_session::Session; use rustc_span::source_map::SourceMap; use rustc_span::{BytePos, DUMMY_SP, FileName, Pos, Span}; @@ -63,8 +62,7 @@ impl DocTestVisitor for RustCollector { fn visit_header(&mut self, _name: &str, _level: u32) {} } -pub(super) struct HirCollector<'a, 'tcx> { - sess: &'a Session, +pub(super) struct HirCollector<'tcx> { map: Map<'tcx>, codes: ErrorCodes, tcx: TyCtxt<'tcx>, @@ -72,21 +70,20 @@ pub(super) struct HirCollector<'a, 'tcx> { collector: RustCollector, } -impl<'a, 'tcx> HirCollector<'a, 'tcx> { +impl<'tcx> HirCollector<'tcx> { pub fn new( - sess: &'a Session, map: Map<'tcx>, codes: ErrorCodes, enable_per_target_ignores: bool, tcx: TyCtxt<'tcx>, ) -> Self { let collector = RustCollector { - source_map: sess.psess.clone_source_map(), + source_map: tcx.sess.psess.clone_source_map(), cur_path: vec![], position: DUMMY_SP, tests: vec![], }; - Self { sess, map, codes, enable_per_target_ignores, tcx, collector } + Self { map, codes, enable_per_target_ignores, tcx, collector } } pub fn collect_crate(mut self) -> Vec { @@ -98,7 +95,7 @@ impl<'a, 'tcx> HirCollector<'a, 'tcx> { } } -impl<'a, 'tcx> HirCollector<'a, 'tcx> { +impl<'tcx> HirCollector<'tcx> { fn visit_testable( &mut self, name: String, @@ -108,7 +105,7 @@ impl<'a, 'tcx> HirCollector<'a, 'tcx> { ) { let ast_attrs = self.tcx.hir().attrs(self.tcx.local_def_id_to_hir_id(def_id)); if let Some(ref cfg) = ast_attrs.cfg(self.tcx, &FxHashSet::default()) { - if !cfg.matches(&self.sess.psess, Some(self.tcx.features())) { + if !cfg.matches(&self.tcx.sess.psess, Some(self.tcx.features())) { return; } } @@ -141,7 +138,7 @@ impl<'a, 'tcx> HirCollector<'a, 'tcx> { } } -impl<'a, 'tcx> intravisit::Visitor<'tcx> for HirCollector<'a, 'tcx> { +impl<'tcx> intravisit::Visitor<'tcx> for HirCollector<'tcx> { type NestedFilter = nested_filter::All; fn nested_visit_map(&mut self) -> Self::Map { From 711c91da7fa5882020c8d9f7a6edfa6d0b9c062f Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 16 Sep 2024 09:24:22 +1000 Subject: [PATCH 020/120] Remove `HirCollector::map`. Because `rustc_middle::hir::map::Map` is a trivial wrapper around `TyCtxt`, and `HirCollector` has a `TyCtxt` field. --- src/librustdoc/doctest.rs | 1 - src/librustdoc/doctest/rust.rs | 15 ++++----------- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 9ef203b4f1258..48272a41c938c 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -186,7 +186,6 @@ pub(crate) fn run( let mut collector = CreateRunnableDocTests::new(options, opts); let hir_collector = HirCollector::new( - tcx.hir(), ErrorCodes::from(compiler.sess.opts.unstable_features.is_nightly_build()), enable_per_target_ignores, tcx, diff --git a/src/librustdoc/doctest/rust.rs b/src/librustdoc/doctest/rust.rs index 7739790c310a7..a9ab02e29cd95 100644 --- a/src/librustdoc/doctest/rust.rs +++ b/src/librustdoc/doctest/rust.rs @@ -6,7 +6,6 @@ use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::sync::Lrc; use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId}; use rustc_hir::{self as hir, CRATE_HIR_ID, intravisit}; -use rustc_middle::hir::map::Map; use rustc_middle::hir::nested_filter; use rustc_middle::ty::TyCtxt; use rustc_resolve::rustdoc::span_of_fragments; @@ -63,7 +62,6 @@ impl DocTestVisitor for RustCollector { } pub(super) struct HirCollector<'tcx> { - map: Map<'tcx>, codes: ErrorCodes, tcx: TyCtxt<'tcx>, enable_per_target_ignores: bool, @@ -71,19 +69,14 @@ pub(super) struct HirCollector<'tcx> { } impl<'tcx> HirCollector<'tcx> { - pub fn new( - map: Map<'tcx>, - codes: ErrorCodes, - enable_per_target_ignores: bool, - tcx: TyCtxt<'tcx>, - ) -> Self { + pub fn new(codes: ErrorCodes, enable_per_target_ignores: bool, tcx: TyCtxt<'tcx>) -> Self { let collector = RustCollector { source_map: tcx.sess.psess.clone_source_map(), cur_path: vec![], position: DUMMY_SP, tests: vec![], }; - Self { map, codes, enable_per_target_ignores, tcx, collector } + Self { codes, enable_per_target_ignores, tcx, collector } } pub fn collect_crate(mut self) -> Vec { @@ -142,13 +135,13 @@ impl<'tcx> intravisit::Visitor<'tcx> for HirCollector<'tcx> { type NestedFilter = nested_filter::All; fn nested_visit_map(&mut self) -> Self::Map { - self.map + self.tcx.hir() } fn visit_item(&mut self, item: &'tcx hir::Item<'_>) { let name = match &item.kind { hir::ItemKind::Impl(impl_) => { - rustc_hir_pretty::id_to_string(&self.map, impl_.self_ty.hir_id) + rustc_hir_pretty::id_to_string(&self.tcx.hir(), impl_.self_ty.hir_id) } _ => item.ident.to_string(), }; From 0882ad57a5012be0bac72d2d37ea95bc32d901c6 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 16 Sep 2024 09:30:45 +1000 Subject: [PATCH 021/120] Rename `AstValidator::session` as `AstValidator::sess`. Because `sess` is the standard name used everywhere else. --- .../rustc_ast_passes/src/ast_validation.rs | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 229d04f8de2fd..5bdd9b6eda80f 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -63,7 +63,7 @@ impl TraitOrTraitImpl { } struct AstValidator<'a> { - session: &'a Session, + sess: &'a Session, features: &'a Features, /// The span of the `extern` in an `extern { ... }` block, if any. @@ -267,7 +267,7 @@ impl<'a> AstValidator<'a> { } fn dcx(&self) -> DiagCtxtHandle<'a> { - self.session.dcx() + self.sess.dcx() } fn visibility_not_permitted(&self, vis: &Visibility, note: errors::VisibilityNotPermittedNote) { @@ -359,7 +359,7 @@ impl<'a> AstValidator<'a> { in_impl: matches!(parent, TraitOrTraitImpl::TraitImpl { .. }), const_context_label: parent_constness, remove_const_sugg: ( - self.session.source_map().span_extend_while_whitespace(span), + self.sess.source_map().span_extend_while_whitespace(span), match parent_constness { Some(_) => rustc_errors::Applicability::MachineApplicable, None => rustc_errors::Applicability::MaybeIncorrect, @@ -472,7 +472,7 @@ impl<'a> AstValidator<'a> { fn check_defaultness(&self, span: Span, defaultness: Defaultness) { if let Defaultness::Default(def_span) = defaultness { - let span = self.session.source_map().guess_head_span(span); + let span = self.sess.source_map().guess_head_span(span); self.dcx().emit_err(errors::ForbiddenDefault { span, def_span }); } } @@ -480,7 +480,7 @@ impl<'a> AstValidator<'a> { /// If `sp` ends with a semicolon, returns it as a `Span` /// Otherwise, returns `sp.shrink_to_hi()` fn ending_semi_or_hi(&self, sp: Span) -> Span { - let source_map = self.session.source_map(); + let source_map = self.sess.source_map(); let end = source_map.end_point(sp); if source_map.span_to_snippet(end).is_ok_and(|s| s == ";") { @@ -552,7 +552,7 @@ impl<'a> AstValidator<'a> { } fn current_extern_span(&self) -> Span { - self.session.source_map().guess_head_span(self.extern_mod.unwrap()) + self.sess.source_map().guess_head_span(self.extern_mod.unwrap()) } /// An `fn` in `extern { ... }` cannot have qualifiers, e.g. `async fn`. @@ -648,7 +648,7 @@ impl<'a> AstValidator<'a> { if ident.name.as_str().is_ascii() { return; } - let span = self.session.source_map().guess_head_span(item_span); + let span = self.sess.source_map().guess_head_span(item_span); self.dcx().emit_err(errors::NoMangleAscii { span }); } @@ -753,7 +753,7 @@ impl<'a> AstValidator<'a> { self.dcx().emit_err(errors::PatternFnPointer { span }); }); if let Extern::Implicit(_) = bfty.ext { - let sig_span = self.session.source_map().next_point(ty.span.shrink_to_lo()); + let sig_span = self.sess.source_map().next_point(ty.span.shrink_to_lo()); self.maybe_lint_missing_abi(sig_span, ty.id); } } @@ -795,7 +795,7 @@ impl<'a> AstValidator<'a> { // FIXME(davidtwco): This is a hack to detect macros which produce spans of the // call site which do not have a macro backtrace. See #61963. if self - .session + .sess .source_map() .span_to_snippet(span) .is_ok_and(|snippet| !snippet.starts_with("#[")) @@ -885,7 +885,7 @@ fn validate_generic_param_order(dcx: DiagCtxtHandle<'_>, generics: &[GenericPara impl<'a> Visitor<'a> for AstValidator<'a> { fn visit_attribute(&mut self, attr: &Attribute) { - validate_attr::check_attr(&self.session.psess, attr); + validate_attr::check_attr(&self.sess.psess, attr); } fn visit_ty(&mut self, ty: &'a Ty) { @@ -1192,7 +1192,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } else if where_clauses.after.has_where_token { self.dcx().emit_err(errors::WhereClauseAfterTypeAlias { span: where_clauses.after.span, - help: self.session.is_nightly_build(), + help: self.sess.is_nightly_build(), }); } } @@ -1328,7 +1328,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { (BoundKind::SuperTraits, BoundConstness::Never, BoundPolarity::Maybe(_)) if !self.features.more_maybe_bounds => { - self.session + self.sess .create_feature_err( errors::OptionalTraitSupertrait { span: trait_ref.span, @@ -1341,7 +1341,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { (BoundKind::TraitObject, BoundConstness::Never, BoundPolarity::Maybe(_)) if !self.features.more_maybe_bounds => { - self.session + self.sess .create_feature_err( errors::OptionalTraitObject { span: trait_ref.span }, sym::more_maybe_bounds, @@ -1752,13 +1752,13 @@ fn deny_equality_constraints( } pub fn check_crate( - session: &Session, + sess: &Session, features: &Features, krate: &Crate, lints: &mut LintBuffer, ) -> bool { let mut validator = AstValidator { - session, + sess, features, extern_mod: None, outer_trait_or_trait_impl: None, From 55fcd46f8803f8e1a5de98a04f14578cea5fcc04 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Sun, 18 Aug 2024 07:24:11 -0400 Subject: [PATCH 022/120] Extend `needless_lifetimes` to suggest eliding `impl` lifetimes --- clippy_lints/src/lifetimes.rs | 229 +++++++++++++++++++++++----------- 1 file changed, 159 insertions(+), 70 deletions(-) diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 755afe959b37a..dc6043ae98163 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -6,8 +6,8 @@ use rustc_errors::Applicability; use rustc_hir::FnRetTy::Return; use rustc_hir::intravisit::nested_filter::{self as hir_nested_filter, NestedFilter}; use rustc_hir::intravisit::{ - Visitor, walk_fn_decl, walk_generic_param, walk_generics, walk_impl_item_ref, walk_item, walk_param_bound, - walk_poly_trait_ref, walk_trait_ref, walk_ty, + Visitor, walk_fn_decl, walk_generic_arg, walk_generics, walk_impl_item_ref, walk_item, walk_param_bound, + walk_poly_trait_ref, walk_trait_ref, walk_ty, walk_where_predicate, }; use rustc_hir::{ BareFnTy, BodyId, FnDecl, FnSig, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, Impl, @@ -21,7 +21,7 @@ use rustc_middle::lint::in_external_macro; use rustc_session::declare_lint_pass; use rustc_span::Span; use rustc_span::def_id::LocalDefId; -use rustc_span::symbol::{Ident, Symbol, kw}; +use rustc_span::symbol::{Ident, kw}; use std::ops::ControlFlow; declare_clippy_lint! { @@ -191,45 +191,10 @@ fn check_fn_inner<'tcx>( if usages.iter().any(|usage| !usage.ident.span.eq_ctxt(span)) { return; } - - let lts = elidable_lts - .iter() - // In principle, the result of the call to `Node::ident` could be `unwrap`ped, as `DefId` should refer to a - // `Node::GenericParam`. - .filter_map(|&def_id| cx.tcx.hir_node_by_def_id(def_id).ident()) - .map(|ident| ident.to_string()) - .collect::>() - .join(", "); - - span_lint_and_then( - cx, - NEEDLESS_LIFETIMES, - elidable_lts - .iter() - .map(|<| cx.tcx.def_span(lt)) - .chain(usages.iter().filter_map(|usage| { - if let LifetimeName::Param(def_id) = usage.res - && elidable_lts.contains(&def_id) - { - return Some(usage.ident.span); - } - - None - })) - .collect_vec(), - format!("the following explicit lifetimes could be elided: {lts}"), - |diag| { - if sig.header.is_async() { - // async functions have usages whose spans point at the lifetime declaration which messes up - // suggestions - return; - }; - - if let Some(suggestions) = elision_suggestions(cx, generics, &elidable_lts, &usages) { - diag.multipart_suggestion("elide the lifetimes", suggestions, Applicability::MachineApplicable); - } - }, - ); + // async functions have usages whose spans point at the lifetime declaration which messes up + // suggestions + let include_suggestions = !sig.header.is_async(); + report_elidable_lifetimes(cx, generics, &elidable_lts, &usages, include_suggestions); } if report_extra_lifetimes { @@ -237,6 +202,52 @@ fn check_fn_inner<'tcx>( } } +/// Generate diagnostic messages for elidable lifetimes. +fn report_elidable_lifetimes( + cx: &LateContext<'_>, + generics: &Generics<'_>, + elidable_lts: &[LocalDefId], + usages: &[Lifetime], + include_suggestions: bool, +) { + let lts = elidable_lts + .iter() + // In principle, the result of the call to `Node::ident` could be `unwrap`ped, as `DefId` should refer to a + // `Node::GenericParam`. + .filter_map(|&def_id| cx.tcx.hir_node_by_def_id(def_id).ident()) + .map(|ident| ident.to_string()) + .collect::>() + .join(", "); + + span_lint_and_then( + cx, + NEEDLESS_LIFETIMES, + elidable_lts + .iter() + .map(|<| cx.tcx.def_span(lt)) + .chain(usages.iter().filter_map(|usage| { + if let LifetimeName::Param(def_id) = usage.res + && elidable_lts.contains(&def_id) + { + return Some(usage.ident.span); + } + + None + })) + .collect_vec(), + format!("the following explicit lifetimes could be elided: {lts}"), + |diag| { + if !include_suggestions { + return; + }; + + if let Some(suggestions) = elision_suggestions(cx, generics, elidable_lts, usages) { + diag.multipart_suggestion("elide the lifetimes", suggestions, Applicability::MachineApplicable); + } + }, + ); +} + fn elision_suggestions( cx: &LateContext<'_>, generics: &Generics<'_>, @@ -594,23 +605,34 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, generics: &'tcx Generics<'_ false } +struct Usage { + lifetime: Lifetime, + in_where_predicate: bool, + in_generic_arg: bool, +} + struct LifetimeChecker<'cx, 'tcx, F> { cx: &'cx LateContext<'tcx>, - map: FxHashMap, + map: FxHashMap>, + where_predicate_depth: usize, + generic_arg_depth: usize, phantom: std::marker::PhantomData, } impl<'cx, 'tcx, F> LifetimeChecker<'cx, 'tcx, F> { - fn new(cx: &'cx LateContext<'tcx>, map: FxHashMap) -> LifetimeChecker<'cx, 'tcx, F> { + fn new(cx: &'cx LateContext<'tcx>, def_ids: Vec) -> LifetimeChecker<'cx, 'tcx, F> { + let map = def_ids.into_iter().map(|def_id| (def_id, Vec::new())).collect(); Self { cx, map, + where_predicate_depth: 0, + generic_arg_depth: 0, phantom: std::marker::PhantomData, } } } -impl<'cx, 'tcx, F> Visitor<'tcx> for LifetimeChecker<'cx, 'tcx, F> +impl<'tcx, F> Visitor<'tcx> for LifetimeChecker<'_, 'tcx, F> where F: NestedFilter<'tcx>, { @@ -619,18 +641,27 @@ where // for lifetimes as parameters of generics fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) { - self.map.remove(&lifetime.ident.name); + if let LifetimeName::Param(def_id) = lifetime.res + && let Some(usages) = self.map.get_mut(&def_id) + { + usages.push(Usage { + lifetime: *lifetime, + in_where_predicate: self.where_predicate_depth != 0, + in_generic_arg: self.generic_arg_depth != 0, + }); + } } - fn visit_generic_param(&mut self, param: &'tcx GenericParam<'_>) { - // don't actually visit `<'a>` or `<'a: 'b>` - // we've already visited the `'a` declarations and - // don't want to spuriously remove them - // `'b` in `'a: 'b` is useless unless used elsewhere in - // a non-lifetime bound - if let GenericParamKind::Type { .. } = param.kind { - walk_generic_param(self, param); - } + fn visit_where_predicate(&mut self, predicate: &'tcx WherePredicate<'tcx>) { + self.where_predicate_depth += 1; + walk_where_predicate(self, predicate); + self.where_predicate_depth -= 1; + } + + fn visit_generic_arg(&mut self, generic_arg: &'tcx GenericArg<'tcx>) -> Self::Result { + self.generic_arg_depth += 1; + walk_generic_arg(self, generic_arg); + self.generic_arg_depth -= 1; } fn nested_visit_map(&mut self) -> Self::Map { @@ -639,44 +670,49 @@ where } fn report_extra_lifetimes<'tcx>(cx: &LateContext<'tcx>, func: &'tcx FnDecl<'_>, generics: &'tcx Generics<'_>) { - let hs = generics + let def_ids = generics .params .iter() .filter_map(|par| match par.kind { GenericParamKind::Lifetime { kind: LifetimeParamKind::Explicit, - } => Some((par.name.ident().name, par.span)), + } => Some(par.def_id), _ => None, }) .collect(); - let mut checker = LifetimeChecker::::new(cx, hs); + let mut checker = LifetimeChecker::::new(cx, def_ids); walk_generics(&mut checker, generics); walk_fn_decl(&mut checker, func); - for &v in checker.map.values() { - span_lint( - cx, - EXTRA_UNUSED_LIFETIMES, - v, - "this lifetime isn't used in the function definition", - ); + for (def_id, usages) in checker.map { + if usages + .iter() + .all(|usage| usage.in_where_predicate && !usage.in_generic_arg) + { + span_lint( + cx, + EXTRA_UNUSED_LIFETIMES, + cx.tcx.def_span(def_id), + "this lifetime isn't used in the function definition", + ); + } } } fn report_extra_impl_lifetimes<'tcx>(cx: &LateContext<'tcx>, impl_: &'tcx Impl<'_>) { - let hs = impl_ + let def_ids = impl_ .generics .params .iter() .filter_map(|par| match par.kind { GenericParamKind::Lifetime { kind: LifetimeParamKind::Explicit, - } => Some((par.name.ident().name, par.span)), + } => Some(par.def_id), _ => None, }) .collect(); - let mut checker = LifetimeChecker::::new(cx, hs); + let mut checker = LifetimeChecker::::new(cx, def_ids); walk_generics(&mut checker, impl_.generics); if let Some(ref trait_ref) = impl_.of_trait { @@ -687,9 +723,62 @@ fn report_extra_impl_lifetimes<'tcx>(cx: &LateContext<'tcx>, impl_: &'tcx Impl<' walk_impl_item_ref(&mut checker, item); } - for &v in checker.map.values() { - span_lint(cx, EXTRA_UNUSED_LIFETIMES, v, "this lifetime isn't used in the impl"); + for (&def_id, usages) in &checker.map { + if usages + .iter() + .all(|usage| usage.in_where_predicate && !usage.in_generic_arg) + { + span_lint( + cx, + EXTRA_UNUSED_LIFETIMES, + cx.tcx.def_span(def_id), + "this lifetime isn't used in the impl", + ); + } } + + report_elidable_impl_lifetimes(cx, impl_, &checker.map); +} + +// An `impl` lifetime is elidable if it satisfies the following conditions: +// - It is used exactly once. +// - That single use is not in a `GenericArg` in a `WherePredicate`. (Note that a `GenericArg` is +// different from a `GenericParam`.) +fn report_elidable_impl_lifetimes<'tcx>( + cx: &LateContext<'tcx>, + impl_: &'tcx Impl<'_>, + map: &FxHashMap>, +) { + let single_usages = map + .iter() + .filter_map(|(def_id, usages)| { + if let [ + Usage { + lifetime, + in_where_predicate: false, + .. + } + | Usage { + lifetime, + in_generic_arg: false, + .. + }, + ] = usages.as_slice() + { + Some((def_id, lifetime)) + } else { + None + } + }) + .collect::>(); + + if single_usages.is_empty() { + return; + } + + let (elidable_lts, usages): (Vec<_>, Vec<_>) = single_usages.into_iter().unzip(); + + report_elidable_lifetimes(cx, impl_.generics, &elidable_lts, &usages, true); } struct BodyLifetimeChecker; From 0a11c5c49ac7412492fc3bc73100912ea11c6360 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Sun, 18 Aug 2024 07:04:22 -0400 Subject: [PATCH 023/120] Reorder functions --- clippy_lints/src/lifetimes.rs | 274 +++++++++++++++++----------------- 1 file changed, 137 insertions(+), 137 deletions(-) diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index dc6043ae98163..42083b1a87963 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -202,143 +202,6 @@ fn check_fn_inner<'tcx>( } } -/// Generate diagnostic messages for elidable lifetimes. -fn report_elidable_lifetimes( - cx: &LateContext<'_>, - generics: &Generics<'_>, - elidable_lts: &[LocalDefId], - usages: &[Lifetime], - include_suggestions: bool, -) { - let lts = elidable_lts - .iter() - // In principle, the result of the call to `Node::ident` could be `unwrap`ped, as `DefId` should refer to a - // `Node::GenericParam`. - .filter_map(|&def_id| cx.tcx.hir_node_by_def_id(def_id).ident()) - .map(|ident| ident.to_string()) - .collect::>() - .join(", "); - - span_lint_and_then( - cx, - NEEDLESS_LIFETIMES, - elidable_lts - .iter() - .map(|<| cx.tcx.def_span(lt)) - .chain(usages.iter().filter_map(|usage| { - if let LifetimeName::Param(def_id) = usage.res - && elidable_lts.contains(&def_id) - { - return Some(usage.ident.span); - } - - None - })) - .collect_vec(), - format!("the following explicit lifetimes could be elided: {lts}"), - |diag| { - if !include_suggestions { - return; - }; - - if let Some(suggestions) = elision_suggestions(cx, generics, elidable_lts, usages) { - diag.multipart_suggestion("elide the lifetimes", suggestions, Applicability::MachineApplicable); - } - }, - ); -} - -fn elision_suggestions( - cx: &LateContext<'_>, - generics: &Generics<'_>, - elidable_lts: &[LocalDefId], - usages: &[Lifetime], -) -> Option> { - let explicit_params = generics - .params - .iter() - .filter(|param| !param.is_elided_lifetime() && !param.is_impl_trait()) - .collect::>(); - - let mut suggestions = if elidable_lts.len() == explicit_params.len() { - // if all the params are elided remove the whole generic block - // - // fn x<'a>() {} - // ^^^^ - vec![(generics.span, String::new())] - } else { - elidable_lts - .iter() - .map(|&id| { - let pos = explicit_params.iter().position(|param| param.def_id == id)?; - let param = explicit_params.get(pos)?; - - let span = if let Some(next) = explicit_params.get(pos + 1) { - // fn x<'prev, 'a, 'next>() {} - // ^^^^ - param.span.until(next.span) - } else { - // `pos` should be at least 1 here, because the param in position 0 would either have a `next` - // param or would have taken the `elidable_lts.len() == explicit_params.len()` branch. - let prev = explicit_params.get(pos - 1)?; - - // fn x<'prev, 'a>() {} - // ^^^^ - param.span.with_lo(prev.span.hi()) - }; - - Some((span, String::new())) - }) - .collect::>>()? - }; - - suggestions.extend( - usages - .iter() - .filter(|usage| named_lifetime(usage).map_or(false, |id| elidable_lts.contains(&id))) - .map(|usage| { - match cx.tcx.parent_hir_node(usage.hir_id) { - Node::Ty(Ty { - kind: TyKind::Ref(..), .. - }) => { - // expand `&'a T` to `&'a T` - // ^^ ^^^ - let span = cx.sess().source_map().span_extend_while_whitespace(usage.ident.span); - - (span, String::new()) - }, - // `T<'a>` and `impl Foo + 'a` should be replaced by `'_` - _ => (usage.ident.span, String::from("'_")), - } - }), - ); - - Some(suggestions) -} - -// elision doesn't work for explicit self types, see rust-lang/rust#69064 -fn explicit_self_type<'tcx>(cx: &LateContext<'tcx>, func: &FnDecl<'tcx>, ident: Option) -> bool { - if let Some(ident) = ident - && ident.name == kw::SelfLower - && !func.implicit_self.has_implicit_self() - && let Some(self_ty) = func.inputs.first() - { - let mut visitor = RefVisitor::new(cx); - visitor.visit_ty(self_ty); - - !visitor.all_lts().is_empty() - } else { - false - } -} - -fn named_lifetime(lt: &Lifetime) -> Option { - match lt.res { - LifetimeName::Param(id) if !lt.is_anonymous() => Some(id), - _ => None, - } -} - fn could_use_elision<'tcx>( cx: &LateContext<'tcx>, func: &'tcx FnDecl<'_>, @@ -461,6 +324,22 @@ fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxHashSet(cx: &LateContext<'tcx>, func: &FnDecl<'tcx>, ident: Option) -> bool { + if let Some(ident) = ident + && ident.name == kw::SelfLower + && !func.implicit_self.has_implicit_self() + && let Some(self_ty) = func.inputs.first() + { + let mut visitor = RefVisitor::new(cx); + visitor.visit_ty(self_ty); + + !visitor.all_lts().is_empty() + } else { + false + } +} + /// Number of times each named lifetime occurs in the given slice. Returns a vector to preserve /// relative order. #[must_use] @@ -481,6 +360,13 @@ fn named_lifetime_occurrences(lts: &[Lifetime]) -> Vec<(LocalDefId, usize)> { occurrences } +fn named_lifetime(lt: &Lifetime) -> Option { + match lt.res { + LifetimeName::Param(id) if !lt.is_anonymous() => Some(id), + _ => None, + } +} + struct RefVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, lts: Vec, @@ -781,6 +667,120 @@ fn report_elidable_impl_lifetimes<'tcx>( report_elidable_lifetimes(cx, impl_.generics, &elidable_lts, &usages, true); } +/// Generate diagnostic messages for elidable lifetimes. +fn report_elidable_lifetimes( + cx: &LateContext<'_>, + generics: &Generics<'_>, + elidable_lts: &[LocalDefId], + usages: &[Lifetime], + include_suggestions: bool, +) { + let lts = elidable_lts + .iter() + // In principle, the result of the call to `Node::ident` could be `unwrap`ped, as `DefId` should refer to a + // `Node::GenericParam`. + .filter_map(|&def_id| cx.tcx.hir_node_by_def_id(def_id).ident()) + .map(|ident| ident.to_string()) + .collect::>() + .join(", "); + + span_lint_and_then( + cx, + NEEDLESS_LIFETIMES, + elidable_lts + .iter() + .map(|<| cx.tcx.def_span(lt)) + .chain(usages.iter().filter_map(|usage| { + if let LifetimeName::Param(def_id) = usage.res + && elidable_lts.contains(&def_id) + { + return Some(usage.ident.span); + } + + None + })) + .collect_vec(), + format!("the following explicit lifetimes could be elided: {lts}"), + |diag| { + if !include_suggestions { + return; + }; + + if let Some(suggestions) = elision_suggestions(cx, generics, elidable_lts, usages) { + diag.multipart_suggestion("elide the lifetimes", suggestions, Applicability::MachineApplicable); + } + }, + ); +} + +fn elision_suggestions( + cx: &LateContext<'_>, + generics: &Generics<'_>, + elidable_lts: &[LocalDefId], + usages: &[Lifetime], +) -> Option> { + let explicit_params = generics + .params + .iter() + .filter(|param| !param.is_elided_lifetime() && !param.is_impl_trait()) + .collect::>(); + + let mut suggestions = if elidable_lts.len() == explicit_params.len() { + // if all the params are elided remove the whole generic block + // + // fn x<'a>() {} + // ^^^^ + vec![(generics.span, String::new())] + } else { + elidable_lts + .iter() + .map(|&id| { + let pos = explicit_params.iter().position(|param| param.def_id == id)?; + let param = explicit_params.get(pos)?; + + let span = if let Some(next) = explicit_params.get(pos + 1) { + // fn x<'prev, 'a, 'next>() {} + // ^^^^ + param.span.until(next.span) + } else { + // `pos` should be at least 1 here, because the param in position 0 would either have a `next` + // param or would have taken the `elidable_lts.len() == explicit_params.len()` branch. + let prev = explicit_params.get(pos - 1)?; + + // fn x<'prev, 'a>() {} + // ^^^^ + param.span.with_lo(prev.span.hi()) + }; + + Some((span, String::new())) + }) + .collect::>>()? + }; + + suggestions.extend( + usages + .iter() + .filter(|usage| named_lifetime(usage).map_or(false, |id| elidable_lts.contains(&id))) + .map(|usage| { + match cx.tcx.parent_hir_node(usage.hir_id) { + Node::Ty(Ty { + kind: TyKind::Ref(..), .. + }) => { + // expand `&'a T` to `&'a T` + // ^^ ^^^ + let span = cx.sess().source_map().span_extend_while_whitespace(usage.ident.span); + + (span, String::new()) + }, + // `T<'a>` and `impl Foo + 'a` should be replaced by `'_` + _ => (usage.ident.span, String::from("'_")), + } + }), + ); + + Some(suggestions) +} + struct BodyLifetimeChecker; impl<'tcx> Visitor<'tcx> for BodyLifetimeChecker { From f2495a17772e0f90fa00fc8838c12b30b4a1c46e Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Sun, 18 Aug 2024 07:31:03 -0400 Subject: [PATCH 024/120] Fix lifetime tests --- .../needless_lifetimes_impl_trait.fixed | 2 +- .../needless_lifetimes_impl_trait.stderr | 20 +++++++++++++++---- tests/ui/extra_unused_lifetimes.rs | 8 ++++++++ tests/ui/extra_unused_lifetimes.stderr | 8 +++++++- tests/ui/needless_lifetimes.fixed | 2 +- tests/ui/needless_lifetimes.stderr | 14 ++++++++++++- 6 files changed, 46 insertions(+), 8 deletions(-) diff --git a/tests/ui/crashes/needless_lifetimes_impl_trait.fixed b/tests/ui/crashes/needless_lifetimes_impl_trait.fixed index 8bd9eea75bb26..837069cae6d15 100644 --- a/tests/ui/crashes/needless_lifetimes_impl_trait.fixed +++ b/tests/ui/crashes/needless_lifetimes_impl_trait.fixed @@ -9,7 +9,7 @@ struct Baz<'a> { bar: &'a Bar, } -impl<'a> Foo for Baz<'a> {} +impl Foo for Baz<'_> {} impl Bar { fn baz(&self) -> impl Foo + '_ { diff --git a/tests/ui/crashes/needless_lifetimes_impl_trait.stderr b/tests/ui/crashes/needless_lifetimes_impl_trait.stderr index 3a2d1f4410ef1..bed6aab25c4d2 100644 --- a/tests/ui/crashes/needless_lifetimes_impl_trait.stderr +++ b/tests/ui/crashes/needless_lifetimes_impl_trait.stderr @@ -1,8 +1,8 @@ error: the following explicit lifetimes could be elided: 'a - --> tests/ui/crashes/needless_lifetimes_impl_trait.rs:15:12 + --> tests/ui/crashes/needless_lifetimes_impl_trait.rs:12:6 | -LL | fn baz<'a>(&'a self) -> impl Foo + 'a { - | ^^ ^^ ^^ +LL | impl<'a> Foo for Baz<'a> {} + | ^^ ^^ | note: the lint level is defined here --> tests/ui/crashes/needless_lifetimes_impl_trait.rs:1:9 @@ -11,9 +11,21 @@ LL | #![deny(clippy::needless_lifetimes)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: elide the lifetimes | +LL - impl<'a> Foo for Baz<'a> {} +LL + impl Foo for Baz<'_> {} + | + +error: the following explicit lifetimes could be elided: 'a + --> tests/ui/crashes/needless_lifetimes_impl_trait.rs:15:12 + | +LL | fn baz<'a>(&'a self) -> impl Foo + 'a { + | ^^ ^^ ^^ + | +help: elide the lifetimes + | LL - fn baz<'a>(&'a self) -> impl Foo + 'a { LL + fn baz(&self) -> impl Foo + '_ { | -error: aborting due to 1 previous error +error: aborting due to 2 previous errors diff --git a/tests/ui/extra_unused_lifetimes.rs b/tests/ui/extra_unused_lifetimes.rs index cdfaf8d3afea8..17d2ed9f50cfc 100644 --- a/tests/ui/extra_unused_lifetimes.rs +++ b/tests/ui/extra_unused_lifetimes.rs @@ -114,9 +114,17 @@ mod second_case { fn hey(); } + // Should lint. The response to the above comment incorrectly called this a false positive. The + // lifetime `'a` can be removed, as demonstrated below. impl<'a, T: Source + ?Sized + 'a> Source for Box { fn hey() {} } + + struct OtherBox(Box); + + impl Source for OtherBox { + fn hey() {} + } } // Should not lint diff --git a/tests/ui/extra_unused_lifetimes.stderr b/tests/ui/extra_unused_lifetimes.stderr index 56292cb5d1a51..85fbb7568ff4c 100644 --- a/tests/ui/extra_unused_lifetimes.stderr +++ b/tests/ui/extra_unused_lifetimes.stderr @@ -37,5 +37,11 @@ error: this lifetime isn't used in the function definition LL | pub fn something<'c>() -> Self { | ^^ -error: aborting due to 6 previous errors +error: this lifetime isn't used in the impl + --> tests/ui/extra_unused_lifetimes.rs:119:10 + | +LL | impl<'a, T: Source + ?Sized + 'a> Source for Box { + | ^^ + +error: aborting due to 7 previous errors diff --git a/tests/ui/needless_lifetimes.fixed b/tests/ui/needless_lifetimes.fixed index d1787b35abd23..e4bb7f86d12f7 100644 --- a/tests/ui/needless_lifetimes.fixed +++ b/tests/ui/needless_lifetimes.fixed @@ -329,7 +329,7 @@ mod issue2944 { bar: &'a Bar, } - impl<'a> Foo for Baz<'a> {} + impl Foo for Baz<'_> {} impl Bar { fn baz(&self) -> impl Foo + '_ { Baz { bar: self } diff --git a/tests/ui/needless_lifetimes.stderr b/tests/ui/needless_lifetimes.stderr index 50f845e2d9291..166e6d0eabf72 100644 --- a/tests/ui/needless_lifetimes.stderr +++ b/tests/ui/needless_lifetimes.stderr @@ -335,6 +335,18 @@ LL - fn needless_lt<'a>(_x: &'a u8) {} LL + fn needless_lt(_x: &u8) {} | +error: the following explicit lifetimes could be elided: 'a + --> tests/ui/needless_lifetimes.rs:332:10 + | +LL | impl<'a> Foo for Baz<'a> {} + | ^^ ^^ + | +help: elide the lifetimes + | +LL - impl<'a> Foo for Baz<'a> {} +LL + impl Foo for Baz<'_> {} + | + error: the following explicit lifetimes could be elided: 'a --> tests/ui/needless_lifetimes.rs:334:16 | @@ -564,5 +576,5 @@ LL - fn one_input<'a>(x: &'a u8) -> &'a u8 { LL + fn one_input(x: &u8) -> &u8 { | -error: aborting due to 47 previous errors +error: aborting due to 48 previous errors From 877585089e015ed5b32de6ccb1d970a7f32960ba Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Sun, 18 Aug 2024 07:31:47 -0400 Subject: [PATCH 025/120] Fix non-lifetime tests --- tests/ui-toml/suppress_lint_in_const/test.rs | 3 +- .../suppress_lint_in_const/test.stderr | 12 +-- tests/ui/borrow_box.fixed | 3 +- tests/ui/borrow_box.rs | 3 +- tests/ui/borrow_box.stderr | 20 ++-- tests/ui/boxed_local.rs | 3 +- tests/ui/boxed_local.stderr | 8 +- tests/ui/derive.rs | 7 +- tests/ui/derive.stderr | 20 ++-- tests/ui/eta.fixed | 3 +- tests/ui/eta.rs | 3 +- tests/ui/eta.stderr | 68 +++++++------- tests/ui/explicit_auto_deref.fixed | 3 +- tests/ui/explicit_auto_deref.rs | 3 +- tests/ui/explicit_auto_deref.stderr | 92 +++++++++---------- .../if_let_slice_binding.rs | 2 +- tests/ui/iter_without_into_iter.rs | 1 + tests/ui/iter_without_into_iter.stderr | 16 ++-- tests/ui/mem_replace.fixed | 3 +- tests/ui/mem_replace.rs | 3 +- tests/ui/mem_replace.stderr | 48 +++++----- tests/ui/mem_replace_no_std.fixed | 3 +- tests/ui/mem_replace_no_std.rs | 3 +- tests/ui/mem_replace_no_std.stderr | 14 +-- tests/ui/mismatching_type_param_order.rs | 2 +- tests/ui/needless_borrow.fixed | 3 +- tests/ui/needless_borrow.rs | 3 +- tests/ui/needless_borrow.stderr | 56 +++++------ tests/ui/needless_pass_by_value.rs | 3 +- tests/ui/needless_pass_by_value.stderr | 52 +++++------ tests/ui/new_without_default.fixed | 3 +- tests/ui/new_without_default.rs | 3 +- tests/ui/new_without_default.stderr | 18 ++-- tests/ui/ref_as_ptr.fixed | 2 +- tests/ui/ref_as_ptr.rs | 2 +- tests/ui/serde.rs | 2 +- tests/ui/significant_drop_in_scrutinee.rs | 7 +- tests/ui/significant_drop_in_scrutinee.stderr | 58 ++++++------ tests/ui/str_split.fixed | 1 + tests/ui/str_split.rs | 1 + tests/ui/str_split.stderr | 20 ++-- tests/ui/temporary_assignment.rs | 1 + tests/ui/temporary_assignment.stderr | 8 +- tests/ui/unconditional_recursion.rs | 3 +- tests/ui/unconditional_recursion.stderr | 90 +++++++++--------- tests/ui/useful_asref.rs | 1 + 46 files changed, 354 insertions(+), 329 deletions(-) diff --git a/tests/ui-toml/suppress_lint_in_const/test.rs b/tests/ui-toml/suppress_lint_in_const/test.rs index 232bccf6a1543..4613a74b85d83 100644 --- a/tests/ui-toml/suppress_lint_in_const/test.rs +++ b/tests/ui-toml/suppress_lint_in_const/test.rs @@ -7,7 +7,8 @@ clippy::no_effect, clippy::unnecessary_operation, clippy::useless_vec, - clippy::out_of_bounds_indexing + clippy::out_of_bounds_indexing, + clippy::needless_lifetimes )] const ARR: [i32; 2] = [1, 2]; diff --git a/tests/ui-toml/suppress_lint_in_const/test.stderr b/tests/ui-toml/suppress_lint_in_const/test.stderr index 5ce2ed2ffaeec..120f5c35cb036 100644 --- a/tests/ui-toml/suppress_lint_in_const/test.stderr +++ b/tests/ui-toml/suppress_lint_in_const/test.stderr @@ -1,5 +1,5 @@ error: indexing may panic - --> tests/ui-toml/suppress_lint_in_const/test.rs:26:5 + --> tests/ui-toml/suppress_lint_in_const/test.rs:27:5 | LL | x[index]; | ^^^^^^^^ @@ -9,7 +9,7 @@ LL | x[index]; = help: to override `-D warnings` add `#[allow(clippy::indexing_slicing)]` error: indexing may panic - --> tests/ui-toml/suppress_lint_in_const/test.rs:41:5 + --> tests/ui-toml/suppress_lint_in_const/test.rs:42:5 | LL | v[0]; | ^^^^ @@ -17,7 +17,7 @@ LL | v[0]; = help: consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic - --> tests/ui-toml/suppress_lint_in_const/test.rs:42:5 + --> tests/ui-toml/suppress_lint_in_const/test.rs:43:5 | LL | v[10]; | ^^^^^ @@ -25,7 +25,7 @@ LL | v[10]; = help: consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic - --> tests/ui-toml/suppress_lint_in_const/test.rs:43:5 + --> tests/ui-toml/suppress_lint_in_const/test.rs:44:5 | LL | v[1 << 3]; | ^^^^^^^^^ @@ -33,7 +33,7 @@ LL | v[1 << 3]; = help: consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic - --> tests/ui-toml/suppress_lint_in_const/test.rs:49:5 + --> tests/ui-toml/suppress_lint_in_const/test.rs:50:5 | LL | v[N]; | ^^^^ @@ -41,7 +41,7 @@ LL | v[N]; = help: consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic - --> tests/ui-toml/suppress_lint_in_const/test.rs:50:5 + --> tests/ui-toml/suppress_lint_in_const/test.rs:51:5 | LL | v[M]; | ^^^^ diff --git a/tests/ui/borrow_box.fixed b/tests/ui/borrow_box.fixed index 5984cc4e93021..08ea60583ea40 100644 --- a/tests/ui/borrow_box.fixed +++ b/tests/ui/borrow_box.fixed @@ -3,7 +3,8 @@ #![allow( clippy::uninlined_format_args, clippy::disallowed_names, - clippy::needless_pass_by_ref_mut + clippy::needless_pass_by_ref_mut, + clippy::needless_lifetimes )] use std::fmt::Display; diff --git a/tests/ui/borrow_box.rs b/tests/ui/borrow_box.rs index 7f15fc83a1d57..b55de1701daa8 100644 --- a/tests/ui/borrow_box.rs +++ b/tests/ui/borrow_box.rs @@ -3,7 +3,8 @@ #![allow( clippy::uninlined_format_args, clippy::disallowed_names, - clippy::needless_pass_by_ref_mut + clippy::needless_pass_by_ref_mut, + clippy::needless_lifetimes )] use std::fmt::Display; diff --git a/tests/ui/borrow_box.stderr b/tests/ui/borrow_box.stderr index ed4308161bbb2..6f80f86c3b3d4 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` - --> tests/ui/borrow_box.rs:24:14 + --> tests/ui/borrow_box.rs:25: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` - --> tests/ui/borrow_box.rs:29:10 + --> tests/ui/borrow_box.rs:30:10 | LL | foo: &'a Box, | ^^^^^^^^^^^^^ help: try: `&'a bool` error: you seem to be trying to use `&Box`. Consider using just `&T` - --> tests/ui/borrow_box.rs:34:17 + --> tests/ui/borrow_box.rs:35:17 | LL | fn test4(a: &Box); | ^^^^^^^^^^ help: try: `&bool` error: you seem to be trying to use `&Box`. Consider using just `&T` - --> tests/ui/borrow_box.rs:95:25 + --> tests/ui/borrow_box.rs:96:25 | LL | pub fn test14(_display: &Box) {} | ^^^^^^^^^^^^^^^^^ help: try: `&dyn Display` error: you seem to be trying to use `&Box`. Consider using just `&T` - --> tests/ui/borrow_box.rs:97:25 + --> tests/ui/borrow_box.rs:98: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` - --> tests/ui/borrow_box.rs:99:29 + --> tests/ui/borrow_box.rs:100: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` - --> tests/ui/borrow_box.rs:102:25 + --> tests/ui/borrow_box.rs:103:25 | LL | pub fn test17(_display: &Box) {} | ^^^^^^^^^^^^^^^^^^ help: try: `&impl Display` error: you seem to be trying to use `&Box`. Consider using just `&T` - --> tests/ui/borrow_box.rs:104:25 + --> tests/ui/borrow_box.rs:105: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` - --> tests/ui/borrow_box.rs:106:29 + --> tests/ui/borrow_box.rs:107: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` - --> tests/ui/borrow_box.rs:112:25 + --> tests/ui/borrow_box.rs:113:25 | LL | pub fn test20(_display: &Box<(dyn Display + Send)>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&(dyn Display + Send)` diff --git a/tests/ui/boxed_local.rs b/tests/ui/boxed_local.rs index fbd9e12fc1891..e2c27e585fce8 100644 --- a/tests/ui/boxed_local.rs +++ b/tests/ui/boxed_local.rs @@ -3,7 +3,8 @@ clippy::needless_pass_by_value, clippy::unused_unit, clippy::redundant_clone, - clippy::match_single_binding + clippy::match_single_binding, + clippy::needless_lifetimes )] #![warn(clippy::boxed_local)] diff --git a/tests/ui/boxed_local.stderr b/tests/ui/boxed_local.stderr index 7710233fa4db8..d3156c820b2c5 100644 --- a/tests/ui/boxed_local.stderr +++ b/tests/ui/boxed_local.stderr @@ -1,5 +1,5 @@ error: local variable doesn't need to be boxed here - --> tests/ui/boxed_local.rs:39:13 + --> tests/ui/boxed_local.rs:40:13 | LL | fn warn_arg(x: Box) { | ^ @@ -8,19 +8,19 @@ LL | fn warn_arg(x: Box) { = help: to override `-D warnings` add `#[allow(clippy::boxed_local)]` error: local variable doesn't need to be boxed here - --> tests/ui/boxed_local.rs:122:12 + --> tests/ui/boxed_local.rs:123:12 | LL | pub fn new(_needs_name: Box>) -> () {} | ^^^^^^^^^^^ error: local variable doesn't need to be boxed here - --> tests/ui/boxed_local.rs:187:44 + --> tests/ui/boxed_local.rs:188:44 | LL | fn default_impl_x(self: Box, x: Box) -> u32 { | ^ error: local variable doesn't need to be boxed here - --> tests/ui/boxed_local.rs:195:16 + --> tests/ui/boxed_local.rs:196:16 | LL | fn foo(x: Box) {} | ^ diff --git a/tests/ui/derive.rs b/tests/ui/derive.rs index 4ac21f2cb4bc5..3647b24250581 100644 --- a/tests/ui/derive.rs +++ b/tests/ui/derive.rs @@ -1,4 +1,9 @@ -#![allow(clippy::non_canonical_clone_impl, clippy::non_canonical_partial_ord_impl, dead_code)] +#![allow( + clippy::non_canonical_clone_impl, + clippy::non_canonical_partial_ord_impl, + clippy::needless_lifetimes, + dead_code +)] #![warn(clippy::expl_impl_clone_on_copy)] #[derive(Copy)] diff --git a/tests/ui/derive.stderr b/tests/ui/derive.stderr index 486e6dc1b6baf..c072a9a6277b3 100644 --- a/tests/ui/derive.stderr +++ b/tests/ui/derive.stderr @@ -1,5 +1,5 @@ error: you are implementing `Clone` explicitly on a `Copy` type - --> tests/ui/derive.rs:7:1 + --> tests/ui/derive.rs:12:1 | LL | / impl Clone for Qux { LL | | @@ -10,7 +10,7 @@ LL | | } | |_^ | note: consider deriving `Clone` or removing `Copy` - --> tests/ui/derive.rs:7:1 + --> tests/ui/derive.rs:12:1 | LL | / impl Clone for Qux { LL | | @@ -23,7 +23,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::expl_impl_clone_on_copy)]` error: you are implementing `Clone` explicitly on a `Copy` type - --> tests/ui/derive.rs:32:1 + --> tests/ui/derive.rs:37:1 | LL | / impl<'a> Clone for Lt<'a> { LL | | @@ -34,7 +34,7 @@ LL | | } | |_^ | note: consider deriving `Clone` or removing `Copy` - --> tests/ui/derive.rs:32:1 + --> tests/ui/derive.rs:37:1 | LL | / impl<'a> Clone for Lt<'a> { LL | | @@ -45,7 +45,7 @@ LL | | } | |_^ error: you are implementing `Clone` explicitly on a `Copy` type - --> tests/ui/derive.rs:44:1 + --> tests/ui/derive.rs:49:1 | LL | / impl Clone for BigArray { LL | | @@ -56,7 +56,7 @@ LL | | } | |_^ | note: consider deriving `Clone` or removing `Copy` - --> tests/ui/derive.rs:44:1 + --> tests/ui/derive.rs:49:1 | LL | / impl Clone for BigArray { LL | | @@ -67,7 +67,7 @@ LL | | } | |_^ error: you are implementing `Clone` explicitly on a `Copy` type - --> tests/ui/derive.rs:56:1 + --> tests/ui/derive.rs:61:1 | LL | / impl Clone for FnPtr { LL | | @@ -78,7 +78,7 @@ LL | | } | |_^ | note: consider deriving `Clone` or removing `Copy` - --> tests/ui/derive.rs:56:1 + --> tests/ui/derive.rs:61:1 | LL | / impl Clone for FnPtr { LL | | @@ -89,7 +89,7 @@ LL | | } | |_^ error: you are implementing `Clone` explicitly on a `Copy` type - --> tests/ui/derive.rs:77:1 + --> tests/ui/derive.rs:82:1 | LL | / impl Clone for Generic2 { LL | | @@ -100,7 +100,7 @@ LL | | } | |_^ | note: consider deriving `Clone` or removing `Copy` - --> tests/ui/derive.rs:77:1 + --> tests/ui/derive.rs:82:1 | LL | / impl Clone for Generic2 { LL | | diff --git a/tests/ui/eta.fixed b/tests/ui/eta.fixed index ca422ee29c104..f1baf28200e96 100644 --- a/tests/ui/eta.fixed +++ b/tests/ui/eta.fixed @@ -9,7 +9,8 @@ clippy::redundant_closure_call, clippy::uninlined_format_args, clippy::useless_vec, - clippy::unnecessary_map_on_constructor + clippy::unnecessary_map_on_constructor, + clippy::needless_lifetimes )] use std::path::{Path, PathBuf}; diff --git a/tests/ui/eta.rs b/tests/ui/eta.rs index c0db91c03ef45..c52a51880bfc9 100644 --- a/tests/ui/eta.rs +++ b/tests/ui/eta.rs @@ -9,7 +9,8 @@ clippy::redundant_closure_call, clippy::uninlined_format_args, clippy::useless_vec, - clippy::unnecessary_map_on_constructor + clippy::unnecessary_map_on_constructor, + clippy::needless_lifetimes )] use std::path::{Path, PathBuf}; diff --git a/tests/ui/eta.stderr b/tests/ui/eta.stderr index 5540261fc5744..1731a4377f534 100644 --- a/tests/ui/eta.stderr +++ b/tests/ui/eta.stderr @@ -1,5 +1,5 @@ error: redundant closure - --> tests/ui/eta.rs:30:27 + --> tests/ui/eta.rs:31:27 | LL | let a = Some(1u8).map(|a| foo(a)); | ^^^^^^^^^^ help: replace the closure with the function itself: `foo` @@ -8,31 +8,31 @@ LL | let a = Some(1u8).map(|a| foo(a)); = help: to override `-D warnings` add `#[allow(clippy::redundant_closure)]` error: redundant closure - --> tests/ui/eta.rs:34:40 + --> tests/ui/eta.rs:35:40 | LL | let _: Option> = true.then(|| vec![]); // special case vec! | ^^^^^^^^^ help: replace the closure with `Vec::new`: `std::vec::Vec::new` error: redundant closure - --> tests/ui/eta.rs:35:35 + --> tests/ui/eta.rs:36:35 | LL | let d = Some(1u8).map(|a| foo((|b| foo2(b))(a))); //is adjusted? | ^^^^^^^^^^^^^ help: replace the closure with the function itself: `foo2` error: redundant closure - --> tests/ui/eta.rs:36:26 + --> tests/ui/eta.rs:37:26 | LL | all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted | ^^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `below` error: redundant closure - --> tests/ui/eta.rs:43:27 + --> tests/ui/eta.rs:44:27 | LL | let e = Some(1u8).map(|a| generic(a)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `generic` error: redundant closure - --> tests/ui/eta.rs:95:51 + --> tests/ui/eta.rs:96:51 | LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo()); | ^^^^^^^^^^^ help: replace the closure with the method itself: `TestStruct::foo` @@ -41,169 +41,169 @@ LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo()); = help: to override `-D warnings` add `#[allow(clippy::redundant_closure_for_method_calls)]` error: redundant closure - --> tests/ui/eta.rs:96:51 + --> tests/ui/eta.rs:97:51 | LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo()); | ^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `TestTrait::trait_foo` error: redundant closure - --> tests/ui/eta.rs:98:42 + --> tests/ui/eta.rs:99:42 | LL | let e = Some(&mut vec![1, 2, 3]).map(|v| v.clear()); | ^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::vec::Vec::clear` error: redundant closure - --> tests/ui/eta.rs:102:29 + --> tests/ui/eta.rs:103:29 | LL | let e = Some("str").map(|s| s.to_string()); | ^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::string::ToString::to_string` error: redundant closure - --> tests/ui/eta.rs:103:27 + --> tests/ui/eta.rs:104:27 | LL | let e = Some('a').map(|s| s.to_uppercase()); | ^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `char::to_uppercase` error: redundant closure - --> tests/ui/eta.rs:105:65 + --> tests/ui/eta.rs:106:65 | LL | let e: std::vec::Vec = vec!['a', 'b', 'c'].iter().map(|c| c.to_ascii_uppercase()).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `char::to_ascii_uppercase` error: redundant closure - --> tests/ui/eta.rs:168:22 + --> tests/ui/eta.rs:169:22 | LL | requires_fn_once(|| x()); | ^^^^^^ help: replace the closure with the function itself: `x` error: redundant closure - --> tests/ui/eta.rs:175:27 + --> tests/ui/eta.rs:176:27 | LL | let a = Some(1u8).map(|a| foo_ptr(a)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `foo_ptr` error: redundant closure - --> tests/ui/eta.rs:180:27 + --> tests/ui/eta.rs:181:27 | LL | let a = Some(1u8).map(|a| closure(a)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `closure` error: redundant closure - --> tests/ui/eta.rs:212:28 + --> tests/ui/eta.rs:213:28 | LL | x.into_iter().for_each(|x| add_to_res(x)); | ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res` error: redundant closure - --> tests/ui/eta.rs:213:28 + --> tests/ui/eta.rs:214:28 | LL | y.into_iter().for_each(|x| add_to_res(x)); | ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res` error: redundant closure - --> tests/ui/eta.rs:214:28 + --> tests/ui/eta.rs:215:28 | LL | z.into_iter().for_each(|x| add_to_res(x)); | ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `add_to_res` error: redundant closure - --> tests/ui/eta.rs:221:21 + --> tests/ui/eta.rs:222:21 | LL | Some(1).map(|n| closure(n)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut closure` error: redundant closure - --> tests/ui/eta.rs:225:21 + --> tests/ui/eta.rs:226:21 | LL | Some(1).map(|n| in_loop(n)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `in_loop` error: redundant closure - --> tests/ui/eta.rs:318:18 + --> tests/ui/eta.rs:319:18 | LL | takes_fn_mut(|| f()); | ^^^^^^ help: replace the closure with the function itself: `&mut f` error: redundant closure - --> tests/ui/eta.rs:321:19 + --> tests/ui/eta.rs:322:19 | LL | takes_fn_once(|| f()); | ^^^^^^ help: replace the closure with the function itself: `&mut f` error: redundant closure - --> tests/ui/eta.rs:325:26 + --> tests/ui/eta.rs:326:26 | LL | move || takes_fn_mut(|| f_used_once()) | ^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut f_used_once` error: redundant closure - --> tests/ui/eta.rs:337:19 + --> tests/ui/eta.rs:338:19 | LL | array_opt.map(|a| a.as_slice()); | ^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `<[u8; 3]>::as_slice` error: redundant closure - --> tests/ui/eta.rs:340:19 + --> tests/ui/eta.rs:341:19 | LL | slice_opt.map(|s| s.len()); | ^^^^^^^^^^^ help: replace the closure with the method itself: `<[u8]>::len` error: redundant closure - --> tests/ui/eta.rs:343:17 + --> tests/ui/eta.rs:344:17 | LL | ptr_opt.map(|p| p.is_null()); | ^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `<*const usize>::is_null` error: redundant closure - --> tests/ui/eta.rs:347:17 + --> tests/ui/eta.rs:348:17 | LL | dyn_opt.map(|d| d.method_on_dyn()); | ^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `::method_on_dyn` error: redundant closure - --> tests/ui/eta.rs:407:19 + --> tests/ui/eta.rs:408:19 | LL | let _ = f(&0, |x, y| f2(x, y)); | ^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `f2` error: redundant closure - --> tests/ui/eta.rs:435:22 + --> tests/ui/eta.rs:436:22 | LL | test.map(|t| t.method()) | ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `Test::method` error: redundant closure - --> tests/ui/eta.rs:439:22 + --> tests/ui/eta.rs:440:22 | LL | test.map(|t| t.method()) | ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `super::Outer::method` error: redundant closure - --> tests/ui/eta.rs:452:18 + --> tests/ui/eta.rs:453:18 | LL | test.map(|t| t.method()) | ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `test_mod::Test::method` error: redundant closure - --> tests/ui/eta.rs:459:30 + --> tests/ui/eta.rs:460:30 | LL | test.map(|t| t.method()) | ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `crate::issue_10854::d::Test::method` error: redundant closure - --> tests/ui/eta.rs:478:38 + --> tests/ui/eta.rs:479:38 | LL | let x = Box::new(|| None.map(|x| f(x))); | ^^^^^^^^ help: replace the closure with the function itself: `&f` error: redundant closure - --> tests/ui/eta.rs:482:38 + --> tests/ui/eta.rs:483:38 | LL | let x = Box::new(|| None.map(|x| f(x))); | ^^^^^^^^ help: replace the closure with the function itself: `f` error: redundant closure - --> tests/ui/eta.rs:499:35 + --> tests/ui/eta.rs:500:35 | LL | let _field = bind.or_else(|| get_default()).unwrap(); | ^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `get_default` diff --git a/tests/ui/explicit_auto_deref.fixed b/tests/ui/explicit_auto_deref.fixed index 255b2c5a220d8..9d476259b87e0 100644 --- a/tests/ui/explicit_auto_deref.fixed +++ b/tests/ui/explicit_auto_deref.fixed @@ -10,7 +10,8 @@ clippy::redundant_field_names, clippy::too_many_arguments, clippy::borrow_deref_ref, - clippy::let_unit_value + clippy::let_unit_value, + clippy::needless_lifetimes )] trait CallableStr { diff --git a/tests/ui/explicit_auto_deref.rs b/tests/ui/explicit_auto_deref.rs index 99906999f01d9..23307c837f0e8 100644 --- a/tests/ui/explicit_auto_deref.rs +++ b/tests/ui/explicit_auto_deref.rs @@ -10,7 +10,8 @@ clippy::redundant_field_names, clippy::too_many_arguments, clippy::borrow_deref_ref, - clippy::let_unit_value + clippy::let_unit_value, + clippy::needless_lifetimes )] trait CallableStr { diff --git a/tests/ui/explicit_auto_deref.stderr b/tests/ui/explicit_auto_deref.stderr index 53784934f6386..0b05a554eb11f 100644 --- a/tests/ui/explicit_auto_deref.stderr +++ b/tests/ui/explicit_auto_deref.stderr @@ -1,5 +1,5 @@ error: deref which would be done by auto-deref - --> tests/ui/explicit_auto_deref.rs:68:19 + --> tests/ui/explicit_auto_deref.rs:69:19 | LL | let _: &str = &*s; | ^^^ help: try: `&s` @@ -8,271 +8,271 @@ LL | let _: &str = &*s; = help: to override `-D warnings` add `#[allow(clippy::explicit_auto_deref)]` error: deref which would be done by auto-deref - --> tests/ui/explicit_auto_deref.rs:69:19 + --> tests/ui/explicit_auto_deref.rs:70:19 | LL | let _: &str = &*{ String::new() }; | ^^^^^^^^^^^^^^^^^^^ help: try: `&{ String::new() }` error: deref which would be done by auto-deref - --> tests/ui/explicit_auto_deref.rs:70:19 + --> tests/ui/explicit_auto_deref.rs:71:19 | LL | let _: &str = &mut *{ String::new() }; | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut { String::new() }` error: deref which would be done by auto-deref - --> tests/ui/explicit_auto_deref.rs:74:11 + --> tests/ui/explicit_auto_deref.rs:75:11 | LL | f_str(&*s); | ^^^ help: try: `&s` error: deref which would be done by auto-deref - --> tests/ui/explicit_auto_deref.rs:78:13 + --> tests/ui/explicit_auto_deref.rs:79:13 | LL | f_str_t(&*s, &*s); // Don't lint second param. | ^^^ help: try: `&s` error: deref which would be done by auto-deref - --> tests/ui/explicit_auto_deref.rs:81:24 + --> tests/ui/explicit_auto_deref.rs:82:24 | LL | let _: &Box = &**b; | ^^^^ help: try: `&b` error: deref which would be done by auto-deref - --> tests/ui/explicit_auto_deref.rs:87:7 + --> tests/ui/explicit_auto_deref.rs:88:7 | LL | c(&*s); | ^^^ help: try: `&s` error: deref which would be done by auto-deref - --> tests/ui/explicit_auto_deref.rs:93:9 + --> tests/ui/explicit_auto_deref.rs:94:9 | LL | &**x | ^^^^ help: try: `x` error: deref which would be done by auto-deref - --> tests/ui/explicit_auto_deref.rs:97:11 + --> tests/ui/explicit_auto_deref.rs:98:11 | LL | { &**x } | ^^^^ help: try: `x` error: deref which would be done by auto-deref - --> tests/ui/explicit_auto_deref.rs:101:9 + --> tests/ui/explicit_auto_deref.rs:102:9 | LL | &**{ x } | ^^^^^^^^ help: try: `{ x }` error: deref which would be done by auto-deref - --> tests/ui/explicit_auto_deref.rs:105:9 + --> tests/ui/explicit_auto_deref.rs:106:9 | LL | &***x | ^^^^^ help: try: `x` error: deref which would be done by auto-deref - --> tests/ui/explicit_auto_deref.rs:122:12 + --> tests/ui/explicit_auto_deref.rs:123:12 | LL | f1(&*x); | ^^^ help: try: `&x` error: deref which would be done by auto-deref - --> tests/ui/explicit_auto_deref.rs:123:12 + --> tests/ui/explicit_auto_deref.rs:124:12 | LL | f2(&*x); | ^^^ help: try: `&x` error: deref which would be done by auto-deref - --> tests/ui/explicit_auto_deref.rs:124:12 + --> tests/ui/explicit_auto_deref.rs:125:12 | LL | f3(&*x); | ^^^ help: try: `&x` error: deref which would be done by auto-deref - --> tests/ui/explicit_auto_deref.rs:125:27 + --> tests/ui/explicit_auto_deref.rs:126:27 | LL | f4.callable_str()(&*x); | ^^^ help: try: `&x` error: deref which would be done by auto-deref - --> tests/ui/explicit_auto_deref.rs:126:12 + --> tests/ui/explicit_auto_deref.rs:127:12 | LL | f5(&*x); | ^^^ help: try: `&x` error: deref which would be done by auto-deref - --> tests/ui/explicit_auto_deref.rs:127:12 + --> tests/ui/explicit_auto_deref.rs:128:12 | LL | f6(&*x); | ^^^ help: try: `&x` error: deref which would be done by auto-deref - --> tests/ui/explicit_auto_deref.rs:128:27 + --> tests/ui/explicit_auto_deref.rs:129:27 | LL | f7.callable_str()(&*x); | ^^^ help: try: `&x` error: deref which would be done by auto-deref - --> tests/ui/explicit_auto_deref.rs:129:25 + --> tests/ui/explicit_auto_deref.rs:130:25 | LL | f8.callable_t()(&*x); | ^^^ help: try: `&x` error: deref which would be done by auto-deref - --> tests/ui/explicit_auto_deref.rs:130:12 + --> tests/ui/explicit_auto_deref.rs:131:12 | LL | f9(&*x); | ^^^ help: try: `&x` error: deref which would be done by auto-deref - --> tests/ui/explicit_auto_deref.rs:131:13 + --> tests/ui/explicit_auto_deref.rs:132:13 | LL | f10(&*x); | ^^^ help: try: `&x` error: deref which would be done by auto-deref - --> tests/ui/explicit_auto_deref.rs:132:26 + --> tests/ui/explicit_auto_deref.rs:133:26 | LL | f11.callable_t()(&*x); | ^^^ help: try: `&x` error: deref which would be done by auto-deref - --> tests/ui/explicit_auto_deref.rs:136:16 + --> tests/ui/explicit_auto_deref.rs:137:16 | LL | let _ = S1(&*s); | ^^^ help: try: `&s` error: deref which would be done by auto-deref - --> tests/ui/explicit_auto_deref.rs:141:21 + --> tests/ui/explicit_auto_deref.rs:142:21 | LL | let _ = S2 { s: &*s }; | ^^^ help: try: `&s` error: deref which would be done by auto-deref - --> tests/ui/explicit_auto_deref.rs:157:30 + --> tests/ui/explicit_auto_deref.rs:158:30 | LL | let _ = Self::S1(&**s); | ^^^^ help: try: `s` error: deref which would be done by auto-deref - --> tests/ui/explicit_auto_deref.rs:158:35 + --> tests/ui/explicit_auto_deref.rs:159:35 | LL | let _ = Self::S2 { s: &**s }; | ^^^^ help: try: `s` error: deref which would be done by auto-deref - --> tests/ui/explicit_auto_deref.rs:161:20 + --> tests/ui/explicit_auto_deref.rs:162:20 | LL | let _ = E1::S1(&*s); | ^^^ help: try: `&s` error: deref which would be done by auto-deref - --> tests/ui/explicit_auto_deref.rs:162:25 + --> tests/ui/explicit_auto_deref.rs:163:25 | LL | let _ = E1::S2 { s: &*s }; | ^^^ help: try: `&s` error: deref which would be done by auto-deref - --> tests/ui/explicit_auto_deref.rs:180:13 + --> tests/ui/explicit_auto_deref.rs:181:13 | LL | let _ = (*b).foo; | ^^^^ help: try: `b` error: deref which would be done by auto-deref - --> tests/ui/explicit_auto_deref.rs:181:13 + --> tests/ui/explicit_auto_deref.rs:182:13 | LL | let _ = (**b).foo; | ^^^^^ help: try: `b` error: deref which would be done by auto-deref - --> tests/ui/explicit_auto_deref.rs:196:19 + --> tests/ui/explicit_auto_deref.rs:197:19 | LL | let _ = f_str(*ref_str); | ^^^^^^^^ help: try: `ref_str` error: deref which would be done by auto-deref - --> tests/ui/explicit_auto_deref.rs:198:19 + --> tests/ui/explicit_auto_deref.rs:199:19 | LL | let _ = f_str(**ref_ref_str); | ^^^^^^^^^^^^^ help: try: `ref_ref_str` error: deref which would be done by auto-deref - --> tests/ui/explicit_auto_deref.rs:208:12 + --> tests/ui/explicit_auto_deref.rs:209:12 | LL | f_str(&&*ref_str); // `needless_borrow` will suggest removing both references | ^^^^^^^^^ help: try: `ref_str` error: deref which would be done by auto-deref - --> tests/ui/explicit_auto_deref.rs:209:12 + --> tests/ui/explicit_auto_deref.rs:210:12 | LL | f_str(&&**ref_str); // `needless_borrow` will suggest removing only one reference | ^^^^^^^^^^ help: try: `ref_str` error: deref which would be done by auto-deref - --> tests/ui/explicit_auto_deref.rs:218:41 + --> tests/ui/explicit_auto_deref.rs:219:41 | LL | let _ = || -> &'static str { return *s }; | ^^ help: try: `s` error: deref which would be done by auto-deref - --> tests/ui/explicit_auto_deref.rs:237:9 + --> tests/ui/explicit_auto_deref.rs:238:9 | LL | &**x | ^^^^ help: try: `x` error: deref which would be done by auto-deref - --> tests/ui/explicit_auto_deref.rs:260:8 + --> tests/ui/explicit_auto_deref.rs:261:8 | LL | c1(*x); | ^^ help: try: `x` error: deref which would be done by auto-deref - --> tests/ui/explicit_auto_deref.rs:263:20 + --> tests/ui/explicit_auto_deref.rs:264:20 | LL | return *x; | ^^ help: try: `x` error: deref which would be done by auto-deref - --> tests/ui/explicit_auto_deref.rs:265:9 + --> tests/ui/explicit_auto_deref.rs:266:9 | LL | *x | ^^ help: try: `x` error: deref which would be done by auto-deref - --> tests/ui/explicit_auto_deref.rs:299:20 + --> tests/ui/explicit_auto_deref.rs:300:20 | LL | Some(x) => &mut *x, | ^^^^^^^ help: try: `x` error: deref which would be done by auto-deref - --> tests/ui/explicit_auto_deref.rs:332:22 + --> tests/ui/explicit_auto_deref.rs:333:22 | LL | let _ = &mut (*{ x.u }).x; | ^^^^^^^^^^ help: try: `{ x.u }` error: deref which would be done by auto-deref - --> tests/ui/explicit_auto_deref.rs:338:22 + --> tests/ui/explicit_auto_deref.rs:339:22 | LL | let _ = &mut (**x.u).x; | ^^^^^^^ help: try: `(*x.u)` error: deref which would be done by auto-deref - --> tests/ui/explicit_auto_deref.rs:339:22 + --> tests/ui/explicit_auto_deref.rs:340:22 | LL | let _ = &mut (**{ x.u }).x; | ^^^^^^^^^^^ help: try: `{ x.u }` error: deref which would be done by auto-deref - --> tests/ui/explicit_auto_deref.rs:343:22 + --> tests/ui/explicit_auto_deref.rs:344:22 | LL | let _ = &mut (*x.u).x; | ^^^^^^ help: try: `x.u` error: deref which would be done by auto-deref - --> tests/ui/explicit_auto_deref.rs:344:22 + --> tests/ui/explicit_auto_deref.rs:345:22 | LL | let _ = &mut (*{ x.u }).x; | ^^^^^^^^^^ help: try: `{ x.u }` error: deref which would be done by auto-deref - --> tests/ui/explicit_auto_deref.rs:367:13 + --> tests/ui/explicit_auto_deref.rs:368:13 | LL | foo(&*wrapped_bar); | ^^^^^^^^^^^^^ help: try: `&wrapped_bar` 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 5bbdabcaad19b..a4cb50bd68220 100644 --- a/tests/ui/index_refutable_slice/if_let_slice_binding.rs +++ b/tests/ui/index_refutable_slice/if_let_slice_binding.rs @@ -1,5 +1,5 @@ #![deny(clippy::index_refutable_slice)] -#![allow(clippy::uninlined_format_args)] +#![allow(clippy::uninlined_format_args, clippy::needless_lifetimes)] //@no-rustfix: need to change the suggestion to a multipart suggestion diff --git a/tests/ui/iter_without_into_iter.rs b/tests/ui/iter_without_into_iter.rs index 3054d848efb7d..d5b28e45453b4 100644 --- a/tests/ui/iter_without_into_iter.rs +++ b/tests/ui/iter_without_into_iter.rs @@ -1,6 +1,7 @@ //@no-rustfix //@aux-build:proc_macros.rs #![warn(clippy::iter_without_into_iter)] +#![allow(clippy::needless_lifetimes)] extern crate proc_macros; pub struct S1; diff --git a/tests/ui/iter_without_into_iter.stderr b/tests/ui/iter_without_into_iter.stderr index 382a7606f48c6..7c42fa1dd89ce 100644 --- a/tests/ui/iter_without_into_iter.stderr +++ b/tests/ui/iter_without_into_iter.stderr @@ -1,5 +1,5 @@ error: `iter` method without an `IntoIterator` impl for `&S1` - --> tests/ui/iter_without_into_iter.rs:8:5 + --> tests/ui/iter_without_into_iter.rs:9:5 | LL | / pub fn iter(&self) -> std::slice::Iter<'_, u8> { LL | | @@ -22,7 +22,7 @@ LL + } | error: `iter_mut` method without an `IntoIterator` impl for `&mut S1` - --> tests/ui/iter_without_into_iter.rs:12:5 + --> tests/ui/iter_without_into_iter.rs:13:5 | LL | / pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, u8> { LL | | @@ -43,7 +43,7 @@ LL + } | error: `iter` method without an `IntoIterator` impl for `&S3<'a>` - --> tests/ui/iter_without_into_iter.rs:28:5 + --> tests/ui/iter_without_into_iter.rs:29:5 | LL | / pub fn iter(&self) -> std::slice::Iter<'_, u8> { LL | | @@ -64,7 +64,7 @@ LL + } | error: `iter_mut` method without an `IntoIterator` impl for `&mut S3<'a>` - --> tests/ui/iter_without_into_iter.rs:32:5 + --> tests/ui/iter_without_into_iter.rs:33:5 | LL | / pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, u8> { LL | | @@ -85,7 +85,7 @@ LL + } | error: `iter` method without an `IntoIterator` impl for `&S8` - --> tests/ui/iter_without_into_iter.rs:69:5 + --> tests/ui/iter_without_into_iter.rs:70:5 | LL | / pub fn iter(&self) -> std::slice::Iter<'static, T> { LL | | todo!() @@ -105,7 +105,7 @@ LL + } | error: `iter` method without an `IntoIterator` impl for `&S9` - --> tests/ui/iter_without_into_iter.rs:77:5 + --> tests/ui/iter_without_into_iter.rs:78:5 | LL | / pub fn iter(&self) -> std::slice::Iter<'_, T> { LL | | @@ -126,7 +126,7 @@ LL + } | error: `iter_mut` method without an `IntoIterator` impl for `&mut S9` - --> tests/ui/iter_without_into_iter.rs:81:5 + --> tests/ui/iter_without_into_iter.rs:82:5 | LL | / pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, T> { LL | | @@ -147,7 +147,7 @@ LL + } | error: `iter` method without an `IntoIterator` impl for `&Issue12037` - --> tests/ui/iter_without_into_iter.rs:130:13 + --> tests/ui/iter_without_into_iter.rs:131:13 | LL | / fn iter(&self) -> std::slice::Iter<'_, u8> { LL | | todo!() diff --git a/tests/ui/mem_replace.fixed b/tests/ui/mem_replace.fixed index 78d8b3e9bce6b..4210dbbe82d37 100644 --- a/tests/ui/mem_replace.fixed +++ b/tests/ui/mem_replace.fixed @@ -1,6 +1,5 @@ -#![allow(unused)] +#![allow(unused, clippy::needless_lifetimes)] #![warn( - clippy::all, clippy::style, clippy::mem_replace_option_with_none, clippy::mem_replace_with_default diff --git a/tests/ui/mem_replace.rs b/tests/ui/mem_replace.rs index 28915bf6daee8..bd7ad78b2af26 100644 --- a/tests/ui/mem_replace.rs +++ b/tests/ui/mem_replace.rs @@ -1,6 +1,5 @@ -#![allow(unused)] +#![allow(unused, clippy::needless_lifetimes)] #![warn( - clippy::all, clippy::style, clippy::mem_replace_option_with_none, clippy::mem_replace_with_default diff --git a/tests/ui/mem_replace.stderr b/tests/ui/mem_replace.stderr index 44be2c9b63d75..c33f80b01b852 100644 --- a/tests/ui/mem_replace.stderr +++ b/tests/ui/mem_replace.stderr @@ -1,5 +1,5 @@ error: replacing an `Option` with `None` - --> tests/ui/mem_replace.rs:14:13 + --> tests/ui/mem_replace.rs:13:13 | LL | let _ = mem::replace(&mut an_option, None); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()` @@ -8,13 +8,13 @@ LL | let _ = mem::replace(&mut an_option, None); = help: to override `-D warnings` add `#[allow(clippy::mem_replace_option_with_none)]` error: replacing an `Option` with `None` - --> tests/ui/mem_replace.rs:16:13 + --> tests/ui/mem_replace.rs:15:13 | LL | let _ = mem::replace(an_option, None); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> tests/ui/mem_replace.rs:21:13 + --> tests/ui/mem_replace.rs:20:13 | LL | let _ = std::mem::replace(&mut s, String::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut s)` @@ -23,127 +23,127 @@ LL | let _ = std::mem::replace(&mut s, String::default()); = help: to override `-D warnings` add `#[allow(clippy::mem_replace_with_default)]` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> tests/ui/mem_replace.rs:24:13 + --> tests/ui/mem_replace.rs:23:13 | LL | let _ = std::mem::replace(s, String::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(s)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> tests/ui/mem_replace.rs:25:13 + --> tests/ui/mem_replace.rs:24:13 | LL | let _ = std::mem::replace(s, Default::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(s)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> tests/ui/mem_replace.rs:28:13 + --> tests/ui/mem_replace.rs:27:13 | LL | let _ = std::mem::replace(&mut v, Vec::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> tests/ui/mem_replace.rs:29:13 + --> tests/ui/mem_replace.rs:28:13 | LL | let _ = std::mem::replace(&mut v, Default::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> tests/ui/mem_replace.rs:30:13 + --> tests/ui/mem_replace.rs:29:13 | LL | let _ = std::mem::replace(&mut v, Vec::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> tests/ui/mem_replace.rs:31:13 + --> tests/ui/mem_replace.rs:30:13 | LL | let _ = std::mem::replace(&mut v, vec![]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> tests/ui/mem_replace.rs:34:13 + --> tests/ui/mem_replace.rs:33:13 | LL | let _ = std::mem::replace(&mut hash_map, HashMap::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut hash_map)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> tests/ui/mem_replace.rs:37:13 + --> tests/ui/mem_replace.rs:36:13 | LL | let _ = std::mem::replace(&mut btree_map, BTreeMap::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut btree_map)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> tests/ui/mem_replace.rs:40:13 + --> tests/ui/mem_replace.rs:39:13 | LL | let _ = std::mem::replace(&mut vd, VecDeque::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut vd)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> tests/ui/mem_replace.rs:43:13 + --> tests/ui/mem_replace.rs:42:13 | LL | let _ = std::mem::replace(&mut hash_set, HashSet::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut hash_set)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> tests/ui/mem_replace.rs:46:13 + --> tests/ui/mem_replace.rs:45:13 | LL | let _ = std::mem::replace(&mut btree_set, BTreeSet::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut btree_set)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> tests/ui/mem_replace.rs:49:13 + --> tests/ui/mem_replace.rs:48:13 | LL | let _ = std::mem::replace(&mut list, LinkedList::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut list)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> tests/ui/mem_replace.rs:52:13 + --> tests/ui/mem_replace.rs:51:13 | LL | let _ = std::mem::replace(&mut binary_heap, BinaryHeap::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut binary_heap)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> tests/ui/mem_replace.rs:55:13 + --> tests/ui/mem_replace.rs:54:13 | LL | let _ = std::mem::replace(&mut tuple, (vec![], BinaryHeap::new())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut tuple)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> tests/ui/mem_replace.rs:58:13 + --> tests/ui/mem_replace.rs:57:13 | LL | let _ = std::mem::replace(&mut refstr, ""); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut refstr)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> tests/ui/mem_replace.rs:61:13 + --> tests/ui/mem_replace.rs:60:13 | LL | let _ = std::mem::replace(&mut slice, &[]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut slice)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> tests/ui/mem_replace.rs:97:13 + --> tests/ui/mem_replace.rs:96:13 | LL | let _ = std::mem::replace(&mut s, String::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut s)` error: replacing an `Option` with `None` - --> tests/ui/mem_replace.rs:127:13 + --> tests/ui/mem_replace.rs:126:13 | LL | let _ = std::mem::replace(&mut f.0, None); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `f.0.take()` error: replacing an `Option` with `None` - --> tests/ui/mem_replace.rs:128:13 + --> tests/ui/mem_replace.rs:127:13 | LL | let _ = std::mem::replace(&mut *f, None); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `(*f).take()` error: replacing an `Option` with `None` - --> tests/ui/mem_replace.rs:129:13 + --> tests/ui/mem_replace.rs:128:13 | LL | let _ = std::mem::replace(&mut b.opt, None); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `b.opt.take()` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> tests/ui/mem_replace.rs:131:13 + --> tests/ui/mem_replace.rs:130:13 | LL | let _ = std::mem::replace(&mut b.val, String::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut b.val)` diff --git a/tests/ui/mem_replace_no_std.fixed b/tests/ui/mem_replace_no_std.fixed index c970f2ba2814b..60f523c8ef1ec 100644 --- a/tests/ui/mem_replace_no_std.fixed +++ b/tests/ui/mem_replace_no_std.fixed @@ -1,6 +1,5 @@ -#![allow(unused)] +#![allow(unused, clippy::needless_lifetimes)] #![warn( - clippy::all, clippy::style, clippy::mem_replace_option_with_none, clippy::mem_replace_with_default diff --git a/tests/ui/mem_replace_no_std.rs b/tests/ui/mem_replace_no_std.rs index 673d5c7b4f45a..d1cb9a5817bad 100644 --- a/tests/ui/mem_replace_no_std.rs +++ b/tests/ui/mem_replace_no_std.rs @@ -1,6 +1,5 @@ -#![allow(unused)] +#![allow(unused, clippy::needless_lifetimes)] #![warn( - clippy::all, clippy::style, clippy::mem_replace_option_with_none, clippy::mem_replace_with_default diff --git a/tests/ui/mem_replace_no_std.stderr b/tests/ui/mem_replace_no_std.stderr index eea538da4277a..6ba6d2162a745 100644 --- a/tests/ui/mem_replace_no_std.stderr +++ b/tests/ui/mem_replace_no_std.stderr @@ -1,5 +1,5 @@ error: replacing an `Option` with `None` - --> tests/ui/mem_replace_no_std.rs:24:13 + --> tests/ui/mem_replace_no_std.rs:23:13 | LL | let _ = mem::replace(&mut an_option, None); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()` @@ -8,13 +8,13 @@ LL | let _ = mem::replace(&mut an_option, None); = help: to override `-D warnings` add `#[allow(clippy::mem_replace_option_with_none)]` error: replacing an `Option` with `None` - --> tests/ui/mem_replace_no_std.rs:26:13 + --> tests/ui/mem_replace_no_std.rs:25:13 | LL | let _ = mem::replace(an_option, None); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()` error: replacing a value of type `T` with `T::default()` is better expressed using `core::mem::take` - --> tests/ui/mem_replace_no_std.rs:31:13 + --> tests/ui/mem_replace_no_std.rs:30:13 | LL | let _ = mem::replace(&mut refstr, ""); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `core::mem::take(&mut refstr)` @@ -23,25 +23,25 @@ LL | let _ = mem::replace(&mut refstr, ""); = help: to override `-D warnings` add `#[allow(clippy::mem_replace_with_default)]` error: replacing a value of type `T` with `T::default()` is better expressed using `core::mem::take` - --> tests/ui/mem_replace_no_std.rs:34:13 + --> tests/ui/mem_replace_no_std.rs:33:13 | LL | let _ = mem::replace(&mut slice, &[]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `core::mem::take(&mut slice)` error: replacing an `Option` with `None` - --> tests/ui/mem_replace_no_std.rs:77:13 + --> tests/ui/mem_replace_no_std.rs:76:13 | LL | let _ = mem::replace(&mut f.0, None); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `f.0.take()` error: replacing an `Option` with `None` - --> tests/ui/mem_replace_no_std.rs:78:13 + --> tests/ui/mem_replace_no_std.rs:77:13 | LL | let _ = mem::replace(&mut *f, None); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `(*f).take()` error: replacing an `Option` with `None` - --> tests/ui/mem_replace_no_std.rs:79:13 + --> tests/ui/mem_replace_no_std.rs:78:13 | LL | let _ = mem::replace(&mut b.opt, None); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `b.opt.take()` diff --git a/tests/ui/mismatching_type_param_order.rs b/tests/ui/mismatching_type_param_order.rs index af2882e41fb10..a4560f9e9a99b 100644 --- a/tests/ui/mismatching_type_param_order.rs +++ b/tests/ui/mismatching_type_param_order.rs @@ -1,5 +1,5 @@ #![warn(clippy::mismatching_type_param_order)] -#![allow(clippy::disallowed_names)] +#![allow(clippy::disallowed_names, clippy::needless_lifetimes)] fn main() { struct Foo { diff --git a/tests/ui/needless_borrow.fixed b/tests/ui/needless_borrow.fixed index cabdc22bda840..2763830e09c9f 100644 --- a/tests/ui/needless_borrow.fixed +++ b/tests/ui/needless_borrow.fixed @@ -4,7 +4,8 @@ clippy::uninlined_format_args, clippy::unnecessary_mut_passed, clippy::unnecessary_to_owned, - clippy::unnecessary_literal_unwrap + clippy::unnecessary_literal_unwrap, + clippy::needless_lifetimes )] #![warn(clippy::needless_borrow)] diff --git a/tests/ui/needless_borrow.rs b/tests/ui/needless_borrow.rs index 50062589645f2..b46f82b18c641 100644 --- a/tests/ui/needless_borrow.rs +++ b/tests/ui/needless_borrow.rs @@ -4,7 +4,8 @@ clippy::uninlined_format_args, clippy::unnecessary_mut_passed, clippy::unnecessary_to_owned, - clippy::unnecessary_literal_unwrap + clippy::unnecessary_literal_unwrap, + clippy::needless_lifetimes )] #![warn(clippy::needless_borrow)] diff --git a/tests/ui/needless_borrow.stderr b/tests/ui/needless_borrow.stderr index bf0e265c25030..4b2b17e7e570a 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 - --> tests/ui/needless_borrow.rs:15:15 + --> tests/ui/needless_borrow.rs:16:15 | LL | let _ = x(&&a); // warn | ^^^ help: change this to: `&a` @@ -8,163 +8,163 @@ LL | let _ = x(&&a); // warn = help: to override `-D warnings` add `#[allow(clippy::needless_borrow)]` error: this expression creates a reference which is immediately dereferenced by the compiler - --> tests/ui/needless_borrow.rs:19:13 + --> tests/ui/needless_borrow.rs:20: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 - --> tests/ui/needless_borrow.rs:31:13 + --> tests/ui/needless_borrow.rs:32:13 | LL | &&a | ^^^ help: change this to: `&a` error: this expression creates a reference which is immediately dereferenced by the compiler - --> tests/ui/needless_borrow.rs:33:15 + --> tests/ui/needless_borrow.rs:34:15 | LL | 46 => &&a, | ^^^ help: change this to: `&a` error: this expression creates a reference which is immediately dereferenced by the compiler - --> tests/ui/needless_borrow.rs:39:27 + --> tests/ui/needless_borrow.rs:40:27 | LL | break &ref_a; | ^^^^^^ help: change this to: `ref_a` error: this expression creates a reference which is immediately dereferenced by the compiler - --> tests/ui/needless_borrow.rs:46:15 + --> tests/ui/needless_borrow.rs:47:15 | LL | let _ = x(&&&a); | ^^^^ help: change this to: `&a` error: this expression creates a reference which is immediately dereferenced by the compiler - --> tests/ui/needless_borrow.rs:47:15 + --> tests/ui/needless_borrow.rs:48:15 | LL | let _ = x(&mut &&a); | ^^^^^^^^ help: change this to: `&a` error: this expression creates a reference which is immediately dereferenced by the compiler - --> tests/ui/needless_borrow.rs:48:15 + --> tests/ui/needless_borrow.rs:49: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 - --> tests/ui/needless_borrow.rs:49:15 + --> tests/ui/needless_borrow.rs:50: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 - --> tests/ui/needless_borrow.rs:52:11 + --> tests/ui/needless_borrow.rs:53:11 | LL | x(&b); | ^^ help: change this to: `b` error: this expression creates a reference which is immediately dereferenced by the compiler - --> tests/ui/needless_borrow.rs:59:13 + --> tests/ui/needless_borrow.rs:60:13 | LL | mut_ref(&mut x); | ^^^^^^ help: change this to: `x` error: this expression creates a reference which is immediately dereferenced by the compiler - --> tests/ui/needless_borrow.rs:60:13 + --> tests/ui/needless_borrow.rs:61: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 - --> tests/ui/needless_borrow.rs:61:23 + --> tests/ui/needless_borrow.rs:62: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 - --> tests/ui/needless_borrow.rs:62:23 + --> tests/ui/needless_borrow.rs:63: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 - --> tests/ui/needless_borrow.rs:71:14 + --> tests/ui/needless_borrow.rs:72:14 | LL | 0 => &mut x, | ^^^^^^ help: change this to: `x` error: this expression creates a reference which is immediately dereferenced by the compiler - --> tests/ui/needless_borrow.rs:77:14 + --> tests/ui/needless_borrow.rs:78:14 | LL | 0 => &mut x, | ^^^^^^ help: change this to: `x` error: this expression borrows a value the compiler would automatically borrow - --> tests/ui/needless_borrow.rs:89:13 + --> tests/ui/needless_borrow.rs:90:13 | LL | let _ = (&x).0; | ^^^^ help: change this to: `x` error: this expression borrows a value the compiler would automatically borrow - --> tests/ui/needless_borrow.rs:91:22 + --> tests/ui/needless_borrow.rs:92:22 | LL | let _ = unsafe { (&*x).0 }; | ^^^^^ help: change this to: `(*x)` error: this expression creates a reference which is immediately dereferenced by the compiler - --> tests/ui/needless_borrow.rs:101:5 + --> tests/ui/needless_borrow.rs:102:5 | LL | (&&()).foo(); | ^^^^^^ help: change this to: `(&())` error: this expression creates a reference which is immediately dereferenced by the compiler - --> tests/ui/needless_borrow.rs:110:5 + --> tests/ui/needless_borrow.rs:111:5 | LL | (&&5).foo(); | ^^^^^ help: change this to: `(&5)` error: this expression creates a reference which is immediately dereferenced by the compiler - --> tests/ui/needless_borrow.rs:136:23 + --> tests/ui/needless_borrow.rs:137:23 | LL | let x: (&str,) = (&"",); | ^^^ help: change this to: `""` error: this expression borrows a value the compiler would automatically borrow - --> tests/ui/needless_borrow.rs:178:13 + --> tests/ui/needless_borrow.rs:179:13 | LL | (&self.f)() | ^^^^^^^^^ help: change this to: `(self.f)` error: this expression borrows a value the compiler would automatically borrow - --> tests/ui/needless_borrow.rs:187:13 + --> tests/ui/needless_borrow.rs:188:13 | LL | (&mut self.f)() | ^^^^^^^^^^^^^ help: change this to: `(self.f)` error: this expression borrows a value the compiler would automatically borrow - --> tests/ui/needless_borrow.rs:224:22 + --> tests/ui/needless_borrow.rs:225:22 | LL | let _ = &mut (&mut { x.u }).x; | ^^^^^^^^^^^^^^ help: change this to: `{ x.u }` error: this expression borrows a value the compiler would automatically borrow - --> tests/ui/needless_borrow.rs:231:22 + --> tests/ui/needless_borrow.rs:232:22 | LL | let _ = &mut (&mut { x.u }).x; | ^^^^^^^^^^^^^^ help: change this to: `{ x.u }` error: this expression borrows a value the compiler would automatically borrow - --> tests/ui/needless_borrow.rs:235:22 + --> tests/ui/needless_borrow.rs:236:22 | LL | let _ = &mut (&mut x.u).x; | ^^^^^^^^^^ help: change this to: `x.u` error: this expression borrows a value the compiler would automatically borrow - --> tests/ui/needless_borrow.rs:236:22 + --> tests/ui/needless_borrow.rs:237:22 | LL | let _ = &mut (&mut { x.u }).x; | ^^^^^^^^^^^^^^ help: change this to: `{ x.u }` error: this expression creates a reference which is immediately dereferenced by the compiler - --> tests/ui/needless_borrow.rs:257:23 + --> tests/ui/needless_borrow.rs:258:23 | LL | option.unwrap_or((&x.0,)); | ^^^^ help: change this to: `x.0` diff --git a/tests/ui/needless_pass_by_value.rs b/tests/ui/needless_pass_by_value.rs index 9408b8c948fed..a8d9db95dcc77 100644 --- a/tests/ui/needless_pass_by_value.rs +++ b/tests/ui/needless_pass_by_value.rs @@ -5,7 +5,8 @@ clippy::redundant_clone, clippy::redundant_pattern_matching, clippy::single_match, - clippy::uninlined_format_args + clippy::uninlined_format_args, + clippy::needless_lifetimes )] //@no-rustfix use std::borrow::Borrow; diff --git a/tests/ui/needless_pass_by_value.stderr b/tests/ui/needless_pass_by_value.stderr index 46ef8f3e8da43..2587d3f8c52f7 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 - --> tests/ui/needless_pass_by_value.rs:18:23 + --> tests/ui/needless_pass_by_value.rs:19:23 | LL | fn foo(v: Vec, w: Vec, mut x: Vec, y: Vec) -> Vec { | ^^^^^^ help: consider changing the type to: `&[T]` @@ -8,55 +8,55 @@ LL | fn foo(v: Vec, w: Vec, mut x: Vec, y: Vec) -> Vec tests/ui/needless_pass_by_value.rs:34:11 + --> tests/ui/needless_pass_by_value.rs:35: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 - --> tests/ui/needless_pass_by_value.rs:34:22 + --> tests/ui/needless_pass_by_value.rs:35: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 - --> tests/ui/needless_pass_by_value.rs:42:71 + --> tests/ui/needless_pass_by_value.rs:43: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 - --> tests/ui/needless_pass_by_value.rs:55:18 + --> tests/ui/needless_pass_by_value.rs:56: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 - --> tests/ui/needless_pass_by_value.rs:69:24 + --> tests/ui/needless_pass_by_value.rs:70: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 - --> tests/ui/needless_pass_by_value.rs:69:36 + --> tests/ui/needless_pass_by_value.rs:70: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 - --> tests/ui/needless_pass_by_value.rs:87:49 + --> tests/ui/needless_pass_by_value.rs:88:49 | LL | fn test_blanket_ref(vals: T, serializable: S) {} | ^ help: consider taking a reference instead: `&T` error: this argument is passed by value, but not consumed in the function body - --> tests/ui/needless_pass_by_value.rs:90:18 + --> tests/ui/needless_pass_by_value.rs:91: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 - --> tests/ui/needless_pass_by_value.rs:90:29 + --> tests/ui/needless_pass_by_value.rs:91:29 | LL | fn issue_2114(s: String, t: String, u: Vec, v: Vec) { | ^^^^^^ @@ -71,13 +71,13 @@ LL | let _ = t.to_string(); | ~~~~~~~~~~~~~ error: this argument is passed by value, but not consumed in the function body - --> tests/ui/needless_pass_by_value.rs:90:40 + --> tests/ui/needless_pass_by_value.rs:91: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 - --> tests/ui/needless_pass_by_value.rs:90:53 + --> tests/ui/needless_pass_by_value.rs:91:53 | LL | fn issue_2114(s: String, t: String, u: Vec, v: Vec) { | ^^^^^^^^ @@ -92,85 +92,85 @@ LL | let _ = v.to_owned(); | ~~~~~~~~~~~~ error: this argument is passed by value, but not consumed in the function body - --> tests/ui/needless_pass_by_value.rs:107:12 + --> tests/ui/needless_pass_by_value.rs:108: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 - --> tests/ui/needless_pass_by_value.rs:109:12 + --> tests/ui/needless_pass_by_value.rs:110: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 - --> tests/ui/needless_pass_by_value.rs:119:23 + --> tests/ui/needless_pass_by_value.rs:120:23 | LL | fn baz(&self, uu: U, ss: Self) {} | ^ help: consider taking a reference instead: `&U` error: this argument is passed by value, but not consumed in the function body - --> tests/ui/needless_pass_by_value.rs:119:30 + --> tests/ui/needless_pass_by_value.rs:120:30 | LL | fn baz(&self, uu: U, ss: Self) {} | ^^^^ help: consider taking a reference instead: `&Self` error: this argument is passed by value, but not consumed in the function body - --> tests/ui/needless_pass_by_value.rs:143:24 + --> tests/ui/needless_pass_by_value.rs:144:24 | LL | fn bar_copy(x: u32, y: CopyWrapper) { | ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper` | help: or consider marking this type as `Copy` - --> tests/ui/needless_pass_by_value.rs:141:1 + --> tests/ui/needless_pass_by_value.rs:142:1 | LL | struct CopyWrapper(u32); | ^^^^^^^^^^^^^^^^^^ error: this argument is passed by value, but not consumed in the function body - --> tests/ui/needless_pass_by_value.rs:150:29 + --> tests/ui/needless_pass_by_value.rs:151:29 | LL | fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) { | ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper` | help: or consider marking this type as `Copy` - --> tests/ui/needless_pass_by_value.rs:141:1 + --> tests/ui/needless_pass_by_value.rs:142:1 | LL | struct CopyWrapper(u32); | ^^^^^^^^^^^^^^^^^^ error: this argument is passed by value, but not consumed in the function body - --> tests/ui/needless_pass_by_value.rs:150:45 + --> tests/ui/needless_pass_by_value.rs:151:45 | LL | fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) { | ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper` | help: or consider marking this type as `Copy` - --> tests/ui/needless_pass_by_value.rs:141:1 + --> tests/ui/needless_pass_by_value.rs:142:1 | LL | struct CopyWrapper(u32); | ^^^^^^^^^^^^^^^^^^ error: this argument is passed by value, but not consumed in the function body - --> tests/ui/needless_pass_by_value.rs:150:61 + --> tests/ui/needless_pass_by_value.rs:151:61 | LL | fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) { | ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper` | help: or consider marking this type as `Copy` - --> tests/ui/needless_pass_by_value.rs:141:1 + --> tests/ui/needless_pass_by_value.rs:142:1 | LL | struct CopyWrapper(u32); | ^^^^^^^^^^^^^^^^^^ error: this argument is passed by value, but not consumed in the function body - --> tests/ui/needless_pass_by_value.rs:165:40 + --> tests/ui/needless_pass_by_value.rs:166:40 | LL | fn some_fun<'b, S: Bar<'b, ()>>(items: S) {} | ^ help: consider taking a reference instead: `&S` error: this argument is passed by value, but not consumed in the function body - --> tests/ui/needless_pass_by_value.rs:171:20 + --> tests/ui/needless_pass_by_value.rs:172:20 | LL | fn more_fun(items: impl Club<'static, i32>) {} | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider taking a reference instead: `&impl Club<'static, i32>` diff --git a/tests/ui/new_without_default.fixed b/tests/ui/new_without_default.fixed index 85408c4e17f45..5a6a92394a7fd 100644 --- a/tests/ui/new_without_default.fixed +++ b/tests/ui/new_without_default.fixed @@ -2,7 +2,8 @@ dead_code, clippy::missing_safety_doc, clippy::extra_unused_lifetimes, - clippy::extra_unused_type_parameters + clippy::extra_unused_type_parameters, + clippy::needless_lifetimes )] #![warn(clippy::new_without_default)] diff --git a/tests/ui/new_without_default.rs b/tests/ui/new_without_default.rs index 3ac7292c2362f..12ea729253ac9 100644 --- a/tests/ui/new_without_default.rs +++ b/tests/ui/new_without_default.rs @@ -2,7 +2,8 @@ dead_code, clippy::missing_safety_doc, clippy::extra_unused_lifetimes, - clippy::extra_unused_type_parameters + clippy::extra_unused_type_parameters, + clippy::needless_lifetimes )] #![warn(clippy::new_without_default)] diff --git a/tests/ui/new_without_default.stderr b/tests/ui/new_without_default.stderr index a30830ae7b2e9..57bf4bd847cce 100644 --- a/tests/ui/new_without_default.stderr +++ b/tests/ui/new_without_default.stderr @@ -1,5 +1,5 @@ error: you should consider adding a `Default` implementation for `Foo` - --> tests/ui/new_without_default.rs:12:5 + --> tests/ui/new_without_default.rs:13:5 | LL | / pub fn new() -> Foo { LL | | @@ -20,7 +20,7 @@ LL + } | error: you should consider adding a `Default` implementation for `Bar` - --> tests/ui/new_without_default.rs:22:5 + --> tests/ui/new_without_default.rs:23:5 | LL | / pub fn new() -> Self { LL | | @@ -38,7 +38,7 @@ LL + } | error: you should consider adding a `Default` implementation for `LtKo<'c>` - --> tests/ui/new_without_default.rs:87:5 + --> tests/ui/new_without_default.rs:88:5 | LL | / pub fn new() -> LtKo<'c> { LL | | @@ -56,7 +56,7 @@ LL + } | error: you should consider adding a `Default` implementation for `Const` - --> tests/ui/new_without_default.rs:120:5 + --> tests/ui/new_without_default.rs:121:5 | LL | / pub const fn new() -> Const { LL | | Const @@ -73,7 +73,7 @@ LL + } | error: you should consider adding a `Default` implementation for `NewNotEqualToDerive` - --> tests/ui/new_without_default.rs:180:5 + --> tests/ui/new_without_default.rs:181:5 | LL | / pub fn new() -> Self { LL | | @@ -91,7 +91,7 @@ LL + } | error: you should consider adding a `Default` implementation for `FooGenerics` - --> tests/ui/new_without_default.rs:189:5 + --> tests/ui/new_without_default.rs:190:5 | LL | / pub fn new() -> Self { LL | | @@ -109,7 +109,7 @@ LL + } | error: you should consider adding a `Default` implementation for `BarGenerics` - --> tests/ui/new_without_default.rs:197:5 + --> tests/ui/new_without_default.rs:198:5 | LL | / pub fn new() -> Self { LL | | @@ -127,7 +127,7 @@ LL + } | error: you should consider adding a `Default` implementation for `Foo` - --> tests/ui/new_without_default.rs:209:9 + --> tests/ui/new_without_default.rs:210:9 | LL | / pub fn new() -> Self { LL | | @@ -147,7 +147,7 @@ LL ~ impl Foo { | error: you should consider adding a `Default` implementation for `MyStruct` - --> tests/ui/new_without_default.rs:255:5 + --> tests/ui/new_without_default.rs:256:5 | LL | / pub fn new() -> Self { LL | | Self { _kv: None } diff --git a/tests/ui/ref_as_ptr.fixed b/tests/ui/ref_as_ptr.fixed index 466a628a00265..6048267092f1b 100644 --- a/tests/ui/ref_as_ptr.fixed +++ b/tests/ui/ref_as_ptr.fixed @@ -1,5 +1,5 @@ #![warn(clippy::ref_as_ptr)] -#![allow(clippy::unnecessary_mut_passed)] +#![allow(clippy::unnecessary_mut_passed, clippy::needless_lifetimes)] fn f(_: T) {} diff --git a/tests/ui/ref_as_ptr.rs b/tests/ui/ref_as_ptr.rs index 0fdc753dc225a..7f1d59b856e4b 100644 --- a/tests/ui/ref_as_ptr.rs +++ b/tests/ui/ref_as_ptr.rs @@ -1,5 +1,5 @@ #![warn(clippy::ref_as_ptr)] -#![allow(clippy::unnecessary_mut_passed)] +#![allow(clippy::unnecessary_mut_passed, clippy::needless_lifetimes)] fn f(_: T) {} diff --git a/tests/ui/serde.rs b/tests/ui/serde.rs index 610a50020ec56..af8b10f3e6aeb 100644 --- a/tests/ui/serde.rs +++ b/tests/ui/serde.rs @@ -1,5 +1,5 @@ #![warn(clippy::serde_api_misuse)] -#![allow(dead_code)] +#![allow(dead_code, clippy::needless_lifetimes)] extern crate serde; diff --git a/tests/ui/significant_drop_in_scrutinee.rs b/tests/ui/significant_drop_in_scrutinee.rs index 0db6fbfb7be9b..8468d1d7c7d42 100644 --- a/tests/ui/significant_drop_in_scrutinee.rs +++ b/tests/ui/significant_drop_in_scrutinee.rs @@ -2,7 +2,12 @@ //@no-rustfix #![warn(clippy::significant_drop_in_scrutinee)] #![allow(dead_code, unused_assignments)] -#![allow(clippy::match_single_binding, clippy::single_match, clippy::uninlined_format_args)] +#![allow( + clippy::match_single_binding, + clippy::single_match, + clippy::uninlined_format_args, + clippy::needless_lifetimes +)] 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 c0c93cd10c027..62030cbe70e7f 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 - --> tests/ui/significant_drop_in_scrutinee.rs:55:11 + --> tests/ui/significant_drop_in_scrutinee.rs:60:11 | LL | match mutex.lock().unwrap().foo() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -20,7 +20,7 @@ LL ~ match value { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> tests/ui/significant_drop_in_scrutinee.rs:143:11 + --> tests/ui/significant_drop_in_scrutinee.rs:148:11 | LL | match s.lock_m().get_the_value() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -42,7 +42,7 @@ LL ~ match value { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> tests/ui/significant_drop_in_scrutinee.rs:166:11 + --> tests/ui/significant_drop_in_scrutinee.rs:171:11 | LL | match s.lock_m_m().get_the_value() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -64,7 +64,7 @@ LL ~ match value { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> tests/ui/significant_drop_in_scrutinee.rs:216:11 + --> tests/ui/significant_drop_in_scrutinee.rs:221:11 | LL | match counter.temp_increment().len() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -80,7 +80,7 @@ LL ~ match value { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> tests/ui/significant_drop_in_scrutinee.rs:241:16 + --> tests/ui/significant_drop_in_scrutinee.rs:246:16 | LL | match (mutex1.lock().unwrap().s.len(), true) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -99,7 +99,7 @@ LL ~ match (value, true) { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> tests/ui/significant_drop_in_scrutinee.rs:252:22 + --> tests/ui/significant_drop_in_scrutinee.rs:257:22 | LL | match (true, mutex1.lock().unwrap().s.len(), true) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -118,7 +118,7 @@ LL ~ match (true, value, true) { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> tests/ui/significant_drop_in_scrutinee.rs:264:16 + --> tests/ui/significant_drop_in_scrutinee.rs:269:16 | LL | match (mutex1.lock().unwrap().s.len(), true, mutex2.lock().unwrap().s.len()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -139,7 +139,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 - --> tests/ui/significant_drop_in_scrutinee.rs:264:54 + --> tests/ui/significant_drop_in_scrutinee.rs:269:54 | LL | match (mutex1.lock().unwrap().s.len(), true, mutex2.lock().unwrap().s.len()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -160,7 +160,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 - --> tests/ui/significant_drop_in_scrutinee.rs:319:11 + --> tests/ui/significant_drop_in_scrutinee.rs:324:11 | LL | match mutex.lock().unwrap().s.len() > 1 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -179,7 +179,7 @@ LL ~ match value > 1 { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> tests/ui/significant_drop_in_scrutinee.rs:328:15 + --> tests/ui/significant_drop_in_scrutinee.rs:333:15 | LL | match 1 < mutex.lock().unwrap().s.len() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -198,7 +198,7 @@ LL ~ match 1 < value { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> tests/ui/significant_drop_in_scrutinee.rs:348:11 + --> tests/ui/significant_drop_in_scrutinee.rs:353:11 | LL | match mutex1.lock().unwrap().s.len() < mutex2.lock().unwrap().s.len() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -219,7 +219,7 @@ LL ~ match value < mutex2.lock().unwrap().s.len() { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> tests/ui/significant_drop_in_scrutinee.rs:348:44 + --> tests/ui/significant_drop_in_scrutinee.rs:353:44 | LL | match mutex1.lock().unwrap().s.len() < mutex2.lock().unwrap().s.len() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -240,7 +240,7 @@ LL ~ match mutex1.lock().unwrap().s.len() < value { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> tests/ui/significant_drop_in_scrutinee.rs:361:11 + --> tests/ui/significant_drop_in_scrutinee.rs:366:11 | LL | match mutex1.lock().unwrap().s.len() >= mutex2.lock().unwrap().s.len() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -261,7 +261,7 @@ LL ~ match value >= mutex2.lock().unwrap().s.len() { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> tests/ui/significant_drop_in_scrutinee.rs:361:45 + --> tests/ui/significant_drop_in_scrutinee.rs:366:45 | LL | match mutex1.lock().unwrap().s.len() >= mutex2.lock().unwrap().s.len() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -282,7 +282,7 @@ LL ~ match mutex1.lock().unwrap().s.len() >= value { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> tests/ui/significant_drop_in_scrutinee.rs:398:11 + --> tests/ui/significant_drop_in_scrutinee.rs:403:11 | LL | match get_mutex_guard().s.len() > 1 { | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -301,7 +301,7 @@ LL ~ match value > 1 { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> tests/ui/significant_drop_in_scrutinee.rs:417:11 + --> tests/ui/significant_drop_in_scrutinee.rs:422:11 | LL | match match i { | ___________^ @@ -334,7 +334,7 @@ LL ~ match value | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> tests/ui/significant_drop_in_scrutinee.rs:445:11 + --> tests/ui/significant_drop_in_scrutinee.rs:450:11 | LL | match if i > 1 { | ___________^ @@ -368,7 +368,7 @@ LL ~ match value | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> tests/ui/significant_drop_in_scrutinee.rs:501:11 + --> tests/ui/significant_drop_in_scrutinee.rs:506:11 | LL | match s.lock().deref().deref() { | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -386,7 +386,7 @@ LL ~ match (&value) { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> tests/ui/significant_drop_in_scrutinee.rs:551:11 + --> tests/ui/significant_drop_in_scrutinee.rs:556:11 | LL | match mutex.lock().unwrap().i = i { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -405,7 +405,7 @@ LL ~ match () { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> tests/ui/significant_drop_in_scrutinee.rs:559:15 + --> tests/ui/significant_drop_in_scrutinee.rs:564:15 | LL | match i = mutex.lock().unwrap().i { | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -424,7 +424,7 @@ LL ~ match i = value { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> tests/ui/significant_drop_in_scrutinee.rs:567:11 + --> tests/ui/significant_drop_in_scrutinee.rs:572:11 | LL | match mutex.lock().unwrap().i += 1 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -443,7 +443,7 @@ LL ~ match () { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> tests/ui/significant_drop_in_scrutinee.rs:575:16 + --> tests/ui/significant_drop_in_scrutinee.rs:580:16 | LL | match i += mutex.lock().unwrap().i { | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -462,7 +462,7 @@ LL ~ match i += value { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> tests/ui/significant_drop_in_scrutinee.rs:640:11 + --> tests/ui/significant_drop_in_scrutinee.rs:645:11 | LL | match rwlock.read().unwrap().to_number() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -478,7 +478,7 @@ LL ~ match value { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> tests/ui/significant_drop_in_scrutinee.rs:668:11 + --> tests/ui/significant_drop_in_scrutinee.rs:673:11 | LL | match mutex.lock().unwrap().foo() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -494,7 +494,7 @@ LL ~ match value { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> tests/ui/significant_drop_in_scrutinee.rs:731:11 + --> tests/ui/significant_drop_in_scrutinee.rs:736:11 | LL | match guard.take().len() { | ^^^^^^^^^^^^^^^^^^ @@ -510,7 +510,7 @@ LL ~ match value { | error: temporary with significant `Drop` in `for` loop condition will live until the end of the `for` expression - --> tests/ui/significant_drop_in_scrutinee.rs:757:16 + --> tests/ui/significant_drop_in_scrutinee.rs:762:16 | LL | for val in mutex.lock().unwrap().copy_old_lifetime() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -526,7 +526,7 @@ LL ~ for val in value { | error: temporary with significant `Drop` in `for` loop condition will live until the end of the `for` expression - --> tests/ui/significant_drop_in_scrutinee.rs:797:17 + --> tests/ui/significant_drop_in_scrutinee.rs:802:17 | LL | for val in [mutex.lock().unwrap()[0], 2] { | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -542,7 +542,7 @@ LL ~ for val in [value, 2] { | error: temporary with significant `Drop` in `if let` scrutinee will live until the end of the `if let` expression - --> tests/ui/significant_drop_in_scrutinee.rs:807:24 + --> tests/ui/significant_drop_in_scrutinee.rs:812:24 | LL | if let Some(val) = mutex.lock().unwrap().first().copied() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -558,7 +558,7 @@ LL ~ if let Some(val) = value { | error: temporary with significant `Drop` in `while let` scrutinee will live until the end of the `while let` expression - --> tests/ui/significant_drop_in_scrutinee.rs:823:27 + --> tests/ui/significant_drop_in_scrutinee.rs:828:27 | LL | while let Some(val) = mutex.lock().unwrap().pop() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/str_split.fixed b/tests/ui/str_split.fixed index 4f33241da7a22..57a3c315a8779 100644 --- a/tests/ui/str_split.fixed +++ b/tests/ui/str_split.fixed @@ -1,4 +1,5 @@ #![warn(clippy::str_split_at_newline)] +#![allow(clippy::needless_lifetimes)] use core::str::Split; use std::ops::Deref; diff --git a/tests/ui/str_split.rs b/tests/ui/str_split.rs index f24caa61c3053..fcff036f2649a 100644 --- a/tests/ui/str_split.rs +++ b/tests/ui/str_split.rs @@ -1,4 +1,5 @@ #![warn(clippy::str_split_at_newline)] +#![allow(clippy::needless_lifetimes)] use core::str::Split; use std::ops::Deref; diff --git a/tests/ui/str_split.stderr b/tests/ui/str_split.stderr index ebe0d4ef4d301..7b560468f1269 100644 --- a/tests/ui/str_split.stderr +++ b/tests/ui/str_split.stderr @@ -1,5 +1,5 @@ error: using `str.trim().split()` with hard-coded newlines - --> tests/ui/str_split.rs:59:13 + --> tests/ui/str_split.rs:60:13 | LL | let _ = s1.trim().split('\n'); | ^^^^^^^^^^^^^^^^^^^^^ help: use `str.lines()` instead: `s1.lines()` @@ -8,55 +8,55 @@ LL | let _ = s1.trim().split('\n'); = help: to override `-D warnings` add `#[allow(clippy::str_split_at_newline)]` error: using `str.trim().split()` with hard-coded newlines - --> tests/ui/str_split.rs:61:13 + --> tests/ui/str_split.rs:62:13 | LL | let _ = s1.trim().split("\n"); | ^^^^^^^^^^^^^^^^^^^^^ help: use `str.lines()` instead: `s1.lines()` error: using `str.trim().split()` with hard-coded newlines - --> tests/ui/str_split.rs:62:13 + --> tests/ui/str_split.rs:63:13 | LL | let _ = s1.trim().split("\r\n"); | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `str.lines()` instead: `s1.lines()` error: using `str.trim().split()` with hard-coded newlines - --> tests/ui/str_split.rs:65:13 + --> tests/ui/str_split.rs:66:13 | LL | let _ = s2.trim().split('\n'); | ^^^^^^^^^^^^^^^^^^^^^ help: use `str.lines()` instead: `s2.lines()` error: using `str.trim().split()` with hard-coded newlines - --> tests/ui/str_split.rs:67:13 + --> tests/ui/str_split.rs:68:13 | LL | let _ = s2.trim().split("\n"); | ^^^^^^^^^^^^^^^^^^^^^ help: use `str.lines()` instead: `s2.lines()` error: using `str.trim().split()` with hard-coded newlines - --> tests/ui/str_split.rs:68:13 + --> tests/ui/str_split.rs:69:13 | LL | let _ = s2.trim().split("\r\n"); | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `str.lines()` instead: `s2.lines()` error: using `str.trim().split()` with hard-coded newlines - --> tests/ui/str_split.rs:72:13 + --> tests/ui/str_split.rs:73:13 | LL | let _ = s3.trim().split('\n'); | ^^^^^^^^^^^^^^^^^^^^^ help: use `str.lines()` instead: `s3.lines()` error: using `str.trim().split()` with hard-coded newlines - --> tests/ui/str_split.rs:74:13 + --> tests/ui/str_split.rs:75:13 | LL | let _ = s3.trim().split("\n"); | ^^^^^^^^^^^^^^^^^^^^^ help: use `str.lines()` instead: `s3.lines()` error: using `str.trim().split()` with hard-coded newlines - --> tests/ui/str_split.rs:75:13 + --> tests/ui/str_split.rs:76:13 | LL | let _ = s3.trim().split("\r\n"); | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `str.lines()` instead: `s3.lines()` error: using `str.trim().split()` with hard-coded newlines - --> tests/ui/str_split.rs:78:13 + --> tests/ui/str_split.rs:79:13 | LL | let _ = make_str!(s1).trim().split('\n'); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `str.lines()` instead: `make_str!(s1).lines()` diff --git a/tests/ui/temporary_assignment.rs b/tests/ui/temporary_assignment.rs index e2b982aeaf52a..d269f91b9fa82 100644 --- a/tests/ui/temporary_assignment.rs +++ b/tests/ui/temporary_assignment.rs @@ -1,4 +1,5 @@ #![warn(clippy::temporary_assignment)] +#![allow(clippy::needless_lifetimes)] use std::ops::{Deref, DerefMut}; diff --git a/tests/ui/temporary_assignment.stderr b/tests/ui/temporary_assignment.stderr index 1b6007f0b9878..8c28459407572 100644 --- a/tests/ui/temporary_assignment.stderr +++ b/tests/ui/temporary_assignment.stderr @@ -1,5 +1,5 @@ error: assignment to temporary - --> tests/ui/temporary_assignment.rs:47:5 + --> tests/ui/temporary_assignment.rs:48:5 | LL | Struct { field: 0 }.field = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | Struct { field: 0 }.field = 1; = help: to override `-D warnings` add `#[allow(clippy::temporary_assignment)]` error: assignment to temporary - --> tests/ui/temporary_assignment.rs:50:5 + --> tests/ui/temporary_assignment.rs:51:5 | LL | / MultiStruct { LL | | @@ -19,13 +19,13 @@ LL | | .field = 1; | |______________^ error: assignment to temporary - --> tests/ui/temporary_assignment.rs:56:5 + --> tests/ui/temporary_assignment.rs:57:5 | LL | ArrayStruct { array: [0] }.array[0] = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: assignment to temporary - --> tests/ui/temporary_assignment.rs:58:5 + --> tests/ui/temporary_assignment.rs:59:5 | LL | (0, 0).0 = 1; | ^^^^^^^^^^^^ diff --git a/tests/ui/unconditional_recursion.rs b/tests/ui/unconditional_recursion.rs index a51fc567f50c9..b8476a7088a10 100644 --- a/tests/ui/unconditional_recursion.rs +++ b/tests/ui/unconditional_recursion.rs @@ -4,7 +4,8 @@ #![allow( clippy::partialeq_ne_impl, clippy::default_constructed_unit_structs, - clippy::only_used_in_recursion + clippy::only_used_in_recursion, + clippy::needless_lifetimes )] enum Foo { diff --git a/tests/ui/unconditional_recursion.stderr b/tests/ui/unconditional_recursion.stderr index 03c27bd8ed8aa..6a0078ee09068 100644 --- a/tests/ui/unconditional_recursion.stderr +++ b/tests/ui/unconditional_recursion.stderr @@ -1,5 +1,5 @@ error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:46:5 + --> tests/ui/unconditional_recursion.rs:47:5 | LL | fn ne(&self, other: &Self) -> bool { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing @@ -12,7 +12,7 @@ LL | self.ne(other) = help: to override `-D warnings` add `#[allow(unconditional_recursion)]` error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:50:5 + --> tests/ui/unconditional_recursion.rs:51:5 | LL | fn eq(&self, other: &Self) -> bool { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing @@ -23,7 +23,7 @@ LL | self.eq(other) = help: a `loop` may express intention better if this is on purpose error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:215:5 + --> tests/ui/unconditional_recursion.rs:216:5 | LL | fn to_string(&self) -> String { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing @@ -34,7 +34,7 @@ LL | self.to_string() = help: a `loop` may express intention better if this is on purpose error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:225:5 + --> tests/ui/unconditional_recursion.rs:226:5 | LL | fn to_string(&self) -> String { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing @@ -45,7 +45,7 @@ LL | x.to_string() = help: a `loop` may express intention better if this is on purpose error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:236:5 + --> tests/ui/unconditional_recursion.rs:237:5 | LL | fn to_string(&self) -> String { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing @@ -56,7 +56,7 @@ LL | (self as &Self).to_string() = help: a `loop` may express intention better if this is on purpose error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:16:5 + --> tests/ui/unconditional_recursion.rs:17:5 | LL | / fn ne(&self, other: &Self) -> bool { LL | | @@ -65,7 +65,7 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:18:9 + --> tests/ui/unconditional_recursion.rs:19:9 | LL | self != other | ^^^^^^^^^^^^^ @@ -73,7 +73,7 @@ LL | self != other = help: to override `-D warnings` add `#[allow(clippy::unconditional_recursion)]` error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:20:5 + --> tests/ui/unconditional_recursion.rs:21:5 | LL | / fn eq(&self, other: &Self) -> bool { LL | | @@ -82,13 +82,13 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:22:9 + --> tests/ui/unconditional_recursion.rs:23:9 | LL | self == other | ^^^^^^^^^^^^^ error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:32:5 + --> tests/ui/unconditional_recursion.rs:33:5 | LL | / fn ne(&self, other: &Self) -> bool { LL | | self != &Foo2::B // no error here @@ -96,13 +96,13 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:33:9 + --> tests/ui/unconditional_recursion.rs:34:9 | LL | self != &Foo2::B // no error here | ^^^^^^^^^^^^^^^^ error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:35:5 + --> tests/ui/unconditional_recursion.rs:36:5 | LL | / fn eq(&self, other: &Self) -> bool { LL | | self == &Foo2::B // no error here @@ -110,13 +110,13 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:36:9 + --> tests/ui/unconditional_recursion.rs:37:9 | LL | self == &Foo2::B // no error here | ^^^^^^^^^^^^^^^^ error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:46:5 + --> tests/ui/unconditional_recursion.rs:47:5 | LL | / fn ne(&self, other: &Self) -> bool { LL | | @@ -125,13 +125,13 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:48:9 + --> tests/ui/unconditional_recursion.rs:49:9 | LL | self.ne(other) | ^^^^^^^^^^^^^^ error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:50:5 + --> tests/ui/unconditional_recursion.rs:51:5 | LL | / fn eq(&self, other: &Self) -> bool { LL | | @@ -140,13 +140,13 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:52:9 + --> tests/ui/unconditional_recursion.rs:53:9 | LL | self.eq(other) | ^^^^^^^^^^^^^^ error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:94:5 + --> tests/ui/unconditional_recursion.rs:95:5 | LL | / fn ne(&self, other: &Self) -> bool { LL | | @@ -155,13 +155,13 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:96:9 + --> tests/ui/unconditional_recursion.rs:97:9 | LL | other != self | ^^^^^^^^^^^^^ error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:98:5 + --> tests/ui/unconditional_recursion.rs:99:5 | LL | / fn eq(&self, other: &Self) -> bool { LL | | @@ -170,13 +170,13 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:100:9 + --> tests/ui/unconditional_recursion.rs:101:9 | LL | other == self | ^^^^^^^^^^^^^ error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:108:5 + --> tests/ui/unconditional_recursion.rs:109:5 | LL | / fn ne(&self, other: &Self) -> bool { LL | | @@ -185,13 +185,13 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:110:9 + --> tests/ui/unconditional_recursion.rs:111:9 | LL | other != other | ^^^^^^^^^^^^^^ error: equal expressions as operands to `!=` - --> tests/ui/unconditional_recursion.rs:110:9 + --> tests/ui/unconditional_recursion.rs:111:9 | LL | other != other | ^^^^^^^^^^^^^^ @@ -199,7 +199,7 @@ LL | other != other = note: `#[deny(clippy::eq_op)]` on by default error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:112:5 + --> tests/ui/unconditional_recursion.rs:113:5 | LL | / fn eq(&self, other: &Self) -> bool { LL | | @@ -208,19 +208,19 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:114:9 + --> tests/ui/unconditional_recursion.rs:115:9 | LL | other == other | ^^^^^^^^^^^^^^ error: equal expressions as operands to `==` - --> tests/ui/unconditional_recursion.rs:114:9 + --> tests/ui/unconditional_recursion.rs:115:9 | LL | other == other | ^^^^^^^^^^^^^^ error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:121:5 + --> tests/ui/unconditional_recursion.rs:122:5 | LL | / fn ne(&self, _other: &Self) -> bool { LL | | @@ -229,19 +229,19 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:123:9 + --> tests/ui/unconditional_recursion.rs:124:9 | LL | self != self | ^^^^^^^^^^^^ error: equal expressions as operands to `!=` - --> tests/ui/unconditional_recursion.rs:123:9 + --> tests/ui/unconditional_recursion.rs:124:9 | LL | self != self | ^^^^^^^^^^^^ error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:125:5 + --> tests/ui/unconditional_recursion.rs:126:5 | LL | / fn eq(&self, _other: &Self) -> bool { LL | | @@ -250,19 +250,19 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:127:9 + --> tests/ui/unconditional_recursion.rs:128:9 | LL | self == self | ^^^^^^^^^^^^ error: equal expressions as operands to `==` - --> tests/ui/unconditional_recursion.rs:127:9 + --> tests/ui/unconditional_recursion.rs:128:9 | LL | self == self | ^^^^^^^^^^^^ error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:153:13 + --> tests/ui/unconditional_recursion.rs:154:13 | LL | / fn eq(&self, other: &Self) -> bool { LL | | @@ -274,7 +274,7 @@ LL | impl_partial_eq!(S5); | -------------------- in this macro invocation | note: recursive call site - --> tests/ui/unconditional_recursion.rs:155:17 + --> tests/ui/unconditional_recursion.rs:156:17 | LL | self == other | ^^^^^^^^^^^^^ @@ -284,7 +284,7 @@ LL | impl_partial_eq!(S5); = note: this error originates in the macro `impl_partial_eq` (in Nightly builds, run with -Z macro-backtrace for more info) error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:182:5 + --> tests/ui/unconditional_recursion.rs:183:5 | LL | / fn eq(&self, other: &Self) -> bool { LL | | @@ -295,13 +295,13 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:186:9 + --> tests/ui/unconditional_recursion.rs:187:9 | LL | mine == theirs | ^^^^^^^^^^^^^^ error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:251:5 + --> tests/ui/unconditional_recursion.rs:252:5 | LL | / fn new() -> Self { LL | | @@ -310,13 +310,13 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:253:9 + --> tests/ui/unconditional_recursion.rs:254:9 | LL | Self::default() | ^^^^^^^^^^^^^^^ error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:290:5 + --> tests/ui/unconditional_recursion.rs:291:5 | LL | / fn eq(&self, other: &Self) -> bool { LL | | @@ -327,13 +327,13 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:294:9 + --> tests/ui/unconditional_recursion.rs:295:9 | LL | mine.eq(theirs) | ^^^^^^^^^^^^^^^ error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:361:5 + --> tests/ui/unconditional_recursion.rs:362:5 | LL | / fn from(f: BadFromTy1<'a>) -> Self { LL | | f.into() @@ -341,13 +341,13 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:362:9 + --> tests/ui/unconditional_recursion.rs:363:9 | LL | f.into() | ^^^^^^^^ error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:370:5 + --> tests/ui/unconditional_recursion.rs:371:5 | LL | / fn from(f: BadFromTy2<'a>) -> Self { LL | | Into::into(f) @@ -355,7 +355,7 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:371:9 + --> tests/ui/unconditional_recursion.rs:372:9 | LL | Into::into(f) | ^^^^^^^^^^^^^ diff --git a/tests/ui/useful_asref.rs b/tests/ui/useful_asref.rs index a9f0170a79cd9..d17db9371ee8d 100644 --- a/tests/ui/useful_asref.rs +++ b/tests/ui/useful_asref.rs @@ -1,4 +1,5 @@ #![deny(clippy::useless_asref)] +#![allow(clippy::needless_lifetimes)] trait Trait { fn as_ptr(&self); From 66f1f544afaf7e277fefa7702bcc91ca498879c5 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Sun, 18 Aug 2024 07:32:24 -0400 Subject: [PATCH 026/120] Fix `clippy_lints` and `clippy_utils` --- clippy_lints/src/booleans.rs | 8 ++++---- clippy_lints/src/box_default.rs | 2 +- clippy_lints/src/checked_conversions.rs | 2 +- clippy_lints/src/default_numeric_fallback.rs | 2 +- clippy_lints/src/doc/mod.rs | 2 +- clippy_lints/src/empty_enum.rs | 2 +- clippy_lints/src/escape.rs | 4 ++-- clippy_lints/src/excessive_nesting.rs | 2 +- clippy_lints/src/extra_unused_type_parameters.rs | 2 +- clippy_lints/src/fallible_impl_from.rs | 2 +- clippy_lints/src/format_args.rs | 2 +- clippy_lints/src/format_impl.rs | 2 +- clippy_lints/src/from_over_into.rs | 2 +- clippy_lints/src/implicit_hasher.rs | 4 ++-- clippy_lints/src/index_refutable_slice.rs | 2 +- clippy_lints/src/lifetimes.rs | 2 +- clippy_lints/src/loops/manual_memcpy.rs | 2 +- clippy_lints/src/loops/needless_range_loop.rs | 4 ++-- clippy_lints/src/loops/same_item_push.rs | 2 +- clippy_lints/src/loops/utils.rs | 4 ++-- clippy_lints/src/loops/while_immutable_condition.rs | 4 ++-- clippy_lints/src/loops/while_let_on_iterator.rs | 2 +- clippy_lints/src/macro_metavars_in_unsafe.rs | 2 +- clippy_lints/src/macro_use.rs | 2 +- clippy_lints/src/manual_clamp.rs | 2 +- clippy_lints/src/manual_strip.rs | 2 +- clippy_lints/src/map_unit_fn.rs | 2 +- clippy_lints/src/matches/match_str_case_mismatch.rs | 4 ++-- clippy_lints/src/matches/overlapping_arms.rs | 4 ++-- .../src/matches/significant_drop_in_scrutinee.rs | 4 ++-- clippy_lints/src/methods/needless_collect.rs | 2 +- clippy_lints/src/methods/option_map_unwrap_or.rs | 4 ++-- clippy_lints/src/methods/utils.rs | 4 ++-- clippy_lints/src/mixed_read_write_in_expression.rs | 6 +++--- clippy_lints/src/mut_mut.rs | 2 +- clippy_lints/src/mutable_debug_assertion.rs | 2 +- clippy_lints/src/needless_for_each.rs | 2 +- clippy_lints/src/needless_pass_by_ref_mut.rs | 2 +- clippy_lints/src/non_expressive_names.rs | 10 +++++----- clippy_lints/src/non_send_fields_in_send_ty.rs | 2 +- clippy_lints/src/pass_by_ref_or_value.rs | 4 ++-- clippy_lints/src/pathbuf_init_then_push.rs | 2 +- clippy_lints/src/redundant_closure_call.rs | 2 +- clippy_lints/src/returns.rs | 4 ++-- clippy_lints/src/significant_drop_tightening.rs | 2 +- clippy_lints/src/single_component_path_imports.rs | 2 +- clippy_lints/src/slow_vector_initialization.rs | 4 ++-- clippy_lints/src/swap.rs | 2 +- clippy_lints/src/unused_async.rs | 2 +- clippy_lints/src/unwrap.rs | 4 ++-- clippy_lints/src/use_self.rs | 2 +- .../src/utils/internal_lints/lint_without_lint_pass.rs | 2 +- clippy_lints/src/zombie_processes.rs | 4 ++-- clippy_utils/src/consts.rs | 6 +++--- clippy_utils/src/eager_or_lazy.rs | 2 +- clippy_utils/src/lib.rs | 4 ++-- clippy_utils/src/mir/mod.rs | 2 +- clippy_utils/src/mir/possible_borrower.rs | 6 +++--- clippy_utils/src/mir/possible_origin.rs | 2 +- clippy_utils/src/ty/type_certainty/mod.rs | 2 +- clippy_utils/src/usage.rs | 6 +++--- clippy_utils/src/visitors.rs | 4 ++-- 62 files changed, 94 insertions(+), 94 deletions(-) diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index 3c2af72624f60..87aaf7ec16da7 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -205,7 +205,7 @@ struct Hir2Qmm<'a, 'tcx, 'v> { cx: &'a LateContext<'tcx>, } -impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, 'v> { +impl<'v> Hir2Qmm<'_, '_, 'v> { fn extract(&mut self, op: BinOpKind, a: &[&'v Expr<'_>], mut v: Vec) -> Result, String> { for a in a { if let ExprKind::Binary(binop, lhs, rhs) = &a.kind { @@ -292,7 +292,7 @@ struct SuggestContext<'a, 'tcx, 'v> { output: String, } -impl<'a, 'tcx, 'v> SuggestContext<'a, 'tcx, 'v> { +impl SuggestContext<'_, '_, '_> { fn recurse(&mut self, suggestion: &Bool) -> Option<()> { use quine_mc_cluskey::Bool::{And, False, Not, Or, Term, True}; match suggestion { @@ -475,7 +475,7 @@ fn terminal_stats(b: &Bool) -> Stats { stats } -impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> { +impl<'tcx> NonminimalBoolVisitor<'_, 'tcx> { fn bool_expr(&self, e: &'tcx Expr<'_>) { let mut h2q = Hir2Qmm { terminals: Vec::new(), @@ -582,7 +582,7 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> { } } -impl<'a, 'tcx> Visitor<'tcx> for NonminimalBoolVisitor<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for NonminimalBoolVisitor<'_, 'tcx> { fn visit_expr(&mut self, e: &'tcx Expr<'_>) { if !e.span.from_expansion() { match &e.kind { diff --git a/clippy_lints/src/box_default.rs b/clippy_lints/src/box_default.rs index 8261c65354fd5..40d154c0bdfe3 100644 --- a/clippy_lints/src/box_default.rs +++ b/clippy_lints/src/box_default.rs @@ -91,7 +91,7 @@ fn is_local_vec_expn(cx: &LateContext<'_>, expr: &Expr<'_>, ref_expr: &Expr<'_>) #[derive(Default)] struct InferVisitor(bool); -impl<'tcx> Visitor<'tcx> for InferVisitor { +impl Visitor<'_> for InferVisitor { fn visit_ty(&mut self, t: &Ty<'_>) { self.0 |= matches!(t.kind, TyKind::Infer | TyKind::OpaqueDef(..) | TyKind::TraitObject(..)); if !self.0 { diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs index d3aa2fd1ea12c..2e7f91a842e1e 100644 --- a/clippy_lints/src/checked_conversions.rs +++ b/clippy_lints/src/checked_conversions.rs @@ -48,7 +48,7 @@ impl CheckedConversions { impl_lint_pass!(CheckedConversions => [CHECKED_CONVERSIONS]); -impl<'tcx> LateLintPass<'tcx> for CheckedConversions { +impl LateLintPass<'_> for CheckedConversions { fn check_expr(&mut self, cx: &LateContext<'_>, item: &Expr<'_>) { if let ExprKind::Binary(op, lhs, rhs) = item.kind && let (lt1, gt1, op2) = match op.node { diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs index a065dc2cf7e52..4808c372754c4 100644 --- a/clippy_lints/src/default_numeric_fallback.rs +++ b/clippy_lints/src/default_numeric_fallback.rs @@ -119,7 +119,7 @@ impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> { } } -impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for NumericFallbackVisitor<'_, 'tcx> { fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { match &expr.kind { ExprKind::Block( diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index 0ae1fad569213..e090644ae4410 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -970,7 +970,7 @@ impl<'a, 'tcx> FindPanicUnwrap<'a, 'tcx> { } } -impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for FindPanicUnwrap<'_, 'tcx> { type NestedFilter = nested_filter::OnlyBodies; fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { diff --git a/clippy_lints/src/empty_enum.rs b/clippy_lints/src/empty_enum.rs index f4c55738cb83a..70eb81fa09c1a 100644 --- a/clippy_lints/src/empty_enum.rs +++ b/clippy_lints/src/empty_enum.rs @@ -60,7 +60,7 @@ declare_clippy_lint! { declare_lint_pass!(EmptyEnum => [EMPTY_ENUM]); -impl<'tcx> LateLintPass<'tcx> for EmptyEnum { +impl LateLintPass<'_> for EmptyEnum { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { if let ItemKind::Enum(..) = item.kind // Only suggest the `never_type` if the feature is enabled diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index 5588124e791ea..a89f0d9c43274 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -141,7 +141,7 @@ fn is_argument(tcx: TyCtxt<'_>, id: HirId) -> bool { matches!(tcx.parent_hir_node(id), Node::Param(_)) } -impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> { +impl<'tcx> Delegate<'tcx> for EscapeDelegate<'_, 'tcx> { fn consume(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId) { if cmt.place.projections.is_empty() { if let PlaceBase::Local(lid) = cmt.place.base { @@ -188,7 +188,7 @@ impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> { fn fake_read(&mut self, _: &PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} } -impl<'a, 'tcx> EscapeDelegate<'a, 'tcx> { +impl<'tcx> EscapeDelegate<'_, 'tcx> { fn is_large_box(&self, ty: Ty<'tcx>) -> bool { // Large types need to be boxed to avoid stack overflows. if let Some(boxed_ty) = ty.boxed_ty() { diff --git a/clippy_lints/src/excessive_nesting.rs b/clippy_lints/src/excessive_nesting.rs index ce0e0faa01489..ffc76366983aa 100644 --- a/clippy_lints/src/excessive_nesting.rs +++ b/clippy_lints/src/excessive_nesting.rs @@ -135,7 +135,7 @@ impl NestingVisitor<'_, '_> { } } -impl<'conf, 'cx> Visitor<'_> for NestingVisitor<'conf, 'cx> { +impl Visitor<'_> for NestingVisitor<'_, '_> { fn visit_block(&mut self, block: &Block) { if block.span.from_expansion() { return; diff --git a/clippy_lints/src/extra_unused_type_parameters.rs b/clippy_lints/src/extra_unused_type_parameters.rs index bf9388b4a70fd..3b93d3ff93ed4 100644 --- a/clippy_lints/src/extra_unused_type_parameters.rs +++ b/clippy_lints/src/extra_unused_type_parameters.rs @@ -193,7 +193,7 @@ fn bound_to_trait_def_id(bound: &GenericBound<'_>) -> Option { bound.trait_ref()?.trait_def_id()?.as_local() } -impl<'cx, 'tcx> Visitor<'tcx> for TypeWalker<'cx, 'tcx> { +impl<'tcx> Visitor<'tcx> for TypeWalker<'_, 'tcx> { type NestedFilter = nested_filter::OnlyBodies; fn visit_ty(&mut self, t: &'tcx Ty<'tcx>) { diff --git a/clippy_lints/src/fallible_impl_from.rs b/clippy_lints/src/fallible_impl_from.rs index 747ea9a434464..f822432cce63a 100644 --- a/clippy_lints/src/fallible_impl_from.rs +++ b/clippy_lints/src/fallible_impl_from.rs @@ -73,7 +73,7 @@ fn lint_impl_body(cx: &LateContext<'_>, impl_span: Span, impl_items: &[hir::Impl result: Vec, } - impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> { + impl<'tcx> Visitor<'tcx> for FindPanicUnwrap<'_, 'tcx> { fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { if let Some(macro_call) = root_macro_call_first_node(self.lcx, expr) { if is_panic(self.lcx, macro_call.def_id) { diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index 83ab9f6557bdd..4c043f8dc14b7 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -219,7 +219,7 @@ struct FormatArgsExpr<'a, 'tcx> { ignore_mixed: bool, } -impl<'a, 'tcx> FormatArgsExpr<'a, 'tcx> { +impl FormatArgsExpr<'_, '_> { fn check_templates(&self) { for piece in &self.format_args.template { if let FormatArgsPiece::Placeholder(placeholder) = piece diff --git a/clippy_lints/src/format_impl.rs b/clippy_lints/src/format_impl.rs index c196f404ce677..7c0515b8c5608 100644 --- a/clippy_lints/src/format_impl.rs +++ b/clippy_lints/src/format_impl.rs @@ -148,7 +148,7 @@ struct FormatImplExpr<'a, 'tcx> { format_trait_impl: FormatTraitNames, } -impl<'a, 'tcx> FormatImplExpr<'a, 'tcx> { +impl FormatImplExpr<'_, '_> { fn check_to_string_in_display(&self) { if self.format_trait_impl.name == sym::Display && let ExprKind::MethodCall(path, self_arg, ..) = self.expr.kind diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs index d716a624cc672..0aac81fa3880b 100644 --- a/clippy_lints/src/from_over_into.rs +++ b/clippy_lints/src/from_over_into.rs @@ -126,7 +126,7 @@ struct SelfFinder<'a, 'tcx> { invalid: bool, } -impl<'a, 'tcx> Visitor<'tcx> for SelfFinder<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for SelfFinder<'_, 'tcx> { type NestedFilter = OnlyBodies; fn nested_visit_map(&mut self) -> Self::Map { diff --git a/clippy_lints/src/implicit_hasher.rs b/clippy_lints/src/implicit_hasher.rs index f683925145a28..4c5375730b8e3 100644 --- a/clippy_lints/src/implicit_hasher.rs +++ b/clippy_lints/src/implicit_hasher.rs @@ -281,7 +281,7 @@ impl<'a, 'tcx> ImplicitHasherTypeVisitor<'a, 'tcx> { } } -impl<'a, 'tcx> Visitor<'tcx> for ImplicitHasherTypeVisitor<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for ImplicitHasherTypeVisitor<'_, 'tcx> { fn visit_ty(&mut self, t: &'tcx hir::Ty<'_>) { if let Some(target) = ImplicitHasherType::new(self.cx, t) { self.found.push(target); @@ -318,7 +318,7 @@ impl<'a, 'b, 'tcx> ImplicitHasherConstructorVisitor<'a, 'b, 'tcx> { } } -impl<'a, 'b, 'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'a, 'b, 'tcx> { +impl<'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'_, '_, 'tcx> { type NestedFilter = nested_filter::OnlyBodies; fn visit_body(&mut self, body: &Body<'tcx>) { diff --git a/clippy_lints/src/index_refutable_slice.rs b/clippy_lints/src/index_refutable_slice.rs index 39afb6810b8e2..73ebe6aec15a0 100644 --- a/clippy_lints/src/index_refutable_slice.rs +++ b/clippy_lints/src/index_refutable_slice.rs @@ -226,7 +226,7 @@ struct SliceIndexLintingVisitor<'a, 'tcx> { max_suggested_slice: u64, } -impl<'a, 'tcx> Visitor<'tcx> for SliceIndexLintingVisitor<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for SliceIndexLintingVisitor<'_, 'tcx> { type NestedFilter = nested_filter::OnlyBodies; fn nested_visit_map(&mut self) -> Self::Map { diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 42083b1a87963..5a3c749cab12b 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -397,7 +397,7 @@ impl<'a, 'tcx> RefVisitor<'a, 'tcx> { } } -impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for RefVisitor<'_, 'tcx> { // for lifetimes as parameters of generics fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) { self.lts.push(*lifetime); diff --git a/clippy_lints/src/loops/manual_memcpy.rs b/clippy_lints/src/loops/manual_memcpy.rs index a7c1d1bd6cd36..68d063ad5e54e 100644 --- a/clippy_lints/src/loops/manual_memcpy.rs +++ b/clippy_lints/src/loops/manual_memcpy.rs @@ -209,7 +209,7 @@ fn build_manual_memcpy_suggestion<'tcx>( #[derive(Clone)] struct MinifyingSugg<'a>(Sugg<'a>); -impl<'a> Display for MinifyingSugg<'a> { +impl Display for MinifyingSugg<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.0.fmt(f) } diff --git a/clippy_lints/src/loops/needless_range_loop.rs b/clippy_lints/src/loops/needless_range_loop.rs index 20dd5a311dca0..745f070a577d5 100644 --- a/clippy_lints/src/loops/needless_range_loop.rs +++ b/clippy_lints/src/loops/needless_range_loop.rs @@ -241,7 +241,7 @@ struct VarVisitor<'a, 'tcx> { prefer_mutable: bool, } -impl<'a, 'tcx> VarVisitor<'a, 'tcx> { +impl<'tcx> VarVisitor<'_, 'tcx> { fn check(&mut self, idx: &'tcx Expr<'_>, seqexpr: &'tcx Expr<'_>, expr: &'tcx Expr<'_>) -> bool { if let ExprKind::Path(ref seqpath) = seqexpr.kind // the indexed container is referenced by a name @@ -292,7 +292,7 @@ impl<'a, 'tcx> VarVisitor<'a, 'tcx> { } } -impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for VarVisitor<'_, 'tcx> { fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { if let ExprKind::MethodCall(meth, args_0, [args_1, ..], _) = &expr.kind // a range index op diff --git a/clippy_lints/src/loops/same_item_push.rs b/clippy_lints/src/loops/same_item_push.rs index 5662d3013e15b..f8659897ffe97 100644 --- a/clippy_lints/src/loops/same_item_push.rs +++ b/clippy_lints/src/loops/same_item_push.rs @@ -123,7 +123,7 @@ impl<'a, 'tcx> SameItemPushVisitor<'a, 'tcx> { } } -impl<'a, 'tcx> Visitor<'tcx> for SameItemPushVisitor<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for SameItemPushVisitor<'_, 'tcx> { fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { match &expr.kind { // Non-determinism may occur ... don't give a lint diff --git a/clippy_lints/src/loops/utils.rs b/clippy_lints/src/loops/utils.rs index 9a89a41d2b395..c4c504e1ae4ff 100644 --- a/clippy_lints/src/loops/utils.rs +++ b/clippy_lints/src/loops/utils.rs @@ -44,7 +44,7 @@ impl<'a, 'tcx> IncrementVisitor<'a, 'tcx> { } } -impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for IncrementVisitor<'_, 'tcx> { fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { // If node is a variable if let Some(def_id) = path_to_local(expr) { @@ -138,7 +138,7 @@ impl<'a, 'tcx> InitializeVisitor<'a, 'tcx> { } } -impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for InitializeVisitor<'_, 'tcx> { type NestedFilter = nested_filter::OnlyBodies; fn visit_local(&mut self, l: &'tcx LetStmt<'_>) { diff --git a/clippy_lints/src/loops/while_immutable_condition.rs b/clippy_lints/src/loops/while_immutable_condition.rs index eab096e9a227d..1a1cde3c5bd63 100644 --- a/clippy_lints/src/loops/while_immutable_condition.rs +++ b/clippy_lints/src/loops/while_immutable_condition.rs @@ -84,7 +84,7 @@ struct VarCollectorVisitor<'a, 'tcx> { skip: bool, } -impl<'a, 'tcx> VarCollectorVisitor<'a, 'tcx> { +impl<'tcx> VarCollectorVisitor<'_, 'tcx> { fn insert_def_id(&mut self, ex: &'tcx Expr<'_>) { if let ExprKind::Path(ref qpath) = ex.kind && let QPath::Resolved(None, _) = *qpath @@ -103,7 +103,7 @@ impl<'a, 'tcx> VarCollectorVisitor<'a, 'tcx> { } } -impl<'a, 'tcx> Visitor<'tcx> for VarCollectorVisitor<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for VarCollectorVisitor<'_, 'tcx> { fn visit_expr(&mut self, ex: &'tcx Expr<'_>) { match ex.kind { ExprKind::Path(_) => self.insert_def_id(ex), diff --git a/clippy_lints/src/loops/while_let_on_iterator.rs b/clippy_lints/src/loops/while_let_on_iterator.rs index 7d9fbaf3ceabf..74467522619e9 100644 --- a/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/clippy_lints/src/loops/while_let_on_iterator.rs @@ -283,7 +283,7 @@ fn needs_mutable_borrow(cx: &LateContext<'_>, iter_expr: &IterExpr, loop_expr: & found_local: bool, used_after: bool, } - impl<'a, 'b, 'tcx> Visitor<'tcx> for NestedLoopVisitor<'a, 'b, 'tcx> { + impl<'tcx> Visitor<'tcx> for NestedLoopVisitor<'_, '_, 'tcx> { type NestedFilter = OnlyBodies; fn nested_visit_map(&mut self) -> Self::Map { self.cx.tcx.hir() diff --git a/clippy_lints/src/macro_metavars_in_unsafe.rs b/clippy_lints/src/macro_metavars_in_unsafe.rs index ccc554042d685..312bcb55a9533 100644 --- a/clippy_lints/src/macro_metavars_in_unsafe.rs +++ b/clippy_lints/src/macro_metavars_in_unsafe.rs @@ -149,7 +149,7 @@ fn is_public_macro(cx: &LateContext<'_>, def_id: LocalDefId) -> bool { && !cx.tcx.is_doc_hidden(def_id) } -impl<'a, 'tcx> Visitor<'tcx> for BodyVisitor<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for BodyVisitor<'_, 'tcx> { fn visit_stmt(&mut self, s: &'tcx Stmt<'tcx>) { let from_expn = s.span.from_expansion(); if from_expn { diff --git a/clippy_lints/src/macro_use.rs b/clippy_lints/src/macro_use.rs index bd6b3f1a47b16..50680331fbc4e 100644 --- a/clippy_lints/src/macro_use.rs +++ b/clippy_lints/src/macro_use.rs @@ -81,7 +81,7 @@ impl MacroUseImports { } } -impl<'tcx> LateLintPass<'tcx> for MacroUseImports { +impl LateLintPass<'_> for MacroUseImports { fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { if cx.sess().opts.edition >= Edition::Edition2018 && let hir::ItemKind::Use(path, _kind) = &item.kind diff --git a/clippy_lints/src/manual_clamp.rs b/clippy_lints/src/manual_clamp.rs index 788649fd4f90e..fd66cacdfe933 100644 --- a/clippy_lints/src/manual_clamp.rs +++ b/clippy_lints/src/manual_clamp.rs @@ -741,7 +741,7 @@ enum MaybeBorrowedStmtKind<'a> { Owned(StmtKind<'a>), } -impl<'a> Clone for MaybeBorrowedStmtKind<'a> { +impl Clone for MaybeBorrowedStmtKind<'_> { fn clone(&self) -> Self { match self { Self::Borrowed(t) => Self::Borrowed(t), diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index 9aceca66bf774..828c5a3f6ffd2 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -203,7 +203,7 @@ fn find_stripping<'tcx>( results: Vec, } - impl<'a, 'tcx> Visitor<'tcx> for StrippingFinder<'a, 'tcx> { + impl<'tcx> Visitor<'tcx> for StrippingFinder<'_, 'tcx> { fn visit_expr(&mut self, ex: &'tcx Expr<'_>) { if is_ref_str(self.cx, ex) && let unref = peel_ref(ex) diff --git a/clippy_lints/src/map_unit_fn.rs b/clippy_lints/src/map_unit_fn.rs index a97dbbbc33fd0..3221a04d2d03f 100644 --- a/clippy_lints/src/map_unit_fn.rs +++ b/clippy_lints/src/map_unit_fn.rs @@ -251,7 +251,7 @@ fn lint_map_unit_fn( } } -impl<'tcx> LateLintPass<'tcx> for MapUnit { +impl LateLintPass<'_> for MapUnit { fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &hir::Stmt<'_>) { if let hir::StmtKind::Semi(expr) = stmt.kind && !stmt.span.from_expansion() diff --git a/clippy_lints/src/matches/match_str_case_mismatch.rs b/clippy_lints/src/matches/match_str_case_mismatch.rs index 40518ce2ca775..463aa602bc8b9 100644 --- a/clippy_lints/src/matches/match_str_case_mismatch.rs +++ b/clippy_lints/src/matches/match_str_case_mismatch.rs @@ -40,7 +40,7 @@ struct MatchExprVisitor<'a, 'tcx> { case_method: Option, } -impl<'a, 'tcx> Visitor<'tcx> for MatchExprVisitor<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for MatchExprVisitor<'_, 'tcx> { fn visit_expr(&mut self, ex: &'tcx Expr<'_>) { match ex.kind { ExprKind::MethodCall(segment, receiver, [], _) if self.case_altered(segment.ident.as_str(), receiver) => {}, @@ -49,7 +49,7 @@ impl<'a, 'tcx> Visitor<'tcx> for MatchExprVisitor<'a, 'tcx> { } } -impl<'a, 'tcx> MatchExprVisitor<'a, 'tcx> { +impl MatchExprVisitor<'_, '_> { fn case_altered(&mut self, segment_ident: &str, receiver: &Expr<'_>) -> bool { if let Some(case_method) = get_case_method(segment_ident) { let ty = self.cx.typeck_results().expr_ty(receiver).peel_refs(); diff --git a/clippy_lints/src/matches/overlapping_arms.rs b/clippy_lints/src/matches/overlapping_arms.rs index 6a4c553cee0ee..856311899f268 100644 --- a/clippy_lints/src/matches/overlapping_arms.rs +++ b/clippy_lints/src/matches/overlapping_arms.rs @@ -96,13 +96,13 @@ where #[derive(Copy, Clone, Debug, Eq, PartialEq)] struct RangeBound<'a, T>(T, BoundKind, &'a SpannedRange); - impl<'a, T: Copy + Ord> PartialOrd for RangeBound<'a, T> { + impl PartialOrd for RangeBound<'_, T> { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } - impl<'a, T: Copy + Ord> Ord for RangeBound<'a, T> { + impl Ord for RangeBound<'_, T> { fn cmp(&self, RangeBound(other_value, other_kind, _): &Self) -> Ordering { let RangeBound(self_value, self_kind, _) = *self; (self_value, self_kind).cmp(&(*other_value, *other_kind)) diff --git a/clippy_lints/src/matches/significant_drop_in_scrutinee.rs b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs index 537f7272f7f34..7372f52e1e5bd 100644 --- a/clippy_lints/src/matches/significant_drop_in_scrutinee.rs +++ b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs @@ -424,7 +424,7 @@ fn ty_has_erased_regions(ty: Ty<'_>) -> bool { ty.visit_with(&mut V).is_break() } -impl<'a, 'tcx> Visitor<'tcx> for SigDropHelper<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for SigDropHelper<'_, 'tcx> { fn visit_expr(&mut self, ex: &'tcx Expr<'_>) { // We've emitted a lint on some neighborhood expression. That lint will suggest to move out the // _parent_ expression (not the expression itself). Since we decide to move out the parent @@ -495,7 +495,7 @@ fn has_significant_drop_in_arms<'tcx>(cx: &LateContext<'tcx>, arms: &[&'tcx Expr helper.found_sig_drop_spans } -impl<'a, 'tcx> Visitor<'tcx> for ArmSigDropHelper<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for ArmSigDropHelper<'_, 'tcx> { fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { if self.sig_drop_checker.is_sig_drop_expr(ex) { self.found_sig_drop_spans.insert(ex.span); diff --git a/clippy_lints/src/methods/needless_collect.rs b/clippy_lints/src/methods/needless_collect.rs index 421c7a5e070d9..c58e27e37ad31 100644 --- a/clippy_lints/src/methods/needless_collect.rs +++ b/clippy_lints/src/methods/needless_collect.rs @@ -441,7 +441,7 @@ struct UsedCountVisitor<'a, 'tcx> { count: usize, } -impl<'a, 'tcx> Visitor<'tcx> for UsedCountVisitor<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for UsedCountVisitor<'_, 'tcx> { type NestedFilter = nested_filter::OnlyBodies; fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { diff --git a/clippy_lints/src/methods/option_map_unwrap_or.rs b/clippy_lints/src/methods/option_map_unwrap_or.rs index b160ab6de8e95..528e2204cf8f7 100644 --- a/clippy_lints/src/methods/option_map_unwrap_or.rs +++ b/clippy_lints/src/methods/option_map_unwrap_or.rs @@ -130,7 +130,7 @@ struct UnwrapVisitor<'a, 'tcx> { identifiers: FxHashSet, } -impl<'a, 'tcx> Visitor<'tcx> for UnwrapVisitor<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for UnwrapVisitor<'_, 'tcx> { type NestedFilter = nested_filter::All; fn visit_path(&mut self, path: &Path<'tcx>, _: HirId) { @@ -154,7 +154,7 @@ struct ReferenceVisitor<'a, 'tcx> { unwrap_or_span: Span, } -impl<'a, 'tcx> Visitor<'tcx> for ReferenceVisitor<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for ReferenceVisitor<'_, 'tcx> { type NestedFilter = nested_filter::All; type Result = ControlFlow<()>; fn visit_expr(&mut self, expr: &'tcx rustc_hir::Expr<'_>) -> ControlFlow<()> { diff --git a/clippy_lints/src/methods/utils.rs b/clippy_lints/src/methods/utils.rs index 4e33dc1df54d8..cf0ee569f1310 100644 --- a/clippy_lints/src/methods/utils.rs +++ b/clippy_lints/src/methods/utils.rs @@ -86,7 +86,7 @@ struct CloneOrCopyVisitor<'cx, 'tcx> { references_to_binding: Vec<(Span, String)>, } -impl<'cx, 'tcx> Visitor<'tcx> for CloneOrCopyVisitor<'cx, 'tcx> { +impl<'tcx> Visitor<'tcx> for CloneOrCopyVisitor<'_, 'tcx> { type NestedFilter = nested_filter::OnlyBodies; fn nested_visit_map(&mut self) -> Self::Map { @@ -123,7 +123,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for CloneOrCopyVisitor<'cx, 'tcx> { } } -impl<'cx, 'tcx> CloneOrCopyVisitor<'cx, 'tcx> { +impl<'tcx> CloneOrCopyVisitor<'_, 'tcx> { fn is_binding(&self, expr: &Expr<'tcx>) -> bool { self.binding_hir_ids .iter() diff --git a/clippy_lints/src/mixed_read_write_in_expression.rs b/clippy_lints/src/mixed_read_write_in_expression.rs index d333b71edb1ae..a7452c8a3c84e 100644 --- a/clippy_lints/src/mixed_read_write_in_expression.rs +++ b/clippy_lints/src/mixed_read_write_in_expression.rs @@ -116,7 +116,7 @@ struct DivergenceVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, } -impl<'a, 'tcx> DivergenceVisitor<'a, 'tcx> { +impl<'tcx> DivergenceVisitor<'_, 'tcx> { fn maybe_walk_expr(&mut self, e: &'tcx Expr<'_>) { match e.kind { ExprKind::Closure(..) | ExprKind::If(..) | ExprKind::Loop(..) => {}, @@ -148,7 +148,7 @@ fn stmt_might_diverge(stmt: &Stmt<'_>) -> bool { !matches!(stmt.kind, StmtKind::Item(..)) } -impl<'a, 'tcx> Visitor<'tcx> for DivergenceVisitor<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for DivergenceVisitor<'_, 'tcx> { fn visit_expr(&mut self, e: &'tcx Expr<'_>) { match e.kind { // fix #10776 @@ -321,7 +321,7 @@ struct ReadVisitor<'a, 'tcx> { last_expr: &'tcx Expr<'tcx>, } -impl<'a, 'tcx> Visitor<'tcx> for ReadVisitor<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for ReadVisitor<'_, 'tcx> { fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { if expr.hir_id == self.last_expr.hir_id { return; diff --git a/clippy_lints/src/mut_mut.rs b/clippy_lints/src/mut_mut.rs index 60372121a7a4c..6cddd7ea813b2 100644 --- a/clippy_lints/src/mut_mut.rs +++ b/clippy_lints/src/mut_mut.rs @@ -55,7 +55,7 @@ pub struct MutVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, } -impl<'a, 'tcx> intravisit::Visitor<'tcx> for MutVisitor<'a, 'tcx> { +impl<'tcx> intravisit::Visitor<'tcx> for MutVisitor<'_, 'tcx> { fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { if in_external_macro(self.cx.sess(), expr.span) { return; diff --git a/clippy_lints/src/mutable_debug_assertion.rs b/clippy_lints/src/mutable_debug_assertion.rs index 6ab811b4f2f9b..e589b3608b31f 100644 --- a/clippy_lints/src/mutable_debug_assertion.rs +++ b/clippy_lints/src/mutable_debug_assertion.rs @@ -87,7 +87,7 @@ impl<'a, 'tcx> MutArgVisitor<'a, 'tcx> { } } -impl<'a, 'tcx> Visitor<'tcx> for MutArgVisitor<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for MutArgVisitor<'_, 'tcx> { type NestedFilter = nested_filter::OnlyBodies; fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { diff --git a/clippy_lints/src/needless_for_each.rs b/clippy_lints/src/needless_for_each.rs index b54eb164e81d5..93e20f37ef8ac 100644 --- a/clippy_lints/src/needless_for_each.rs +++ b/clippy_lints/src/needless_for_each.rs @@ -133,7 +133,7 @@ struct RetCollector { loop_depth: u16, } -impl<'tcx> Visitor<'tcx> for RetCollector { +impl Visitor<'_> for RetCollector { fn visit_expr(&mut self, expr: &Expr<'_>) { match expr.kind { ExprKind::Ret(..) => { diff --git a/clippy_lints/src/needless_pass_by_ref_mut.rs b/clippy_lints/src/needless_pass_by_ref_mut.rs index 19cbf59590840..c2facb2fcf68c 100644 --- a/clippy_lints/src/needless_pass_by_ref_mut.rs +++ b/clippy_lints/src/needless_pass_by_ref_mut.rs @@ -311,7 +311,7 @@ struct MutablyUsedVariablesCtxt<'tcx> { tcx: TyCtxt<'tcx>, } -impl<'tcx> MutablyUsedVariablesCtxt<'tcx> { +impl MutablyUsedVariablesCtxt<'_> { fn add_mutably_used_var(&mut self, used_id: HirId) { self.mutably_used_vars.insert(used_id); } diff --git a/clippy_lints/src/non_expressive_names.rs b/clippy_lints/src/non_expressive_names.rs index d85032e9eee84..2fee1c72a91b5 100644 --- a/clippy_lints/src/non_expressive_names.rs +++ b/clippy_lints/src/non_expressive_names.rs @@ -104,7 +104,7 @@ struct SimilarNamesLocalVisitor<'a, 'tcx> { single_char_names: Vec>, } -impl<'a, 'tcx> SimilarNamesLocalVisitor<'a, 'tcx> { +impl SimilarNamesLocalVisitor<'_, '_> { fn check_single_char_names(&self) { if self.single_char_names.last().map(Vec::len) == Some(0) { return; @@ -152,7 +152,7 @@ fn chars_are_similar(a: char, b: char) -> bool { struct SimilarNamesNameVisitor<'a, 'tcx, 'b>(&'b mut SimilarNamesLocalVisitor<'a, 'tcx>); -impl<'a, 'tcx, 'b> Visitor<'tcx> for SimilarNamesNameVisitor<'a, 'tcx, 'b> { +impl<'tcx> Visitor<'tcx> for SimilarNamesNameVisitor<'_, 'tcx, '_> { fn visit_pat(&mut self, pat: &'tcx Pat) { match pat.kind { PatKind::Ident(_, ident, _) => { @@ -189,7 +189,7 @@ fn allowed_to_be_similar(interned_name: &str, list: &[&str]) -> bool { .any(|&name| interned_name.starts_with(name) || interned_name.ends_with(name)) } -impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> { +impl SimilarNamesNameVisitor<'_, '_, '_> { fn check_short_ident(&mut self, ident: Ident) { // Ignore shadowing if self @@ -329,7 +329,7 @@ impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> { } } -impl<'a, 'b> SimilarNamesLocalVisitor<'a, 'b> { +impl SimilarNamesLocalVisitor<'_, '_> { /// ensure scoping rules work fn apply Fn(&'c mut Self)>(&mut self, f: F) { let n = self.names.len(); @@ -340,7 +340,7 @@ impl<'a, 'b> SimilarNamesLocalVisitor<'a, 'b> { } } -impl<'a, 'tcx> Visitor<'tcx> for SimilarNamesLocalVisitor<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for SimilarNamesLocalVisitor<'_, 'tcx> { fn visit_local(&mut self, local: &'tcx Local) { if let Some((init, els)) = &local.kind.init_else_opt() { self.apply(|this| walk_expr(this, init)); diff --git a/clippy_lints/src/non_send_fields_in_send_ty.rs b/clippy_lints/src/non_send_fields_in_send_ty.rs index a60988ac5dbc2..793eb5d945603 100644 --- a/clippy_lints/src/non_send_fields_in_send_ty.rs +++ b/clippy_lints/src/non_send_fields_in_send_ty.rs @@ -159,7 +159,7 @@ struct NonSendField<'tcx> { generic_params: Vec>, } -impl<'tcx> NonSendField<'tcx> { +impl NonSendField<'_> { fn generic_params_string(&self) -> String { self.generic_params .iter() diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs index 75d8c09f2b08d..1bddfab39c694 100644 --- a/clippy_lints/src/pass_by_ref_or_value.rs +++ b/clippy_lints/src/pass_by_ref_or_value.rs @@ -110,7 +110,7 @@ pub struct PassByRefOrValue { avoid_breaking_exported_api: bool, } -impl<'tcx> PassByRefOrValue { +impl PassByRefOrValue { pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { let ref_min_size = conf.trivial_copy_size_limit.unwrap_or_else(|| { let bit_width = u64::from(tcx.sess.target.pointer_width); @@ -130,7 +130,7 @@ impl<'tcx> PassByRefOrValue { } } - fn check_poly_fn(&mut self, cx: &LateContext<'tcx>, def_id: LocalDefId, decl: &FnDecl<'_>, span: Option) { + fn check_poly_fn(&mut self, cx: &LateContext<'_>, def_id: LocalDefId, decl: &FnDecl<'_>, span: Option) { if self.avoid_breaking_exported_api && cx.effective_visibilities.is_exported(def_id) { return; } diff --git a/clippy_lints/src/pathbuf_init_then_push.rs b/clippy_lints/src/pathbuf_init_then_push.rs index 1b9a5a4438297..9f84686a0b12e 100644 --- a/clippy_lints/src/pathbuf_init_then_push.rs +++ b/clippy_lints/src/pathbuf_init_then_push.rs @@ -60,7 +60,7 @@ struct PathbufPushSearcher<'tcx> { err_span: Span, } -impl<'tcx> PathbufPushSearcher<'tcx> { +impl PathbufPushSearcher<'_> { /// Try to generate a suggestion with `PathBuf::from`. /// Returns `None` if the suggestion would be invalid. fn gen_pathbuf_from(&self, cx: &LateContext<'_>) -> Option { diff --git a/clippy_lints/src/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs index 6930a01d48be4..41a44de536b15 100644 --- a/clippy_lints/src/redundant_closure_call.rs +++ b/clippy_lints/src/redundant_closure_call.rs @@ -228,7 +228,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { path: &'tcx hir::Path<'tcx>, count: usize, } - impl<'a, 'tcx> Visitor<'tcx> for ClosureUsageCount<'a, 'tcx> { + impl<'tcx> Visitor<'tcx> for ClosureUsageCount<'_, 'tcx> { type NestedFilter = nested_filter::OnlyBodies; fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 34bd9a1210c3c..1f223048ce5ce 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -140,7 +140,7 @@ enum RetReplacement<'tcx> { Expr(Cow<'tcx, str>, Applicability), } -impl<'tcx> RetReplacement<'tcx> { +impl RetReplacement<'_> { fn sugg_help(&self) -> &'static str { match self { Self::Empty | Self::Expr(..) => "remove `return`", @@ -158,7 +158,7 @@ impl<'tcx> RetReplacement<'tcx> { } } -impl<'tcx> Display for RetReplacement<'tcx> { +impl Display for RetReplacement<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Empty => write!(f, ""), diff --git a/clippy_lints/src/significant_drop_tightening.rs b/clippy_lints/src/significant_drop_tightening.rs index d1114cb29f7a3..0eece9221438a 100644 --- a/clippy_lints/src/significant_drop_tightening.rs +++ b/clippy_lints/src/significant_drop_tightening.rs @@ -249,7 +249,7 @@ impl<'ap, 'lc, 'others, 'stmt, 'tcx> StmtsChecker<'ap, 'lc, 'others, 'stmt, 'tcx } } -impl<'ap, 'lc, 'others, 'stmt, 'tcx> Visitor<'tcx> for StmtsChecker<'ap, 'lc, 'others, 'stmt, 'tcx> { +impl<'tcx> Visitor<'tcx> for StmtsChecker<'_, '_, '_, '_, 'tcx> { fn visit_block(&mut self, block: &'tcx hir::Block<'tcx>) { self.ap.curr_block_hir_id = block.hir_id; self.ap.curr_block_span = block.span; diff --git a/clippy_lints/src/single_component_path_imports.rs b/clippy_lints/src/single_component_path_imports.rs index c986c3e8aa6e8..9fdee8543a854 100644 --- a/clippy_lints/src/single_component_path_imports.rs +++ b/clippy_lints/src/single_component_path_imports.rs @@ -102,7 +102,7 @@ struct ImportUsageVisitor { imports_referenced_with_self: Vec, } -impl<'tcx> Visitor<'tcx> for ImportUsageVisitor { +impl Visitor<'_> for ImportUsageVisitor { fn visit_expr(&mut self, expr: &Expr) { if let ExprKind::Path(_, path) = &expr.kind && path.segments.len() > 1 diff --git a/clippy_lints/src/slow_vector_initialization.rs b/clippy_lints/src/slow_vector_initialization.rs index 5129bbf26655e..fc799cad67e8b 100644 --- a/clippy_lints/src/slow_vector_initialization.rs +++ b/clippy_lints/src/slow_vector_initialization.rs @@ -229,7 +229,7 @@ struct VectorInitializationVisitor<'a, 'tcx> { initialization_found: bool, } -impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> { +impl<'tcx> VectorInitializationVisitor<'_, 'tcx> { /// Checks if the given expression is extending a vector with `repeat(0).take(..)` fn search_slow_extend_filling(&mut self, expr: &'tcx Expr<'_>) { if self.initialization_found @@ -299,7 +299,7 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> { } } -impl<'a, 'tcx> Visitor<'tcx> for VectorInitializationVisitor<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for VectorInitializationVisitor<'_, 'tcx> { fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) { if self.initialization_found { match stmt.kind { diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index e05fa4095b8e3..a3145c4647cab 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -332,7 +332,7 @@ struct IndexBinding<'a, 'tcx> { applicability: &'a mut Applicability, } -impl<'a, 'tcx> IndexBinding<'a, 'tcx> { +impl<'tcx> IndexBinding<'_, 'tcx> { fn snippet_index_bindings(&mut self, exprs: &[&'tcx Expr<'tcx>]) -> String { let mut bindings = FxHashSet::default(); for expr in exprs { diff --git a/clippy_lints/src/unused_async.rs b/clippy_lints/src/unused_async.rs index a1f08cf6623bc..d6f79ae4836a4 100644 --- a/clippy_lints/src/unused_async.rs +++ b/clippy_lints/src/unused_async.rs @@ -67,7 +67,7 @@ struct AsyncFnVisitor<'a, 'tcx> { async_depth: usize, } -impl<'a, 'tcx> Visitor<'tcx> for AsyncFnVisitor<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for AsyncFnVisitor<'_, 'tcx> { type NestedFilter = nested_filter::OnlyBodies; fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index 596f0fd9c8b02..6fe660b6a479f 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -235,7 +235,7 @@ impl<'tcx> Delegate<'tcx> for MutationVisitor<'tcx> { fn fake_read(&mut self, _: &PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} } -impl<'a, 'tcx> UnwrappableVariablesVisitor<'a, 'tcx> { +impl<'tcx> UnwrappableVariablesVisitor<'_, 'tcx> { fn visit_branch( &mut self, if_expr: &'tcx Expr<'_>, @@ -288,7 +288,7 @@ fn consume_option_as_ref<'tcx>(expr: &'tcx Expr<'tcx>) -> (&'tcx Expr<'tcx>, Opt } } -impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'_, 'tcx> { type NestedFilter = nested_filter::OnlyBodies; fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index e340b419bd07b..08449de79b364 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -280,7 +280,7 @@ struct SkipTyCollector { types_to_skip: Vec, } -impl<'tcx> Visitor<'tcx> for SkipTyCollector { +impl Visitor<'_> for SkipTyCollector { fn visit_infer(&mut self, inf: &hir::InferArg) { self.types_to_skip.push(inf.hir_id); diff --git a/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs b/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs index 7c45a5b2f0971..dd45602221267 100644 --- a/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs +++ b/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs @@ -270,7 +270,7 @@ struct LintCollector<'a, 'tcx> { cx: &'a LateContext<'tcx>, } -impl<'a, 'tcx> Visitor<'tcx> for LintCollector<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for LintCollector<'_, 'tcx> { type NestedFilter = nested_filter::All; fn visit_path(&mut self, path: &Path<'_>, _: HirId) { diff --git a/clippy_lints/src/zombie_processes.rs b/clippy_lints/src/zombie_processes.rs index ba2a80ee66b05..58d71bee299a4 100644 --- a/clippy_lints/src/zombie_processes.rs +++ b/clippy_lints/src/zombie_processes.rs @@ -118,7 +118,7 @@ enum WaitFinder<'a, 'tcx> { Found(&'a LateContext<'tcx>, HirId), } -impl<'a, 'tcx> Visitor<'tcx> for WaitFinder<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for WaitFinder<'_, 'tcx> { type Result = ControlFlow; fn visit_local(&mut self, l: &'tcx LetStmt<'tcx>) -> Self::Result { @@ -300,7 +300,7 @@ struct ExitPointFinder<'a, 'tcx> { struct ExitCallFound; -impl<'a, 'tcx> Visitor<'tcx> for ExitPointFinder<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for ExitPointFinder<'_, 'tcx> { type Result = ControlFlow; fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) -> Self::Result { diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index bf47cf6d372d2..e0bc27ebe2e5a 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -118,7 +118,7 @@ impl IntTypeBounds for IntTy { } } -impl<'tcx> PartialEq for Constant<'tcx> { +impl PartialEq for Constant<'_> { fn eq(&self, other: &Self) -> bool { match (self, other) { (Self::Str(ls), Self::Str(rs)) => ls == rs, @@ -147,7 +147,7 @@ impl<'tcx> PartialEq for Constant<'tcx> { } } -impl<'tcx> Hash for Constant<'tcx> { +impl Hash for Constant<'_> { fn hash(&self, state: &mut H) where H: Hasher, @@ -203,7 +203,7 @@ impl<'tcx> Hash for Constant<'tcx> { } } -impl<'tcx> Constant<'tcx> { +impl Constant<'_> { pub fn partial_cmp(tcx: TyCtxt<'_>, cmp_type: Ty<'_>, left: &Self, right: &Self) -> Option { match (left, right) { (Self::Str(ls), Self::Str(rs)) => Some(ls.cmp(rs)), diff --git a/clippy_utils/src/eager_or_lazy.rs b/clippy_utils/src/eager_or_lazy.rs index 9420d84b9591d..a2e97919d042f 100644 --- a/clippy_utils/src/eager_or_lazy.rs +++ b/clippy_utils/src/eager_or_lazy.rs @@ -118,7 +118,7 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS eagerness: EagernessSuggestion, } - impl<'cx, 'tcx> Visitor<'tcx> for V<'cx, 'tcx> { + impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> { fn visit_expr(&mut self, e: &'tcx Expr<'_>) { use EagernessSuggestion::{ForceNoChange, Lazy, NoChange}; if self.eagerness == ForceNoChange { diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 2fee6473910b4..12e1bf9a6d63e 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1345,7 +1345,7 @@ pub struct ContainsName<'a, 'tcx> { pub result: bool, } -impl<'a, 'tcx> Visitor<'tcx> for ContainsName<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for ContainsName<'_, 'tcx> { type NestedFilter = nested_filter::OnlyBodies; fn visit_name(&mut self, name: Symbol) { @@ -3115,7 +3115,7 @@ pub fn is_never_expr<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option< is_never: bool, } - impl<'tcx> V<'_, 'tcx> { + impl V<'_, '_> { fn push_break_target(&mut self, id: HirId) { self.break_targets.push(BreakTarget { id, unused: true }); self.break_targets_for_result_ty += u32::from(self.in_final_expr); diff --git a/clippy_utils/src/mir/mod.rs b/clippy_utils/src/mir/mod.rs index 59bb5e35cda66..3924e384c371d 100644 --- a/clippy_utils/src/mir/mod.rs +++ b/clippy_utils/src/mir/mod.rs @@ -58,7 +58,7 @@ struct V<'a> { results: Vec, } -impl<'a, 'tcx> Visitor<'tcx> for V<'a> { +impl<'tcx> Visitor<'tcx> for V<'_> { 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; diff --git a/clippy_utils/src/mir/possible_borrower.rs b/clippy_utils/src/mir/possible_borrower.rs index 07e6705cd3d95..6bb434a466fdf 100644 --- a/clippy_utils/src/mir/possible_borrower.rs +++ b/clippy_utils/src/mir/possible_borrower.rs @@ -65,7 +65,7 @@ impl<'a, 'b, 'tcx> PossibleBorrowerVisitor<'a, 'b, 'tcx> { } } -impl<'a, 'b, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'b, 'tcx> { +impl<'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'_, '_, 'tcx> { fn visit_assign(&mut self, place: &mir::Place<'tcx>, rvalue: &mir::Rvalue<'_>, _location: mir::Location) { let lhs = place.local; match rvalue { @@ -177,8 +177,8 @@ pub struct PossibleBorrowerMap<'b, 'tcx> { pub bitset: (BitSet, BitSet), } -impl<'a, 'b, 'tcx> PossibleBorrowerMap<'b, 'tcx> { - pub fn new(cx: &'a LateContext<'tcx>, mir: &'b mir::Body<'tcx>) -> Self { +impl<'b, 'tcx> PossibleBorrowerMap<'b, 'tcx> { + pub fn new(cx: &LateContext<'tcx>, mir: &'b mir::Body<'tcx>) -> Self { let possible_origin = { let mut vis = PossibleOriginVisitor::new(mir); vis.visit_body(mir); diff --git a/clippy_utils/src/mir/possible_origin.rs b/clippy_utils/src/mir/possible_origin.rs index da04266863f94..4157b3f493066 100644 --- a/clippy_utils/src/mir/possible_origin.rs +++ b/clippy_utils/src/mir/possible_origin.rs @@ -39,7 +39,7 @@ impl<'a, 'tcx> PossibleOriginVisitor<'a, 'tcx> { } } -impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleOriginVisitor<'a, 'tcx> { +impl<'tcx> mir::visit::Visitor<'tcx> for PossibleOriginVisitor<'_, 'tcx> { fn visit_assign(&mut self, place: &mir::Place<'tcx>, rvalue: &mir::Rvalue<'_>, _location: mir::Location) { let lhs = place.local; match rvalue { diff --git a/clippy_utils/src/ty/type_certainty/mod.rs b/clippy_utils/src/ty/type_certainty/mod.rs index e612e9c6cb606..91ec120adbf6f 100644 --- a/clippy_utils/src/ty/type_certainty/mod.rs +++ b/clippy_utils/src/ty/type_certainty/mod.rs @@ -108,7 +108,7 @@ impl<'cx, 'tcx> CertaintyVisitor<'cx, 'tcx> { } } -impl<'cx, 'tcx> Visitor<'cx> for CertaintyVisitor<'cx, 'tcx> { +impl<'cx> Visitor<'cx> for CertaintyVisitor<'cx, '_> { fn visit_qpath(&mut self, qpath: &'cx QPath<'_>, hir_id: HirId, _: Span) { self.certainty = self.certainty.meet(qpath_certainty(self.cx, qpath, true)); if self.certainty != Certainty::Uncertain { diff --git a/clippy_utils/src/usage.rs b/clippy_utils/src/usage.rs index 1230b60c3a6b7..8af3bdccaa1ca 100644 --- a/clippy_utils/src/usage.rs +++ b/clippy_utils/src/usage.rs @@ -46,8 +46,8 @@ struct MutVarsDelegate { skip: bool, } -impl<'tcx> MutVarsDelegate { - fn update(&mut self, cat: &PlaceWithHirId<'tcx>) { +impl MutVarsDelegate { + fn update(&mut self, cat: &PlaceWithHirId<'_>) { match cat.place.base { PlaceBase::Local(id) => { self.used_mutably.insert(id); @@ -122,7 +122,7 @@ impl<'a, 'tcx> BindingUsageFinder<'a, 'tcx> { finder.usage_found } } -impl<'a, 'tcx> Visitor<'tcx> for BindingUsageFinder<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for BindingUsageFinder<'_, 'tcx> { type NestedFilter = nested_filter::OnlyBodies; fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs index 6d9a85a1181c4..02931306f162d 100644 --- a/clippy_utils/src/visitors.rs +++ b/clippy_utils/src/visitors.rs @@ -552,7 +552,7 @@ pub fn for_each_local_use_after_expr<'tcx, B>( res: ControlFlow, f: F, } - impl<'cx, 'tcx, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow, B> Visitor<'tcx> for V<'cx, 'tcx, F, B> { + impl<'tcx, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow, B> Visitor<'tcx> for V<'_, 'tcx, F, B> { type NestedFilter = nested_filter::OnlyBodies; fn nested_visit_map(&mut self) -> Self::Map { self.cx.tcx.hir() @@ -734,7 +734,7 @@ pub fn for_each_local_assignment<'tcx, B>( res: ControlFlow, f: F, } - impl<'cx, 'tcx, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow, B> Visitor<'tcx> for V<'cx, 'tcx, F, B> { + impl<'tcx, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow, B> Visitor<'tcx> for V<'_, 'tcx, F, B> { type NestedFilter = nested_filter::OnlyBodies; fn nested_visit_map(&mut self) -> Self::Map { self.cx.tcx.hir() From 5c2f6db2892ab7abdfa7eebf96fb3600938a674a Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Sun, 18 Aug 2024 13:42:16 -0400 Subject: [PATCH 027/120] Fix typo in `needless_lifetimes` test --- tests/ui/needless_lifetimes.fixed | 2 +- tests/ui/needless_lifetimes.rs | 2 +- tests/ui/needless_lifetimes.stderr | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/ui/needless_lifetimes.fixed b/tests/ui/needless_lifetimes.fixed index e4bb7f86d12f7..7ded5b4234cf3 100644 --- a/tests/ui/needless_lifetimes.fixed +++ b/tests/ui/needless_lifetimes.fixed @@ -384,7 +384,7 @@ mod nested_elision_sites { f() } // lint - fn where_clause_elidadable(i: &i32, f: T) -> &i32 + fn where_clause_elidable(i: &i32, f: T) -> &i32 where T: Fn(&i32) -> &i32, { diff --git a/tests/ui/needless_lifetimes.rs b/tests/ui/needless_lifetimes.rs index 03d6f2013586c..8b3a885f4a35f 100644 --- a/tests/ui/needless_lifetimes.rs +++ b/tests/ui/needless_lifetimes.rs @@ -384,7 +384,7 @@ mod nested_elision_sites { f() } // lint - fn where_clause_elidadable<'a, T>(i: &'a i32, f: T) -> &'a i32 + fn where_clause_elidable<'a, T>(i: &'a i32, f: T) -> &'a i32 where T: Fn(&i32) -> &i32, { diff --git a/tests/ui/needless_lifetimes.stderr b/tests/ui/needless_lifetimes.stderr index 166e6d0eabf72..e56c914cc86d6 100644 --- a/tests/ui/needless_lifetimes.stderr +++ b/tests/ui/needless_lifetimes.stderr @@ -384,15 +384,15 @@ LL + fn generics_elidable &i32>(i: &i32, f: T) -> &i32 { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:387:32 + --> tests/ui/needless_lifetimes.rs:387:30 | -LL | fn where_clause_elidadable<'a, T>(i: &'a i32, f: T) -> &'a i32 - | ^^ ^^ ^^ +LL | fn where_clause_elidable<'a, T>(i: &'a i32, f: T) -> &'a i32 + | ^^ ^^ ^^ | help: elide the lifetimes | -LL - fn where_clause_elidadable<'a, T>(i: &'a i32, f: T) -> &'a i32 -LL + fn where_clause_elidadable(i: &i32, f: T) -> &i32 +LL - fn where_clause_elidable<'a, T>(i: &'a i32, f: T) -> &'a i32 +LL + fn where_clause_elidable(i: &i32, f: T) -> &i32 | error: the following explicit lifetimes could be elided: 'a From bc070273978b66dad4407ede21c79eee5c0f45ab Mon Sep 17 00:00:00 2001 From: VictorHugoPilled Date: Wed, 25 Sep 2024 13:12:19 -0500 Subject: [PATCH 028/120] fix(clippy_lints/matches): wildcard_in_or_patterns will no longer be triggered for types annotated with #[nonexhaustive] --- clippy_lints/src/matches/mod.rs | 2 +- clippy_lints/src/matches/wild_in_or_pats.rs | 14 ++++- tests/ui/wild_in_or_pats.rs | 68 +++++++++++++++++++++ tests/ui/wild_in_or_pats.stderr | 18 +++++- 4 files changed, 97 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index 686fc4a0fa0a5..28adcc2f22746 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -1045,7 +1045,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { if !from_expansion { // These don't depend on a relationship between multiple arms match_wild_err_arm::check(cx, ex, arms); - wild_in_or_pats::check(cx, arms); + wild_in_or_pats::check(cx, ex, arms); } if let MatchSource::TryDesugar(_) = source { diff --git a/clippy_lints/src/matches/wild_in_or_pats.rs b/clippy_lints/src/matches/wild_in_or_pats.rs index 459513e65bfad..390ba889fd2e6 100644 --- a/clippy_lints/src/matches/wild_in_or_pats.rs +++ b/clippy_lints/src/matches/wild_in_or_pats.rs @@ -1,11 +1,19 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::is_wild; -use rustc_hir::{Arm, PatKind}; +use clippy_utils::{has_non_exhaustive_attr, is_wild}; +use rustc_hir::{Arm, Expr, PatKind}; use rustc_lint::LateContext; +use rustc_middle::ty; use super::WILDCARD_IN_OR_PATTERNS; -pub(crate) fn check(cx: &LateContext<'_>, arms: &[Arm<'_>]) { +pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arms: &[Arm<'_>]) { + // first check if we are matching on an enum that has the non_exhaustive attribute + let ty = cx.typeck_results().expr_ty(expr).peel_refs(); + if let ty::Adt(adt_def, _) = ty.kind() + && has_non_exhaustive_attr(cx.tcx, *adt_def) + { + return; + }; for arm in arms { if let PatKind::Or(fields) = arm.pat.kind { // look for multiple fields in this arm that contains at least one Wild pattern diff --git a/tests/ui/wild_in_or_pats.rs b/tests/ui/wild_in_or_pats.rs index f8bb31b83c48b..bc8a1dbee71db 100644 --- a/tests/ui/wild_in_or_pats.rs +++ b/tests/ui/wild_in_or_pats.rs @@ -37,4 +37,72 @@ fn main() { dbg!("matched (bar or) wild"); }, }; + + // shouldn't lint + #[non_exhaustive] + pub enum NonExhaustiveEnum<'a> { + Message(&'a str), + Quit(&'a str), + Other, + } + + match NonExhaustiveEnum::Message("Pass") { + NonExhaustiveEnum::Message(_) => dbg!("message"), + NonExhaustiveEnum::Quit(_) => dbg!("quit"), + NonExhaustiveEnum::Other | _ => dbg!("wildcard"), + }; + + // should lint + enum ExhaustiveEnum { + Quit, + Write(String), + ChangeColor(i32, i32, i32), + } + + match ExhaustiveEnum::ChangeColor(0, 160, 255) { + ExhaustiveEnum::Write(text) => { + dbg!("Write"); + }, + ExhaustiveEnum::ChangeColor(r, g, b) => { + dbg!("Change the color"); + }, + ExhaustiveEnum::Quit | _ => { + dbg!("Quit or other"); + }, + }; + + // shouldn't lint + #[non_exhaustive] + struct NonExhaustiveStruct { + a: u32, + b: u32, + c: u64, + } + + let b = NonExhaustiveStruct { a: 5, b: 42, c: 342 }; + + match b { + NonExhaustiveStruct { a: 5, b: 42, .. } => {}, + NonExhaustiveStruct { a: 0, b: 0, c: 128 } => {}, + NonExhaustiveStruct { a: 0, b: 0, c: 128, .. } | _ => {}, + } + + // should lint + struct ExhaustiveStruct { + x: i32, + y: i32, + } + + let p = ExhaustiveStruct { x: 0, y: 7 }; + match p { + ExhaustiveStruct { x: 0, y: 0 } => { + dbg!("On the x axis at {x}"); + }, + ExhaustiveStruct { x: 0, y: 1 } => { + dbg!("On the y axis at {y}"); + }, + ExhaustiveStruct { x: 1, y: 1 } | _ => { + dbg!("On neither axis: ({x}, {y})"); + }, + } } diff --git a/tests/ui/wild_in_or_pats.stderr b/tests/ui/wild_in_or_pats.stderr index 06b2415d221e9..5e409d6dfa6d7 100644 --- a/tests/ui/wild_in_or_pats.stderr +++ b/tests/ui/wild_in_or_pats.stderr @@ -32,5 +32,21 @@ LL | _ | "bar" => { | = help: consider handling `_` separately -error: aborting due to 4 previous errors +error: wildcard pattern covers any other pattern as it will match anyway + --> tests/ui/wild_in_or_pats.rs:69:9 + | +LL | ExhaustiveEnum::Quit | _ => { + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider handling `_` separately + +error: wildcard pattern covers any other pattern as it will match anyway + --> tests/ui/wild_in_or_pats.rs:104:9 + | +LL | ExhaustiveStruct { x: 1, y: 1 } | _ => { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider handling `_` separately + +error: aborting due to 6 previous errors From 51b527266e9270ced4c6f1c1bd0735ce49b6ed88 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Wed, 25 Sep 2024 14:28:19 -0400 Subject: [PATCH 029/120] Fix bug found during review https://github.com/rust-lang/rust-clippy/pull/13286#issuecomment-2374245772 --- clippy_lints/src/lifetimes.rs | 28 ++++++++++++++-------------- tests/ui/needless_lifetimes.fixed | 19 +++++++++++++++++++ tests/ui/needless_lifetimes.rs | 19 +++++++++++++++++++ 3 files changed, 52 insertions(+), 14 deletions(-) diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 5a3c749cab12b..a4555e003c46d 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -6,12 +6,12 @@ use rustc_errors::Applicability; use rustc_hir::FnRetTy::Return; use rustc_hir::intravisit::nested_filter::{self as hir_nested_filter, NestedFilter}; use rustc_hir::intravisit::{ - Visitor, walk_fn_decl, walk_generic_arg, walk_generics, walk_impl_item_ref, walk_item, walk_param_bound, + Visitor, walk_fn_decl, walk_generic_args, walk_generics, walk_impl_item_ref, walk_item, walk_param_bound, walk_poly_trait_ref, walk_trait_ref, walk_ty, walk_where_predicate, }; use rustc_hir::{ - BareFnTy, BodyId, FnDecl, FnSig, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, Impl, - ImplItem, ImplItemKind, Item, ItemKind, Lifetime, LifetimeName, LifetimeParamKind, Node, PolyTraitRef, + BareFnTy, BodyId, FnDecl, FnSig, GenericArg, GenericArgs, GenericBound, GenericParam, GenericParamKind, Generics, + Impl, ImplItem, ImplItemKind, Item, ItemKind, Lifetime, LifetimeName, LifetimeParamKind, Node, PolyTraitRef, PredicateOrigin, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WherePredicate, lang_items, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -494,14 +494,14 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, generics: &'tcx Generics<'_ struct Usage { lifetime: Lifetime, in_where_predicate: bool, - in_generic_arg: bool, + in_generics_arg: bool, } struct LifetimeChecker<'cx, 'tcx, F> { cx: &'cx LateContext<'tcx>, map: FxHashMap>, where_predicate_depth: usize, - generic_arg_depth: usize, + generic_args_depth: usize, phantom: std::marker::PhantomData, } @@ -512,7 +512,7 @@ impl<'cx, 'tcx, F> LifetimeChecker<'cx, 'tcx, F> { cx, map, where_predicate_depth: 0, - generic_arg_depth: 0, + generic_args_depth: 0, phantom: std::marker::PhantomData, } } @@ -533,7 +533,7 @@ where usages.push(Usage { lifetime: *lifetime, in_where_predicate: self.where_predicate_depth != 0, - in_generic_arg: self.generic_arg_depth != 0, + in_generics_arg: self.generic_args_depth != 0, }); } } @@ -544,10 +544,10 @@ where self.where_predicate_depth -= 1; } - fn visit_generic_arg(&mut self, generic_arg: &'tcx GenericArg<'tcx>) -> Self::Result { - self.generic_arg_depth += 1; - walk_generic_arg(self, generic_arg); - self.generic_arg_depth -= 1; + fn visit_generic_args(&mut self, generic_args: &'tcx GenericArgs<'tcx>) -> Self::Result { + self.generic_args_depth += 1; + walk_generic_args(self, generic_args); + self.generic_args_depth -= 1; } fn nested_visit_map(&mut self) -> Self::Map { @@ -574,7 +574,7 @@ fn report_extra_lifetimes<'tcx>(cx: &LateContext<'tcx>, func: &'tcx FnDecl<'_>, for (def_id, usages) in checker.map { if usages .iter() - .all(|usage| usage.in_where_predicate && !usage.in_generic_arg) + .all(|usage| usage.in_where_predicate && !usage.in_generics_arg) { span_lint( cx, @@ -612,7 +612,7 @@ fn report_extra_impl_lifetimes<'tcx>(cx: &LateContext<'tcx>, impl_: &'tcx Impl<' for (&def_id, usages) in &checker.map { if usages .iter() - .all(|usage| usage.in_where_predicate && !usage.in_generic_arg) + .all(|usage| usage.in_where_predicate && !usage.in_generics_arg) { span_lint( cx, @@ -646,7 +646,7 @@ fn report_elidable_impl_lifetimes<'tcx>( } | Usage { lifetime, - in_generic_arg: false, + in_generics_arg: false, .. }, ] = usages.as_slice() diff --git a/tests/ui/needless_lifetimes.fixed b/tests/ui/needless_lifetimes.fixed index 7ded5b4234cf3..cfa4cf9da3c56 100644 --- a/tests/ui/needless_lifetimes.fixed +++ b/tests/ui/needless_lifetimes.fixed @@ -543,4 +543,23 @@ mod issue5787 { } } +// https://github.com/rust-lang/rust-clippy/pull/13286#issuecomment-2374245772 +mod rayon { + trait ParallelIterator { + type Item; + } + + struct Copied { + base: I, + } + + impl<'a, T, I> ParallelIterator for Copied + where + I: ParallelIterator, + T: 'a + Copy + Send + Sync, + { + type Item = T; + } +} + fn main() {} diff --git a/tests/ui/needless_lifetimes.rs b/tests/ui/needless_lifetimes.rs index 8b3a885f4a35f..5e9d51164268b 100644 --- a/tests/ui/needless_lifetimes.rs +++ b/tests/ui/needless_lifetimes.rs @@ -543,4 +543,23 @@ mod issue5787 { } } +// https://github.com/rust-lang/rust-clippy/pull/13286#issuecomment-2374245772 +mod rayon { + trait ParallelIterator { + type Item; + } + + struct Copied { + base: I, + } + + impl<'a, T, I> ParallelIterator for Copied + where + I: ParallelIterator, + T: 'a + Copy + Send + Sync, + { + type Item = T; + } +} + fn main() {} From 1e241045873cf4aebb5f6fd7ef3a91f32196dbf1 Mon Sep 17 00:00:00 2001 From: Urgau Date: Wed, 25 Sep 2024 13:23:06 +0200 Subject: [PATCH 030/120] Add `[Option; N]::transpose` --- library/core/src/option.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 30c667e2494b2..f8c631851e76a 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -2545,3 +2545,27 @@ impl Option> { } } } + +impl [Option; N] { + /// Transposes a `[Option; N]` into a `Option<[T; N]>`. + /// + /// # Examples + /// + /// ``` + /// #![feature(option_array_transpose)] + /// # use std::option::Option; + /// + /// let data = [Some(0); 1000]; + /// let data: Option<[u8; 1000]> = data.transpose(); + /// assert_eq!(data, Some([0; 1000])); + /// + /// let data = [Some(0), None]; + /// let data: Option<[u8; 2]> = data.transpose(); + /// assert_eq!(data, None); + /// ``` + #[inline] + #[unstable(feature = "option_array_transpose", issue = "130828")] + pub fn transpose(self) -> Option<[T; N]> { + self.try_map(core::convert::identity) + } +} From eff6d414c05411a0f83d74eb745e951e8bdc9408 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 27 Sep 2024 00:45:02 +0000 Subject: [PATCH 031/120] On implicit `Sized` bound on fn argument, point at type instead of pattern Instead of ``` error[E0277]: the size for values of type `(dyn ThriftService<(), AssocType = _> + 'static)` cannot be known at compilation time --> $DIR/issue-59324.rs:23:20 | LL | fn with_factory(factory: dyn ThriftService<()>) {} | ^^^^^^^ doesn't have a size known at compile-time ``` output ``` error[E0277]: the size for values of type `(dyn ThriftService<(), AssocType = _> + 'static)` cannot be known at compilation time --> $DIR/issue-59324.rs:23:29 | LL | fn with_factory(factory: dyn ThriftService<()>) {} | ^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time ``` --- tests/ui/crashes/ice-6251.stderr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ui/crashes/ice-6251.stderr b/tests/ui/crashes/ice-6251.stderr index 82e7d72358697..77c3e6288ce3c 100644 --- a/tests/ui/crashes/ice-6251.stderr +++ b/tests/ui/crashes/ice-6251.stderr @@ -1,8 +1,8 @@ error[E0277]: the size for values of type `[u8]` cannot be known at compilation time - --> tests/ui/crashes/ice-6251.rs:4:45 + --> tests/ui/crashes/ice-6251.rs:4:48 | LL | fn bug() -> impl Iterator { - | ^ doesn't have a size known at compile-time + | ^^^^ doesn't have a size known at compile-time | = help: the trait `std::marker::Sized` is not implemented for `[u8]` = help: unsized fn params are gated as an unstable feature From c1c0bd7a251865e82b1e8d0292451f78f19f22a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Thu, 26 Sep 2024 13:44:06 +0200 Subject: [PATCH 032/120] Upload average CPU consumption of CI jobs to DataDog --- .github/workflows/ci.yml | 10 ++++ src/ci/scripts/upload-artifacts.sh | 2 +- src/ci/scripts/upload-build-metrics.py | 81 ++++++++++++++++++++++++++ 3 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 src/ci/scripts/upload-build-metrics.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8032154a7365b..b6dc27f123465 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -212,6 +212,16 @@ jobs: # erroring about invalid credentials instead. if: github.event_name == 'push' || env.DEPLOY == '1' || env.DEPLOY_ALT == '1' + - name: upload job metrics to DataDog + if: needs.calculate_matrix.outputs.run_type != 'pr' + env: + DATADOG_SITE: datadoghq.com + DATADOG_API_KEY: ${{ secrets.DATADOG_API_KEY }} + DD_GITHUB_JOB_NAME: ${{ matrix.name }} + run: | + npm install -g @datadog/datadog-ci@^2.x.x + python3 src/ci/scripts/upload-build-metrics.py build/cpu-usage.csv + # This job isused to tell bors the final status of the build, as there is no practical way to detect # when a workflow is successful listening to webhooks only in our current bors implementation (homu). outcome: diff --git a/src/ci/scripts/upload-artifacts.sh b/src/ci/scripts/upload-artifacts.sh index 61c187fa77c01..129ede636f397 100755 --- a/src/ci/scripts/upload-artifacts.sh +++ b/src/ci/scripts/upload-artifacts.sh @@ -23,7 +23,7 @@ if [[ "${DEPLOY-0}" -eq "1" ]] || [[ "${DEPLOY_ALT-0}" -eq "1" ]]; then fi # CPU usage statistics. -mv build/cpu-usage.csv "${upload_dir}/cpu-${CI_JOB_NAME}.csv" +cp build/cpu-usage.csv "${upload_dir}/cpu-${CI_JOB_NAME}.csv" # Build metrics generated by x.py. mv "${build_dir}/metrics.json" "${upload_dir}/metrics-${CI_JOB_NAME}.json" diff --git a/src/ci/scripts/upload-build-metrics.py b/src/ci/scripts/upload-build-metrics.py new file mode 100644 index 0000000000000..a95e0949d700a --- /dev/null +++ b/src/ci/scripts/upload-build-metrics.py @@ -0,0 +1,81 @@ +""" +This script postprocesses data gathered during a CI run, computes certain metrics +from them, and uploads these metrics to DataDog. + +This script is expected to be executed from within a GitHub Actions job. + +It expects the following environment variables: +- DATADOG_SITE: path to the DataDog API endpoint +- DATADOG_API_KEY: DataDog API token +- DD_GITHUB_JOB_NAME: Name of the current GitHub Actions job + +And it also expects the presence of a binary called `datadog-ci` to be in PATH. +It can be installed with `npm install -g @datadog/datadog-ci`. + +Usage: +```bash +$ python3 upload-build-metrics.py +``` + +`path-to-CPU-usage-CSV` is a path to a CSV generated by the `src/ci/cpu-usage-over-time.py` script. +""" +import argparse +import csv +import os +import subprocess +import sys +from pathlib import Path +from typing import List + + +def load_cpu_usage(path: Path) -> List[float]: + usage = [] + with open(path) as f: + reader = csv.reader(f, delimiter=',') + for row in reader: + # The log might contain incomplete rows or some Python exception + if len(row) == 2: + try: + idle = float(row[1]) + usage.append(100.0 - idle) + except ValueError: + pass + return usage + + +def upload_datadog_measure(name: str, value: float): + """ + Uploads a single numeric metric for the current GitHub Actions job to DataDog. + """ + print(f"Metric {name}: {value:.4f}") + + datadog_cmd = "datadog-ci" + if os.getenv("GITHUB_ACTIONS") is not None and sys.platform.lower().startswith("win"): + # Due to weird interaction of MSYS2 and Python, we need to use an absolute path, + # and also specify the ".cmd" at the end. See https://github.com/rust-lang/rust/pull/125771. + datadog_cmd = "C:\\npm\\prefix\\datadog-ci.cmd" + + subprocess.run([ + datadog_cmd, + "measure", + "--level", "job", + "--measures", f"{name}:{value}" + ], + check=False + ) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + prog="DataDog metric uploader" + ) + parser.add_argument("cpu-usage-history-csv") + args = parser.parse_args() + + build_usage_csv = vars(args)["cpu-usage-history-csv"] + usage_timeseries = load_cpu_usage(Path(build_usage_csv)) + if len(usage_timeseries) > 0: + avg_cpu_usage = sum(usage_timeseries) / len(usage_timeseries) + else: + avg_cpu_usage = 0 + upload_datadog_measure("avg-cpu-usage", avg_cpu_usage) From 94a3f96f71e78e74c1fdf76ed6dad5b042accfaf Mon Sep 17 00:00:00 2001 From: binarycat Date: Fri, 27 Sep 2024 12:43:34 -0400 Subject: [PATCH 033/120] rustdoc: lists items that contain multiple paragraphs are more clear fixes https://github.com/rust-lang/rust/issues/130622 --- src/librustdoc/html/static/css/rustdoc.css | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index 40391b1b4dfee..67d74399a1f2c 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -958,6 +958,13 @@ pre, .rustdoc.src .example-wrap, .example-wrap .src-line-numbers { display: inline-block; } +.docblock li { + margin-bottom: .8em; +} +.docblock li p { + margin-bottom: .1em; +} + /* "where ..." clauses with block display are also smaller */ div.where { white-space: pre-wrap; From 605f88aab80de3e8f02d919456323702abba9f81 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Fri, 27 Sep 2024 16:56:04 +0000 Subject: [PATCH 034/120] Ignore `--print`/`-Vv` requests in `clippy-driver` --- src/driver.rs | 47 +++++++++++++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/src/driver.rs b/src/driver.rs index 324f754e61556..afd9df6c0fc3c 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -22,7 +22,6 @@ use rustc_span::symbol::Symbol; use std::env; use std::fs::read_to_string; -use std::ops::Deref; use std::path::Path; use std::process::exit; @@ -30,12 +29,8 @@ use anstream::println; /// If a command-line option matches `find_arg`, then apply the predicate `pred` on its value. If /// true, then return it. The parameter is assumed to be either `--arg=value` or `--arg value`. -fn arg_value<'a, T: Deref>( - args: &'a [T], - find_arg: &str, - pred: impl Fn(&str) -> bool, -) -> Option<&'a str> { - let mut args = args.iter().map(Deref::deref); +fn arg_value<'a>(args: &'a [String], find_arg: &str, pred: impl Fn(&str) -> bool) -> Option<&'a str> { + let mut args = args.iter().map(String::as_str); while let Some(arg) = args.next() { let mut arg = arg.splitn(2, '='); if arg.next() != Some(find_arg) { @@ -50,11 +45,15 @@ fn arg_value<'a, T: Deref>( None } +fn has_arg(args: &[String], find_arg: &str) -> bool { + args.iter().any(|arg| find_arg == arg.split('=').next().unwrap()) +} + #[test] fn test_arg_value() { - let args = &["--bar=bar", "--foobar", "123", "--foo"]; + let args = &["--bar=bar", "--foobar", "123", "--foo"].map(String::from); - assert_eq!(arg_value(&[] as &[&str], "--foobar", |_| true), None); + assert_eq!(arg_value(&[], "--foobar", |_| true), None); assert_eq!(arg_value(args, "--bar", |_| false), None); assert_eq!(arg_value(args, "--bar", |_| true), Some("bar")); assert_eq!(arg_value(args, "--bar", |p| p == "bar"), Some("bar")); @@ -65,6 +64,16 @@ fn test_arg_value() { assert_eq!(arg_value(args, "--foo", |_| true), None); } +#[test] +fn test_has_arg() { + let args = &["--foo=bar", "-vV", "--baz"].map(String::from); + assert!(has_arg(args, "--foo")); + assert!(has_arg(args, "--baz")); + assert!(has_arg(args, "-vV")); + + assert!(!has_arg(args, "--bar")); +} + fn track_clippy_args(psess: &mut ParseSess, args_env_var: &Option) { psess.env_depinfo.get_mut().insert(( Symbol::intern("CLIPPY_ARGS"), @@ -189,7 +198,7 @@ pub fn main() { let mut orig_args = rustc_driver::args::raw_args(&early_dcx)?; let has_sysroot_arg = |args: &mut [String]| -> bool { - if arg_value(args, "--sysroot", |_| true).is_some() { + if has_arg(args, "--sysroot") { return true; } // https://doc.rust-lang.org/rustc/command-line-arguments.html#path-load-command-line-flags-from-a-path @@ -199,7 +208,7 @@ pub fn main() { if let Some(arg_file_path) = arg.strip_prefix('@') { if let Ok(arg_file) = read_to_string(arg_file_path) { let split_arg_file: Vec = arg_file.lines().map(ToString::to_string).collect(); - if arg_value(&split_arg_file, "--sysroot", |_| true).is_some() { + if has_arg(&split_arg_file, "--sysroot") { return true; } } @@ -271,16 +280,18 @@ pub fn main() { .chain(vec!["--cfg".into(), "clippy".into()]) .collect::>(); - // We enable Clippy if one of the following conditions is met - // - IF Clippy is run on its test suite OR - // - IF Clippy is run on the main crate, not on deps (`!cap_lints_allow`) THEN - // - IF `--no-deps` is not set (`!no_deps`) OR - // - IF `--no-deps` is set and Clippy is run on the specified primary package + // If no Clippy lints will be run we do not need to run Clippy let cap_lints_allow = arg_value(&orig_args, "--cap-lints", |val| val == "allow").is_some() && arg_value(&orig_args, "--force-warn", |val| val.contains("clippy::")).is_none(); - let in_primary_package = env::var("CARGO_PRIMARY_PACKAGE").is_ok(); - let clippy_enabled = !cap_lints_allow && (!no_deps || in_primary_package); + // If `--no-deps` is enabled only lint the primary pacakge + let relevant_package = !no_deps || env::var("CARGO_PRIMARY_PACKAGE").is_ok(); + + // Do not run Clippy for Cargo's info queries so that invalid CLIPPY_ARGS are not cached + // https://github.com/rust-lang/cargo/issues/14385 + let info_query = has_arg(&orig_args, "-vV") || has_arg(&orig_args, "--print"); + + let clippy_enabled = !cap_lints_allow && relevant_package && !info_query; if clippy_enabled { args.extend(clippy_args); rustc_driver::RunCompiler::new(&args, &mut ClippyCallbacks { clippy_args_var }) From d24a63199a5ad140fd52f6feb2e279e2a51cc201 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Fri, 27 Sep 2024 21:38:38 +0000 Subject: [PATCH 035/120] remove method call receiver special casing in `unused_async` lint --- clippy_lints/src/unused_async.rs | 23 ++++++++++------------- tests/ui/unused_async.rs | 16 ++++++++++++++++ tests/ui/unused_async.stderr | 4 ++-- 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/clippy_lints/src/unused_async.rs b/clippy_lints/src/unused_async.rs index a1f08cf6623bc..0ed64fd1f8ef2 100644 --- a/clippy_lints/src/unused_async.rs +++ b/clippy_lints/src/unused_async.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::is_def_id_trait_method; use rustc_hir::def::DefKind; use rustc_hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn}; -use rustc_hir::{Body, Expr, ExprKind, FnDecl, Node, YieldSource}; +use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId, Node, YieldSource}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; use rustc_session::impl_lint_pass; @@ -137,17 +137,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync { } } - fn check_path(&mut self, cx: &LateContext<'tcx>, path: &rustc_hir::Path<'tcx>, hir_id: rustc_hir::HirId) { - fn is_node_func_call(node: Node<'_>, expected_receiver: Span) -> bool { - matches!( - node, - Node::Expr(Expr { - kind: ExprKind::Call(Expr { span, .. }, _) | ExprKind::MethodCall(_, Expr { span, .. }, ..), - .. - }) if *span == expected_receiver - ) - } - + fn check_path(&mut self, cx: &LateContext<'tcx>, path: &rustc_hir::Path<'tcx>, hir_id: HirId) { // Find paths to local async functions that aren't immediately called. // E.g. `async fn f() {}; let x = f;` // Depending on how `x` is used, f's asyncness might be required despite not having any `await` @@ -156,7 +146,14 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync { && let Some(local_def_id) = def_id.as_local() && cx.tcx.def_kind(def_id) == DefKind::Fn && cx.tcx.asyncness(def_id).is_async() - && !is_node_func_call(cx.tcx.parent_hir_node(hir_id), path.span) + && let parent = cx.tcx.parent_hir_node(hir_id) + && !matches!( + parent, + Node::Expr(Expr { + kind: ExprKind::Call(Expr { span, .. }, _), + .. + }) if *span == path.span + ) { self.async_fns_as_value.insert(local_def_id); } diff --git a/tests/ui/unused_async.rs b/tests/ui/unused_async.rs index 7ec8a3adb4cd5..838d6f0aa9711 100644 --- a/tests/ui/unused_async.rs +++ b/tests/ui/unused_async.rs @@ -55,6 +55,22 @@ mod issue9695 { } } +mod issue13466 { + use std::future::Future; + + struct Wrap(F); + impl From for Wrap { + fn from(f: F) -> Self { + Self(f) + } + } + fn takes_fut Fut, Fut: Future>(_: Wrap) {} + async fn unused_async() {} + fn fp() { + takes_fut(unused_async.into()); + } +} + async fn foo() -> i32 { //~^ ERROR: unused `async` for function with no await statements 4 diff --git a/tests/ui/unused_async.stderr b/tests/ui/unused_async.stderr index 337c650e029f8..4811df63658a9 100644 --- a/tests/ui/unused_async.stderr +++ b/tests/ui/unused_async.stderr @@ -27,7 +27,7 @@ LL | async fn f3() {} = help: consider removing the `async` from this function error: unused `async` for function with no await statements - --> tests/ui/unused_async.rs:58:1 + --> tests/ui/unused_async.rs:74:1 | LL | / async fn foo() -> i32 { LL | | @@ -38,7 +38,7 @@ LL | | } = help: consider removing the `async` from this function error: unused `async` for function with no await statements - --> tests/ui/unused_async.rs:70:5 + --> tests/ui/unused_async.rs:86:5 | LL | / async fn unused(&self) -> i32 { LL | | From 5efb3ef2da9348d23c26171dce78ec02fe888653 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Bj=C3=B8rnager=20Jensen?= Date: Sat, 28 Sep 2024 08:31:51 +0200 Subject: [PATCH 036/120] Update Unicode escapes; --- library/core/src/char/methods.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs index 3dcaab6a12beb..3f11e4fe21cc9 100644 --- a/library/core/src/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -69,7 +69,7 @@ impl char { /// assert_eq!(char::from_u32(value_at_max + 1), None); /// ``` #[stable(feature = "assoc_char_consts", since = "1.52.0")] - pub const MAX: char = '\u{10ffff}'; + pub const MAX: char = '\u{10FFFF}'; /// `U+FFFD REPLACEMENT CHARACTER` (�) is used in Unicode to represent a /// decoding error. @@ -1837,7 +1837,6 @@ pub const fn encode_utf16_raw(mut code: u32, dst: &mut [u16]) -> &mut [u16] { } (2, [a, b, ..]) => { code -= 0x1_0000; - *a = (code >> 10) as u16 | 0xD800; *b = (code & 0x3FF) as u16 | 0xDC00; } From 73a16c10dba19efbd5b3363be5f1d37a60d6436c Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Tue, 3 Sep 2024 00:21:02 -0400 Subject: [PATCH 037/120] Suggest `Option<&T>` instead of `&Option` --- CHANGELOG.md | 1 + book/src/lint_configuration.md | 1 + clippy_config/src/conf.rs | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/functions/mod.rs | 57 ++++++ clippy_lints/src/functions/ref_option.rs | 122 +++++++++++++ tests/ui/ref_option/all/clippy.toml | 1 + tests/ui/ref_option/private/clippy.toml | 1 + tests/ui/ref_option/ref_option.all.fixed | 62 +++++++ tests/ui/ref_option/ref_option.all.stderr | 162 ++++++++++++++++++ tests/ui/ref_option/ref_option.private.fixed | 62 +++++++ tests/ui/ref_option/ref_option.private.stderr | 108 ++++++++++++ tests/ui/ref_option/ref_option.rs | 62 +++++++ .../ref_option/ref_option_traits.all.stderr | 37 ++++ .../ref_option_traits.private.stderr | 21 +++ tests/ui/ref_option/ref_option_traits.rs | 37 ++++ 16 files changed, 736 insertions(+) create mode 100644 clippy_lints/src/functions/ref_option.rs create mode 100644 tests/ui/ref_option/all/clippy.toml create mode 100644 tests/ui/ref_option/private/clippy.toml create mode 100644 tests/ui/ref_option/ref_option.all.fixed create mode 100644 tests/ui/ref_option/ref_option.all.stderr create mode 100644 tests/ui/ref_option/ref_option.private.fixed create mode 100644 tests/ui/ref_option/ref_option.private.stderr create mode 100644 tests/ui/ref_option/ref_option.rs create mode 100644 tests/ui/ref_option/ref_option_traits.all.stderr create mode 100644 tests/ui/ref_option/ref_option_traits.private.stderr create mode 100644 tests/ui/ref_option/ref_option_traits.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 41a86e8ce5102..5d253d52531e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5871,6 +5871,7 @@ Released 2018-09-13 [`ref_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_as_ptr [`ref_binding_to_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_binding_to_reference [`ref_in_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_in_deref +[`ref_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option [`ref_option_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option_ref [`ref_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_patterns [`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index 91159bc79c511..07a56fb33df11 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -353,6 +353,7 @@ Suppress lints whenever the suggested change would cause breakage for other crat * [`rc_buffer`](https://rust-lang.github.io/rust-clippy/master/index.html#rc_buffer) * [`rc_mutex`](https://rust-lang.github.io/rust-clippy/master/index.html#rc_mutex) * [`redundant_allocation`](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation) +* [`ref_option`](https://rust-lang.github.io/rust-clippy/master/index.html#ref_option) * [`single_call_fn`](https://rust-lang.github.io/rust-clippy/master/index.html#single_call_fn) * [`trivially_copy_pass_by_ref`](https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref) * [`unnecessary_box_returns`](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_box_returns) diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 757620341ccc1..e4e2c97fdc1d9 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -378,6 +378,7 @@ define_Conf! { rc_buffer, rc_mutex, redundant_allocation, + ref_option, single_call_fn, trivially_copy_pass_by_ref, unnecessary_box_returns, diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 2b229d2fe6ac5..9cec672beb004 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -202,6 +202,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::functions::MUST_USE_CANDIDATE_INFO, crate::functions::MUST_USE_UNIT_INFO, crate::functions::NOT_UNSAFE_PTR_ARG_DEREF_INFO, + crate::functions::REF_OPTION_INFO, crate::functions::RENAMED_FUNCTION_PARAMS_INFO, crate::functions::RESULT_LARGE_ERR_INFO, crate::functions::RESULT_UNIT_ERR_INFO, diff --git a/clippy_lints/src/functions/mod.rs b/clippy_lints/src/functions/mod.rs index ba8a459c917e6..cfa63d3befce4 100644 --- a/clippy_lints/src/functions/mod.rs +++ b/clippy_lints/src/functions/mod.rs @@ -2,6 +2,7 @@ mod impl_trait_in_params; mod misnamed_getters; mod must_use; mod not_unsafe_ptr_arg_deref; +mod ref_option; mod renamed_function_params; mod result; mod too_many_arguments; @@ -399,6 +400,50 @@ declare_clippy_lint! { "renamed function parameters in trait implementation" } +declare_clippy_lint! { + /// ### What it does + /// Warns when a function signature uses `&Option` instead of `Option<&T>`. + /// + /// ### Why is this bad? + /// More flexibility, better memory optimization, and more idiomatic Rust code. + /// + /// `&Option` in a function signature breaks encapsulation because the caller must own T + /// and move it into an Option to call with it. When returned, the owner must internally store + /// it as `Option` in order to return it. + /// At a lower level `&Option` points to memory that has `presence` bit flag + value, + /// whereas `Option<&T>` is always optimized to a single pointer. + /// + /// See this [YouTube video](https://www.youtube.com/watch?v=6c7pZYP_iIE) by + /// Logan Smith for an in-depth explanation of why this is important. + /// + /// ### Known problems + /// This lint recommends changing the function signatures, but it cannot + /// automatically change the function calls or the function implementations. + /// + /// ### Example + /// ```no_run + /// // caller uses foo(&opt) + /// fn foo(a: &Option) {} + /// # struct Unit {} + /// # impl Unit { + /// fn bar(&self) -> &Option { &None } + /// # } + /// ``` + /// Use instead: + /// ```no_run + /// // caller should use foo(opt.as_ref()) + /// fn foo(a: Option<&String>) {} + /// # struct Unit {} + /// # impl Unit { + /// fn bar(&self) -> Option<&String> { None } + /// # } + /// ``` + #[clippy::version = "1.82.0"] + pub REF_OPTION, + nursery, + "function signature uses `&Option` instead of `Option<&T>`" +} + pub struct Functions { too_many_arguments_threshold: u64, too_many_lines_threshold: u64, @@ -437,6 +482,7 @@ impl_lint_pass!(Functions => [ MISNAMED_GETTERS, IMPL_TRAIT_IN_PARAMS, RENAMED_FUNCTION_PARAMS, + REF_OPTION, ]); impl<'tcx> LateLintPass<'tcx> for Functions { @@ -455,6 +501,16 @@ impl<'tcx> LateLintPass<'tcx> for Functions { not_unsafe_ptr_arg_deref::check_fn(cx, kind, decl, body, def_id); misnamed_getters::check_fn(cx, kind, decl, body, span); impl_trait_in_params::check_fn(cx, &kind, body, hir_id); + ref_option::check_fn( + cx, + kind, + decl, + span, + hir_id, + def_id, + body, + self.avoid_breaking_exported_api, + ); } fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { @@ -475,5 +531,6 @@ impl<'tcx> LateLintPass<'tcx> for Functions { must_use::check_trait_item(cx, item); result::check_trait_item(cx, item, self.large_error_threshold); impl_trait_in_params::check_trait_item(cx, item, self.avoid_breaking_exported_api); + ref_option::check_trait_item(cx, item, self.avoid_breaking_exported_api); } } diff --git a/clippy_lints/src/functions/ref_option.rs b/clippy_lints/src/functions/ref_option.rs new file mode 100644 index 0000000000000..373ecd74cb379 --- /dev/null +++ b/clippy_lints/src/functions/ref_option.rs @@ -0,0 +1,122 @@ +use crate::functions::REF_OPTION; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::is_trait_impl_item; +use clippy_utils::source::snippet; +use clippy_utils::ty::is_type_diagnostic_item; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::intravisit::FnKind; +use rustc_hir::{FnDecl, HirId}; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, GenericArgKind, Mutability, Ty}; +use rustc_span::def_id::LocalDefId; +use rustc_span::{Span, sym}; + +fn check_ty<'a>(cx: &LateContext<'a>, param: &rustc_hir::Ty<'a>, param_ty: Ty<'a>, fixes: &mut Vec<(Span, String)>) { + if let ty::Ref(_, opt_ty, Mutability::Not) = param_ty.kind() + && is_type_diagnostic_item(cx, *opt_ty, sym::Option) + && let ty::Adt(_, opt_gen) = opt_ty.kind() + && let [gen] = opt_gen.as_slice() + && let GenericArgKind::Type(gen_ty) = gen.unpack() + && !gen_ty.is_ref() + // Need to gen the original spans, so first parsing mid, and hir parsing afterward + && let hir::TyKind::Ref(lifetime, hir::MutTy { ty, .. }) = param.kind + && let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = ty.kind + && let (Some(first), Some(last)) = (path.segments.first(), path.segments.last()) + && let Some(hir::GenericArgs { + args: [hir::GenericArg::Type(opt_ty)], + .. + }) = last.args + { + let lifetime = snippet(cx, lifetime.ident.span, ".."); + fixes.push(( + param.span, + format!( + "{}<&{lifetime}{}{}>", + snippet(cx, first.ident.span.to(last.ident.span), ".."), + if lifetime.is_empty() { "" } else { " " }, + snippet(cx, opt_ty.span, "..") + ), + )); + } +} + +fn check_fn_sig<'a>(cx: &LateContext<'a>, decl: &FnDecl<'a>, span: Span, sig: ty::FnSig<'a>) { + let mut fixes = Vec::new(); + // Check function arguments' types + for (param, param_ty) in decl.inputs.iter().zip(sig.inputs()) { + check_ty(cx, param, *param_ty, &mut fixes); + } + // Check return type + if let hir::FnRetTy::Return(ty) = &decl.output { + check_ty(cx, ty, sig.output(), &mut fixes); + } + if !fixes.is_empty() { + span_lint_and_then( + cx, + REF_OPTION, + span, + "it is more idiomatic to use `Option<&T>` instead of `&Option`", + |diag| { + diag.multipart_suggestion("change this to", fixes, Applicability::Unspecified); + }, + ); + } +} + +#[allow(clippy::too_many_arguments)] +pub(crate) fn check_fn<'a>( + cx: &LateContext<'a>, + kind: FnKind<'_>, + decl: &FnDecl<'a>, + span: Span, + hir_id: HirId, + def_id: LocalDefId, + body: &hir::Body<'_>, + avoid_breaking_exported_api: bool, +) { + if avoid_breaking_exported_api && cx.effective_visibilities.is_exported(def_id) { + return; + } + + if let FnKind::Closure = kind { + // Compute the span of the closure parameters + return type if set + let span = if let hir::FnRetTy::Return(out_ty) = &decl.output { + if decl.inputs.is_empty() { + out_ty.span + } else { + span.with_hi(out_ty.span.hi()) + } + } else if let (Some(first), Some(last)) = (decl.inputs.first(), decl.inputs.last()) { + first.span.to(last.span) + } else { + // No parameters - no point in checking + return; + }; + + // Figure out the signature of the closure + let ty::Closure(_, args) = cx.typeck_results().expr_ty(body.value).kind() else { + return; + }; + let sig = args.as_closure().sig().skip_binder(); + + check_fn_sig(cx, decl, span, sig); + } else if !is_trait_impl_item(cx, hir_id) { + let sig = cx.tcx.fn_sig(def_id).instantiate_identity().skip_binder(); + check_fn_sig(cx, decl, span, sig); + } +} + +pub(super) fn check_trait_item<'a>( + cx: &LateContext<'a>, + trait_item: &hir::TraitItem<'a>, + avoid_breaking_exported_api: bool, +) { + if let hir::TraitItemKind::Fn(ref sig, _) = trait_item.kind + && !(avoid_breaking_exported_api && cx.effective_visibilities.is_exported(trait_item.owner_id.def_id)) + { + let def_id = trait_item.owner_id.def_id; + let ty_sig = cx.tcx.fn_sig(def_id).instantiate_identity().skip_binder(); + check_fn_sig(cx, sig.decl, sig.span, ty_sig); + } +} diff --git a/tests/ui/ref_option/all/clippy.toml b/tests/ui/ref_option/all/clippy.toml new file mode 100644 index 0000000000000..cda8d17eed44c --- /dev/null +++ b/tests/ui/ref_option/all/clippy.toml @@ -0,0 +1 @@ +avoid-breaking-exported-api = false diff --git a/tests/ui/ref_option/private/clippy.toml b/tests/ui/ref_option/private/clippy.toml new file mode 100644 index 0000000000000..5f304987aa94a --- /dev/null +++ b/tests/ui/ref_option/private/clippy.toml @@ -0,0 +1 @@ +avoid-breaking-exported-api = true diff --git a/tests/ui/ref_option/ref_option.all.fixed b/tests/ui/ref_option/ref_option.all.fixed new file mode 100644 index 0000000000000..47781a97c983f --- /dev/null +++ b/tests/ui/ref_option/ref_option.all.fixed @@ -0,0 +1,62 @@ +//@revisions: private all +//@[private] rustc-env:CLIPPY_CONF_DIR=tests/ui/ref_option/private +//@[all] rustc-env:CLIPPY_CONF_DIR=tests/ui/ref_option/all + +#![allow(unused, clippy::needless_lifetimes, clippy::borrowed_box)] +#![warn(clippy::ref_option)] + +fn opt_u8(a: Option<&u8>) {} +fn opt_gen(a: Option<&T>) {} +fn opt_string(a: std::option::Option<&String>) {} +fn ret_string<'a>(p: &'a str) -> Option<&'a u8> { + panic!() +} +fn ret_string_static() -> Option<&'static u8> { + panic!() +} +fn mult_string(a: Option<&String>, b: Option<&Vec>) {} +fn ret_box<'a>() -> Option<&'a Box> { + panic!() +} + +pub fn pub_opt_string(a: Option<&String>) {} +pub fn pub_mult_string(a: Option<&String>, b: Option<&Vec>) {} + +pub trait PubTrait { + fn pub_trait_opt(&self, a: Option<&Vec>); + fn pub_trait_ret(&self) -> Option<&Vec>; +} + +trait PrivateTrait { + fn trait_opt(&self, a: Option<&String>); + fn trait_ret(&self) -> Option<&String>; +} + +pub struct PubStruct; + +impl PubStruct { + pub fn pub_opt_params(&self, a: Option<&()>) {} + pub fn pub_opt_ret(&self) -> Option<&String> { + panic!() + } + + fn private_opt_params(&self, a: Option<&()>) {} + fn private_opt_ret(&self) -> Option<&String> { + panic!() + } +} + +// valid, don't change +fn mut_u8(a: &mut Option) {} +pub fn pub_mut_u8(a: &mut Option) {} + +// might be good to catch in the future +fn mut_u8_ref(a: &mut &Option) {} +pub fn pub_mut_u8_ref(a: &mut &Option) {} +fn lambdas() { + // Not handled for now, not sure if we should + let x = |a: &Option| {}; + let x = |a: &Option| -> &Option { panic!() }; +} + +fn main() {} diff --git a/tests/ui/ref_option/ref_option.all.stderr b/tests/ui/ref_option/ref_option.all.stderr new file mode 100644 index 0000000000000..b4c69ac629625 --- /dev/null +++ b/tests/ui/ref_option/ref_option.all.stderr @@ -0,0 +1,162 @@ +error: it is more idiomatic to use `Option<&T>` instead of `&Option` + --> tests/ui/ref_option/ref_option.rs:8:1 + | +LL | fn opt_u8(a: &Option) {} + | ^^^^^^^^^^^^^-----------^^^^ + | | + | help: change this to: `Option<&u8>` + | + = note: `-D clippy::ref-option` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::ref_option)]` + +error: it is more idiomatic to use `Option<&T>` instead of `&Option` + --> tests/ui/ref_option/ref_option.rs:9:1 + | +LL | fn opt_gen(a: &Option) {} + | ^^^^^^^^^^^^^^^^^----------^^^^ + | | + | help: change this to: `Option<&T>` + +error: it is more idiomatic to use `Option<&T>` instead of `&Option` + --> tests/ui/ref_option/ref_option.rs:10:1 + | +LL | fn opt_string(a: &std::option::Option) {} + | ^^^^^^^^^^^^^^^^^----------------------------^^^^ + | | + | help: change this to: `std::option::Option<&String>` + +error: it is more idiomatic to use `Option<&T>` instead of `&Option` + --> tests/ui/ref_option/ref_option.rs:11:1 + | +LL | fn ret_string<'a>(p: &'a str) -> &'a Option { + | ^ -------------- help: change this to: `Option<&'a u8>` + | _| + | | +LL | | panic!() +LL | | } + | |_^ + +error: it is more idiomatic to use `Option<&T>` instead of `&Option` + --> tests/ui/ref_option/ref_option.rs:14:1 + | +LL | fn ret_string_static() -> &'static Option { + | ^ ------------------- help: change this to: `Option<&'static u8>` + | _| + | | +LL | | panic!() +LL | | } + | |_^ + +error: it is more idiomatic to use `Option<&T>` instead of `&Option` + --> tests/ui/ref_option/ref_option.rs:17:1 + | +LL | fn mult_string(a: &Option, b: &Option>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL | fn mult_string(a: Option<&String>, b: Option<&Vec>) {} + | ~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~ + +error: it is more idiomatic to use `Option<&T>` instead of `&Option` + --> tests/ui/ref_option/ref_option.rs:18:1 + | +LL | fn ret_box<'a>() -> &'a Option> { + | ^ ------------------- help: change this to: `Option<&'a Box>` + | _| + | | +LL | | panic!() +LL | | } + | |_^ + +error: it is more idiomatic to use `Option<&T>` instead of `&Option` + --> tests/ui/ref_option/ref_option.rs:22:1 + | +LL | pub fn pub_opt_string(a: &Option) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^---------------^^^^ + | | + | help: change this to: `Option<&String>` + +error: it is more idiomatic to use `Option<&T>` instead of `&Option` + --> tests/ui/ref_option/ref_option.rs:23:1 + | +LL | pub fn pub_mult_string(a: &Option, b: &Option>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL | pub fn pub_mult_string(a: Option<&String>, b: Option<&Vec>) {} + | ~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~ + +error: it is more idiomatic to use `Option<&T>` instead of `&Option` + --> tests/ui/ref_option/ref_option.rs:26:5 + | +LL | fn pub_trait_opt(&self, a: &Option>); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^----------------^^ + | | + | help: change this to: `Option<&Vec>` + +error: it is more idiomatic to use `Option<&T>` instead of `&Option` + --> tests/ui/ref_option/ref_option.rs:27:5 + | +LL | fn pub_trait_ret(&self) -> &Option>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^----------------^ + | | + | help: change this to: `Option<&Vec>` + +error: it is more idiomatic to use `Option<&T>` instead of `&Option` + --> tests/ui/ref_option/ref_option.rs:31:5 + | +LL | fn trait_opt(&self, a: &Option); + | ^^^^^^^^^^^^^^^^^^^^^^^---------------^^ + | | + | help: change this to: `Option<&String>` + +error: it is more idiomatic to use `Option<&T>` instead of `&Option` + --> tests/ui/ref_option/ref_option.rs:32:5 + | +LL | fn trait_ret(&self) -> &Option; + | ^^^^^^^^^^^^^^^^^^^^^^^---------------^ + | | + | help: change this to: `Option<&String>` + +error: it is more idiomatic to use `Option<&T>` instead of `&Option` + --> tests/ui/ref_option/ref_option.rs:38:5 + | +LL | pub fn pub_opt_params(&self, a: &Option<()>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-----------^^^^ + | | + | help: change this to: `Option<&()>` + +error: it is more idiomatic to use `Option<&T>` instead of `&Option` + --> tests/ui/ref_option/ref_option.rs:39:5 + | +LL | pub fn pub_opt_ret(&self) -> &Option { + | ^ --------------- help: change this to: `Option<&String>` + | _____| + | | +LL | | panic!() +LL | | } + | |_____^ + +error: it is more idiomatic to use `Option<&T>` instead of `&Option` + --> tests/ui/ref_option/ref_option.rs:43:5 + | +LL | fn private_opt_params(&self, a: &Option<()>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-----------^^^^ + | | + | help: change this to: `Option<&()>` + +error: it is more idiomatic to use `Option<&T>` instead of `&Option` + --> tests/ui/ref_option/ref_option.rs:44:5 + | +LL | fn private_opt_ret(&self) -> &Option { + | ^ --------------- help: change this to: `Option<&String>` + | _____| + | | +LL | | panic!() +LL | | } + | |_____^ + +error: aborting due to 17 previous errors + diff --git a/tests/ui/ref_option/ref_option.private.fixed b/tests/ui/ref_option/ref_option.private.fixed new file mode 100644 index 0000000000000..8c42556e9b0d7 --- /dev/null +++ b/tests/ui/ref_option/ref_option.private.fixed @@ -0,0 +1,62 @@ +//@revisions: private all +//@[private] rustc-env:CLIPPY_CONF_DIR=tests/ui/ref_option/private +//@[all] rustc-env:CLIPPY_CONF_DIR=tests/ui/ref_option/all + +#![allow(unused, clippy::needless_lifetimes, clippy::borrowed_box)] +#![warn(clippy::ref_option)] + +fn opt_u8(a: Option<&u8>) {} +fn opt_gen(a: Option<&T>) {} +fn opt_string(a: std::option::Option<&String>) {} +fn ret_string<'a>(p: &'a str) -> Option<&'a u8> { + panic!() +} +fn ret_string_static() -> Option<&'static u8> { + panic!() +} +fn mult_string(a: Option<&String>, b: Option<&Vec>) {} +fn ret_box<'a>() -> Option<&'a Box> { + panic!() +} + +pub fn pub_opt_string(a: &Option) {} +pub fn pub_mult_string(a: &Option, b: &Option>) {} + +pub trait PubTrait { + fn pub_trait_opt(&self, a: &Option>); + fn pub_trait_ret(&self) -> &Option>; +} + +trait PrivateTrait { + fn trait_opt(&self, a: Option<&String>); + fn trait_ret(&self) -> Option<&String>; +} + +pub struct PubStruct; + +impl PubStruct { + pub fn pub_opt_params(&self, a: &Option<()>) {} + pub fn pub_opt_ret(&self) -> &Option { + panic!() + } + + fn private_opt_params(&self, a: Option<&()>) {} + fn private_opt_ret(&self) -> Option<&String> { + panic!() + } +} + +// valid, don't change +fn mut_u8(a: &mut Option) {} +pub fn pub_mut_u8(a: &mut Option) {} + +// might be good to catch in the future +fn mut_u8_ref(a: &mut &Option) {} +pub fn pub_mut_u8_ref(a: &mut &Option) {} +fn lambdas() { + // Not handled for now, not sure if we should + let x = |a: &Option| {}; + let x = |a: &Option| -> &Option { panic!() }; +} + +fn main() {} diff --git a/tests/ui/ref_option/ref_option.private.stderr b/tests/ui/ref_option/ref_option.private.stderr new file mode 100644 index 0000000000000..17c90536da344 --- /dev/null +++ b/tests/ui/ref_option/ref_option.private.stderr @@ -0,0 +1,108 @@ +error: it is more idiomatic to use `Option<&T>` instead of `&Option` + --> tests/ui/ref_option/ref_option.rs:8:1 + | +LL | fn opt_u8(a: &Option) {} + | ^^^^^^^^^^^^^-----------^^^^ + | | + | help: change this to: `Option<&u8>` + | + = note: `-D clippy::ref-option` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::ref_option)]` + +error: it is more idiomatic to use `Option<&T>` instead of `&Option` + --> tests/ui/ref_option/ref_option.rs:9:1 + | +LL | fn opt_gen(a: &Option) {} + | ^^^^^^^^^^^^^^^^^----------^^^^ + | | + | help: change this to: `Option<&T>` + +error: it is more idiomatic to use `Option<&T>` instead of `&Option` + --> tests/ui/ref_option/ref_option.rs:10:1 + | +LL | fn opt_string(a: &std::option::Option) {} + | ^^^^^^^^^^^^^^^^^----------------------------^^^^ + | | + | help: change this to: `std::option::Option<&String>` + +error: it is more idiomatic to use `Option<&T>` instead of `&Option` + --> tests/ui/ref_option/ref_option.rs:11:1 + | +LL | fn ret_string<'a>(p: &'a str) -> &'a Option { + | ^ -------------- help: change this to: `Option<&'a u8>` + | _| + | | +LL | | panic!() +LL | | } + | |_^ + +error: it is more idiomatic to use `Option<&T>` instead of `&Option` + --> tests/ui/ref_option/ref_option.rs:14:1 + | +LL | fn ret_string_static() -> &'static Option { + | ^ ------------------- help: change this to: `Option<&'static u8>` + | _| + | | +LL | | panic!() +LL | | } + | |_^ + +error: it is more idiomatic to use `Option<&T>` instead of `&Option` + --> tests/ui/ref_option/ref_option.rs:17:1 + | +LL | fn mult_string(a: &Option, b: &Option>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL | fn mult_string(a: Option<&String>, b: Option<&Vec>) {} + | ~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~ + +error: it is more idiomatic to use `Option<&T>` instead of `&Option` + --> tests/ui/ref_option/ref_option.rs:18:1 + | +LL | fn ret_box<'a>() -> &'a Option> { + | ^ ------------------- help: change this to: `Option<&'a Box>` + | _| + | | +LL | | panic!() +LL | | } + | |_^ + +error: it is more idiomatic to use `Option<&T>` instead of `&Option` + --> tests/ui/ref_option/ref_option.rs:31:5 + | +LL | fn trait_opt(&self, a: &Option); + | ^^^^^^^^^^^^^^^^^^^^^^^---------------^^ + | | + | help: change this to: `Option<&String>` + +error: it is more idiomatic to use `Option<&T>` instead of `&Option` + --> tests/ui/ref_option/ref_option.rs:32:5 + | +LL | fn trait_ret(&self) -> &Option; + | ^^^^^^^^^^^^^^^^^^^^^^^---------------^ + | | + | help: change this to: `Option<&String>` + +error: it is more idiomatic to use `Option<&T>` instead of `&Option` + --> tests/ui/ref_option/ref_option.rs:43:5 + | +LL | fn private_opt_params(&self, a: &Option<()>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-----------^^^^ + | | + | help: change this to: `Option<&()>` + +error: it is more idiomatic to use `Option<&T>` instead of `&Option` + --> tests/ui/ref_option/ref_option.rs:44:5 + | +LL | fn private_opt_ret(&self) -> &Option { + | ^ --------------- help: change this to: `Option<&String>` + | _____| + | | +LL | | panic!() +LL | | } + | |_____^ + +error: aborting due to 11 previous errors + diff --git a/tests/ui/ref_option/ref_option.rs b/tests/ui/ref_option/ref_option.rs new file mode 100644 index 0000000000000..05251bcf12cd0 --- /dev/null +++ b/tests/ui/ref_option/ref_option.rs @@ -0,0 +1,62 @@ +//@revisions: private all +//@[private] rustc-env:CLIPPY_CONF_DIR=tests/ui/ref_option/private +//@[all] rustc-env:CLIPPY_CONF_DIR=tests/ui/ref_option/all + +#![allow(unused, clippy::needless_lifetimes, clippy::borrowed_box)] +#![warn(clippy::ref_option)] + +fn opt_u8(a: &Option) {} +fn opt_gen(a: &Option) {} +fn opt_string(a: &std::option::Option) {} +fn ret_string<'a>(p: &'a str) -> &'a Option { + panic!() +} +fn ret_string_static() -> &'static Option { + panic!() +} +fn mult_string(a: &Option, b: &Option>) {} +fn ret_box<'a>() -> &'a Option> { + panic!() +} + +pub fn pub_opt_string(a: &Option) {} +pub fn pub_mult_string(a: &Option, b: &Option>) {} + +pub trait PubTrait { + fn pub_trait_opt(&self, a: &Option>); + fn pub_trait_ret(&self) -> &Option>; +} + +trait PrivateTrait { + fn trait_opt(&self, a: &Option); + fn trait_ret(&self) -> &Option; +} + +pub struct PubStruct; + +impl PubStruct { + pub fn pub_opt_params(&self, a: &Option<()>) {} + pub fn pub_opt_ret(&self) -> &Option { + panic!() + } + + fn private_opt_params(&self, a: &Option<()>) {} + fn private_opt_ret(&self) -> &Option { + panic!() + } +} + +// valid, don't change +fn mut_u8(a: &mut Option) {} +pub fn pub_mut_u8(a: &mut Option) {} + +// might be good to catch in the future +fn mut_u8_ref(a: &mut &Option) {} +pub fn pub_mut_u8_ref(a: &mut &Option) {} +fn lambdas() { + // Not handled for now, not sure if we should + let x = |a: &Option| {}; + let x = |a: &Option| -> &Option { panic!() }; +} + +fn main() {} diff --git a/tests/ui/ref_option/ref_option_traits.all.stderr b/tests/ui/ref_option/ref_option_traits.all.stderr new file mode 100644 index 0000000000000..a9967168c12e3 --- /dev/null +++ b/tests/ui/ref_option/ref_option_traits.all.stderr @@ -0,0 +1,37 @@ +error: it is more idiomatic to use `Option<&T>` instead of `&Option` + --> tests/ui/ref_option/ref_option_traits.rs:10:5 + | +LL | fn pub_trait_opt(&self, a: &Option>); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^----------------^^ + | | + | help: change this to: `Option<&Vec>` + | + = note: `-D clippy::ref-option` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::ref_option)]` + +error: it is more idiomatic to use `Option<&T>` instead of `&Option` + --> tests/ui/ref_option/ref_option_traits.rs:11:5 + | +LL | fn pub_trait_ret(&self) -> &Option>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^----------------^ + | | + | help: change this to: `Option<&Vec>` + +error: it is more idiomatic to use `Option<&T>` instead of `&Option` + --> tests/ui/ref_option/ref_option_traits.rs:15:5 + | +LL | fn trait_opt(&self, a: &Option); + | ^^^^^^^^^^^^^^^^^^^^^^^---------------^^ + | | + | help: change this to: `Option<&String>` + +error: it is more idiomatic to use `Option<&T>` instead of `&Option` + --> tests/ui/ref_option/ref_option_traits.rs:16:5 + | +LL | fn trait_ret(&self) -> &Option; + | ^^^^^^^^^^^^^^^^^^^^^^^---------------^ + | | + | help: change this to: `Option<&String>` + +error: aborting due to 4 previous errors + diff --git a/tests/ui/ref_option/ref_option_traits.private.stderr b/tests/ui/ref_option/ref_option_traits.private.stderr new file mode 100644 index 0000000000000..36d0833af8a23 --- /dev/null +++ b/tests/ui/ref_option/ref_option_traits.private.stderr @@ -0,0 +1,21 @@ +error: it is more idiomatic to use `Option<&T>` instead of `&Option` + --> tests/ui/ref_option/ref_option_traits.rs:15:5 + | +LL | fn trait_opt(&self, a: &Option); + | ^^^^^^^^^^^^^^^^^^^^^^^---------------^^ + | | + | help: change this to: `Option<&String>` + | + = note: `-D clippy::ref-option` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::ref_option)]` + +error: it is more idiomatic to use `Option<&T>` instead of `&Option` + --> tests/ui/ref_option/ref_option_traits.rs:16:5 + | +LL | fn trait_ret(&self) -> &Option; + | ^^^^^^^^^^^^^^^^^^^^^^^---------------^ + | | + | help: change this to: `Option<&String>` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/ref_option/ref_option_traits.rs b/tests/ui/ref_option/ref_option_traits.rs new file mode 100644 index 0000000000000..5d5f113c83db5 --- /dev/null +++ b/tests/ui/ref_option/ref_option_traits.rs @@ -0,0 +1,37 @@ +//@no-rustfix: fixes are only done to traits, not the impls +//@revisions: private all +//@[private] rustc-env:CLIPPY_CONF_DIR=tests/ui/ref_option/private +//@[all] rustc-env:CLIPPY_CONF_DIR=tests/ui/ref_option/all + +#![allow(unused, clippy::all)] +#![warn(clippy::ref_option)] + +pub trait PubTrait { + fn pub_trait_opt(&self, a: &Option>); + fn pub_trait_ret(&self) -> &Option>; +} + +trait PrivateTrait { + fn trait_opt(&self, a: &Option); + fn trait_ret(&self) -> &Option; +} + +pub struct PubStruct; + +impl PubTrait for PubStruct { + fn pub_trait_opt(&self, a: &Option>) {} + fn pub_trait_ret(&self) -> &Option> { + panic!() + } +} + +struct PrivateStruct; + +impl PrivateTrait for PrivateStruct { + fn trait_opt(&self, a: &Option) {} + fn trait_ret(&self) -> &Option { + panic!() + } +} + +fn main() {} From 38295a0d8acf04a55567cbc1bfa3eddd45e5e702 Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Sat, 28 Sep 2024 13:01:51 -0400 Subject: [PATCH 038/120] update docs --- clippy_lints/src/functions/mod.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/functions/mod.rs b/clippy_lints/src/functions/mod.rs index cfa63d3befce4..50b3d039ed3ad 100644 --- a/clippy_lints/src/functions/mod.rs +++ b/clippy_lints/src/functions/mod.rs @@ -410,8 +410,9 @@ declare_clippy_lint! { /// `&Option` in a function signature breaks encapsulation because the caller must own T /// and move it into an Option to call with it. When returned, the owner must internally store /// it as `Option` in order to return it. - /// At a lower level `&Option` points to memory that has `presence` bit flag + value, - /// whereas `Option<&T>` is always optimized to a single pointer. + /// At a lower level, `&Option` points to memory with the `presence` bit flag plus the `T` value, + /// whereas `Option<&T>` is usually [optimized](https://doc.rust-lang.org/1.81.0/std/option/index.html#representation) + /// to a single pointer, so it may be more optimal. /// /// See this [YouTube video](https://www.youtube.com/watch?v=6c7pZYP_iIE) by /// Logan Smith for an in-depth explanation of why this is important. From 10e02cf8e3e8b46d3bc7f8194c9224755604eac4 Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Sat, 28 Sep 2024 13:17:29 -0400 Subject: [PATCH 039/120] suggest &str --- clippy_lints/src/functions/mod.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/functions/mod.rs b/clippy_lints/src/functions/mod.rs index 50b3d039ed3ad..203c3ce9eec04 100644 --- a/clippy_lints/src/functions/mod.rs +++ b/clippy_lints/src/functions/mod.rs @@ -432,8 +432,10 @@ declare_clippy_lint! { /// ``` /// Use instead: /// ```no_run - /// // caller should use foo(opt.as_ref()) - /// fn foo(a: Option<&String>) {} + /// // caller should use `foo1(opt.as_ref())` + /// fn foo1(a: Option<&String>) {} + /// // better yet, use string slice `foo2(opt.as_deref())` + /// fn foo2(a: Option<&str>) {} /// # struct Unit {} /// # impl Unit { /// fn bar(&self) -> Option<&String> { None } From b3cf448761eb016a5c20470a757f0d5358e30e35 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Sat, 28 Sep 2024 16:30:31 -0400 Subject: [PATCH 040/120] Adjust comment --- clippy_lints/src/lifetimes.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index a4555e003c46d..37d244ff53d30 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -628,8 +628,8 @@ fn report_extra_impl_lifetimes<'tcx>(cx: &LateContext<'tcx>, impl_: &'tcx Impl<' // An `impl` lifetime is elidable if it satisfies the following conditions: // - It is used exactly once. -// - That single use is not in a `GenericArg` in a `WherePredicate`. (Note that a `GenericArg` is -// different from a `GenericParam`.) +// - That single use is not in `GenericArgs` in a `WherePredicate`. (Note that `GenericArgs` are +// different from `GenericParam`s.) fn report_elidable_impl_lifetimes<'tcx>( cx: &LateContext<'tcx>, impl_: &'tcx Impl<'_>, From f7d5d9d892ea8affb3e3fb4e30241300ba7b63f7 Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Fri, 27 Sep 2024 23:48:00 -0400 Subject: [PATCH 041/120] Convert `&Option` to `Option<&T>` --- clippy_lints/src/cargo/common_metadata.rs | 16 ++-- clippy_lints/src/functions/mod.rs | 2 +- clippy_lints/src/unnested_or_patterns.rs | 11 ++- clippy_utils/src/ast_utils.rs | 109 +++++++++++++--------- clippy_utils/src/hir_utils.rs | 49 ++++++---- lintcheck/src/main.rs | 4 +- src/driver.rs | 14 +-- 7 files changed, 122 insertions(+), 83 deletions(-) diff --git a/clippy_lints/src/cargo/common_metadata.rs b/clippy_lints/src/cargo/common_metadata.rs index fed0aa8b2758b..6714c053913c9 100644 --- a/clippy_lints/src/cargo/common_metadata.rs +++ b/clippy_lints/src/cargo/common_metadata.rs @@ -10,27 +10,27 @@ pub(super) fn check(cx: &LateContext<'_>, metadata: &Metadata, ignore_publish: b // only run the lint if publish is `None` (`publish = true` or skipped entirely) // or if the vector isn't empty (`publish = ["something"]`) if package.publish.as_ref().filter(|publish| publish.is_empty()).is_none() || ignore_publish { - if is_empty_str(&package.description) { + if is_empty_str(package.description.as_ref()) { missing_warning(cx, package, "package.description"); } - if is_empty_str(&package.license) && is_empty_str(&package.license_file) { + if is_empty_str(package.license.as_ref()) && is_empty_str(package.license_file.as_ref()) { missing_warning(cx, package, "either package.license or package.license_file"); } - if is_empty_str(&package.repository) { + if is_empty_str(package.repository.as_ref()) { missing_warning(cx, package, "package.repository"); } - if is_empty_str(&package.readme) { + if is_empty_str(package.readme.as_ref()) { missing_warning(cx, package, "package.readme"); } - if is_empty_vec(&package.keywords) { + if is_empty_vec(package.keywords.as_ref()) { missing_warning(cx, package, "package.keywords"); } - if is_empty_vec(&package.categories) { + if is_empty_vec(package.categories.as_ref()) { missing_warning(cx, package, "package.categories"); } } @@ -42,8 +42,8 @@ fn missing_warning(cx: &LateContext<'_>, package: &cargo_metadata::Package, fiel span_lint(cx, CARGO_COMMON_METADATA, DUMMY_SP, message); } -fn is_empty_str>(value: &Option) -> bool { - value.as_ref().map_or(true, |s| s.as_ref().is_empty()) +fn is_empty_str>(value: Option<&T>) -> bool { + value.map_or(true, |s| s.as_ref().is_empty()) } fn is_empty_vec(value: &[String]) -> bool { diff --git a/clippy_lints/src/functions/mod.rs b/clippy_lints/src/functions/mod.rs index 203c3ce9eec04..91d73c2a9c962 100644 --- a/clippy_lints/src/functions/mod.rs +++ b/clippy_lints/src/functions/mod.rs @@ -443,7 +443,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.82.0"] pub REF_OPTION, - nursery, + pedantic, "function signature uses `&Option` instead of `Option<&T>`" } diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs index 104be63bb1579..c7c837de505e4 100644 --- a/clippy_lints/src/unnested_or_patterns.rs +++ b/clippy_lints/src/unnested_or_patterns.rs @@ -275,12 +275,15 @@ fn transform_with_focus_on_idx(alternatives: &mut ThinVec>, focus_idx: us |k, ps1, idx| matches!( k, TupleStruct(qself2, path2, ps2) - if eq_maybe_qself(qself1, qself2) && eq_path(path1, path2) && eq_pre_post(ps1, ps2, idx) + if eq_maybe_qself(qself1.as_ref(), qself2.as_ref()) + && eq_path(path1, path2) && eq_pre_post(ps1, ps2, idx) ), |k| always_pat!(k, TupleStruct(_, _, ps) => ps), ), // Transform a record pattern `S { fp_0, ..., fp_n }`. - Struct(qself1, path1, fps1, rest1) => extend_with_struct_pat(qself1, path1, fps1, *rest1, start, alternatives), + Struct(qself1, path1, fps1, rest1) => { + extend_with_struct_pat(qself1.as_ref(), path1, fps1, *rest1, start, alternatives) + }, }; alternatives[focus_idx].kind = focus_kind; @@ -292,7 +295,7 @@ fn transform_with_focus_on_idx(alternatives: &mut ThinVec>, focus_idx: us /// So when we fixate on some `ident_k: pat_k`, we try to find `ident_k` in the other pattern /// and check that all `fp_i` where `i ∈ ((0...n) \ k)` between two patterns are equal. fn extend_with_struct_pat( - qself1: &Option>, + qself1: Option<&P>, path1: &ast::Path, fps1: &mut [ast::PatField], rest1: ast::PatFieldsRest, @@ -307,7 +310,7 @@ fn extend_with_struct_pat( |k| { matches!(k, Struct(qself2, path2, fps2, rest2) if rest1 == *rest2 // If one struct pattern has `..` so must the other. - && eq_maybe_qself(qself1, qself2) + && eq_maybe_qself(qself1, qself2.as_ref()) && eq_path(path1, path2) && fps1.len() == fps2.len() && fps1.iter().enumerate().all(|(idx_1, fp1)| { diff --git a/clippy_utils/src/ast_utils.rs b/clippy_utils/src/ast_utils.rs index 49323492e124d..68f74e52ed7b7 100644 --- a/clippy_utils/src/ast_utils.rs +++ b/clippy_utils/src/ast_utils.rs @@ -37,20 +37,27 @@ pub fn eq_pat(l: &Pat, r: &Pat) -> bool { (_, Paren(r)) => eq_pat(l, r), (Wild, Wild) | (Rest, Rest) => true, (Lit(l), Lit(r)) => eq_expr(l, r), - (Ident(b1, i1, s1), Ident(b2, i2, s2)) => b1 == b2 && eq_id(*i1, *i2) && both(s1, s2, |l, r| eq_pat(l, r)), + (Ident(b1, i1, s1), Ident(b2, i2, s2)) => { + b1 == b2 && eq_id(*i1, *i2) && both(s1.as_deref(), s2.as_deref(), eq_pat) + }, (Range(lf, lt, le), Range(rf, rt, re)) => { - eq_expr_opt(lf, rf) && eq_expr_opt(lt, rt) && eq_range_end(&le.node, &re.node) + eq_expr_opt(lf.as_ref(), rf.as_ref()) + && eq_expr_opt(lt.as_ref(), rt.as_ref()) + && eq_range_end(&le.node, &re.node) }, (Box(l), Box(r)) | (Ref(l, Mutability::Not), Ref(r, Mutability::Not)) | (Ref(l, Mutability::Mut), Ref(r, Mutability::Mut)) => eq_pat(l, r), (Tuple(l), Tuple(r)) | (Slice(l), Slice(r)) => over(l, r, |l, r| eq_pat(l, r)), - (Path(lq, lp), Path(rq, rp)) => both(lq, rq, eq_qself) && eq_path(lp, rp), + (Path(lq, lp), Path(rq, rp)) => both(lq.as_ref(), rq.as_ref(), eq_qself) && eq_path(lp, rp), (TupleStruct(lqself, lp, lfs), TupleStruct(rqself, rp, rfs)) => { - eq_maybe_qself(lqself, rqself) && eq_path(lp, rp) && over(lfs, rfs, |l, r| eq_pat(l, r)) + eq_maybe_qself(lqself.as_ref(), rqself.as_ref()) && eq_path(lp, rp) && over(lfs, rfs, |l, r| eq_pat(l, r)) }, (Struct(lqself, lp, lfs, lr), Struct(rqself, rp, rfs, rr)) => { - lr == rr && eq_maybe_qself(lqself, rqself) && eq_path(lp, rp) && unordered_over(lfs, rfs, eq_field_pat) + lr == rr + && eq_maybe_qself(lqself.as_ref(), rqself.as_ref()) + && eq_path(lp, rp) + && unordered_over(lfs, rfs, eq_field_pat) }, (Or(ls), Or(rs)) => unordered_over(ls, rs, |l, r| eq_pat(l, r)), (MacCall(l), MacCall(r)) => eq_mac_call(l, r), @@ -79,7 +86,7 @@ pub fn eq_qself(l: &P, r: &P) -> bool { l.position == r.position && eq_ty(&l.ty, &r.ty) } -pub fn eq_maybe_qself(l: &Option>, r: &Option>) -> bool { +pub fn eq_maybe_qself(l: Option<&P>, r: Option<&P>) -> bool { match (l, r) { (Some(l), Some(r)) => eq_qself(l, r), (None, None) => true, @@ -92,7 +99,7 @@ pub fn eq_path(l: &Path, r: &Path) -> bool { } pub fn eq_path_seg(l: &PathSegment, r: &PathSegment) -> bool { - eq_id(l.ident, r.ident) && both(&l.args, &r.args, |l, r| eq_generic_args(l, r)) + eq_id(l.ident, r.ident) && both(l.args.as_ref(), r.args.as_ref(), |l, r| eq_generic_args(l, r)) } pub fn eq_generic_args(l: &GenericArgs, r: &GenericArgs) -> bool { @@ -122,7 +129,7 @@ pub fn eq_generic_arg(l: &GenericArg, r: &GenericArg) -> bool { } } -pub fn eq_expr_opt(l: &Option>, r: &Option>) -> bool { +pub fn eq_expr_opt(l: Option<&P>, r: Option<&P>) -> bool { both(l, r, |l, r| eq_expr(l, r)) } @@ -169,8 +176,12 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool { (Lit(l), Lit(r)) => l == r, (Cast(l, lt), Cast(r, rt)) | (Type(l, lt), Type(r, rt)) => eq_expr(l, r) && eq_ty(lt, rt), (Let(lp, le, _, _), Let(rp, re, _, _)) => eq_pat(lp, rp) && eq_expr(le, re), - (If(lc, lt, le), If(rc, rt, re)) => eq_expr(lc, rc) && eq_block(lt, rt) && eq_expr_opt(le, re), - (While(lc, lt, ll), While(rc, rt, rl)) => eq_label(ll, rl) && eq_expr(lc, rc) && eq_block(lt, rt), + (If(lc, lt, le), If(rc, rt, re)) => { + eq_expr(lc, rc) && eq_block(lt, rt) && eq_expr_opt(le.as_ref(), re.as_ref()) + }, + (While(lc, lt, ll), While(rc, rt, rl)) => { + eq_label(ll.as_ref(), rl.as_ref()) && eq_expr(lc, rc) && eq_block(lt, rt) + }, ( ForLoop { pat: lp, @@ -186,13 +197,13 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool { label: rl, kind: rk, }, - ) => eq_label(ll, rl) && eq_pat(lp, rp) && eq_expr(li, ri) && eq_block(lt, rt) && lk == rk, - (Loop(lt, ll, _), Loop(rt, rl, _)) => eq_label(ll, rl) && eq_block(lt, rt), - (Block(lb, ll), Block(rb, rl)) => eq_label(ll, rl) && eq_block(lb, rb), + ) => eq_label(ll.as_ref(), rl.as_ref()) && eq_pat(lp, rp) && eq_expr(li, ri) && eq_block(lt, rt) && lk == rk, + (Loop(lt, ll, _), Loop(rt, rl, _)) => eq_label(ll.as_ref(), rl.as_ref()) && eq_block(lt, rt), + (Block(lb, ll), Block(rb, rl)) => eq_label(ll.as_ref(), rl.as_ref()) && eq_block(lb, rb), (TryBlock(l), TryBlock(r)) => eq_block(l, r), - (Yield(l), Yield(r)) | (Ret(l), Ret(r)) => eq_expr_opt(l, r), - (Break(ll, le), Break(rl, re)) => eq_label(ll, rl) && eq_expr_opt(le, re), - (Continue(ll), Continue(rl)) => eq_label(ll, rl), + (Yield(l), Yield(r)) | (Ret(l), Ret(r)) => eq_expr_opt(l.as_ref(), r.as_ref()), + (Break(ll, le), Break(rl, re)) => eq_label(ll.as_ref(), rl.as_ref()) && eq_expr_opt(le.as_ref(), re.as_ref()), + (Continue(ll), Continue(rl)) => eq_label(ll.as_ref(), rl.as_ref()), (Assign(l1, l2, _), Assign(r1, r2, _)) | (Index(l1, l2, _), Index(r1, r2, _)) => { eq_expr(l1, r1) && eq_expr(l2, r2) }, @@ -227,12 +238,14 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool { && eq_expr(le, re) }, (Gen(lc, lb, lk, _), Gen(rc, rb, rk, _)) => lc == rc && eq_block(lb, rb) && lk == rk, - (Range(lf, lt, ll), Range(rf, rt, rl)) => ll == rl && eq_expr_opt(lf, rf) && eq_expr_opt(lt, rt), + (Range(lf, lt, ll), Range(rf, rt, rl)) => { + ll == rl && eq_expr_opt(lf.as_ref(), rf.as_ref()) && eq_expr_opt(lt.as_ref(), rt.as_ref()) + }, (AddrOf(lbk, lm, le), AddrOf(rbk, rm, re)) => lbk == rbk && lm == rm && eq_expr(le, re), - (Path(lq, lp), Path(rq, rp)) => both(lq, rq, eq_qself) && eq_path(lp, rp), + (Path(lq, lp), Path(rq, rp)) => both(lq.as_ref(), rq.as_ref(), eq_qself) && eq_path(lp, rp), (MacCall(l), MacCall(r)) => eq_mac_call(l, r), (Struct(lse), Struct(rse)) => { - eq_maybe_qself(&lse.qself, &rse.qself) + eq_maybe_qself(lse.qself.as_ref(), rse.qself.as_ref()) && eq_path(&lse.path, &rse.path) && eq_struct_rest(&lse.rest, &rse.rest) && unordered_over(&lse.fields, &rse.fields, eq_field) @@ -264,12 +277,12 @@ pub fn eq_field(l: &ExprField, r: &ExprField) -> bool { pub fn eq_arm(l: &Arm, r: &Arm) -> bool { l.is_placeholder == r.is_placeholder && eq_pat(&l.pat, &r.pat) - && eq_expr_opt(&l.body, &r.body) - && eq_expr_opt(&l.guard, &r.guard) + && eq_expr_opt(l.body.as_ref(), r.body.as_ref()) + && eq_expr_opt(l.guard.as_ref(), r.guard.as_ref()) && over(&l.attrs, &r.attrs, eq_attr) } -pub fn eq_label(l: &Option: Unsize>` - // requires that `A = B`; we don't allow *upcasting* objects - // between the same trait with different args. If we, for - // some reason, were to relax the `Unsize` trait, it could become - // unsound, so let's assert here that the trait refs are *equal*. - // - // We can use `assert_eq` because the binders should have been anonymized, - // and because higher-ranked equality now requires the binders are equal. - debug_assert_eq!( - data_a.principal(), - data_b.principal(), + debug_assert!( + validate_trivial_unsize(fx.tcx, data_a, data_b), "NOP unsize vtable changed principal trait ref: {data_a} -> {data_b}" ); return old_info; diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index 81590674ec7c1..346f07dfa0a62 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -34,6 +34,7 @@ rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_symbol_mangling = { path = "../rustc_symbol_mangling" } rustc_target = { path = "../rustc_target" } +rustc_trait_selection = { path = "../rustc_trait_selection" } rustc_type_ir = { path = "../rustc_type_ir" } serde_json = "1.0.59" smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 5c67600e4eec1..1d39125498335 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -27,6 +27,8 @@ use rustc_session::config::{self, CrateType, EntryFnType, OptLevel, OutputType}; use rustc_span::symbol::sym; use rustc_span::{DUMMY_SP, Symbol}; use rustc_target::abi::FIRST_VARIANT; +use rustc_trait_selection::infer::TyCtxtInferExt; +use rustc_trait_selection::traits::{ObligationCause, ObligationCtxt}; use tracing::{debug, info}; use crate::assert_module_sources::CguReuse; @@ -101,6 +103,38 @@ pub fn compare_simd_types<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( bx.sext(cmp, ret_ty) } +/// Codegen takes advantage of the additional assumption, where if the +/// principal trait def id of what's being casted doesn't change, +/// then we don't need to adjust the vtable at all. This +/// corresponds to the fact that `dyn Tr: Unsize>` +/// requires that `A = B`; we don't allow *upcasting* objects +/// between the same trait with different args. If we, for +/// some reason, were to relax the `Unsize` trait, it could become +/// unsound, so let's validate here that the trait refs are subtypes. +pub fn validate_trivial_unsize<'tcx>( + tcx: TyCtxt<'tcx>, + data_a: &'tcx ty::List>, + data_b: &'tcx ty::List>, +) -> bool { + match (data_a.principal(), data_b.principal()) { + (Some(principal_a), Some(principal_b)) => { + let infcx = tcx.infer_ctxt().build(); + let ocx = ObligationCtxt::new(&infcx); + let Ok(()) = ocx.sub( + &ObligationCause::dummy(), + ty::ParamEnv::reveal_all(), + principal_a, + principal_b, + ) else { + return false; + }; + ocx.select_all_or_error().is_empty() + } + (None, None) => true, + _ => false, + } +} + /// Retrieves the information we are losing (making dynamic) in an unsizing /// adjustment. /// @@ -133,12 +167,8 @@ fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( // between the same trait with different args. If we, for // some reason, were to relax the `Unsize` trait, it could become // unsound, so let's assert here that the trait refs are *equal*. - // - // We can use `assert_eq` because the binders should have been anonymized, - // and because higher-ranked equality now requires the binders are equal. - debug_assert_eq!( - data_a.principal(), - data_b.principal(), + debug_assert!( + validate_trivial_unsize(cx.tcx(), data_a, data_b), "NOP unsize vtable changed principal trait ref: {data_a} -> {data_b}" ); diff --git a/tests/ui/codegen/sub-principals-in-codegen.rs b/tests/ui/codegen/sub-principals-in-codegen.rs new file mode 100644 index 0000000000000..178c10da5968f --- /dev/null +++ b/tests/ui/codegen/sub-principals-in-codegen.rs @@ -0,0 +1,8 @@ +//@ build-pass + +// Regression test for an overly aggressive assertion in #130855. + +fn main() { + let subtype: &(dyn for<'a> Fn(&'a i32) -> &'a i32) = &|x| x; + let supertype: &(dyn Fn(&'static i32) -> &'static i32) = subtype; +} From cbb5047d35d76cd34ccf238220ccc70316f6ab77 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 30 Sep 2024 12:42:29 -0400 Subject: [PATCH 053/120] Relate binders explicitly, do a leak check too --- compiler/rustc_codegen_ssa/src/base.rs | 45 +++++++++++++------ .../src/traits/engine.rs | 17 ++++++- 2 files changed, 47 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 1d39125498335..d91c0f0790d9d 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -27,7 +27,8 @@ use rustc_session::config::{self, CrateType, EntryFnType, OptLevel, OutputType}; use rustc_span::symbol::sym; use rustc_span::{DUMMY_SP, Symbol}; use rustc_target::abi::FIRST_VARIANT; -use rustc_trait_selection::infer::TyCtxtInferExt; +use rustc_trait_selection::infer::at::ToTrace; +use rustc_trait_selection::infer::{BoundRegionConversionTime, TyCtxtInferExt}; use rustc_trait_selection::traits::{ObligationCause, ObligationCtxt}; use tracing::{debug, info}; @@ -113,22 +114,38 @@ pub fn compare_simd_types<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( /// unsound, so let's validate here that the trait refs are subtypes. pub fn validate_trivial_unsize<'tcx>( tcx: TyCtxt<'tcx>, - data_a: &'tcx ty::List>, - data_b: &'tcx ty::List>, + source_data: &'tcx ty::List>, + target_data: &'tcx ty::List>, ) -> bool { - match (data_a.principal(), data_b.principal()) { - (Some(principal_a), Some(principal_b)) => { + match (source_data.principal(), target_data.principal()) { + (Some(hr_source_principal), Some(hr_target_principal)) => { let infcx = tcx.infer_ctxt().build(); + let universe = infcx.universe(); let ocx = ObligationCtxt::new(&infcx); - let Ok(()) = ocx.sub( - &ObligationCause::dummy(), - ty::ParamEnv::reveal_all(), - principal_a, - principal_b, - ) else { - return false; - }; - ocx.select_all_or_error().is_empty() + infcx.enter_forall(hr_target_principal, |target_principal| { + let source_principal = infcx.instantiate_binder_with_fresh_vars( + DUMMY_SP, + BoundRegionConversionTime::HigherRankedType, + hr_source_principal, + ); + let Ok(()) = ocx.eq_trace( + &ObligationCause::dummy(), + ty::ParamEnv::reveal_all(), + ToTrace::to_trace( + &ObligationCause::dummy(), + hr_target_principal, + hr_source_principal, + ), + target_principal, + source_principal, + ) else { + return false; + }; + if !ocx.select_all_or_error().is_empty() { + return false; + } + infcx.leak_check(universe, None).is_ok() + }) } (None, None) => true, _ => false, diff --git a/compiler/rustc_trait_selection/src/traits/engine.rs b/compiler/rustc_trait_selection/src/traits/engine.rs index de1d4ef15aced..d562692c1a865 100644 --- a/compiler/rustc_trait_selection/src/traits/engine.rs +++ b/compiler/rustc_trait_selection/src/traits/engine.rs @@ -9,12 +9,13 @@ use rustc_infer::infer::canonical::{ Canonical, CanonicalQueryResponse, CanonicalVarValues, QueryResponse, }; use rustc_infer::infer::outlives::env::OutlivesEnvironment; -use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk, RegionResolutionError}; +use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk, RegionResolutionError, TypeTrace}; use rustc_macros::extension; use rustc_middle::arena::ArenaAllocatable; use rustc_middle::traits::query::NoSolution; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, Upcast, Variance}; +use rustc_type_ir::relate::Relate; use super::{FromSolverError, FulfillmentContext, ScrubbedTraitError, TraitEngine}; use crate::error_reporting::InferCtxtErrorExt; @@ -133,6 +134,20 @@ where .map(|infer_ok| self.register_infer_ok_obligations(infer_ok)) } + pub fn eq_trace>>( + &self, + cause: &ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + trace: TypeTrace<'tcx>, + expected: T, + actual: T, + ) -> Result<(), TypeError<'tcx>> { + self.infcx + .at(cause, param_env) + .eq_trace(DefineOpaqueTypes::Yes, trace, expected, actual) + .map(|infer_ok| self.register_infer_ok_obligations(infer_ok)) + } + /// Checks whether `expected` is a subtype of `actual`: `expected <: actual`. pub fn sub>( &self, From af3f212453f667981bb07c0029f86bc607f3baa9 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 29 Sep 2024 17:51:38 -0400 Subject: [PATCH 054/120] Instantiate binders in supertrait_vtable_slot --- .../src/traits/vtable.rs | 40 ++++++++++++++----- .../higher-ranked-upcasting-ok.rs | 15 +++---- 2 files changed, 38 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs index a2760fe6049b0..5aed55ef2d27d 100644 --- a/compiler/rustc_trait_selection/src/traits/vtable.rs +++ b/compiler/rustc_trait_selection/src/traits/vtable.rs @@ -2,6 +2,8 @@ use std::fmt::Debug; use std::ops::ControlFlow; use rustc_hir::def_id::DefId; +use rustc_infer::infer::{BoundRegionConversionTime, TyCtxtInferExt}; +use rustc_infer::traits::ObligationCause; use rustc_infer::traits::util::PredicateSet; use rustc_middle::bug; use rustc_middle::query::Providers; @@ -13,7 +15,7 @@ use smallvec::{SmallVec, smallvec}; use tracing::debug; use crate::errors::DumpVTableEntries; -use crate::traits::{impossible_predicates, is_vtable_safe_method}; +use crate::traits::{ObligationCtxt, impossible_predicates, is_vtable_safe_method}; #[derive(Clone, Debug)] pub enum VtblSegment<'tcx> { @@ -383,17 +385,37 @@ pub(crate) fn supertrait_vtable_slot<'tcx>( let ty::Dynamic(target, _, _) = *target.kind() else { bug!(); }; - let target_principal = tcx - .normalize_erasing_regions(ty::ParamEnv::reveal_all(), target.principal()?) - .with_self_ty(tcx, tcx.types.trait_object_dummy_self); + let target_principal = target.principal()?.with_self_ty(tcx, tcx.types.trait_object_dummy_self); // Given that we have a target principal, it is a bug for there not to be a source principal. let ty::Dynamic(source, _, _) = *source.kind() else { bug!(); }; - let source_principal = tcx - .normalize_erasing_regions(ty::ParamEnv::reveal_all(), source.principal().unwrap()) - .with_self_ty(tcx, tcx.types.trait_object_dummy_self); + let source_principal = + source.principal().unwrap().with_self_ty(tcx, tcx.types.trait_object_dummy_self); + + let infcx = tcx.infer_ctxt().build(); + let param_env = ty::ParamEnv::reveal_all(); + let trait_refs_are_compatible = + |source: ty::PolyTraitRef<'tcx>, target: ty::PolyTraitRef<'tcx>| { + infcx.probe(|_| { + let ocx = ObligationCtxt::new(&infcx); + let source = ocx.normalize(&ObligationCause::dummy(), param_env, source); + let target = ocx.normalize(&ObligationCause::dummy(), param_env, target); + infcx.enter_forall(target, |target| { + let source = infcx.instantiate_binder_with_fresh_vars( + DUMMY_SP, + BoundRegionConversionTime::HigherRankedType, + source, + ); + let Ok(()) = ocx.eq(&ObligationCause::dummy(), param_env, target, source) + else { + return false; + }; + ocx.select_all_or_error().is_empty() + }) + }) + }; let vtable_segment_callback = { let mut vptr_offset = 0; @@ -404,9 +426,7 @@ pub(crate) fn supertrait_vtable_slot<'tcx>( } VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => { vptr_offset += tcx.own_existential_vtable_entries(trait_ref.def_id()).len(); - if tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), trait_ref) - == target_principal - { + if trait_refs_are_compatible(trait_ref, target_principal) { if emit_vptr { return ControlFlow::Break(Some(vptr_offset)); } else { diff --git a/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ok.rs b/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ok.rs index 7f793e1269fd7..c4c070e49fdb7 100644 --- a/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ok.rs +++ b/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ok.rs @@ -1,21 +1,22 @@ //@ revisions: current next //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver -//@ check-pass +//@ build-pass -// We should be able to instantiate a binder during trait upcasting. -// This test could be `check-pass`, but we should make sure that we -// do so in both trait solvers. +// Check that we are able to instantiate a binder during trait upcasting, +// and that it doesn't cause any issues with codegen either. #![feature(trait_upcasting)] trait Supertrait<'a, 'b> {} trait Subtrait<'a, 'b>: Supertrait<'a, 'b> {} -impl<'a> Supertrait<'a, 'a> for () {} -impl<'a> Subtrait<'a, 'a> for () {} +impl Supertrait<'_, '_> for () {} +impl Subtrait<'_, '_> for () {} fn ok(x: &dyn for<'a, 'b> Subtrait<'a, 'b>) -> &dyn for<'a> Supertrait<'a, 'a> { x } -fn main() {} +fn main() { + ok(&()); +} From d87e0ca497e70fe1c3112de0dce87fbd777f6451 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 30 Sep 2024 13:14:44 -0400 Subject: [PATCH 055/120] Extract trait_refs_are_compatible, make it instantiate binders --- .../src/traits/engine.rs | 17 ++++- .../src/traits/vtable.rs | 75 ++++++++++++------- 2 files changed, 64 insertions(+), 28 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/engine.rs b/compiler/rustc_trait_selection/src/traits/engine.rs index de1d4ef15aced..d562692c1a865 100644 --- a/compiler/rustc_trait_selection/src/traits/engine.rs +++ b/compiler/rustc_trait_selection/src/traits/engine.rs @@ -9,12 +9,13 @@ use rustc_infer::infer::canonical::{ Canonical, CanonicalQueryResponse, CanonicalVarValues, QueryResponse, }; use rustc_infer::infer::outlives::env::OutlivesEnvironment; -use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk, RegionResolutionError}; +use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk, RegionResolutionError, TypeTrace}; use rustc_macros::extension; use rustc_middle::arena::ArenaAllocatable; use rustc_middle::traits::query::NoSolution; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, Upcast, Variance}; +use rustc_type_ir::relate::Relate; use super::{FromSolverError, FulfillmentContext, ScrubbedTraitError, TraitEngine}; use crate::error_reporting::InferCtxtErrorExt; @@ -133,6 +134,20 @@ where .map(|infer_ok| self.register_infer_ok_obligations(infer_ok)) } + pub fn eq_trace>>( + &self, + cause: &ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + trace: TypeTrace<'tcx>, + expected: T, + actual: T, + ) -> Result<(), TypeError<'tcx>> { + self.infcx + .at(cause, param_env) + .eq_trace(DefineOpaqueTypes::Yes, trace, expected, actual) + .map(|infer_ok| self.register_infer_ok_obligations(infer_ok)) + } + /// Checks whether `expected` is a subtype of `actual`: `expected <: actual`. pub fn sub>( &self, diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs index 5aed55ef2d27d..4f13524706b2d 100644 --- a/compiler/rustc_trait_selection/src/traits/vtable.rs +++ b/compiler/rustc_trait_selection/src/traits/vtable.rs @@ -2,6 +2,7 @@ use std::fmt::Debug; use std::ops::ControlFlow; use rustc_hir::def_id::DefId; +use rustc_infer::infer::at::ToTrace; use rustc_infer::infer::{BoundRegionConversionTime, TyCtxtInferExt}; use rustc_infer::traits::ObligationCause; use rustc_infer::traits::util::PredicateSet; @@ -24,6 +25,8 @@ pub enum VtblSegment<'tcx> { } /// Prepare the segments for a vtable +// FIXME: This should take a `PolyExistentialTraitRef`, since we don't care +// about our `Self` type here. pub fn prepare_vtable_segments<'tcx, T>( tcx: TyCtxt<'tcx>, trait_ref: ty::PolyTraitRef<'tcx>, @@ -385,7 +388,7 @@ pub(crate) fn supertrait_vtable_slot<'tcx>( let ty::Dynamic(target, _, _) = *target.kind() else { bug!(); }; - let target_principal = target.principal()?.with_self_ty(tcx, tcx.types.trait_object_dummy_self); + let target_principal = target.principal()?; // Given that we have a target principal, it is a bug for there not to be a source principal. let ty::Dynamic(source, _, _) = *source.kind() else { @@ -394,29 +397,6 @@ pub(crate) fn supertrait_vtable_slot<'tcx>( let source_principal = source.principal().unwrap().with_self_ty(tcx, tcx.types.trait_object_dummy_self); - let infcx = tcx.infer_ctxt().build(); - let param_env = ty::ParamEnv::reveal_all(); - let trait_refs_are_compatible = - |source: ty::PolyTraitRef<'tcx>, target: ty::PolyTraitRef<'tcx>| { - infcx.probe(|_| { - let ocx = ObligationCtxt::new(&infcx); - let source = ocx.normalize(&ObligationCause::dummy(), param_env, source); - let target = ocx.normalize(&ObligationCause::dummy(), param_env, target); - infcx.enter_forall(target, |target| { - let source = infcx.instantiate_binder_with_fresh_vars( - DUMMY_SP, - BoundRegionConversionTime::HigherRankedType, - source, - ); - let Ok(()) = ocx.eq(&ObligationCause::dummy(), param_env, target, source) - else { - return false; - }; - ocx.select_all_or_error().is_empty() - }) - }) - }; - let vtable_segment_callback = { let mut vptr_offset = 0; move |segment| { @@ -424,9 +404,15 @@ pub(crate) fn supertrait_vtable_slot<'tcx>( VtblSegment::MetadataDSA => { vptr_offset += TyCtxt::COMMON_VTABLE_ENTRIES.len(); } - VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => { - vptr_offset += tcx.own_existential_vtable_entries(trait_ref.def_id()).len(); - if trait_refs_are_compatible(trait_ref, target_principal) { + VtblSegment::TraitOwnEntries { trait_ref: vtable_principal, emit_vptr } => { + vptr_offset += + tcx.own_existential_vtable_entries(vtable_principal.def_id()).len(); + if trait_refs_are_compatible( + tcx, + vtable_principal + .map_bound(|t| ty::ExistentialTraitRef::erase_self_ty(tcx, t)), + target_principal, + ) { if emit_vptr { return ControlFlow::Break(Some(vptr_offset)); } else { @@ -446,6 +432,41 @@ pub(crate) fn supertrait_vtable_slot<'tcx>( prepare_vtable_segments(tcx, source_principal, vtable_segment_callback).unwrap() } +fn trait_refs_are_compatible<'tcx>( + tcx: TyCtxt<'tcx>, + hr_vtable_principal: ty::PolyExistentialTraitRef<'tcx>, + hr_target_principal: ty::PolyExistentialTraitRef<'tcx>, +) -> bool { + if hr_vtable_principal.def_id() != hr_target_principal.def_id() { + return false; + } + + let infcx = tcx.infer_ctxt().build(); + let param_env = ty::ParamEnv::reveal_all(); + let ocx = ObligationCtxt::new(&infcx); + let hr_source_principal = + ocx.normalize(&ObligationCause::dummy(), param_env, hr_vtable_principal); + let hr_target_principal = + ocx.normalize(&ObligationCause::dummy(), param_env, hr_target_principal); + infcx.enter_forall(hr_target_principal, |target_principal| { + let source_principal = infcx.instantiate_binder_with_fresh_vars( + DUMMY_SP, + BoundRegionConversionTime::HigherRankedType, + hr_source_principal, + ); + let Ok(()) = ocx.eq_trace( + &ObligationCause::dummy(), + param_env, + ToTrace::to_trace(&ObligationCause::dummy(), hr_target_principal, hr_source_principal), + target_principal, + source_principal, + ) else { + return false; + }; + ocx.select_all_or_error().is_empty() + }) +} + pub(super) fn provide(providers: &mut Providers) { *providers = Providers { own_existential_vtable_entries, From 7c552d56b2234018e6aa729cda6bdfbb7d533d28 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 30 Sep 2024 13:17:31 -0400 Subject: [PATCH 056/120] Also fix first_method_vtable_slot --- .../src/traits/vtable.rs | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs index 4f13524706b2d..6e6f948a2cdc4 100644 --- a/compiler/rustc_trait_selection/src/traits/vtable.rs +++ b/compiler/rustc_trait_selection/src/traits/vtable.rs @@ -332,14 +332,10 @@ pub(crate) fn first_method_vtable_slot<'tcx>(tcx: TyCtxt<'tcx>, key: ty::TraitRe let ty::Dynamic(source, _, _) = *key.self_ty().kind() else { bug!(); }; - let source_principal = tcx - .normalize_erasing_regions(ty::ParamEnv::reveal_all(), source.principal().unwrap()) - .with_self_ty(tcx, tcx.types.trait_object_dummy_self); + let source_principal = + source.principal().unwrap().with_self_ty(tcx, tcx.types.trait_object_dummy_self); - let target_principal = tcx - .normalize_erasing_regions(ty::ParamEnv::reveal_all(), key) - // We don't care about the self type, since it will always be the same thing. - .with_self_ty(tcx, tcx.types.trait_object_dummy_self); + let target_principal = ty::Binder::dummy(ty::ExistentialTraitRef::erase_self_ty(tcx, key)); let vtable_segment_callback = { let mut vptr_offset = 0; @@ -348,15 +344,18 @@ pub(crate) fn first_method_vtable_slot<'tcx>(tcx: TyCtxt<'tcx>, key: ty::TraitRe VtblSegment::MetadataDSA => { vptr_offset += TyCtxt::COMMON_VTABLE_ENTRIES.len(); } - VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => { - if tcx - .normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), trait_ref) - == target_principal - { + VtblSegment::TraitOwnEntries { trait_ref: vtable_principal, emit_vptr } => { + if trait_refs_are_compatible( + tcx, + vtable_principal + .map_bound(|t| ty::ExistentialTraitRef::erase_self_ty(tcx, t)), + target_principal, + ) { return ControlFlow::Break(vptr_offset); } - vptr_offset += tcx.own_existential_vtable_entries(trait_ref.def_id()).len(); + vptr_offset += + tcx.own_existential_vtable_entries(vtable_principal.def_id()).len(); if emit_vptr { vptr_offset += 1; From eaaa94318b57e64a1a502a082aae2ed45e343af0 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Mon, 30 Sep 2024 13:26:02 -0400 Subject: [PATCH 057/120] Unpin `cc` and upgrade to the latest version `cc` was previously pinned because version 1.1.106 dropped support for Visual Studio 12 (2013), and we wanted to decouple that from the rest of the automated updates. As noted in [2], there is no longer anything indicating we support VS2013, so it should be okay to unpin it. `cc` 1.1.22 contains a fix that may help improve the high MSVC CI failure rate [3], so we also have motivation to update to that point. [1]: https://github.com/rust-lang/rust/issues/129307 [2]: https://github.com/rust-lang/rust/issues/129307#issuecomment-2383749868 [3]: https://github.com/rust-lang/rust/issues/127883 --- Cargo.lock | 7 +++++-- compiler/rustc_codegen_ssa/Cargo.toml | 2 +- compiler/rustc_llvm/Cargo.toml | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 582b5a763e6f7..70731b198fc96 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -393,9 +393,12 @@ version = "0.1.0" [[package]] name = "cc" -version = "1.0.105" +version = "1.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5208975e568d83b6b05cc0a063c8e7e9acc2b43bee6da15616a5b73e109d7437" +checksum = "3bbb537bb4a30b90362caddba8f360c0a56bc13d3a5570028e7197204cb54a17" +dependencies = [ + "shlex", +] [[package]] name = "cfg-if" diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index 81590674ec7c1..f316a0a6cfc89 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -8,7 +8,7 @@ edition = "2021" ar_archive_writer = "0.4.2" arrayvec = { version = "0.7", default-features = false } bitflags = "2.4.1" -cc = "=1.0.105" # FIXME(cc): pinned to keep support for VS2013 +cc = "1.1.23" either = "1.5.0" itertools = "0.12" jobserver = "0.1.28" diff --git a/compiler/rustc_llvm/Cargo.toml b/compiler/rustc_llvm/Cargo.toml index 1f74aaf9965a7..b29d6b79250f3 100644 --- a/compiler/rustc_llvm/Cargo.toml +++ b/compiler/rustc_llvm/Cargo.toml @@ -10,5 +10,5 @@ libc = "0.2.73" [build-dependencies] # tidy-alphabetical-start -cc = "=1.0.105" # FIXME(cc): pinned to keep support for VS2013 +cc = "1.1.23" # tidy-alphabetical-end From 041e76b7cd7c499837fcea63f7f780fdf774a1a5 Mon Sep 17 00:00:00 2001 From: Obei Sideg Date: Sat, 22 Jun 2024 22:17:57 +0300 Subject: [PATCH 058/120] Add multi-producer, multi-consumer channel (mpmc) --- library/std/src/lib.rs | 4 +- library/std/src/sync/mod.rs | 9 +- library/std/src/sync/mpmc/error.rs | 5 + library/std/src/sync/mpmc/mod.rs | 1035 ++++++++++++++++- library/std/src/sync/mpmc/tests.rs | 728 ++++++++++++ .../const-generics/issues/issue-82956.stderr | 2 +- tests/ui/issues/issue-12041.stderr | 2 +- tests/ui/lint/use_suggestion_json.stderr | 25 +- 8 files changed, 1758 insertions(+), 52 deletions(-) create mode 100644 library/std/src/sync/mpmc/tests.rs diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index b81e7c18abbb0..65a9aa66c7cc6 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -153,7 +153,7 @@ //! the [`io`], [`fs`], and [`net`] modules. //! //! The [`thread`] module contains Rust's threading abstractions. [`sync`] -//! contains further primitive shared memory types, including [`atomic`] and +//! contains further primitive shared memory types, including [`atomic`], [`mpmc`] and //! [`mpsc`], which contains the channel types for message passing. //! //! # Use before and after `main()` @@ -177,6 +177,7 @@ //! - after-main use of thread-locals, which also affects additional features: //! - [`thread::current()`] //! - [`thread::scope()`] +//! - [`sync::mpmc`] //! - [`sync::mpsc`] //! - before-main stdio file descriptors are not guaranteed to be open on unix platforms //! @@ -202,6 +203,7 @@ //! [`atomic`]: sync::atomic //! [`for`]: ../book/ch03-05-control-flow.html#looping-through-a-collection-with-for //! [`str`]: prim@str +//! [`mpmc`]: sync::mpmc //! [`mpsc`]: sync::mpsc //! [`std::cmp`]: cmp //! [`std::slice`]: mod@slice diff --git a/library/std/src/sync/mod.rs b/library/std/src/sync/mod.rs index 0fb8e669bf86e..0fb77331293fe 100644 --- a/library/std/src/sync/mod.rs +++ b/library/std/src/sync/mod.rs @@ -133,6 +133,11 @@ //! inter-thread synchronisation mechanism, at the cost of some //! extra memory. //! +//! - [`mpmc`]: Multi-producer, multi-consumer queues, used for +//! message-based communication. Can provide a lightweight +//! inter-thread synchronisation mechanism, at the cost of some +//! extra memory. +//! //! - [`Mutex`]: Mutual Exclusion mechanism, which ensures that at //! most one thread at a time is able to access some data. //! @@ -153,6 +158,7 @@ //! [`Arc`]: crate::sync::Arc //! [`Barrier`]: crate::sync::Barrier //! [`Condvar`]: crate::sync::Condvar +//! [`mpmc`]: crate::sync::mpmc //! [`mpsc`]: crate::sync::mpsc //! [`Mutex`]: crate::sync::Mutex //! [`Once`]: crate::sync::Once @@ -193,12 +199,13 @@ pub use self::rwlock::{MappedRwLockReadGuard, MappedRwLockWriteGuard}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}; +#[unstable(feature = "mpmc_channel", issue = "126840")] +pub mod mpmc; pub mod mpsc; mod barrier; mod condvar; mod lazy_lock; -mod mpmc; mod mutex; pub(crate) mod once; mod once_lock; diff --git a/library/std/src/sync/mpmc/error.rs b/library/std/src/sync/mpmc/error.rs index e3aec7e76232f..e34b56d08312b 100644 --- a/library/std/src/sync/mpmc/error.rs +++ b/library/std/src/sync/mpmc/error.rs @@ -7,6 +7,7 @@ use crate::{error, fmt}; /// /// [`send_timeout`]: super::Sender::send_timeout #[derive(PartialEq, Eq, Clone, Copy)] +#[unstable(feature = "mpmc_channel", issue = "126840")] pub enum SendTimeoutError { /// The message could not be sent because the channel is full and the operation timed out. /// @@ -18,12 +19,14 @@ pub enum SendTimeoutError { Disconnected(T), } +#[unstable(feature = "mpmc_channel", issue = "126840")] impl fmt::Debug for SendTimeoutError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { "SendTimeoutError(..)".fmt(f) } } +#[unstable(feature = "mpmc_channel", issue = "126840")] impl fmt::Display for SendTimeoutError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { @@ -33,8 +36,10 @@ impl fmt::Display for SendTimeoutError { } } +#[unstable(feature = "mpmc_channel", issue = "126840")] impl error::Error for SendTimeoutError {} +#[unstable(feature = "mpmc_channel", issue = "126840")] impl From> for SendTimeoutError { fn from(err: SendError) -> SendTimeoutError { match err { diff --git a/library/std/src/sync/mpmc/mod.rs b/library/std/src/sync/mpmc/mod.rs index c640e07348ea0..77a67f4fd386d 100644 --- a/library/std/src/sync/mpmc/mod.rs +++ b/library/std/src/sync/mpmc/mod.rs @@ -1,8 +1,112 @@ -//! Multi-producer multi-consumer channels. +//! Multi-producer, multi-consumer FIFO queue communication primitives. +//! +//! This module provides message-based communication over channels, concretely +//! defined by two types: +//! +//! * [`Sender`] +//! * [`Receiver`] +//! +//! [`Sender`]s are used to send data to a set of [`Receiver`]s. Both +//! sender and receiver are cloneable (multi-producer) such that many threads can send +//! simultaneously to receivers (multi-consumer). +//! +//! These channels come in two flavors: +//! +//! 1. An asynchronous, infinitely buffered channel. The [`channel`] function +//! will return a `(Sender, Receiver)` tuple where all sends will be +//! **asynchronous** (they never block). The channel conceptually has an +//! infinite buffer. +//! +//! 2. A synchronous, bounded channel. The [`sync_channel`] function will +//! return a `(SyncSender, Receiver)` tuple where the storage for pending +//! messages is a pre-allocated buffer of a fixed size. All sends will be +//! **synchronous** by blocking until there is buffer space available. Note +//! that a bound of 0 is allowed, causing the channel to become a "rendezvous" +//! channel where each sender atomically hands off a message to a receiver. +//! +//! [`send`]: Sender::send +//! +//! ## Disconnection +//! +//! The send and receive operations on channels will all return a [`Result`] +//! indicating whether the operation succeeded or not. An unsuccessful operation +//! is normally indicative of the other half of a channel having "hung up" by +//! being dropped in its corresponding thread. +//! +//! Once half of a channel has been deallocated, most operations can no longer +//! continue to make progress, so [`Err`] will be returned. Many applications +//! will continue to [`unwrap`] the results returned from this module, +//! instigating a propagation of failure among threads if one unexpectedly dies. +//! +//! [`unwrap`]: Result::unwrap +//! +//! # Examples +//! +//! Simple usage: +//! +//! ``` +//! #![feature(mpmc_channel)] +//! +//! use std::thread; +//! use std::sync::mpmc::channel; +//! +//! // Create a simple streaming channel +//! let (tx, rx) = channel(); +//! thread::spawn(move || { +//! tx.send(10).unwrap(); +//! }); +//! assert_eq!(rx.recv().unwrap(), 10); +//! ``` +//! +//! Shared usage: +//! +//! ``` +//! #![feature(mpmc_channel)] +//! +//! use std::thread; +//! use std::sync::mpmc::channel; +//! +//! // Create a shared channel that can be sent along from many threads +//! // where tx is the sending half (tx for transmission), and rx is the receiving +//! // half (rx for receiving). +//! let (tx, rx) = channel(); +//! for i in 0..10 { +//! let tx = tx.clone(); +//! thread::spawn(move || { +//! tx.send(i).unwrap(); +//! }); +//! } +//! +//! for _ in 0..5 { +//! let rx1 = rx.clone(); +//! let rx2 = rx.clone(); +//! thread::spawn(move || { +//! let j = rx1.recv().unwrap(); +//! assert!(0 <= j && j < 10); +//! }); +//! thread::spawn(move || { +//! let j = rx2.recv().unwrap(); +//! assert!(0 <= j && j < 10); +//! }); +//! } +//! ``` +//! +//! Propagating panics: +//! +//! ``` +//! #![feature(mpmc_channel)] +//! +//! use std::sync::mpmc::channel; +//! +//! // The call to recv() will return an error because the channel has already +//! // hung up (or been deallocated) +//! let (tx, rx) = channel::(); +//! drop(tx); +//! assert!(rx.recv().is_err()); +//! ``` -// This module is not currently exposed publicly, but is used -// as the implementation for the channels in `sync::mpsc`. The -// implementation comes from the crossbeam-channel crate: +// This module is used as the implementation for the channels in `sync::mpsc`. +// The implementation comes from the crossbeam-channel crate: // // Copyright (c) 2019 The Crossbeam Project Developers // @@ -46,9 +150,47 @@ use crate::fmt; use crate::panic::{RefUnwindSafe, UnwindSafe}; use crate::time::{Duration, Instant}; -/// Creates a channel of unbounded capacity. +/// Creates a new asynchronous channel, returning the sender/receiver halves. +/// All data sent on the [`Sender`] will become available on the [`Receiver`] in +/// the same order as it was sent, and no [`send`] will block the calling thread +/// (this channel has an "infinite buffer", unlike [`sync_channel`], which will +/// block after its buffer limit is reached). [`recv`] will block until a message +/// is available while there is at least one [`Sender`] alive (including clones). /// -/// This channel has a growable buffer that can hold any number of messages at a time. +/// The [`Sender`] can be cloned to [`send`] to the same channel multiple times. +/// The [`Receiver`] also can be cloned to have multi receivers. +/// +/// If the [`Receiver`] is disconnected while trying to [`send`] with the +/// [`Sender`], the [`send`] method will return a [`SendError`]. Similarly, if the +/// [`Sender`] is disconnected while trying to [`recv`], the [`recv`] method will +/// return a [`RecvError`]. +/// +/// [`send`]: Sender::send +/// [`recv`]: Receiver::recv +/// +/// # Examples +/// +/// ``` +/// #![feature(mpmc_channel)] +/// +/// use std::sync::mpmc::channel; +/// use std::thread; +/// +/// let (sender, receiver) = channel(); +/// +/// // Spawn off an expensive computation +/// thread::spawn(move || { +/// # fn expensive_computation() {} +/// sender.send(expensive_computation()).unwrap(); +/// }); +/// +/// // Do some useful work for awhile +/// +/// // Let's see what that answer was +/// println!("{:?}", receiver.recv().unwrap()); +/// ``` +#[must_use] +#[unstable(feature = "mpmc_channel", issue = "126840")] pub fn channel() -> (Sender, Receiver) { let (s, r) = counter::new(list::Channel::new()); let s = Sender { flavor: SenderFlavor::List(s) }; @@ -56,12 +198,50 @@ pub fn channel() -> (Sender, Receiver) { (s, r) } -/// Creates a channel of bounded capacity. +/// Creates a new synchronous, bounded channel. +/// All data sent on the [`Sender`] will become available on the [`Receiver`] +/// in the same order as it was sent. Like asynchronous [`channel`]s, the +/// [`Receiver`] will block until a message becomes available. `sync_channel` +/// differs greatly in the semantics of the sender, however. +/// +/// This channel has an internal buffer on which messages will be queued. +/// `bound` specifies the buffer size. When the internal buffer becomes full, +/// future sends will *block* waiting for the buffer to open up. Note that a +/// buffer size of 0 is valid, in which case this becomes "rendezvous channel" +/// where each [`send`] will not return until a [`recv`] is paired with it. +/// +/// The [`Sender`] can be cloned to [`send`] to the same channel multiple +/// times. The [`Receiver`] also can be cloned to have multi receivers. +/// +/// Like asynchronous channels, if the [`Receiver`] is disconnected while trying +/// to [`send`] with the [`Sender`], the [`send`] method will return a +/// [`SendError`]. Similarly, If the [`Sender`] is disconnected while trying +/// to [`recv`], the [`recv`] method will return a [`RecvError`]. +/// +/// [`send`]: Sender::send +/// [`recv`]: Receiver::recv +/// +/// # Examples +/// +/// ``` +/// use std::sync::mpsc::sync_channel; +/// use std::thread; +/// +/// let (sender, receiver) = sync_channel(1); /// -/// This channel has a buffer that can hold at most `cap` messages at a time. +/// // this returns immediately +/// sender.send(1).unwrap(); /// -/// A special case is zero-capacity channel, which cannot hold any messages. Instead, send and -/// receive operations must appear at the same time in order to pair up and pass the message over. +/// thread::spawn(move || { +/// // this will block until the previous message has been received +/// sender.send(2).unwrap(); +/// }); +/// +/// assert_eq!(receiver.recv().unwrap(), 1); +/// assert_eq!(receiver.recv().unwrap(), 2); +/// ``` +#[must_use] +#[unstable(feature = "mpmc_channel", issue = "126840")] pub fn sync_channel(cap: usize) -> (Sender, Receiver) { if cap == 0 { let (s, r) = counter::new(zero::Channel::new()); @@ -76,7 +256,42 @@ pub fn sync_channel(cap: usize) -> (Sender, Receiver) { } } -/// The sending side of a channel. +/// The sending-half of Rust's synchronous [`channel`] type. +/// +/// Messages can be sent through this channel with [`send`]. +/// +/// Note: all senders (the original and its clones) need to be dropped for the receiver +/// to stop blocking to receive messages with [`Receiver::recv`]. +/// +/// [`send`]: Sender::send +/// +/// # Examples +/// +/// ```rust +/// #![feature(mpmc_channel)] +/// +/// use std::sync::mpmc::channel; +/// use std::thread; +/// +/// let (sender, receiver) = channel(); +/// let sender2 = sender.clone(); +/// +/// // First thread owns sender +/// thread::spawn(move || { +/// sender.send(1).unwrap(); +/// }); +/// +/// // Second thread owns sender2 +/// thread::spawn(move || { +/// sender2.send(2).unwrap(); +/// }); +/// +/// let msg = receiver.recv().unwrap(); +/// let msg2 = receiver.recv().unwrap(); +/// +/// assert_eq!(3, msg + msg2); +/// ``` +#[unstable(feature = "mpmc_channel", issue = "126840")] pub struct Sender { flavor: SenderFlavor, } @@ -93,10 +308,14 @@ enum SenderFlavor { Zero(counter::Sender>), } +#[unstable(feature = "mpmc_channel", issue = "126840")] unsafe impl Send for Sender {} +#[unstable(feature = "mpmc_channel", issue = "126840")] unsafe impl Sync for Sender {} +#[unstable(feature = "mpmc_channel", issue = "126840")] impl UnwindSafe for Sender {} +#[unstable(feature = "mpmc_channel", issue = "126840")] impl RefUnwindSafe for Sender {} impl Sender { @@ -107,6 +326,19 @@ impl Sender { /// /// If called on a zero-capacity channel, this method will send the message only if there /// happens to be a receive operation on the other side of the channel at the same time. + /// + /// # Examples + /// + /// ```rust + /// #![feature(mpmc_channel)] + /// + /// use std::sync::mpmc::{channel, Receiver, Sender}; + /// + /// let (sender, _receiver): (Sender, Receiver) = channel(); + /// + /// assert!(sender.try_send(1).is_ok()); + /// ``` + #[unstable(feature = "mpmc_channel", issue = "126840")] pub fn try_send(&self, msg: T) -> Result<(), TrySendError> { match &self.flavor { SenderFlavor::Array(chan) => chan.try_send(msg), @@ -115,14 +347,36 @@ impl Sender { } } - /// Blocks the current thread until a message is sent or the channel is disconnected. + /// Attempts to send a value on this channel, returning it back if it could + /// not be sent. /// - /// If the channel is full and not disconnected, this call will block until the send operation - /// can proceed. If the channel becomes disconnected, this call will wake up and return an - /// error. The returned error contains the original message. + /// A successful send occurs when it is determined that the other end of + /// the channel has not hung up already. An unsuccessful send would be one + /// where the corresponding receiver has already been deallocated. Note + /// that a return value of [`Err`] means that the data will never be + /// received, but a return value of [`Ok`] does *not* mean that the data + /// will be received. It is possible for the corresponding receiver to + /// hang up immediately after this function returns [`Ok`]. /// - /// If called on a zero-capacity channel, this method will wait for a receive operation to - /// appear on the other side of the channel. + /// This method will never block the current thread. + /// + /// # Examples + /// + /// ``` + /// #![feature(mpmc_channel)] + /// + /// use std::sync::mpmc::channel; + /// + /// let (tx, rx) = channel(); + /// + /// // This send is always successful + /// tx.send(1).unwrap(); + /// + /// // This send will fail because the receiver is gone + /// drop(rx); + /// assert!(tx.send(1).is_err()); + /// ``` + #[unstable(feature = "mpmc_channel", issue = "126840")] pub fn send(&self, msg: T) -> Result<(), SendError> { match &self.flavor { SenderFlavor::Array(chan) => chan.send(msg, None), @@ -136,10 +390,6 @@ impl Sender { } } -// The methods below are not used by `sync::mpsc`, but -// are useful and we'll likely want to expose them -// eventually -#[allow(unused)] impl Sender { /// Waits for a message to be sent into the channel, but only for a limited time. /// @@ -149,6 +399,20 @@ impl Sender { /// /// If called on a zero-capacity channel, this method will wait for a receive operation to /// appear on the other side of the channel. + /// + /// # Examples + /// + /// ``` + /// #![feature(mpmc_channel)] + /// + /// use std::sync::mpmc::channel; + /// use std::time::Duration; + /// + /// let (tx, rx) = channel(); + /// + /// tx.send_timeout(1, Duration::from_millis(400)).unwrap(); + /// ``` + #[unstable(feature = "mpmc_channel", issue = "126840")] pub fn send_timeout(&self, msg: T, timeout: Duration) -> Result<(), SendTimeoutError> { match Instant::now().checked_add(timeout) { Some(deadline) => self.send_deadline(msg, deadline), @@ -165,6 +429,21 @@ impl Sender { /// /// If called on a zero-capacity channel, this method will wait for a receive operation to /// appear on the other side of the channel. + /// + /// # Examples + /// + /// ``` + /// #![feature(mpmc_channel)] + /// + /// use std::sync::mpmc::channel; + /// use std::time::{Duration, Instant}; + /// + /// let (tx, rx) = channel(); + /// + /// let t = Instant::now() + Duration::from_millis(400); + /// tx.send_deadline(1, t).unwrap(); + /// ``` + #[unstable(feature = "mpmc_channel", issue = "126840")] pub fn send_deadline(&self, msg: T, deadline: Instant) -> Result<(), SendTimeoutError> { match &self.flavor { SenderFlavor::Array(chan) => chan.send(msg, Some(deadline)), @@ -176,6 +455,31 @@ impl Sender { /// Returns `true` if the channel is empty. /// /// Note: Zero-capacity channels are always empty. + /// + /// # Examples + /// + /// ``` + /// #![feature(mpmc_channel)] + /// + /// use std::sync::mpmc; + /// use std::thread; + /// + /// let (send, _recv) = mpmc::channel(); + /// + /// let tx1 = send.clone(); + /// let tx2 = send.clone(); + /// + /// assert!(tx1.is_empty()); + /// + /// let handle = thread::spawn(move || { + /// tx2.send(1u8).unwrap(); + /// }); + /// + /// handle.join().unwrap(); + /// + /// assert!(!tx1.is_empty()); + /// ``` + #[unstable(feature = "mpmc_channel", issue = "126840")] pub fn is_empty(&self) -> bool { match &self.flavor { SenderFlavor::Array(chan) => chan.is_empty(), @@ -187,6 +491,29 @@ impl Sender { /// Returns `true` if the channel is full. /// /// Note: Zero-capacity channels are always full. + /// + /// # Examples + /// + /// ``` + /// #![feature(mpmc_channel)] + /// + /// use std::sync::mpmc; + /// use std::thread; + /// + /// let (send, _recv) = mpmc::sync_channel(1); + /// + /// let (tx1, tx2) = (send.clone(), send.clone()); + /// assert!(!tx1.is_full()); + /// + /// let handle = thread::spawn(move || { + /// tx2.send(1u8).unwrap(); + /// }); + /// + /// handle.join().unwrap(); + /// + /// assert!(tx1.is_full()); + /// ``` + #[unstable(feature = "mpmc_channel", issue = "126840")] pub fn is_full(&self) -> bool { match &self.flavor { SenderFlavor::Array(chan) => chan.is_full(), @@ -196,6 +523,29 @@ impl Sender { } /// Returns the number of messages in the channel. + /// + /// # Examples + /// + /// ``` + /// #![feature(mpmc_channel)] + /// + /// use std::sync::mpmc; + /// use std::thread; + /// + /// let (send, _recv) = mpmc::channel(); + /// let (tx1, tx2) = (send.clone(), send.clone()); + /// + /// assert_eq!(tx1.len(), 0); + /// + /// let handle = thread::spawn(move || { + /// tx2.send(1u8).unwrap(); + /// }); + /// + /// handle.join().unwrap(); + /// + /// assert_eq!(tx1.len(), 1); + /// ``` + #[unstable(feature = "mpmc_channel", issue = "126840")] pub fn len(&self) -> usize { match &self.flavor { SenderFlavor::Array(chan) => chan.len(), @@ -205,6 +555,29 @@ impl Sender { } /// If the channel is bounded, returns its capacity. + /// + /// # Examples + /// + /// ``` + /// #![feature(mpmc_channel)] + /// + /// use std::sync::mpmc; + /// use std::thread; + /// + /// let (send, _recv) = mpmc::sync_channel(3); + /// let (tx1, tx2) = (send.clone(), send.clone()); + /// + /// assert_eq!(tx1.capacity(), Some(3)); + /// + /// let handle = thread::spawn(move || { + /// tx2.send(1u8).unwrap(); + /// }); + /// + /// handle.join().unwrap(); + /// + /// assert_eq!(tx1.capacity(), Some(3)); + /// ``` + #[unstable(feature = "mpmc_channel", issue = "126840")] pub fn capacity(&self) -> Option { match &self.flavor { SenderFlavor::Array(chan) => chan.capacity(), @@ -214,6 +587,21 @@ impl Sender { } /// Returns `true` if senders belong to the same channel. + /// + /// # Examples + /// + /// ``` + /// #![feature(mpmc_channel)] + /// + /// use std::sync::mpmc; + /// + /// let (tx1, _) = mpmc::channel::(); + /// let (tx2, _) = mpmc::channel::(); + /// + /// assert!(tx1.same_channel(&tx1)); + /// assert!(!tx1.same_channel(&tx2)); + /// ``` + #[unstable(feature = "mpmc_channel", issue = "126840")] pub fn same_channel(&self, other: &Sender) -> bool { match (&self.flavor, &other.flavor) { (SenderFlavor::Array(ref a), SenderFlavor::Array(ref b)) => a == b, @@ -224,6 +612,7 @@ impl Sender { } } +#[unstable(feature = "mpmc_channel", issue = "126840")] impl Drop for Sender { fn drop(&mut self) { unsafe { @@ -236,6 +625,7 @@ impl Drop for Sender { } } +#[unstable(feature = "mpmc_channel", issue = "126840")] impl Clone for Sender { fn clone(&self) -> Self { let flavor = match &self.flavor { @@ -248,17 +638,216 @@ impl Clone for Sender { } } +#[unstable(feature = "mpmc_channel", issue = "126840")] impl fmt::Debug for Sender { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.pad("Sender { .. }") } } -/// The receiving side of a channel. +/// The receiving half of Rust's [`channel`] (or [`sync_channel`]) type. +/// Different threads can share this [`Sender`] by cloning it. +/// +/// Messages sent to the channel can be retrieved using [`recv`]. +/// +/// [`recv`]: Receiver::recv +/// +/// # Examples +/// +/// ```rust +/// #![feature(mpmc_channel)] +/// +/// use std::sync::mpmc::channel; +/// use std::thread; +/// use std::time::Duration; +/// +/// let (send, recv) = channel(); +/// +/// let tx_thread = thread::spawn(move || { +/// send.send("Hello world!").unwrap(); +/// thread::sleep(Duration::from_secs(2)); // block for two seconds +/// send.send("Delayed for 2 seconds").unwrap(); +/// }); +/// +/// let (rx1, rx2) = (recv.clone(), recv.clone()); +/// let rx_thread_1 = thread::spawn(move || { +/// println!("{}", rx1.recv().unwrap()); // Received immediately +/// }); +/// let rx_thread_2 = thread::spawn(move || { +/// println!("{}", rx2.recv().unwrap()); // Received after 2 seconds +/// }); +/// +/// tx_thread.join().unwrap(); +/// rx_thread_1.join().unwrap(); +/// rx_thread_2.join().unwrap(); +/// ``` +#[unstable(feature = "mpmc_channel", issue = "126840")] pub struct Receiver { flavor: ReceiverFlavor, } +/// An iterator over messages on a [`Receiver`], created by [`iter`]. +/// +/// This iterator will block whenever [`next`] is called, +/// waiting for a new message, and [`None`] will be returned +/// when the corresponding channel has hung up. +/// +/// [`iter`]: Receiver::iter +/// [`next`]: Iterator::next +/// +/// # Examples +/// +/// ```rust +/// #![feature(mpmc_channel)] +/// +/// use std::sync::mpmc::channel; +/// use std::thread; +/// +/// let (send, recv) = channel(); +/// +/// thread::spawn(move || { +/// send.send(1u8).unwrap(); +/// send.send(2u8).unwrap(); +/// send.send(3u8).unwrap(); +/// }); +/// +/// for x in recv.iter() { +/// println!("Got: {x}"); +/// } +/// ``` +#[unstable(feature = "mpmc_channel", issue = "126840")] +#[derive(Debug)] +pub struct Iter<'a, T: 'a> { + rx: &'a Receiver, +} + +/// An iterator that attempts to yield all pending values for a [`Receiver`], +/// created by [`try_iter`]. +/// +/// [`None`] will be returned when there are no pending values remaining or +/// if the corresponding channel has hung up. +/// +/// This iterator will never block the caller in order to wait for data to +/// become available. Instead, it will return [`None`]. +/// +/// [`try_iter`]: Receiver::try_iter +/// +/// # Examples +/// +/// ```rust +/// #![feature(mpmc_channel)] +/// +/// use std::sync::mpmc::channel; +/// use std::thread; +/// use std::time::Duration; +/// +/// let (sender, receiver) = channel(); +/// +/// // Nothing is in the buffer yet +/// assert!(receiver.try_iter().next().is_none()); +/// println!("Nothing in the buffer..."); +/// +/// thread::spawn(move || { +/// sender.send(1).unwrap(); +/// sender.send(2).unwrap(); +/// sender.send(3).unwrap(); +/// }); +/// +/// println!("Going to sleep..."); +/// thread::sleep(Duration::from_secs(2)); // block for two seconds +/// +/// for x in receiver.try_iter() { +/// println!("Got: {x}"); +/// } +/// ``` +#[unstable(feature = "mpmc_channel", issue = "126840")] +#[derive(Debug)] +pub struct TryIter<'a, T: 'a> { + rx: &'a Receiver, +} + +/// An owning iterator over messages on a [`Receiver`], +/// created by [`into_iter`]. +/// +/// This iterator will block whenever [`next`] +/// is called, waiting for a new message, and [`None`] will be +/// returned if the corresponding channel has hung up. +/// +/// [`into_iter`]: Receiver::into_iter +/// [`next`]: Iterator::next +/// +/// # Examples +/// +/// ```rust +/// #![feature(mpmc_channel)] +/// +/// use std::sync::mpmc::channel; +/// use std::thread; +/// +/// let (send, recv) = channel(); +/// +/// thread::spawn(move || { +/// send.send(1u8).unwrap(); +/// send.send(2u8).unwrap(); +/// send.send(3u8).unwrap(); +/// }); +/// +/// for x in recv.into_iter() { +/// println!("Got: {x}"); +/// } +/// ``` +#[unstable(feature = "mpmc_channel", issue = "126840")] +#[derive(Debug)] +pub struct IntoIter { + rx: Receiver, +} + +#[unstable(feature = "mpmc_channel", issue = "126840")] +impl<'a, T> Iterator for Iter<'a, T> { + type Item = T; + + fn next(&mut self) -> Option { + self.rx.recv().ok() + } +} + +#[unstable(feature = "mpmc_channel", issue = "126840")] +impl<'a, T> Iterator for TryIter<'a, T> { + type Item = T; + + fn next(&mut self) -> Option { + self.rx.try_recv().ok() + } +} + +#[unstable(feature = "mpmc_channel", issue = "126840")] +impl<'a, T> IntoIterator for &'a Receiver { + type Item = T; + type IntoIter = Iter<'a, T>; + + fn into_iter(self) -> Iter<'a, T> { + self.iter() + } +} + +#[unstable(feature = "mpmc_channel", issue = "126840")] +impl Iterator for IntoIter { + type Item = T; + fn next(&mut self) -> Option { + self.rx.recv().ok() + } +} + +#[unstable(feature = "mpmc_channel", issue = "126840")] +impl IntoIterator for Receiver { + type Item = T; + type IntoIter = IntoIter; + + fn into_iter(self) -> IntoIter { + IntoIter { rx: self } + } +} + /// Receiver flavors. enum ReceiverFlavor { /// Bounded channel based on a preallocated array. @@ -271,20 +860,46 @@ enum ReceiverFlavor { Zero(counter::Receiver>), } +#[unstable(feature = "mpmc_channel", issue = "126840")] unsafe impl Send for Receiver {} +#[unstable(feature = "mpmc_channel", issue = "126840")] unsafe impl Sync for Receiver {} +#[unstable(feature = "mpmc_channel", issue = "126840")] impl UnwindSafe for Receiver {} +#[unstable(feature = "mpmc_channel", issue = "126840")] impl RefUnwindSafe for Receiver {} impl Receiver { /// Attempts to receive a message from the channel without blocking. /// - /// This method will either receive a message from the channel immediately or return an error - /// if the channel is empty. + /// This method will never block the caller in order to wait for data to + /// become available. Instead, this will always return immediately with a + /// possible option of pending data on the channel. /// /// If called on a zero-capacity channel, this method will receive a message only if there /// happens to be a send operation on the other side of the channel at the same time. + /// + /// This is useful for a flavor of "optimistic check" before deciding to + /// block on a receiver. + /// + /// Compared with [`recv`], this function has two failure cases instead of one + /// (one for disconnection, one for an empty buffer). + /// + /// [`recv`]: Self::recv + /// + /// # Examples + /// + /// ```rust + /// #![feature(mpmc_channel)] + /// + /// use std::sync::mpmc::{Receiver, channel}; + /// + /// let (_, receiver): (_, Receiver) = channel(); + /// + /// assert!(receiver.try_recv().is_err()); + /// ``` + #[unstable(feature = "mpmc_channel", issue = "126840")] pub fn try_recv(&self) -> Result { match &self.flavor { ReceiverFlavor::Array(chan) => chan.try_recv(), @@ -293,15 +908,64 @@ impl Receiver { } } - /// Blocks the current thread until a message is received or the channel is empty and - /// disconnected. + /// Attempts to wait for a value on this receiver, returning an error if the + /// corresponding channel has hung up. /// - /// If the channel is empty and not disconnected, this call will block until the receive - /// operation can proceed. If the channel is empty and becomes disconnected, this call will - /// wake up and return an error. + /// This function will always block the current thread if there is no data + /// available and it's possible for more data to be sent (at least one sender + /// still exists). Once a message is sent to the corresponding [`Sender`], + /// this receiver will wake up and return that message. /// - /// If called on a zero-capacity channel, this method will wait for a send operation to appear - /// on the other side of the channel. + /// If the corresponding [`Sender`] has disconnected, or it disconnects while + /// this call is blocking, this call will wake up and return [`Err`] to + /// indicate that no more messages can ever be received on this channel. + /// However, since channels are buffered, messages sent before the disconnect + /// will still be properly received. + /// + /// # Examples + /// + /// ``` + /// #![feature(mpmc_channel)] + /// + /// use std::sync::mpmc; + /// use std::thread; + /// + /// let (send, recv) = mpmc::channel(); + /// let handle = thread::spawn(move || { + /// send.send(1u8).unwrap(); + /// }); + /// + /// handle.join().unwrap(); + /// + /// assert_eq!(Ok(1), recv.recv()); + /// ``` + /// + /// Buffering behavior: + /// + /// ``` + /// #![feature(mpmc_channel)] + /// + /// use std::sync::mpmc; + /// use std::thread; + /// use std::sync::mpmc::RecvError; + /// + /// let (send, recv) = mpmc::channel(); + /// let handle = thread::spawn(move || { + /// send.send(1u8).unwrap(); + /// send.send(2).unwrap(); + /// send.send(3).unwrap(); + /// drop(send); + /// }); + /// + /// // wait for the thread to join so we ensure the sender is dropped + /// handle.join().unwrap(); + /// + /// assert_eq!(Ok(1), recv.recv()); + /// assert_eq!(Ok(2), recv.recv()); + /// assert_eq!(Ok(3), recv.recv()); + /// assert_eq!(Err(RecvError), recv.recv()); + /// ``` + #[unstable(feature = "mpmc_channel", issue = "126840")] pub fn recv(&self) -> Result { match &self.flavor { ReceiverFlavor::Array(chan) => chan.recv(None), @@ -311,14 +975,65 @@ impl Receiver { .map_err(|_| RecvError) } - /// Waits for a message to be received from the channel, but only for a limited time. + /// Attempts to wait for a value on this receiver, returning an error if the + /// corresponding channel has hung up, or if it waits more than `timeout`. + /// + /// This function will always block the current thread if there is no data + /// available and it's possible for more data to be sent (at least one sender + /// still exists). Once a message is sent to the corresponding [`Sender`], + /// this receiver will wake up and return that message. + /// + /// If the corresponding [`Sender`] has disconnected, or it disconnects while + /// this call is blocking, this call will wake up and return [`Err`] to + /// indicate that no more messages can ever be received on this channel. + /// However, since channels are buffered, messages sent before the disconnect + /// will still be properly received. + /// + /// # Examples + /// + /// Successfully receiving value before encountering timeout: /// - /// If the channel is empty and not disconnected, this call will block until the receive - /// operation can proceed or the operation times out. If the channel is empty and becomes - /// disconnected, this call will wake up and return an error. + /// ```no_run + /// #![feature(mpmc_channel)] /// - /// If called on a zero-capacity channel, this method will wait for a send operation to appear - /// on the other side of the channel. + /// use std::thread; + /// use std::time::Duration; + /// use std::sync::mpmc; + /// + /// let (send, recv) = mpmc::channel(); + /// + /// thread::spawn(move || { + /// send.send('a').unwrap(); + /// }); + /// + /// assert_eq!( + /// recv.recv_timeout(Duration::from_millis(400)), + /// Ok('a') + /// ); + /// ``` + /// + /// Receiving an error upon reaching timeout: + /// + /// ```no_run + /// #![feature(mpmc_channel)] + /// + /// use std::thread; + /// use std::time::Duration; + /// use std::sync::mpmc; + /// + /// let (send, recv) = mpmc::channel(); + /// + /// thread::spawn(move || { + /// thread::sleep(Duration::from_millis(800)); + /// send.send('a').unwrap(); + /// }); + /// + /// assert_eq!( + /// recv.recv_timeout(Duration::from_millis(400)), + /// Err(mpmc::RecvTimeoutError::Timeout) + /// ); + /// ``` + #[unstable(feature = "mpmc_channel", issue = "126840")] pub fn recv_timeout(&self, timeout: Duration) -> Result { match Instant::now().checked_add(timeout) { Some(deadline) => self.recv_deadline(deadline), @@ -327,14 +1042,65 @@ impl Receiver { } } - /// Waits for a message to be received from the channel, but only for a limited time. + /// Attempts to wait for a value on this receiver, returning an error if the + /// corresponding channel has hung up, or if `deadline` is reached. + /// + /// This function will always block the current thread if there is no data + /// available and it's possible for more data to be sent. Once a message is + /// sent to the corresponding [`Sender`], then this receiver will wake up + /// and return that message. + /// + /// If the corresponding [`Sender`] has disconnected, or it disconnects while + /// this call is blocking, this call will wake up and return [`Err`] to + /// indicate that no more messages can ever be received on this channel. + /// However, since channels are buffered, messages sent before the disconnect + /// will still be properly received. + /// + /// # Examples + /// + /// Successfully receiving value before reaching deadline: + /// + /// ```no_run + /// #![feature(mpmc_channel)] + /// + /// use std::thread; + /// use std::time::{Duration, Instant}; + /// use std::sync::mpmc; /// - /// If the channel is empty and not disconnected, this call will block until the receive - /// operation can proceed or the operation times out. If the channel is empty and becomes - /// disconnected, this call will wake up and return an error. + /// let (send, recv) = mpmc::channel(); /// - /// If called on a zero-capacity channel, this method will wait for a send operation to appear - /// on the other side of the channel. + /// thread::spawn(move || { + /// send.send('a').unwrap(); + /// }); + /// + /// assert_eq!( + /// recv.recv_deadline(Instant::now() + Duration::from_millis(400)), + /// Ok('a') + /// ); + /// ``` + /// + /// Receiving an error upon reaching deadline: + /// + /// ```no_run + /// #![feature(mpmc_channel)] + /// + /// use std::thread; + /// use std::time::{Duration, Instant}; + /// use std::sync::mpmc; + /// + /// let (send, recv) = mpmc::channel(); + /// + /// thread::spawn(move || { + /// thread::sleep(Duration::from_millis(800)); + /// send.send('a').unwrap(); + /// }); + /// + /// assert_eq!( + /// recv.recv_deadline(Instant::now() + Duration::from_millis(400)), + /// Err(mpmc::RecvTimeoutError::Timeout) + /// ); + /// ``` + #[unstable(feature = "mpmc_channel", issue = "126840")] pub fn recv_deadline(&self, deadline: Instant) -> Result { match &self.flavor { ReceiverFlavor::Array(chan) => chan.recv(Some(deadline)), @@ -342,16 +1108,77 @@ impl Receiver { ReceiverFlavor::Zero(chan) => chan.recv(Some(deadline)), } } + + /// Returns an iterator that will attempt to yield all pending values. + /// It will return `None` if there are no more pending values or if the + /// channel has hung up. The iterator will never [`panic!`] or block the + /// user by waiting for values. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(mpmc_channel)] + /// + /// use std::sync::mpmc::channel; + /// use std::thread; + /// use std::time::Duration; + /// + /// let (sender, receiver) = channel(); + /// + /// // nothing is in the buffer yet + /// assert!(receiver.try_iter().next().is_none()); + /// + /// thread::spawn(move || { + /// thread::sleep(Duration::from_secs(1)); + /// sender.send(1).unwrap(); + /// sender.send(2).unwrap(); + /// sender.send(3).unwrap(); + /// }); + /// + /// // nothing is in the buffer yet + /// assert!(receiver.try_iter().next().is_none()); + /// + /// // block for two seconds + /// thread::sleep(Duration::from_secs(2)); + /// + /// let mut iter = receiver.try_iter(); + /// assert_eq!(iter.next(), Some(1)); + /// assert_eq!(iter.next(), Some(2)); + /// assert_eq!(iter.next(), Some(3)); + /// assert_eq!(iter.next(), None); + /// ``` + #[unstable(feature = "mpmc_channel", issue = "126840")] + pub fn try_iter(&self) -> TryIter<'_, T> { + TryIter { rx: self } + } } -// The methods below are not used by `sync::mpsc`, but -// are useful and we'll likely want to expose them -// eventually -#[allow(unused)] impl Receiver { /// Returns `true` if the channel is empty. /// /// Note: Zero-capacity channels are always empty. + /// + /// # Examples + /// + /// ``` + /// #![feature(mpmc_channel)] + /// + /// use std::sync::mpmc; + /// use std::thread; + /// + /// let (send, recv) = mpmc::channel(); + /// + /// assert!(recv.is_empty()); + /// + /// let handle = thread::spawn(move || { + /// send.send(1u8).unwrap(); + /// }); + /// + /// handle.join().unwrap(); + /// + /// assert!(!recv.is_empty()); + /// ``` + #[unstable(feature = "mpmc_channel", issue = "126840")] pub fn is_empty(&self) -> bool { match &self.flavor { ReceiverFlavor::Array(chan) => chan.is_empty(), @@ -363,6 +1190,28 @@ impl Receiver { /// Returns `true` if the channel is full. /// /// Note: Zero-capacity channels are always full. + /// + /// # Examples + /// + /// ``` + /// #![feature(mpmc_channel)] + /// + /// use std::sync::mpmc; + /// use std::thread; + /// + /// let (send, recv) = mpmc::sync_channel(1); + /// + /// assert!(!recv.is_full()); + /// + /// let handle = thread::spawn(move || { + /// send.send(1u8).unwrap(); + /// }); + /// + /// handle.join().unwrap(); + /// + /// assert!(recv.is_full()); + /// ``` + #[unstable(feature = "mpmc_channel", issue = "126840")] pub fn is_full(&self) -> bool { match &self.flavor { ReceiverFlavor::Array(chan) => chan.is_full(), @@ -372,6 +1221,28 @@ impl Receiver { } /// Returns the number of messages in the channel. + /// + /// # Examples + /// + /// ``` + /// #![feature(mpmc_channel)] + /// + /// use std::sync::mpmc; + /// use std::thread; + /// + /// let (send, recv) = mpmc::channel(); + /// + /// assert_eq!(recv.len(), 0); + /// + /// let handle = thread::spawn(move || { + /// send.send(1u8).unwrap(); + /// }); + /// + /// handle.join().unwrap(); + /// + /// assert_eq!(recv.len(), 1); + /// ``` + #[unstable(feature = "mpmc_channel", issue = "126840")] pub fn len(&self) -> usize { match &self.flavor { ReceiverFlavor::Array(chan) => chan.len(), @@ -381,6 +1252,28 @@ impl Receiver { } /// If the channel is bounded, returns its capacity. + /// + /// # Examples + /// + /// ``` + /// #![feature(mpmc_channel)] + /// + /// use std::sync::mpmc; + /// use std::thread; + /// + /// let (send, recv) = mpmc::sync_channel(3); + /// + /// assert_eq!(recv.capacity(), Some(3)); + /// + /// let handle = thread::spawn(move || { + /// send.send(1u8).unwrap(); + /// }); + /// + /// handle.join().unwrap(); + /// + /// assert_eq!(recv.capacity(), Some(3)); + /// ``` + #[unstable(feature = "mpmc_channel", issue = "126840")] pub fn capacity(&self) -> Option { match &self.flavor { ReceiverFlavor::Array(chan) => chan.capacity(), @@ -390,6 +1283,21 @@ impl Receiver { } /// Returns `true` if receivers belong to the same channel. + /// + /// # Examples + /// + /// ``` + /// #![feature(mpmc_channel)] + /// + /// use std::sync::mpmc; + /// + /// let (_, rx1) = mpmc::channel::(); + /// let (_, rx2) = mpmc::channel::(); + /// + /// assert!(rx1.same_channel(&rx1)); + /// assert!(!rx1.same_channel(&rx2)); + /// ``` + #[unstable(feature = "mpmc_channel", issue = "126840")] pub fn same_channel(&self, other: &Receiver) -> bool { match (&self.flavor, &other.flavor) { (ReceiverFlavor::Array(a), ReceiverFlavor::Array(b)) => a == b, @@ -398,8 +1306,39 @@ impl Receiver { _ => false, } } + + /// Returns an iterator that will block waiting for messages, but never + /// [`panic!`]. It will return [`None`] when the channel has hung up. + /// + /// # Examples + /// + /// ```rust + /// #![feature(mpmc_channel)] + /// + /// use std::sync::mpmc::channel; + /// use std::thread; + /// + /// let (send, recv) = channel(); + /// + /// thread::spawn(move || { + /// send.send(1).unwrap(); + /// send.send(2).unwrap(); + /// send.send(3).unwrap(); + /// }); + /// + /// let mut iter = recv.iter(); + /// assert_eq!(iter.next(), Some(1)); + /// assert_eq!(iter.next(), Some(2)); + /// assert_eq!(iter.next(), Some(3)); + /// assert_eq!(iter.next(), None); + /// ``` + #[unstable(feature = "mpmc_channel", issue = "126840")] + pub fn iter(&self) -> Iter<'_, T> { + Iter { rx: self } + } } +#[unstable(feature = "mpmc_channel", issue = "126840")] impl Drop for Receiver { fn drop(&mut self) { unsafe { @@ -412,6 +1351,7 @@ impl Drop for Receiver { } } +#[unstable(feature = "mpmc_channel", issue = "126840")] impl Clone for Receiver { fn clone(&self) -> Self { let flavor = match &self.flavor { @@ -424,6 +1364,7 @@ impl Clone for Receiver { } } +#[unstable(feature = "mpmc_channel", issue = "126840")] impl fmt::Debug for Receiver { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.pad("Receiver { .. }") diff --git a/library/std/src/sync/mpmc/tests.rs b/library/std/src/sync/mpmc/tests.rs new file mode 100644 index 0000000000000..ab14050df6c98 --- /dev/null +++ b/library/std/src/sync/mpmc/tests.rs @@ -0,0 +1,728 @@ +use super::*; +use crate::{env, thread}; + +pub fn stress_factor() -> usize { + match env::var("RUST_TEST_STRESS") { + Ok(val) => val.parse().unwrap(), + Err(..) => 1, + } +} + +#[test] +fn smoke() { + let (tx, rx) = channel::(); + tx.send(1).unwrap(); + assert_eq!(rx.recv().unwrap(), 1); +} + +#[test] +fn drop_full() { + let (tx, _rx) = channel::>(); + tx.send(Box::new(1)).unwrap(); +} + +#[test] +fn drop_full_shared() { + let (tx, _rx) = channel::>(); + drop(tx.clone()); + drop(tx.clone()); + tx.send(Box::new(1)).unwrap(); +} + +#[test] +fn smoke_shared() { + let (tx, rx) = channel::(); + tx.send(1).unwrap(); + assert_eq!(rx.recv().unwrap(), 1); + let tx = tx.clone(); + tx.send(1).unwrap(); + assert_eq!(rx.recv().unwrap(), 1); +} + +#[test] +fn smoke_threads() { + let (tx, rx) = channel::(); + let t1 = thread::spawn(move || { + for i in 0..2 { + tx.send(i).unwrap(); + } + }); + let t2 = thread::spawn(move || { + assert_eq!(rx.recv().unwrap(), 0); + assert_eq!(rx.recv().unwrap(), 1); + }); + t1.join().unwrap(); + t2.join().unwrap(); +} + +#[test] +fn smoke_port_gone() { + let (tx, rx) = channel::(); + drop(rx); + assert!(tx.send(1).is_err()); +} + +#[test] +fn smoke_shared_port_gone() { + let (tx, rx) = channel::(); + drop(rx); + assert!(tx.send(1).is_err()) +} + +#[test] +fn smoke_shared_port_gone2() { + let (tx, rx) = channel::(); + drop(rx); + let tx2 = tx.clone(); + drop(tx); + assert!(tx2.send(1).is_err()); +} + +#[test] +fn port_gone_concurrent() { + let (tx, rx) = channel::(); + let _t = thread::spawn(move || { + rx.recv().unwrap(); + }); + while tx.send(1).is_ok() {} +} + +#[test] +fn port_gone_concurrent_shared() { + let (tx, rx) = channel::(); + let tx2 = tx.clone(); + let _t = thread::spawn(move || { + rx.recv().unwrap(); + }); + while tx.send(1).is_ok() && tx2.send(1).is_ok() {} +} + +#[test] +fn smoke_chan_gone() { + let (tx, rx) = channel::(); + drop(tx); + assert!(rx.recv().is_err()); +} + +#[test] +fn smoke_chan_gone_shared() { + let (tx, rx) = channel::<()>(); + let tx2 = tx.clone(); + drop(tx); + drop(tx2); + assert!(rx.recv().is_err()); +} + +#[test] +fn chan_gone_concurrent() { + let (tx, rx) = channel::(); + let _t = thread::spawn(move || { + tx.send(1).unwrap(); + tx.send(1).unwrap(); + }); + while rx.recv().is_ok() {} +} + +#[test] +fn stress() { + let count = if cfg!(miri) { 100 } else { 10000 }; + let (tx, rx) = channel::(); + let t = thread::spawn(move || { + for _ in 0..count { + tx.send(1).unwrap(); + } + }); + for _ in 0..count { + assert_eq!(rx.recv().unwrap(), 1); + } + t.join().ok().expect("thread panicked"); +} + +#[test] +fn stress_shared() { + const AMT: u32 = if cfg!(miri) { 100 } else { 10000 }; + const NTHREADS: u32 = 8; + let (tx, rx) = channel::(); + + let t = thread::spawn(move || { + for _ in 0..AMT * NTHREADS { + assert_eq!(rx.recv().unwrap(), 1); + } + match rx.try_recv() { + Ok(..) => panic!(), + _ => {} + } + }); + + for _ in 0..NTHREADS { + let tx = tx.clone(); + thread::spawn(move || { + for _ in 0..AMT { + tx.send(1).unwrap(); + } + }); + } + drop(tx); + t.join().ok().expect("thread panicked"); +} + +#[test] +fn send_from_outside_runtime() { + let (tx1, rx1) = channel::<()>(); + let (tx2, rx2) = channel::(); + let t1 = thread::spawn(move || { + tx1.send(()).unwrap(); + for _ in 0..40 { + assert_eq!(rx2.recv().unwrap(), 1); + } + }); + rx1.recv().unwrap(); + let t2 = thread::spawn(move || { + for _ in 0..40 { + tx2.send(1).unwrap(); + } + }); + t1.join().ok().expect("thread panicked"); + t2.join().ok().expect("thread panicked"); +} + +#[test] +fn recv_from_outside_runtime() { + let (tx, rx) = channel::(); + let t = thread::spawn(move || { + for _ in 0..40 { + assert_eq!(rx.recv().unwrap(), 1); + } + }); + for _ in 0..40 { + tx.send(1).unwrap(); + } + t.join().ok().expect("thread panicked"); +} + +#[test] +fn no_runtime() { + let (tx1, rx1) = channel::(); + let (tx2, rx2) = channel::(); + let t1 = thread::spawn(move || { + assert_eq!(rx1.recv().unwrap(), 1); + tx2.send(2).unwrap(); + }); + let t2 = thread::spawn(move || { + tx1.send(1).unwrap(); + assert_eq!(rx2.recv().unwrap(), 2); + }); + t1.join().ok().expect("thread panicked"); + t2.join().ok().expect("thread panicked"); +} + +#[test] +fn oneshot_single_thread_close_port_first() { + // Simple test of closing without sending + let (_tx, rx) = channel::(); + drop(rx); +} + +#[test] +fn oneshot_single_thread_close_chan_first() { + // Simple test of closing without sending + let (tx, _rx) = channel::(); + drop(tx); +} + +#[test] +fn oneshot_single_thread_send_port_close() { + // Testing that the sender cleans up the payload if receiver is closed + let (tx, rx) = channel::>(); + drop(rx); + assert!(tx.send(Box::new(0)).is_err()); +} + +#[test] +fn oneshot_single_thread_recv_chan_close() { + // Receiving on a closed chan will panic + let res = thread::spawn(move || { + let (tx, rx) = channel::(); + drop(tx); + rx.recv().unwrap(); + }) + .join(); + // What is our res? + assert!(res.is_err()); +} + +#[test] +fn oneshot_single_thread_send_then_recv() { + let (tx, rx) = channel::>(); + tx.send(Box::new(10)).unwrap(); + assert!(*rx.recv().unwrap() == 10); +} + +#[test] +fn oneshot_single_thread_try_send_open() { + let (tx, rx) = channel::(); + assert!(tx.send(10).is_ok()); + assert!(rx.recv().unwrap() == 10); +} + +#[test] +fn oneshot_single_thread_try_send_closed() { + let (tx, rx) = channel::(); + drop(rx); + assert!(tx.send(10).is_err()); +} + +#[test] +fn oneshot_single_thread_try_recv_open() { + let (tx, rx) = channel::(); + tx.send(10).unwrap(); + assert!(rx.recv() == Ok(10)); +} + +#[test] +fn oneshot_single_thread_try_recv_closed() { + let (tx, rx) = channel::(); + drop(tx); + assert!(rx.recv().is_err()); +} + +#[test] +fn oneshot_single_thread_peek_data() { + let (tx, rx) = channel::(); + assert_eq!(rx.try_recv(), Err(TryRecvError::Empty)); + tx.send(10).unwrap(); + assert_eq!(rx.try_recv(), Ok(10)); +} + +#[test] +fn oneshot_single_thread_peek_close() { + let (tx, rx) = channel::(); + drop(tx); + assert_eq!(rx.try_recv(), Err(TryRecvError::Disconnected)); + assert_eq!(rx.try_recv(), Err(TryRecvError::Disconnected)); +} + +#[test] +fn oneshot_single_thread_peek_open() { + let (_tx, rx) = channel::(); + assert_eq!(rx.try_recv(), Err(TryRecvError::Empty)); +} + +#[test] +fn oneshot_multi_task_recv_then_send() { + let (tx, rx) = channel::>(); + let _t = thread::spawn(move || { + assert!(*rx.recv().unwrap() == 10); + }); + + tx.send(Box::new(10)).unwrap(); +} + +#[test] +fn oneshot_multi_task_recv_then_close() { + let (tx, rx) = channel::>(); + let _t = thread::spawn(move || { + drop(tx); + }); + let res = thread::spawn(move || { + assert!(*rx.recv().unwrap() == 10); + }) + .join(); + assert!(res.is_err()); +} + +#[test] +fn oneshot_multi_thread_close_stress() { + for _ in 0..stress_factor() { + let (tx, rx) = channel::(); + let _t = thread::spawn(move || { + drop(rx); + }); + drop(tx); + } +} + +#[test] +fn oneshot_multi_thread_send_close_stress() { + for _ in 0..stress_factor() { + let (tx, rx) = channel::(); + let _t = thread::spawn(move || { + drop(rx); + }); + let _ = thread::spawn(move || { + tx.send(1).unwrap(); + }) + .join(); + } +} + +#[test] +fn oneshot_multi_thread_recv_close_stress() { + for _ in 0..stress_factor() { + let (tx, rx) = channel::(); + thread::spawn(move || { + let res = thread::spawn(move || { + rx.recv().unwrap(); + }) + .join(); + assert!(res.is_err()); + }); + let _t = thread::spawn(move || { + thread::spawn(move || { + drop(tx); + }); + }); + } +} + +#[test] +fn oneshot_multi_thread_send_recv_stress() { + for _ in 0..stress_factor() { + let (tx, rx) = channel::>(); + let _t = thread::spawn(move || { + tx.send(Box::new(10)).unwrap(); + }); + assert!(*rx.recv().unwrap() == 10); + } +} + +#[test] +fn stream_send_recv_stress() { + for _ in 0..stress_factor() { + let (tx, rx) = channel(); + + send(tx, 0); + recv(rx, 0); + + fn send(tx: Sender>, i: i32) { + if i == 10 { + return; + } + + thread::spawn(move || { + tx.send(Box::new(i)).unwrap(); + send(tx, i + 1); + }); + } + + fn recv(rx: Receiver>, i: i32) { + if i == 10 { + return; + } + + thread::spawn(move || { + assert!(*rx.recv().unwrap() == i); + recv(rx, i + 1); + }); + } + } +} + +#[test] +fn oneshot_single_thread_recv_timeout() { + let (tx, rx) = channel(); + tx.send(()).unwrap(); + assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(())); + assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Err(RecvTimeoutError::Timeout)); + tx.send(()).unwrap(); + assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(())); +} + +#[test] +fn stress_recv_timeout_two_threads() { + let (tx, rx) = channel(); + let stress = stress_factor() + 100; + let timeout = Duration::from_millis(100); + + thread::spawn(move || { + for i in 0..stress { + if i % 2 == 0 { + thread::sleep(timeout * 2); + } + tx.send(1usize).unwrap(); + } + }); + + let mut recv_count = 0; + loop { + match rx.recv_timeout(timeout) { + Ok(n) => { + assert_eq!(n, 1usize); + recv_count += 1; + } + Err(RecvTimeoutError::Timeout) => continue, + Err(RecvTimeoutError::Disconnected) => break, + } + } + + assert_eq!(recv_count, stress); +} + +#[test] +fn recv_timeout_upgrade() { + let (tx, rx) = channel::<()>(); + let timeout = Duration::from_millis(1); + let _tx_clone = tx.clone(); + + let start = Instant::now(); + assert_eq!(rx.recv_timeout(timeout), Err(RecvTimeoutError::Timeout)); + assert!(Instant::now() >= start + timeout); +} + +#[test] +fn stress_recv_timeout_shared() { + let (tx, rx) = channel(); + let stress = stress_factor() + 100; + + for i in 0..stress { + let tx = tx.clone(); + thread::spawn(move || { + thread::sleep(Duration::from_millis(i as u64 * 10)); + tx.send(1usize).unwrap(); + }); + } + + drop(tx); + + let mut recv_count = 0; + loop { + match rx.recv_timeout(Duration::from_millis(10)) { + Ok(n) => { + assert_eq!(n, 1usize); + recv_count += 1; + } + Err(RecvTimeoutError::Timeout) => continue, + Err(RecvTimeoutError::Disconnected) => break, + } + } + + assert_eq!(recv_count, stress); +} + +#[test] +fn very_long_recv_timeout_wont_panic() { + let (tx, rx) = channel::<()>(); + let join_handle = thread::spawn(move || rx.recv_timeout(Duration::from_secs(u64::MAX))); + thread::sleep(Duration::from_secs(1)); + assert!(tx.send(()).is_ok()); + assert_eq!(join_handle.join().unwrap(), Ok(())); +} + +#[test] +fn recv_a_lot() { + let count = if cfg!(miri) { 1000 } else { 10000 }; + // Regression test that we don't run out of stack in scheduler context + let (tx, rx) = channel(); + for _ in 0..count { + tx.send(()).unwrap(); + } + for _ in 0..count { + rx.recv().unwrap(); + } +} + +#[test] +fn shared_recv_timeout() { + let (tx, rx) = channel(); + let total = 5; + for _ in 0..total { + let tx = tx.clone(); + thread::spawn(move || { + tx.send(()).unwrap(); + }); + } + + for _ in 0..total { + rx.recv().unwrap(); + } + + assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Err(RecvTimeoutError::Timeout)); + tx.send(()).unwrap(); + assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(())); +} + +#[test] +fn shared_chan_stress() { + let (tx, rx) = channel(); + let total = stress_factor() + 100; + for _ in 0..total { + let tx = tx.clone(); + thread::spawn(move || { + tx.send(()).unwrap(); + }); + } + + for _ in 0..total { + rx.recv().unwrap(); + } +} + +#[test] +fn test_nested_recv_iter() { + let (tx, rx) = channel::(); + let (total_tx, total_rx) = channel::(); + + let _t = thread::spawn(move || { + let mut acc = 0; + for x in rx.iter() { + acc += x; + } + total_tx.send(acc).unwrap(); + }); + + tx.send(3).unwrap(); + tx.send(1).unwrap(); + tx.send(2).unwrap(); + drop(tx); + assert_eq!(total_rx.recv().unwrap(), 6); +} + +#[test] +fn test_recv_iter_break() { + let (tx, rx) = channel::(); + let (count_tx, count_rx) = channel(); + + let _t = thread::spawn(move || { + let mut count = 0; + for x in rx.iter() { + if count >= 3 { + break; + } else { + count += x; + } + } + count_tx.send(count).unwrap(); + }); + + tx.send(2).unwrap(); + tx.send(2).unwrap(); + tx.send(2).unwrap(); + let _ = tx.send(2); + drop(tx); + assert_eq!(count_rx.recv().unwrap(), 4); +} + +#[test] +fn test_recv_try_iter() { + let (request_tx, request_rx) = channel(); + let (response_tx, response_rx) = channel(); + + // Request `x`s until we have `6`. + let t = thread::spawn(move || { + let mut count = 0; + loop { + for x in response_rx.try_iter() { + count += x; + if count == 6 { + return count; + } + } + request_tx.send(()).unwrap(); + } + }); + + for _ in request_rx.iter() { + if response_tx.send(2).is_err() { + break; + } + } + + assert_eq!(t.join().unwrap(), 6); +} + +#[test] +fn test_recv_into_iter_owned() { + let mut iter = { + let (tx, rx) = channel::(); + tx.send(1).unwrap(); + tx.send(2).unwrap(); + + rx.into_iter() + }; + assert_eq!(iter.next().unwrap(), 1); + assert_eq!(iter.next().unwrap(), 2); + assert_eq!(iter.next().is_none(), true); +} + +#[test] +fn test_recv_into_iter_borrowed() { + let (tx, rx) = channel::(); + tx.send(1).unwrap(); + tx.send(2).unwrap(); + drop(tx); + let mut iter = (&rx).into_iter(); + assert_eq!(iter.next().unwrap(), 1); + assert_eq!(iter.next().unwrap(), 2); + assert_eq!(iter.next().is_none(), true); +} + +#[test] +fn try_recv_states() { + let (tx1, rx1) = channel::(); + let (tx2, rx2) = channel::<()>(); + let (tx3, rx3) = channel::<()>(); + let _t = thread::spawn(move || { + rx2.recv().unwrap(); + tx1.send(1).unwrap(); + tx3.send(()).unwrap(); + rx2.recv().unwrap(); + drop(tx1); + tx3.send(()).unwrap(); + }); + + assert_eq!(rx1.try_recv(), Err(TryRecvError::Empty)); + tx2.send(()).unwrap(); + rx3.recv().unwrap(); + assert_eq!(rx1.try_recv(), Ok(1)); + assert_eq!(rx1.try_recv(), Err(TryRecvError::Empty)); + tx2.send(()).unwrap(); + rx3.recv().unwrap(); + assert_eq!(rx1.try_recv(), Err(TryRecvError::Disconnected)); +} + +// This bug used to end up in a livelock inside of the Receiver destructor +// because the internal state of the Shared packet was corrupted +#[test] +fn destroy_upgraded_shared_port_when_sender_still_active() { + let (tx, rx) = channel(); + let (tx2, rx2) = channel(); + let _t = thread::spawn(move || { + rx.recv().unwrap(); // wait on a oneshot + drop(rx); // destroy a shared + tx2.send(()).unwrap(); + }); + // make sure the other thread has gone to sleep + for _ in 0..5000 { + thread::yield_now(); + } + + // upgrade to a shared chan and send a message + let t = tx.clone(); + drop(tx); + t.send(()).unwrap(); + + // wait for the child thread to exit before we exit + rx2.recv().unwrap(); +} + +#[test] +fn issue_32114() { + let (tx, _) = channel(); + let _ = tx.send(123); + assert_eq!(tx.send(123), Err(SendError(123))); +} + +#[test] +fn issue_39364() { + let (tx, rx) = channel::<()>(); + let t = thread::spawn(move || { + thread::sleep(Duration::from_millis(300)); + let _ = tx.clone(); + // Don't drop; hand back to caller. + tx + }); + + let _ = rx.recv_timeout(Duration::from_millis(500)); + let _tx = t.join().unwrap(); // delay dropping until end of test + let _ = rx.recv_timeout(Duration::from_millis(500)); +} diff --git a/tests/ui/const-generics/issues/issue-82956.stderr b/tests/ui/const-generics/issues/issue-82956.stderr index a956fc741f4d4..5e380eea81cab 100644 --- a/tests/ui/const-generics/issues/issue-82956.stderr +++ b/tests/ui/const-generics/issues/issue-82956.stderr @@ -14,7 +14,7 @@ LL + use std::collections::btree_map::IntoIter; | LL + use std::collections::btree_set::IntoIter; | - and 8 other candidates + and 9 other candidates error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-12041.stderr b/tests/ui/issues/issue-12041.stderr index 51061c0262eb1..f2c10b833836b 100644 --- a/tests/ui/issues/issue-12041.stderr +++ b/tests/ui/issues/issue-12041.stderr @@ -4,7 +4,7 @@ error[E0382]: use of moved value: `tx` LL | let tx = tx; | ^^ value moved here, in previous iteration of loop | - = note: move occurs because `tx` has type `Sender`, which does not implement the `Copy` trait + = note: move occurs because `tx` has type `std::sync::mpsc::Sender`, which does not implement the `Copy` trait error: aborting due to 1 previous error diff --git a/tests/ui/lint/use_suggestion_json.stderr b/tests/ui/lint/use_suggestion_json.stderr index 4683e5dd8f3c2..0d4304e2e2e40 100644 --- a/tests/ui/lint/use_suggestion_json.stderr +++ b/tests/ui/lint/use_suggestion_json.stderr @@ -348,6 +348,29 @@ mod foo { "label": null, "suggested_replacement": "use std::slice::Iter; +", + "suggestion_applicability": "MaybeIncorrect", + "expansion": null + }, + { + "file_name": "$DIR/use_suggestion_json.rs", + "byte_start": 541, + "byte_end": 541, + "line_start": 11, + "line_end": 11, + "column_start": 1, + "column_end": 1, + "is_primary": true, + "text": [ + { + "text": "fn main() {", + "highlight_start": 1, + "highlight_end": 1 + } + ], + "label": null, + "suggested_replacement": "use std::sync::mpmc::Iter; + ", "suggestion_applicability": "MaybeIncorrect", "expansion": null @@ -396,7 +419,7 @@ mod foo { \u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m \u001b[0m\u001b[1m\u001b[38;5;12mLL\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[38;5;10m+ use std::collections::hash_map::Iter;\u001b[0m \u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m -\u001b[0m and 8 other candidates\u001b[0m +\u001b[0m and 9 other candidates\u001b[0m " } From 91d0752e1698ac393a90b8f3ad58e59d904070d9 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 30 Sep 2024 16:56:40 -0700 Subject: [PATCH 059/120] Update wasm-component-ld to 0.5.9 This updates the `wasm-component-ld` linker binary for the `wasm32-wasip2` target to 0.5.9, pulling in a few bug fixes and recent updates. --- Cargo.lock | 48 +++++++++++++------------- src/tools/wasm-component-ld/Cargo.toml | 2 +- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 582b5a763e6f7..6c1071ccbc226 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5768,16 +5768,16 @@ checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" [[package]] name = "wasm-component-ld" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb17cdbc91766d4ea0bcd6026c36ba77a21b5c8199aeb1f0993461fe6a6bec2b" +checksum = "fde17bc96539700198e12516230c76095cc215c84ef39ad206e1af3f84243e0f" dependencies = [ "anyhow", "clap", "lexopt", "tempfile", "wasi-preview1-component-adapter-provider", - "wasmparser 0.217.0", + "wasmparser 0.218.0", "wat", "wit-component", "wit-parser", @@ -5801,19 +5801,19 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.217.0" +version = "0.218.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b88b0814c9a2b323a9b46c687e726996c255ac8b64aa237dd11c81ed4854760" +checksum = "22b896fa8ceb71091ace9bcb81e853f54043183a1c9667cf93422c40252ffa0a" dependencies = [ "leb128", - "wasmparser 0.217.0", + "wasmparser 0.218.0", ] [[package]] name = "wasm-metadata" -version = "0.217.0" +version = "0.218.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65a146bf9a60e9264f0548a2599aa9656dba9a641eff9ab88299dc2a637e483c" +checksum = "aa5eeb071abe8a2132fdd5565dabffee70775ee8c24fc7e300ac43f51f4a8a91" dependencies = [ "anyhow", "indexmap", @@ -5821,8 +5821,8 @@ dependencies = [ "serde_derive", "serde_json", "spdx", - "wasm-encoder 0.217.0", - "wasmparser 0.217.0", + "wasm-encoder 0.218.0", + "wasmparser 0.218.0", ] [[package]] @@ -5837,9 +5837,9 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.217.0" +version = "0.218.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca917a21307d3adf2b9857b94dd05ebf8496bdcff4437a9b9fb3899d3e6c74e7" +checksum = "b09e46c7fceceaa72b2dd1a8a137ea7fd8f93dfaa69806010a709918e496c5dc" dependencies = [ "ahash", "bitflags 2.6.0", @@ -5851,22 +5851,22 @@ dependencies = [ [[package]] name = "wast" -version = "217.0.0" +version = "218.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79004ecebded92d3c710d4841383368c7f04b63d0992ddd6b0c7d5029b7629b7" +checksum = "8a53cd1f0fa505df97557e36a58bddb8296e2fcdcd089529545ebfdb18a1b9d7" dependencies = [ "bumpalo", "leb128", "memchr", "unicode-width", - "wasm-encoder 0.217.0", + "wasm-encoder 0.218.0", ] [[package]] name = "wat" -version = "1.217.0" +version = "1.218.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c126271c3d92ca0f7c63e4e462e40c69cca52fd4245fcda730d1cf558fb55088" +checksum = "4f87f8e14e776762e07927c27c2054d2cf678aab9aae2d431a79b3e31e4dd391" dependencies = [ "wast", ] @@ -6143,9 +6143,9 @@ dependencies = [ [[package]] name = "wit-component" -version = "0.217.0" +version = "0.218.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7117809905e49db716d81e794f79590c052bf2fdbbcda1731ca0fb28f6f3ddf" +checksum = "aa53aa7e6bf2b3e8ccaffbcc963fbdb672a603dc0af393a481b6cec24c266406" dependencies = [ "anyhow", "bitflags 2.6.0", @@ -6154,17 +6154,17 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder 0.217.0", + "wasm-encoder 0.218.0", "wasm-metadata", - "wasmparser 0.217.0", + "wasmparser 0.218.0", "wit-parser", ] [[package]] name = "wit-parser" -version = "0.217.0" +version = "0.218.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb893dcd6d370cfdf19a0d9adfcd403efb8e544e1a0ea3a8b81a21fe392eaa78" +checksum = "0d3d1066ab761b115f97fef2b191090faabcb0f37b555b758d3caf42d4ed9e55" dependencies = [ "anyhow", "id-arena", @@ -6175,7 +6175,7 @@ dependencies = [ "serde_derive", "serde_json", "unicode-xid", - "wasmparser 0.217.0", + "wasmparser 0.218.0", ] [[package]] diff --git a/src/tools/wasm-component-ld/Cargo.toml b/src/tools/wasm-component-ld/Cargo.toml index 91ff19ad9fcb0..49f4e59650e71 100644 --- a/src/tools/wasm-component-ld/Cargo.toml +++ b/src/tools/wasm-component-ld/Cargo.toml @@ -10,4 +10,4 @@ name = "wasm-component-ld" path = "src/main.rs" [dependencies] -wasm-component-ld = "0.5.4" +wasm-component-ld = "0.5.9" From 4529b86196836ef1efe5c710a8874877d17b9ef8 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 1 Oct 2024 11:01:45 +0200 Subject: [PATCH 060/120] make test_lots_of_insertions test take less long in Miri --- library/std/src/collections/hash/map/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/collections/hash/map/tests.rs b/library/std/src/collections/hash/map/tests.rs index 2599060ef62eb..fa8ea95b89142 100644 --- a/library/std/src/collections/hash/map/tests.rs +++ b/library/std/src/collections/hash/map/tests.rs @@ -274,7 +274,7 @@ fn test_lots_of_insertions() { for _ in 0..loops { assert!(m.is_empty()); - let count = if cfg!(miri) { 101 } else { 1001 }; + let count = if cfg!(miri) { 66 } else { 1001 }; for i in 1..count { assert!(m.insert(i, i).is_none()); From 125db409ffe4bcec2ce801d401eabd0e607ebf4a Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 25 Jul 2024 15:29:36 +0200 Subject: [PATCH 061/120] Small optimization for integers Display implementation --- library/core/src/fmt/num.rs | 138 ++++++++++++++++++++++++------------ 1 file changed, 92 insertions(+), 46 deletions(-) diff --git a/library/core/src/fmt/num.rs b/library/core/src/fmt/num.rs index e7726da8d91f2..5badc0a024c84 100644 --- a/library/core/src/fmt/num.rs +++ b/library/core/src/fmt/num.rs @@ -208,11 +208,46 @@ static DEC_DIGITS_LUT: &[u8; 200] = b"0001020304050607080910111213141516171819\ 8081828384858687888990919293949596979899"; macro_rules! impl_Display { - ($($t:ident),* as $u:ident via $conv_fn:ident named $name:ident) => { + ($($t:ident => $size:literal $(as $positive:ident in $other:ident)? => named $name:ident,)* ; as $u:ident via $conv_fn:ident named $gen_name:ident) => { + + $( + #[stable(feature = "rust1", since = "1.0.0")] + impl fmt::Display for $t { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // If it's a signed integer. + $( + let is_nonnegative = *self >= 0; + + #[cfg(not(feature = "optimize_for_size"))] + { + if !is_nonnegative { + // convert the negative num to positive by summing 1 to its 2s complement + return $other((!self as $positive).wrapping_add(1), false, f); + } + } + #[cfg(feature = "optimize_for_size")] + { + if !is_nonnegative { + // convert the negative num to positive by summing 1 to its 2s complement + return $other((!self.$conv_fn()).wrapping_add(1), false, f); + } + } + )? + // If it's a positive integer. + #[cfg(not(feature = "optimize_for_size"))] + { + $name(*self, true, f) + } + #[cfg(feature = "optimize_for_size")] + { + $gen_name(*self, true, f) + } + } + } + #[cfg(not(feature = "optimize_for_size"))] - fn $name(mut n: $u, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // 2^128 is about 3*10^38, so 39 gives an extra byte of space - let mut buf = [MaybeUninit::::uninit(); 39]; + fn $name(mut n: $t, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut buf = [MaybeUninit::::uninit(); $size]; let mut curr = buf.len(); let buf_ptr = MaybeUninit::slice_as_mut_ptr(&mut buf); let lut_ptr = DEC_DIGITS_LUT.as_ptr(); @@ -226,22 +261,26 @@ macro_rules! impl_Display { // is safe to access. unsafe { // need at least 16 bits for the 4-characters-at-a-time to work. - assert!(crate::mem::size_of::<$u>() >= 2); - - // eagerly decode 4 characters at a time - while n >= 10000 { - let rem = (n % 10000) as usize; - n /= 10000; - - let d1 = (rem / 100) << 1; - let d2 = (rem % 100) << 1; - curr -= 4; - - // We are allowed to copy to `buf_ptr[curr..curr + 3]` here since - // otherwise `curr < 0`. But then `n` was originally at least `10000^10` - // which is `10^40 > 2^128 > n`. - ptr::copy_nonoverlapping(lut_ptr.add(d1), buf_ptr.add(curr), 2); - ptr::copy_nonoverlapping(lut_ptr.add(d2), buf_ptr.add(curr + 2), 2); + #[allow(overflowing_literals)] + #[allow(unused_comparisons)] + // This block will be removed for smaller types at compile time and in the worst + // case, it will prevent to have the `10000` literal to overflow for `i8` and `u8`. + if core::mem::size_of::<$t>() >= 2 { + // eagerly decode 4 characters at a time + while n >= 10000 { + let rem = (n % 10000) as usize; + n /= 10000; + + let d1 = (rem / 100) << 1; + let d2 = (rem % 100) << 1; + curr -= 4; + + // We are allowed to copy to `buf_ptr[curr..curr + 3]` here since + // otherwise `curr < 0`. But then `n` was originally at least `10000^10` + // which is `10^40 > 2^128 > n`. + ptr::copy_nonoverlapping(lut_ptr.add(d1 as usize), buf_ptr.add(curr), 2); + ptr::copy_nonoverlapping(lut_ptr.add(d2 as usize), buf_ptr.add(curr + 2), 2); + } } // if we reach here numbers are <= 9999, so at most 4 chars long @@ -255,6 +294,8 @@ macro_rules! impl_Display { ptr::copy_nonoverlapping(lut_ptr.add(d1), buf_ptr.add(curr), 2); } + // if we reach here numbers are <= 100, so at most 2 chars long + // The biggest it can be is 99, and 99 << 1 == 198, so a `u8` is enough. // decode last 1 or 2 chars if n < 10 { curr -= 1; @@ -273,11 +314,10 @@ macro_rules! impl_Display { slice::from_raw_parts(buf_ptr.add(curr), buf.len() - curr)) }; f.pad_integral(is_nonnegative, "", buf_slice) - } + })* #[cfg(feature = "optimize_for_size")] - fn $name(mut n: $u, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // 2^128 is about 3*10^38, so 39 gives an extra byte of space + fn $gen_name(mut n: $u, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut buf = [MaybeUninit::::uninit(); 39]; let mut curr = buf.len(); let buf_ptr = MaybeUninit::slice_as_mut_ptr(&mut buf); @@ -306,21 +346,6 @@ macro_rules! impl_Display { }; f.pad_integral(is_nonnegative, "", buf_slice) } - - $(#[stable(feature = "rust1", since = "1.0.0")] - impl fmt::Display for $t { - #[allow(unused_comparisons)] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let is_nonnegative = *self >= 0; - let n = if is_nonnegative { - self.$conv_fn() - } else { - // convert the negative num to positive by summing 1 to it's 2 complement - (!self.$conv_fn()).wrapping_add(1) - }; - $name(n, is_nonnegative, f) - } - })* }; } @@ -374,7 +399,6 @@ macro_rules! impl_Exp { (n, exponent, exponent, added_precision) }; - // 39 digits (worst case u128) + . = 40 // Since `curr` always decreases by the number of digits copied, this means // that `curr >= 0`. let mut buf = [MaybeUninit::::uninit(); 40]; @@ -469,7 +493,7 @@ macro_rules! impl_Exp { let n = if is_nonnegative { self.$conv_fn() } else { - // convert the negative num to positive by summing 1 to it's 2 complement + // convert the negative num to positive by summing 1 to its 2s complement (!self.$conv_fn()).wrapping_add(1) }; $name(n, is_nonnegative, false, f) @@ -484,7 +508,7 @@ macro_rules! impl_Exp { let n = if is_nonnegative { self.$conv_fn() } else { - // convert the negative num to positive by summing 1 to it's 2 complement + // convert the negative num to positive by summing 1 to its 2s complement (!self.$conv_fn()).wrapping_add(1) }; $name(n, is_nonnegative, true, f) @@ -499,8 +523,17 @@ macro_rules! impl_Exp { mod imp { use super::*; impl_Display!( - i8, u8, i16, u16, i32, u32, i64, u64, usize, isize - as u64 via to_u64 named fmt_u64 + i8 => 3 as u8 in fmt_u8 => named fmt_i8, + u8 => 3 => named fmt_u8, + i16 => 5 as u16 in fmt_u16 => named fmt_i16, + u16 => 5 => named fmt_u16, + i32 => 10 as u32 in fmt_u32 => named fmt_i32, + u32 => 10 => named fmt_u32, + i64 => 19 as u64 in fmt_u64 => named fmt_i64, + u64 => 20 => named fmt_u64, + isize => 19 as usize in fmt_usize => named fmt_isize, + usize => 20 => named fmt_usize, + ; as u64 via to_u64 named fmt_u64 ); impl_Exp!( i8, u8, i16, u16, i32, u32, i64, u64, usize, isize @@ -511,8 +544,21 @@ mod imp { #[cfg(not(any(target_pointer_width = "64", target_arch = "wasm32")))] mod imp { use super::*; - impl_Display!(i8, u8, i16, u16, i32, u32, isize, usize as u32 via to_u32 named fmt_u32); - impl_Display!(i64, u64 as u64 via to_u64 named fmt_u64); + impl_Display!( + i8 => 3 as u8 in fmt_u8 => named fmt_i8, + u8 => 3 => named fmt_u8, + i16 => 5 as u16 in fmt_u16 => named fmt_i16, + u16 => 5 => named fmt_u16, + i32 => 10 as u32 in fmt_u32 => named fmt_i32, + u32 => 10 => named fmt_u32, + isize => 10 as usize in fmt_usize => named fmt_isize, + usize => 10 => named fmt_usize, + ; as u32 via to_u32 named fmt_u32); + impl_Display!( + i64 => 19 as u64 in fmt_u64 => named fmt_i64, + u64 => 20 => named fmt_u64, + ; as u64 via to_u64 named fmt_u64); + impl_Exp!(i8, u8, i16, u16, i32, u32, isize, usize as u32 via to_u32 named exp_u32); impl_Exp!(i64, u64 as u64 via to_u64 named exp_u64); } @@ -619,7 +665,7 @@ impl fmt::Display for i128 { let n = if is_nonnegative { self.to_u128() } else { - // convert the negative num to positive by summing 1 to it's 2 complement + // convert the negative num to positive by summing 1 to its 2s complement (!self.to_u128()).wrapping_add(1) }; fmt_u128(n, is_nonnegative, f) From 884e0f0a6865049355b952b9a14c8b4bce0e73b2 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 23 Sep 2024 17:24:31 +0200 Subject: [PATCH 062/120] Simplify `impl_Display` macro --- library/core/src/fmt/num.rs | 177 ++++++++++++++++++------------------ 1 file changed, 90 insertions(+), 87 deletions(-) diff --git a/library/core/src/fmt/num.rs b/library/core/src/fmt/num.rs index 5badc0a024c84..bc62f11d09fbb 100644 --- a/library/core/src/fmt/num.rs +++ b/library/core/src/fmt/num.rs @@ -208,7 +208,7 @@ static DEC_DIGITS_LUT: &[u8; 200] = b"0001020304050607080910111213141516171819\ 8081828384858687888990919293949596979899"; macro_rules! impl_Display { - ($($t:ident => $size:literal $(as $positive:ident in $other:ident)? => named $name:ident,)* ; as $u:ident via $conv_fn:ident named $gen_name:ident) => { + ($($t:ident => $size:literal $(as $positive:ident)? named $name:ident,)* ; as $u:ident via $conv_fn:ident named $gen_name:ident) => { $( #[stable(feature = "rust1", since = "1.0.0")] @@ -222,102 +222,105 @@ macro_rules! impl_Display { { if !is_nonnegative { // convert the negative num to positive by summing 1 to its 2s complement - return $other((!self as $positive).wrapping_add(1), false, f); + return (!self as $positive).wrapping_add(1)._fmt(false, f); } } #[cfg(feature = "optimize_for_size")] { if !is_nonnegative { // convert the negative num to positive by summing 1 to its 2s complement - return $other((!self.$conv_fn()).wrapping_add(1), false, f); + return $gen_name((!self.$conv_fn()).wrapping_add(1), false, f); } } )? // If it's a positive integer. #[cfg(not(feature = "optimize_for_size"))] { - $name(*self, true, f) + self._fmt(true, f) } #[cfg(feature = "optimize_for_size")] { - $gen_name(*self, true, f) + $gen_name(self.$conv_fn(), true, f) } } } #[cfg(not(feature = "optimize_for_size"))] - fn $name(mut n: $t, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut buf = [MaybeUninit::::uninit(); $size]; - let mut curr = buf.len(); - let buf_ptr = MaybeUninit::slice_as_mut_ptr(&mut buf); - let lut_ptr = DEC_DIGITS_LUT.as_ptr(); - - // SAFETY: Since `d1` and `d2` are always less than or equal to `198`, we - // can copy from `lut_ptr[d1..d1 + 1]` and `lut_ptr[d2..d2 + 1]`. To show - // that it's OK to copy into `buf_ptr`, notice that at the beginning - // `curr == buf.len() == 39 > log(n)` since `n < 2^128 < 10^39`, and at - // each step this is kept the same as `n` is divided. Since `n` is always - // non-negative, this means that `curr > 0` so `buf_ptr[curr..curr + 1]` - // is safe to access. - unsafe { - // need at least 16 bits for the 4-characters-at-a-time to work. - #[allow(overflowing_literals)] - #[allow(unused_comparisons)] - // This block will be removed for smaller types at compile time and in the worst - // case, it will prevent to have the `10000` literal to overflow for `i8` and `u8`. - if core::mem::size_of::<$t>() >= 2 { - // eagerly decode 4 characters at a time - while n >= 10000 { - let rem = (n % 10000) as usize; - n /= 10000; - - let d1 = (rem / 100) << 1; - let d2 = (rem % 100) << 1; - curr -= 4; - - // We are allowed to copy to `buf_ptr[curr..curr + 3]` here since - // otherwise `curr < 0`. But then `n` was originally at least `10000^10` - // which is `10^40 > 2^128 > n`. - ptr::copy_nonoverlapping(lut_ptr.add(d1 as usize), buf_ptr.add(curr), 2); - ptr::copy_nonoverlapping(lut_ptr.add(d2 as usize), buf_ptr.add(curr + 2), 2); + impl $t { + fn _fmt(mut self: $t, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut buf = [MaybeUninit::::uninit(); $size]; + let mut curr = $size; + let buf_ptr = MaybeUninit::slice_as_mut_ptr(&mut buf); + let lut_ptr = DEC_DIGITS_LUT.as_ptr(); + + // SAFETY: Since `d1` and `d2` are always less than or equal to `198`, we + // can copy from `lut_ptr[d1..d1 + 1]` and `lut_ptr[d2..d2 + 1]`. To show + // that it's OK to copy into `buf_ptr`, notice that at the beginning + // `curr == buf.len() == 39 > log(n)` since `n < 2^128 < 10^39`, and at + // each step this is kept the same as `n` is divided. Since `n` is always + // non-negative, this means that `curr > 0` so `buf_ptr[curr..curr + 1]` + // is safe to access. + unsafe { + // need at least 16 bits for the 4-characters-at-a-time to work. + #[allow(overflowing_literals)] + #[allow(unused_comparisons)] + // This block will be removed for smaller types at compile time and in the worst + // case, it will prevent to have the `10000` literal to overflow for `i8` and `u8`. + if core::mem::size_of::<$t>() >= 2 { + // eagerly decode 4 characters at a time + while self >= 10000 { + let rem = (self % 10000) as usize; + self /= 10000; + + let d1 = (rem / 100) << 1; + let d2 = (rem % 100) << 1; + curr -= 4; + + // We are allowed to copy to `buf_ptr[curr..curr + 3]` here since + // otherwise `curr < 0`. But then `n` was originally at least `10000^10` + // which is `10^40 > 2^128 > n`. + ptr::copy_nonoverlapping(lut_ptr.add(d1 as usize), buf_ptr.add(curr), 2); + ptr::copy_nonoverlapping(lut_ptr.add(d2 as usize), buf_ptr.add(curr + 2), 2); + } } - } - // if we reach here numbers are <= 9999, so at most 4 chars long - let mut n = n as usize; // possibly reduce 64bit math + // if we reach here numbers are <= 9999, so at most 4 chars long + let mut n = self as usize; // possibly reduce 64bit math - // decode 2 more chars, if > 2 chars - if n >= 100 { - let d1 = (n % 100) << 1; - n /= 100; - curr -= 2; - ptr::copy_nonoverlapping(lut_ptr.add(d1), buf_ptr.add(curr), 2); - } + // decode 2 more chars, if > 2 chars + if n >= 100 { + let d1 = (n % 100) << 1; + n /= 100; + curr -= 2; + ptr::copy_nonoverlapping(lut_ptr.add(d1), buf_ptr.add(curr), 2); + } - // if we reach here numbers are <= 100, so at most 2 chars long - // The biggest it can be is 99, and 99 << 1 == 198, so a `u8` is enough. - // decode last 1 or 2 chars - if n < 10 { - curr -= 1; - *buf_ptr.add(curr) = (n as u8) + b'0'; - } else { - let d1 = n << 1; - curr -= 2; - ptr::copy_nonoverlapping(lut_ptr.add(d1), buf_ptr.add(curr), 2); + // if we reach here numbers are <= 100, so at most 2 chars long + // The biggest it can be is 99, and 99 << 1 == 198, so a `u8` is enough. + // decode last 1 or 2 chars + if n < 10 { + curr -= 1; + *buf_ptr.add(curr) = (n as u8) + b'0'; + } else { + let d1 = n << 1; + curr -= 2; + ptr::copy_nonoverlapping(lut_ptr.add(d1), buf_ptr.add(curr), 2); + } } - } - // SAFETY: `curr` > 0 (since we made `buf` large enough), and all the chars are valid - // UTF-8 since `DEC_DIGITS_LUT` is - let buf_slice = unsafe { - str::from_utf8_unchecked( - slice::from_raw_parts(buf_ptr.add(curr), buf.len() - curr)) - }; - f.pad_integral(is_nonnegative, "", buf_slice) + // SAFETY: `curr` > 0 (since we made `buf` large enough), and all the chars are valid + // UTF-8 since `DEC_DIGITS_LUT` is + let buf_slice = unsafe { + str::from_utf8_unchecked( + slice::from_raw_parts(buf_ptr.add(curr), buf.len() - curr)) + }; + f.pad_integral(is_nonnegative, "", buf_slice) + } })* #[cfg(feature = "optimize_for_size")] fn $gen_name(mut n: $u, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // 2^128 is about 3*10^38, so 39 gives an extra byte of space let mut buf = [MaybeUninit::::uninit(); 39]; let mut curr = buf.len(); let buf_ptr = MaybeUninit::slice_as_mut_ptr(&mut buf); @@ -523,16 +526,16 @@ macro_rules! impl_Exp { mod imp { use super::*; impl_Display!( - i8 => 3 as u8 in fmt_u8 => named fmt_i8, - u8 => 3 => named fmt_u8, - i16 => 5 as u16 in fmt_u16 => named fmt_i16, - u16 => 5 => named fmt_u16, - i32 => 10 as u32 in fmt_u32 => named fmt_i32, - u32 => 10 => named fmt_u32, - i64 => 19 as u64 in fmt_u64 => named fmt_i64, - u64 => 20 => named fmt_u64, - isize => 19 as usize in fmt_usize => named fmt_isize, - usize => 20 => named fmt_usize, + i8 => 3 as u8 named fmt_i8, + u8 => 3 named fmt_u8, + i16 => 5 as u16 named fmt_i16, + u16 => 5 named fmt_u16, + i32 => 10 as u32 named fmt_i32, + u32 => 10 named fmt_u32, + i64 => 19 as u64 named fmt_i64, + u64 => 20 named fmt_u64, + isize => 19 as usize named fmt_isize, + usize => 20 named fmt_usize, ; as u64 via to_u64 named fmt_u64 ); impl_Exp!( @@ -545,18 +548,18 @@ mod imp { mod imp { use super::*; impl_Display!( - i8 => 3 as u8 in fmt_u8 => named fmt_i8, - u8 => 3 => named fmt_u8, - i16 => 5 as u16 in fmt_u16 => named fmt_i16, - u16 => 5 => named fmt_u16, - i32 => 10 as u32 in fmt_u32 => named fmt_i32, - u32 => 10 => named fmt_u32, - isize => 10 as usize in fmt_usize => named fmt_isize, - usize => 10 => named fmt_usize, + i8 => 3 as u8 named fmt_i8, + u8 => 3 named fmt_u8, + i16 => 5 as u16 named fmt_i16, + u16 => 5 named fmt_u16, + i32 => 10 as u32 named fmt_i32, + u32 => 10 named fmt_u32, + isize => 19 as usize named fmt_isize, + usize => 20 named fmt_usize, ; as u32 via to_u32 named fmt_u32); impl_Display!( - i64 => 19 as u64 in fmt_u64 => named fmt_i64, - u64 => 20 => named fmt_u64, + i64 => 19 as u64 named fmt_i64, + u64 => 20 named fmt_u64, ; as u64 via to_u64 named fmt_u64); impl_Exp!(i8, u8, i16, u16, i32, u32, isize, usize as u32 via to_u32 named exp_u32); From 1562bf790974f0061c47d1a83b7a75cc507a744d Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 1 Oct 2024 12:01:55 +0200 Subject: [PATCH 063/120] Remove the need to provide the maximum number of digits to `impl_Display` macro --- library/core/src/fmt/num.rs | 47 +++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/library/core/src/fmt/num.rs b/library/core/src/fmt/num.rs index bc62f11d09fbb..aecd725eca561 100644 --- a/library/core/src/fmt/num.rs +++ b/library/core/src/fmt/num.rs @@ -208,7 +208,7 @@ static DEC_DIGITS_LUT: &[u8; 200] = b"0001020304050607080910111213141516171819\ 8081828384858687888990919293949596979899"; macro_rules! impl_Display { - ($($t:ident => $size:literal $(as $positive:ident)? named $name:ident,)* ; as $u:ident via $conv_fn:ident named $gen_name:ident) => { + ($($t:ident $(as $positive:ident)? named $name:ident,)* ; as $u:ident via $conv_fn:ident named $gen_name:ident) => { $( #[stable(feature = "rust1", since = "1.0.0")] @@ -248,8 +248,9 @@ macro_rules! impl_Display { #[cfg(not(feature = "optimize_for_size"))] impl $t { fn _fmt(mut self: $t, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut buf = [MaybeUninit::::uninit(); $size]; - let mut curr = $size; + const SIZE: usize = $t::MAX.ilog(10) as usize + 1; + let mut buf = [MaybeUninit::::uninit(); SIZE]; + let mut curr = SIZE; let buf_ptr = MaybeUninit::slice_as_mut_ptr(&mut buf); let lut_ptr = DEC_DIGITS_LUT.as_ptr(); @@ -526,16 +527,16 @@ macro_rules! impl_Exp { mod imp { use super::*; impl_Display!( - i8 => 3 as u8 named fmt_i8, - u8 => 3 named fmt_u8, - i16 => 5 as u16 named fmt_i16, - u16 => 5 named fmt_u16, - i32 => 10 as u32 named fmt_i32, - u32 => 10 named fmt_u32, - i64 => 19 as u64 named fmt_i64, - u64 => 20 named fmt_u64, - isize => 19 as usize named fmt_isize, - usize => 20 named fmt_usize, + i8 as u8 named fmt_i8, + u8 named fmt_u8, + i16 as u16 named fmt_i16, + u16 named fmt_u16, + i32 as u32 named fmt_i32, + u32 named fmt_u32, + i64 as u64 named fmt_i64, + u64 named fmt_u64, + isize as usize named fmt_isize, + usize named fmt_usize, ; as u64 via to_u64 named fmt_u64 ); impl_Exp!( @@ -548,18 +549,18 @@ mod imp { mod imp { use super::*; impl_Display!( - i8 => 3 as u8 named fmt_i8, - u8 => 3 named fmt_u8, - i16 => 5 as u16 named fmt_i16, - u16 => 5 named fmt_u16, - i32 => 10 as u32 named fmt_i32, - u32 => 10 named fmt_u32, - isize => 19 as usize named fmt_isize, - usize => 20 named fmt_usize, + i8 as u8 named fmt_i8, + u8 named fmt_u8, + i16 as u16 named fmt_i16, + u16 named fmt_u16, + i32 as u32 named fmt_i32, + u32 named fmt_u32, + isize as usize named fmt_isize, + usize named fmt_usize, ; as u32 via to_u32 named fmt_u32); impl_Display!( - i64 => 19 as u64 named fmt_i64, - u64 => 20 named fmt_u64, + i64 as u64 named fmt_i64, + u64 named fmt_u64, ; as u64 via to_u64 named fmt_u64); impl_Exp!(i8, u8, i16, u16, i32, u32, isize, usize as u32 via to_u32 named exp_u32); From 50a6a3565c10bdb02834a6f25677a09b9a9b4107 Mon Sep 17 00:00:00 2001 From: klensy Date: Tue, 1 Oct 2024 14:16:19 +0300 Subject: [PATCH 064/120] add fixme to remove llvm option when minimal version is 19 --- src/bootstrap/src/core/build_steps/llvm.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index e4011221286f6..3021358415790 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -344,6 +344,7 @@ impl Step for Llvm { .define("LLVM_INCLUDE_DOCS", "OFF") .define("LLVM_INCLUDE_BENCHMARKS", "OFF") .define("LLVM_INCLUDE_TESTS", enable_tests) + // FIXME: remove this when minimal llvm is 19 .define("LLVM_ENABLE_TERMINFO", "OFF") .define("LLVM_ENABLE_LIBEDIT", "OFF") .define("LLVM_ENABLE_BINDINGS", "OFF") From f48194ea5578802c786edff59dd72388da0e6305 Mon Sep 17 00:00:00 2001 From: David Lattimore Date: Tue, 1 Oct 2024 08:55:10 +1000 Subject: [PATCH 065/120] Replace -Z default-hidden-visibility with -Z default-visibility MCP: https://github.com/rust-lang/compiler-team/issues/782 Co-authored-by: bjorn3 <17426603+bjorn3@users.noreply.github.com> --- compiler/rustc_codegen_gcc/src/allocator.rs | 13 +++- compiler/rustc_codegen_llvm/src/allocator.rs | 22 +++--- compiler/rustc_codegen_llvm/src/declare.rs | 13 +--- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 11 +++ compiler/rustc_interface/src/tests.rs | 2 +- compiler/rustc_middle/src/mir/mono.rs | 11 +++ .../rustc_monomorphize/src/partitioning.rs | 34 ++++----- compiler/rustc_session/src/config.rs | 4 +- compiler/rustc_session/src/options.rs | 24 +++++-- compiler/rustc_session/src/session.rs | 12 ++-- compiler/rustc_target/src/spec/mod.rs | 69 ++++++++++++++++--- .../default-hidden-visibility.md | 12 ---- .../src/compiler-flags/default-visibility.md | 44 ++++++++++++ ...en-visibility.rs => default-visibility.rs} | 18 ++--- 14 files changed, 208 insertions(+), 81 deletions(-) delete mode 100644 src/doc/unstable-book/src/compiler-flags/default-hidden-visibility.md create mode 100644 src/doc/unstable-book/src/compiler-flags/default-visibility.md rename tests/codegen/{default-hidden-visibility.rs => default-visibility.rs} (60%) diff --git a/compiler/rustc_codegen_gcc/src/allocator.rs b/compiler/rustc_codegen_gcc/src/allocator.rs index e9498857fb9a3..f13a75648aea8 100644 --- a/compiler/rustc_codegen_gcc/src/allocator.rs +++ b/compiler/rustc_codegen_gcc/src/allocator.rs @@ -104,10 +104,17 @@ fn create_wrapper_function( false, ); - if tcx.sess.default_hidden_visibility() { - #[cfg(feature = "master")] - func.add_attribute(FnAttribute::Visibility(gccjit::Visibility::Hidden)); + #[cfg(feature = "master")] + match tcx.sess.default_visibility() { + rustc_target::spec::SymbolVisibility::Hidden => { + func.add_attribute(FnAttribute::Visibility(gccjit::Visibility::Hidden)) + } + rustc_target::spec::SymbolVisibility::Protected => { + func.add_attribute(FnAttribute::Visibility(gccjit::Visibility::Protected)) + } + rustc_target::spec::SymbolVisibility::Interposable => {} } + if tcx.sess.must_emit_unwind_tables() { // TODO(antoyo): emit unwind tables. } diff --git a/compiler/rustc_codegen_llvm/src/allocator.rs b/compiler/rustc_codegen_llvm/src/allocator.rs index 2adac278c62f9..89f5305840b94 100644 --- a/compiler/rustc_codegen_llvm/src/allocator.rs +++ b/compiler/rustc_codegen_llvm/src/allocator.rs @@ -77,18 +77,20 @@ pub(crate) unsafe fn codegen( // __rust_alloc_error_handler_should_panic let name = OomStrategy::SYMBOL; let ll_g = llvm::LLVMRustGetOrInsertGlobal(llmod, name.as_ptr().cast(), name.len(), i8); - if tcx.sess.default_hidden_visibility() { - llvm::LLVMRustSetVisibility(ll_g, llvm::Visibility::Hidden); - } + llvm::LLVMRustSetVisibility( + ll_g, + llvm::Visibility::from_generic(tcx.sess.default_visibility()), + ); let val = tcx.sess.opts.unstable_opts.oom.should_panic(); let llval = llvm::LLVMConstInt(i8, val as u64, False); llvm::LLVMSetInitializer(ll_g, llval); let name = NO_ALLOC_SHIM_IS_UNSTABLE; let ll_g = llvm::LLVMRustGetOrInsertGlobal(llmod, name.as_ptr().cast(), name.len(), i8); - if tcx.sess.default_hidden_visibility() { - llvm::LLVMRustSetVisibility(ll_g, llvm::Visibility::Hidden); - } + llvm::LLVMRustSetVisibility( + ll_g, + llvm::Visibility::from_generic(tcx.sess.default_visibility()), + ); let llval = llvm::LLVMConstInt(i8, 0, False); llvm::LLVMSetInitializer(ll_g, llval); } @@ -132,9 +134,11 @@ fn create_wrapper_function( None }; - if tcx.sess.default_hidden_visibility() { - llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden); - } + llvm::LLVMRustSetVisibility( + llfn, + llvm::Visibility::from_generic(tcx.sess.default_visibility()), + ); + if tcx.sess.must_emit_unwind_tables() { let uwtable = attributes::uwtable_attr(llcx, tcx.sess.opts.unstable_opts.use_sync_unwind); diff --git a/compiler/rustc_codegen_llvm/src/declare.rs b/compiler/rustc_codegen_llvm/src/declare.rs index b0b29ca1280d5..7be44dd51b577 100644 --- a/compiler/rustc_codegen_llvm/src/declare.rs +++ b/compiler/rustc_codegen_llvm/src/declare.rs @@ -22,6 +22,7 @@ use tracing::debug; use crate::abi::{FnAbi, FnAbiLlvmExt}; use crate::context::CodegenCx; use crate::llvm::AttributePlace::Function; +use crate::llvm::Visibility; use crate::type_::Type; use crate::value::Value; use crate::{attributes, llvm}; @@ -84,11 +85,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { fn_type: &'ll Type, ) -> &'ll Value { // Declare C ABI functions with the visibility used by C by default. - let visibility = if self.tcx.sess.default_hidden_visibility() { - llvm::Visibility::Hidden - } else { - llvm::Visibility::Default - }; + let visibility = Visibility::from_generic(self.tcx.sess.default_visibility()); declare_raw_fn(self, name, llvm::CCallConv, unnamed, visibility, fn_type) } @@ -107,11 +104,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { unnamed: llvm::UnnamedAddr, fn_type: &'ll Type, ) -> &'ll Value { - let visibility = if self.tcx.sess.default_hidden_visibility() { - llvm::Visibility::Hidden - } else { - llvm::Visibility::Default - }; + let visibility = Visibility::from_generic(self.tcx.sess.default_visibility()); declare_raw_fn(self, name, callconv, unnamed, visibility, fn_type) } diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 9aabfd794bad7..d3950df91fbd0 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -4,6 +4,7 @@ use std::marker::PhantomData; use libc::{c_char, c_int, c_uint, c_ulonglong, c_void, size_t}; +use rustc_target::spec::SymbolVisibility; use super::RustString; use super::debuginfo::{ @@ -133,6 +134,16 @@ pub enum Visibility { Protected = 2, } +impl Visibility { + pub fn from_generic(visibility: SymbolVisibility) -> Self { + match visibility { + SymbolVisibility::Hidden => Visibility::Hidden, + SymbolVisibility::Protected => Visibility::Protected, + SymbolVisibility::Interposable => Visibility::Default, + } + } +} + /// LLVMUnnamedAddr #[repr(C)] pub enum UnnamedAddr { diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 57aa3deae806b..895897eca6b8b 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -770,7 +770,7 @@ fn test_unstable_options_tracking_hash() { tracked!(crate_attr, vec!["abc".to_string()]); tracked!(cross_crate_inline_threshold, InliningThreshold::Always); tracked!(debug_info_for_profiling, true); - tracked!(default_hidden_visibility, Some(true)); + tracked!(default_visibility, Some(rustc_target::spec::SymbolVisibility::Hidden)); tracked!(dep_info_omit_d_target, true); tracked!(direct_access_external_data, Some(true)); tracked!(dual_proc_macros, true); diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index 954f746ce5b61..56ca9167d4daa 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -15,6 +15,7 @@ use rustc_query_system::ich::StableHashingContext; use rustc_session::config::OptLevel; use rustc_span::Span; use rustc_span::symbol::Symbol; +use rustc_target::spec::SymbolVisibility; use tracing::debug; use crate::dep_graph::{DepNode, WorkProduct, WorkProductId}; @@ -305,6 +306,16 @@ pub enum Visibility { Protected, } +impl From for Visibility { + fn from(value: SymbolVisibility) -> Self { + match value { + SymbolVisibility::Hidden => Visibility::Hidden, + SymbolVisibility::Protected => Visibility::Protected, + SymbolVisibility::Interposable => Visibility::Default, + } + } +} + impl<'tcx> CodegenUnit<'tcx> { #[inline] pub fn new(name: Symbol) -> CodegenUnit<'tcx> { diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index ad05cca66cab3..5bd484d7bb002 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -904,26 +904,22 @@ fn mono_item_visibility<'tcx>( } fn default_visibility(tcx: TyCtxt<'_>, id: DefId, is_generic: bool) -> Visibility { - if !tcx.sess.default_hidden_visibility() { - return Visibility::Default; - } - - // Generic functions never have export-level C. - if is_generic { - return Visibility::Hidden; - } - - // Things with export level C don't get instantiated in - // downstream crates. - if !id.is_local() { - return Visibility::Hidden; - } + let export_level = if is_generic { + // Generic functions never have export-level C. + SymbolExportLevel::Rust + } else { + match tcx.reachable_non_generics(id.krate).get(&id) { + Some(SymbolExportInfo { level: SymbolExportLevel::C, .. }) => SymbolExportLevel::C, + _ => SymbolExportLevel::Rust, + } + }; + match export_level { + // C-export level items remain at `Default` to allow C code to + // access and interpose them. + SymbolExportLevel::C => Visibility::Default, - // C-export level items remain at `Default`, all other internal - // items become `Hidden`. - match tcx.reachable_non_generics(id.krate).get(&id) { - Some(SymbolExportInfo { level: SymbolExportLevel::C, .. }) => Visibility::Default, - _ => Visibility::Hidden, + // For all other symbols, `default_visibility` determines which visibility to use. + SymbolExportLevel::Rust => tcx.sess.default_visibility().into(), } } diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index b052d8d72c7d8..0d293415aa9c7 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -3008,7 +3008,8 @@ pub(crate) mod dep_tracking { use rustc_span::edition::Edition; use rustc_target::spec::{ CodeModel, FramePointer, MergeFunctions, OnBrokenPipe, PanicStrategy, RelocModel, - RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, TargetTriple, TlsModel, WasmCAbi, + RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, SymbolVisibility, TargetTriple, + TlsModel, WasmCAbi, }; use super::{ @@ -3101,6 +3102,7 @@ pub(crate) mod dep_tracking { StackProtector, SwitchWithOptPath, SymbolManglingVersion, + SymbolVisibility, RemapPathScopeComponents, SourceFileHashAlgorithm, OutFileName, diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index aac776d2919aa..1de09b8be4d63 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -13,8 +13,8 @@ use rustc_span::edition::Edition; use rustc_span::{RealFileName, SourceFileHashAlgorithm}; use rustc_target::spec::{ CodeModel, FramePointer, LinkerFlavorCli, MergeFunctions, OnBrokenPipe, PanicStrategy, - RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, TargetTriple, TlsModel, - WasmCAbi, + RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, SymbolVisibility, + TargetTriple, TlsModel, WasmCAbi, }; use crate::config::*; @@ -416,6 +416,8 @@ mod desc { "one of: `disabled`, `trampolines`, or `aliases`"; pub(crate) const parse_symbol_mangling_version: &str = "one of: `legacy`, `v0` (RFC 2603), or `hashed`"; + pub(crate) const parse_opt_symbol_visibility: &str = + "one of: `hidden`, `protected`, or `interposable`"; pub(crate) const parse_src_file_hash: &str = "either `md5` or `sha1`"; pub(crate) const parse_relocation_model: &str = "one of supported relocation models (`rustc --print relocation-models`)"; @@ -922,6 +924,20 @@ mod parse { true } + pub(crate) fn parse_opt_symbol_visibility( + slot: &mut Option, + v: Option<&str>, + ) -> bool { + if let Some(v) = v { + if let Ok(vis) = SymbolVisibility::from_str(v) { + *slot = Some(vis); + } else { + return false; + } + } + true + } + pub(crate) fn parse_optimization_fuel( slot: &mut Option<(String, u64)>, v: Option<&str>, @@ -1688,8 +1704,8 @@ options! { "compress debug info sections (none, zlib, zstd, default: none)"), deduplicate_diagnostics: bool = (true, parse_bool, [UNTRACKED], "deduplicate identical diagnostics (default: yes)"), - default_hidden_visibility: Option = (None, parse_opt_bool, [TRACKED], - "overrides the `default_hidden_visibility` setting of the target"), + default_visibility: Option = (None, parse_opt_symbol_visibility, [TRACKED], + "overrides the `default_visibility` setting of the target"), dep_info_omit_d_target: bool = (false, parse_bool, [TRACKED], "in dep-info output, omit targets for tracking dependencies of the dep-info files \ themselves (default: no)"), diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index adc28e3846254..d67e69fe0fb72 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -31,7 +31,8 @@ use rustc_span::{FileNameDisplayPreference, RealFileName, Span, Symbol}; use rustc_target::asm::InlineAsmArch; use rustc_target::spec::{ CodeModel, DebuginfoKind, PanicStrategy, RelocModel, RelroLevel, SanitizerSet, - SmallDataThresholdSupport, SplitDebuginfo, StackProtector, Target, TargetTriple, TlsModel, + SmallDataThresholdSupport, SplitDebuginfo, StackProtector, SymbolVisibility, Target, + TargetTriple, TlsModel, }; use crate::code_stats::CodeStats; @@ -617,12 +618,13 @@ impl Session { } } - /// Whether the default visibility of symbols should be "hidden" rather than "default". - pub fn default_hidden_visibility(&self) -> bool { + /// Returns the default symbol visibility. + pub fn default_visibility(&self) -> SymbolVisibility { self.opts .unstable_opts - .default_hidden_visibility - .unwrap_or(self.target.options.default_hidden_visibility) + .default_visibility + .or(self.target.options.default_visibility) + .unwrap_or(SymbolVisibility::Interposable) } } diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index f327c1fd17902..d1fcfe4adef18 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -830,6 +830,46 @@ impl RelroLevel { } } +#[derive(Clone, Copy, Debug, PartialEq, Hash)] +pub enum SymbolVisibility { + Hidden, + Protected, + Interposable, +} + +impl SymbolVisibility { + pub fn desc(&self) -> &str { + match *self { + SymbolVisibility::Hidden => "hidden", + SymbolVisibility::Protected => "protected", + SymbolVisibility::Interposable => "interposable", + } + } +} + +impl FromStr for SymbolVisibility { + type Err = (); + + fn from_str(s: &str) -> Result { + match s { + "hidden" => Ok(SymbolVisibility::Hidden), + "protected" => Ok(SymbolVisibility::Protected), + "interposable" => Ok(SymbolVisibility::Interposable), + _ => Err(()), + } + } +} + +impl ToJson for SymbolVisibility { + fn to_json(&self) -> Json { + match *self { + SymbolVisibility::Hidden => "hidden".to_json(), + SymbolVisibility::Protected => "protected".to_json(), + SymbolVisibility::Interposable => "interposable".to_json(), + } + } +} + impl FromStr for RelroLevel { type Err = (); @@ -2326,13 +2366,12 @@ pub struct TargetOptions { /// for this target unconditionally. pub no_builtins: bool, - /// The default visibility for symbols in this target should be "hidden" - /// rather than "default". + /// The default visibility for symbols in this target. /// - /// This value typically shouldn't be accessed directly, but through - /// the `rustc_session::Session::default_hidden_visibility` method, which - /// allows `rustc` users to override this setting using cmdline flags. - pub default_hidden_visibility: bool, + /// This value typically shouldn't be accessed directly, but through the + /// `rustc_session::Session::default_visibility` method, which allows `rustc` users to override + /// this setting using cmdline flags. + pub default_visibility: Option, /// Whether a .debug_gdb_scripts section will be added to the output object file pub emit_debug_gdb_scripts: bool, @@ -2623,7 +2662,7 @@ impl Default for TargetOptions { requires_lto: false, singlethread: false, no_builtins: false, - default_hidden_visibility: false, + default_visibility: None, emit_debug_gdb_scripts: true, requires_uwtable: false, default_uwtable: false, @@ -2963,6 +3002,18 @@ impl Target { Some(Ok(())) })).unwrap_or(Ok(())) } ); + ($key_name:ident, Option) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + obj.remove(&name).and_then(|o| o.as_str().and_then(|s| { + match s.parse::() { + Ok(level) => base.$key_name = Some(level), + _ => return Some(Err(format!("'{}' is not a valid value for \ + symbol-visibility. Use 'hidden', 'protected, or 'interposable'.", + s))), + } + Some(Ok(())) + })).unwrap_or(Ok(())) + } ); ($key_name:ident, DebuginfoKind) => ( { let name = (stringify!($key_name)).replace("_", "-"); obj.remove(&name).and_then(|o| o.as_str().and_then(|s| { @@ -3353,7 +3404,7 @@ impl Target { key!(requires_lto, bool); key!(singlethread, bool); key!(no_builtins, bool); - key!(default_hidden_visibility, bool); + key!(default_visibility, Option)?; key!(emit_debug_gdb_scripts, bool); key!(requires_uwtable, bool); key!(default_uwtable, bool); @@ -3633,7 +3684,7 @@ impl ToJson for Target { target_option_val!(requires_lto); target_option_val!(singlethread); target_option_val!(no_builtins); - target_option_val!(default_hidden_visibility); + target_option_val!(default_visibility); target_option_val!(emit_debug_gdb_scripts); target_option_val!(requires_uwtable); target_option_val!(default_uwtable); diff --git a/src/doc/unstable-book/src/compiler-flags/default-hidden-visibility.md b/src/doc/unstable-book/src/compiler-flags/default-hidden-visibility.md deleted file mode 100644 index 579add4a9d985..0000000000000 --- a/src/doc/unstable-book/src/compiler-flags/default-hidden-visibility.md +++ /dev/null @@ -1,12 +0,0 @@ -# `default-hidden-visibility` - -The tracking issue for this feature is: https://github.com/rust-lang/compiler-team/issues/656 - ------------------------- - -This flag can be used to override the target's -[`default_hidden_visibility`](https://doc.rust-lang.org/beta/nightly-rustc/rustc_target/spec/struct.TargetOptions.html#structfield.default_hidden_visibility) -setting. -Using `-Zdefault_hidden_visibility=yes` is roughly equivalent to Clang's -[`-fvisibility=hidden`](https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fvisibility) -cmdline flag. diff --git a/src/doc/unstable-book/src/compiler-flags/default-visibility.md b/src/doc/unstable-book/src/compiler-flags/default-visibility.md new file mode 100644 index 0000000000000..ad9e5d84bba80 --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/default-visibility.md @@ -0,0 +1,44 @@ +# `default-visibility` + +The tracking issue for this feature is: https://github.com/rust-lang/rust/issues/131090 + +------------------------ + +This flag can be used to override the target's +[`default_visibility`](https://doc.rust-lang.org/beta/nightly-rustc/rustc_target/spec/struct.TargetOptions.html#structfield.default_visibility) +setting. + +This option only affects building of shared objects and should have no effect on executables. + +Visibility an be set to one of three options: + +* protected +* hidden +* interposable + +## Hidden visibility + +Using `-Zdefault-visibility=hidden` is roughly equivalent to Clang's +[`-fvisibility=hidden`](https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fvisibility) +cmdline flag. Hidden symbols will not be exported from the created shared object, so cannot be +referenced from other shared objects or from executables. + +## Protected visibility + +Using `-Zdefault-visibility=protected` will cause rust-mangled symbols to be emitted with +"protected" visibility. This signals the compiler, the linker and the runtime linker that these +symbols cannot be overridden by the executable or by other shared objects earlier in the load order. + +This will allow the compiler to emit direct references to symbols, which may improve performance. It +also removes the need for these symbols to be resolved when a shared object built with this option +is loaded. + +Using protected visibility when linking with GNU ld prior to 2.40 will result in linker errors when +building for Linux. Other linkers such as LLD are not affected. + +## Interposable + +Using `-Zdefault-visibility=interposable` will cause symbols to be emitted with "default" +visibility. On platforms that support it, this makes it so that symbols can be interposed, which +means that they can be overridden by symbols with the same name from the executable or by other +shared objects earier in the load order. diff --git a/tests/codegen/default-hidden-visibility.rs b/tests/codegen/default-visibility.rs similarity index 60% rename from tests/codegen/default-hidden-visibility.rs rename to tests/codegen/default-visibility.rs index 2bea8f62a40db..884d386ec20a3 100644 --- a/tests/codegen/default-hidden-visibility.rs +++ b/tests/codegen/default-visibility.rs @@ -1,11 +1,12 @@ -// Verifies that `Session::default_hidden_visibility` is affected when using the related cmdline -// flag. This is a regression test for https://github.com/rust-lang/compiler-team/issues/656. See +// Verifies that `Session::default_visibility` is affected when using the related cmdline +// flag. This is a regression test for https://github.com/rust-lang/compiler-team/issues/782. See // also https://github.com/rust-lang/rust/issues/73295 and // https://github.com/rust-lang/rust/issues/37530. -//@ revisions:DEFAULT YES NO -//@[YES] compile-flags: -Zdefault-hidden-visibility=yes -//@[NO] compile-flags: -Zdefault-hidden-visibility=no +//@ revisions:DEFAULT HIDDEN PROTECTED INTERPOSABLE +//@[HIDDEN] compile-flags: -Zdefault-visibility=hidden +//@[PROTECTED] compile-flags: -Zdefault-visibility=protected +//@[INTERPOSABLE] compile-flags: -Zdefault-visibility=interposable // The test scenario is specifically about visibility of symbols exported out of dynamically linked // libraries. @@ -26,6 +27,7 @@ pub static tested_symbol: [u8; 6] = *b"foobar"; // //@ only-x86_64-unknown-linux-gnu -// DEFAULT: @{{.*}}default_hidden_visibility{{.*}}tested_symbol{{.*}} = constant -// YES: @{{.*}}default_hidden_visibility{{.*}}tested_symbol{{.*}} = hidden constant -// NO: @{{.*}}default_hidden_visibility{{.*}}tested_symbol{{.*}} = constant +// HIDDEN: @{{.*}}default_visibility{{.*}}tested_symbol{{.*}} = hidden constant +// PROTECTED: @{{.*}}default_visibility{{.*}}tested_symbol{{.*}} = protected constant +// INTERPOSABLE: @{{.*}}default_visibility{{.*}}tested_symbol{{.*}} = constant +// DEFAULT: @{{.*}}default_visibility{{.*}}tested_symbol{{.*}} = constant From 13881f5404037e25a88d0b79a836e232dc73b1fc Mon Sep 17 00:00:00 2001 From: lcnr Date: Mon, 30 Sep 2024 10:18:55 +0200 Subject: [PATCH 066/120] add caches to multiple type folders --- .../rustc_infer/src/infer/relate/combine.rs | 7 +- .../src/infer/relate/type_relating.rs | 41 ++++++++- compiler/rustc_infer/src/infer/resolve.rs | 14 ++- compiler/rustc_middle/src/ty/fold.rs | 25 ++++- .../rustc_next_trait_solver/src/resolve.rs | 11 ++- .../src/solve/eval_ctxt/mod.rs | 51 +++++++--- .../src/data_structures/delayed_map.rs | 92 +++++++++++++++++++ .../mod.rs} | 3 + 8 files changed, 222 insertions(+), 22 deletions(-) create mode 100644 compiler/rustc_type_ir/src/data_structures/delayed_map.rs rename compiler/rustc_type_ir/src/{data_structures.rs => data_structures/mod.rs} (92%) diff --git a/compiler/rustc_infer/src/infer/relate/combine.rs b/compiler/rustc_infer/src/infer/relate/combine.rs index e75d7b7db1453..3b2ef3fe981e3 100644 --- a/compiler/rustc_infer/src/infer/relate/combine.rs +++ b/compiler/rustc_infer/src/infer/relate/combine.rs @@ -36,10 +36,15 @@ use crate::traits::{Obligation, PredicateObligation}; #[derive(Clone)] pub struct CombineFields<'infcx, 'tcx> { pub infcx: &'infcx InferCtxt<'tcx>, + // Immutable fields pub trace: TypeTrace<'tcx>, pub param_env: ty::ParamEnv<'tcx>, - pub goals: Vec>>, pub define_opaque_types: DefineOpaqueTypes, + // Mutable fields + // + // Adding any additional field likely requires + // changes to the cache of `TypeRelating`. + pub goals: Vec>>, } impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> { diff --git a/compiler/rustc_infer/src/infer/relate/type_relating.rs b/compiler/rustc_infer/src/infer/relate/type_relating.rs index a402a8009ff8d..c8ced4a2cda85 100644 --- a/compiler/rustc_infer/src/infer/relate/type_relating.rs +++ b/compiler/rustc_infer/src/infer/relate/type_relating.rs @@ -4,6 +4,7 @@ use rustc_middle::ty::relate::{ }; use rustc_middle::ty::{self, Ty, TyCtxt, TyVar}; use rustc_span::Span; +use rustc_type_ir::data_structures::DelayedSet; use tracing::{debug, instrument}; use super::combine::CombineFields; @@ -13,9 +14,36 @@ use crate::infer::{DefineOpaqueTypes, InferCtxt, SubregionOrigin}; /// Enforce that `a` is equal to or a subtype of `b`. pub struct TypeRelating<'combine, 'a, 'tcx> { + // Partially mutable. fields: &'combine mut CombineFields<'a, 'tcx>, + + // Immutable fields. structurally_relate_aliases: StructurallyRelateAliases, ambient_variance: ty::Variance, + + /// The cache has only tracks the `ambient_variance` as its the + /// only field which is mutable and which meaningfully changes + /// the result when relating types. + /// + /// The cache does not track whether the state of the + /// `InferCtxt` has been changed or whether we've added any + /// obligations to `self.fields.goals`. Whether a goal is added + /// once or multiple times is not really meaningful. + /// + /// Changes in the inference state may delay some type inference to + /// the next fulfillment loop. Given that this loop is already + /// necessary, this is also not a meaningful change. Consider + /// the following three relations: + /// ```text + /// Vec sub Vec + /// ?0 eq u32 + /// Vec sub Vec + /// ``` + /// Without a cache, the second `Vec sub Vec` would eagerly + /// constrain `?1` to `u32`. When using the cache entry from the + /// first time we've related these types, this only happens when + /// later proving the `Subtype(?0, ?1)` goal from the first relation. + cache: DelayedSet<(ty::Variance, Ty<'tcx>, Ty<'tcx>)>, } impl<'combine, 'infcx, 'tcx> TypeRelating<'combine, 'infcx, 'tcx> { @@ -24,7 +52,12 @@ impl<'combine, 'infcx, 'tcx> TypeRelating<'combine, 'infcx, 'tcx> { structurally_relate_aliases: StructurallyRelateAliases, ambient_variance: ty::Variance, ) -> TypeRelating<'combine, 'infcx, 'tcx> { - TypeRelating { fields: f, structurally_relate_aliases, ambient_variance } + TypeRelating { + fields: f, + structurally_relate_aliases, + ambient_variance, + cache: Default::default(), + } } } @@ -78,6 +111,10 @@ impl<'tcx> TypeRelation> for TypeRelating<'_, '_, 'tcx> { let a = infcx.shallow_resolve(a); let b = infcx.shallow_resolve(b); + if self.cache.contains(&(self.ambient_variance, a, b)) { + return Ok(a); + } + match (a.kind(), b.kind()) { (&ty::Infer(TyVar(a_id)), &ty::Infer(TyVar(b_id))) => { match self.ambient_variance { @@ -160,6 +197,8 @@ impl<'tcx> TypeRelation> for TypeRelating<'_, '_, 'tcx> { } } + assert!(self.cache.insert((self.ambient_variance, a, b))); + Ok(a) } diff --git a/compiler/rustc_infer/src/infer/resolve.rs b/compiler/rustc_infer/src/infer/resolve.rs index 34625ffb77879..671a66d504f9d 100644 --- a/compiler/rustc_infer/src/infer/resolve.rs +++ b/compiler/rustc_infer/src/infer/resolve.rs @@ -2,6 +2,7 @@ use rustc_middle::bug; use rustc_middle::ty::fold::{FallibleTypeFolder, TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::{self, Const, InferConst, Ty, TyCtxt, TypeFoldable}; +use rustc_type_ir::data_structures::DelayedMap; use super::{FixupError, FixupResult, InferCtxt}; @@ -15,12 +16,15 @@ use super::{FixupError, FixupResult, InferCtxt}; /// points for correctness. pub struct OpportunisticVarResolver<'a, 'tcx> { infcx: &'a InferCtxt<'tcx>, + /// We're able to use a cache here as the folder does + /// not have any mutable state. + cache: DelayedMap, Ty<'tcx>>, } impl<'a, 'tcx> OpportunisticVarResolver<'a, 'tcx> { #[inline] pub fn new(infcx: &'a InferCtxt<'tcx>) -> Self { - OpportunisticVarResolver { infcx } + OpportunisticVarResolver { infcx, cache: Default::default() } } } @@ -33,9 +37,13 @@ impl<'a, 'tcx> TypeFolder> for OpportunisticVarResolver<'a, 'tcx> { fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { if !t.has_non_region_infer() { t // micro-optimize -- if there is nothing in this type that this fold affects... + } else if let Some(&ty) = self.cache.get(&t) { + return ty; } else { - let t = self.infcx.shallow_resolve(t); - t.super_fold_with(self) + let shallow = self.infcx.shallow_resolve(t); + let res = shallow.super_fold_with(self); + assert!(self.cache.insert(t, res)); + res } } diff --git a/compiler/rustc_middle/src/ty/fold.rs b/compiler/rustc_middle/src/ty/fold.rs index 2ee7497497aa9..e152d3f5fbe9f 100644 --- a/compiler/rustc_middle/src/ty/fold.rs +++ b/compiler/rustc_middle/src/ty/fold.rs @@ -1,5 +1,6 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_hir::def_id::DefId; +use rustc_type_ir::data_structures::DelayedMap; pub use rustc_type_ir::fold::{ FallibleTypeFolder, TypeFoldable, TypeFolder, TypeSuperFoldable, shift_region, shift_vars, }; @@ -131,12 +132,20 @@ impl<'a, 'tcx> TypeFolder> for RegionFolder<'a, 'tcx> { /////////////////////////////////////////////////////////////////////////// // Bound vars replacer +/// A delegate used when instantiating bound vars. +/// +/// Any implementation must make sure that each bound variable always +/// gets mapped to the same result. `BoundVarReplacer` caches by using +/// a `DelayedMap` which does not cache the first few types it encounters. pub trait BoundVarReplacerDelegate<'tcx> { fn replace_region(&mut self, br: ty::BoundRegion) -> ty::Region<'tcx>; fn replace_ty(&mut self, bt: ty::BoundTy) -> Ty<'tcx>; fn replace_const(&mut self, bv: ty::BoundVar) -> ty::Const<'tcx>; } +/// A simple delegate taking 3 mutable functions. The used functions must +/// always return the same result for each bound variable, no matter how +/// frequently they are called. pub struct FnMutDelegate<'a, 'tcx> { pub regions: &'a mut (dyn FnMut(ty::BoundRegion) -> ty::Region<'tcx> + 'a), pub types: &'a mut (dyn FnMut(ty::BoundTy) -> Ty<'tcx> + 'a), @@ -164,11 +173,15 @@ struct BoundVarReplacer<'tcx, D> { current_index: ty::DebruijnIndex, delegate: D, + + /// This cache only tracks the `DebruijnIndex` and assumes that it does not matter + /// for the delegate how often its methods get used. + cache: DelayedMap<(ty::DebruijnIndex, Ty<'tcx>), Ty<'tcx>>, } impl<'tcx, D: BoundVarReplacerDelegate<'tcx>> BoundVarReplacer<'tcx, D> { fn new(tcx: TyCtxt<'tcx>, delegate: D) -> Self { - BoundVarReplacer { tcx, current_index: ty::INNERMOST, delegate } + BoundVarReplacer { tcx, current_index: ty::INNERMOST, delegate, cache: Default::default() } } } @@ -197,7 +210,15 @@ where debug_assert!(!ty.has_vars_bound_above(ty::INNERMOST)); ty::fold::shift_vars(self.tcx, ty, self.current_index.as_u32()) } - _ if t.has_vars_bound_at_or_above(self.current_index) => t.super_fold_with(self), + _ if t.has_vars_bound_at_or_above(self.current_index) => { + if let Some(&ty) = self.cache.get(&(self.current_index, t)) { + return ty; + } + + let res = t.super_fold_with(self); + assert!(self.cache.insert((self.current_index, t), res)); + res + } _ => t, } } diff --git a/compiler/rustc_next_trait_solver/src/resolve.rs b/compiler/rustc_next_trait_solver/src/resolve.rs index 132b7400300c5..a37066cec669f 100644 --- a/compiler/rustc_next_trait_solver/src/resolve.rs +++ b/compiler/rustc_next_trait_solver/src/resolve.rs @@ -1,3 +1,4 @@ +use rustc_type_ir::data_structures::DelayedMap; use rustc_type_ir::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_type_ir::inherent::*; use rustc_type_ir::visit::TypeVisitableExt; @@ -15,11 +16,12 @@ where I: Interner, { delegate: &'a D, + cache: DelayedMap, } impl<'a, D: SolverDelegate> EagerResolver<'a, D> { pub fn new(delegate: &'a D) -> Self { - EagerResolver { delegate } + EagerResolver { delegate, cache: Default::default() } } } @@ -42,7 +44,12 @@ impl, I: Interner> TypeFolder for EagerResolv ty::Infer(ty::FloatVar(vid)) => self.delegate.opportunistic_resolve_float_var(vid), _ => { if t.has_infer() { - t.super_fold_with(self) + if let Some(&ty) = self.cache.get(&t) { + return ty; + } + let res = t.super_fold_with(self); + assert!(self.cache.insert(t, res)); + res } else { t } diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 12ad0724b5cb1..12b4b3cb3a9d8 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -3,7 +3,7 @@ use std::ops::ControlFlow; use derive_where::derive_where; #[cfg(feature = "nightly")] use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable}; -use rustc_type_ir::data_structures::ensure_sufficient_stack; +use rustc_type_ir::data_structures::{HashMap, HashSet, ensure_sufficient_stack}; use rustc_type_ir::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_type_ir::inherent::*; use rustc_type_ir::relate::Relate; @@ -579,18 +579,16 @@ where #[instrument(level = "trace", skip(self))] pub(super) fn add_normalizes_to_goal(&mut self, mut goal: Goal>) { - goal.predicate = goal - .predicate - .fold_with(&mut ReplaceAliasWithInfer { ecx: self, param_env: goal.param_env }); + goal.predicate = + goal.predicate.fold_with(&mut ReplaceAliasWithInfer::new(self, goal.param_env)); self.inspect.add_normalizes_to_goal(self.delegate, self.max_input_universe, goal); self.nested_goals.normalizes_to_goals.push(goal); } #[instrument(level = "debug", skip(self))] pub(super) fn add_goal(&mut self, source: GoalSource, mut goal: Goal) { - goal.predicate = goal - .predicate - .fold_with(&mut ReplaceAliasWithInfer { ecx: self, param_env: goal.param_env }); + goal.predicate = + goal.predicate.fold_with(&mut ReplaceAliasWithInfer::new(self, goal.param_env)); self.inspect.add_goal(self.delegate, self.max_input_universe, source, goal); self.nested_goals.goals.push((source, goal)); } @@ -654,6 +652,7 @@ where term: I::Term, universe_of_term: ty::UniverseIndex, delegate: &'a D, + cache: HashSet, } impl, I: Interner> ContainsTermOrNotNameable<'_, D, I> { @@ -671,6 +670,10 @@ where { type Result = ControlFlow<()>; fn visit_ty(&mut self, t: I::Ty) -> Self::Result { + if self.cache.contains(&t) { + return ControlFlow::Continue(()); + } + match t.kind() { ty::Infer(ty::TyVar(vid)) => { if let ty::TermKind::Ty(term) = self.term.kind() { @@ -683,17 +686,18 @@ where } } - self.check_nameable(self.delegate.universe_of_ty(vid).unwrap()) + self.check_nameable(self.delegate.universe_of_ty(vid).unwrap())?; } - ty::Placeholder(p) => self.check_nameable(p.universe()), + ty::Placeholder(p) => self.check_nameable(p.universe())?, _ => { if t.has_non_region_infer() || t.has_placeholders() { - t.super_visit_with(self) - } else { - ControlFlow::Continue(()) + t.super_visit_with(self)? } } } + + assert!(self.cache.insert(t)); + ControlFlow::Continue(()) } fn visit_const(&mut self, c: I::Const) -> Self::Result { @@ -728,6 +732,7 @@ where delegate: self.delegate, universe_of_term, term: goal.predicate.term, + cache: Default::default(), }; goal.predicate.alias.visit_with(&mut visitor).is_continue() && goal.param_env.visit_with(&mut visitor).is_continue() @@ -1015,6 +1020,17 @@ where { ecx: &'me mut EvalCtxt<'a, D>, param_env: I::ParamEnv, + cache: HashMap, +} + +impl<'me, 'a, D, I> ReplaceAliasWithInfer<'me, 'a, D, I> +where + D: SolverDelegate, + I: Interner, +{ + fn new(ecx: &'me mut EvalCtxt<'a, D>, param_env: I::ParamEnv) -> Self { + ReplaceAliasWithInfer { ecx, param_env, cache: Default::default() } + } } impl TypeFolder for ReplaceAliasWithInfer<'_, '_, D, I> @@ -1041,7 +1057,16 @@ where ); infer_ty } - _ => ty.super_fold_with(self), + _ if ty.has_aliases() => { + if let Some(&entry) = self.cache.get(&ty) { + return entry; + } + + let res = ty.super_fold_with(self); + assert!(self.cache.insert(ty, res).is_none()); + res + } + _ => ty, } } diff --git a/compiler/rustc_type_ir/src/data_structures/delayed_map.rs b/compiler/rustc_type_ir/src/data_structures/delayed_map.rs new file mode 100644 index 0000000000000..7e7406e37066e --- /dev/null +++ b/compiler/rustc_type_ir/src/data_structures/delayed_map.rs @@ -0,0 +1,92 @@ +use std::hash::Hash; + +use crate::data_structures::{HashMap, HashSet}; + +const CACHE_CUTOFF: u32 = 32; + +/// A hashmap which only starts hashing after ignoring the first few inputs. +/// +/// This is used in type folders as in nearly all cases caching is not worth it +/// as nearly all folded types are tiny. However, there are very rare incredibly +/// large types for which caching is necessary to avoid hangs. +#[derive(Debug)] +pub struct DelayedMap { + cache: HashMap, + count: u32, +} + +impl Default for DelayedMap { + fn default() -> Self { + DelayedMap { cache: Default::default(), count: 0 } + } +} + +impl DelayedMap { + #[inline(always)] + pub fn insert(&mut self, key: K, value: V) -> bool { + if self.count >= CACHE_CUTOFF { + self.cold_insert(key, value) + } else { + self.count += 1; + true + } + } + + #[cold] + #[inline(never)] + fn cold_insert(&mut self, key: K, value: V) -> bool { + self.cache.insert(key, value).is_none() + } + + #[inline(always)] + pub fn get(&self, key: &K) -> Option<&V> { + if self.cache.is_empty() { None } else { self.cold_get(key) } + } + + #[cold] + #[inline(never)] + fn cold_get(&self, key: &K) -> Option<&V> { + self.cache.get(key) + } +} + +#[derive(Debug)] +pub struct DelayedSet { + cache: HashSet, + count: u32, +} + +impl Default for DelayedSet { + fn default() -> Self { + DelayedSet { cache: Default::default(), count: 0 } + } +} + +impl DelayedSet { + #[inline(always)] + pub fn insert(&mut self, value: T) -> bool { + if self.count >= CACHE_CUTOFF { + self.cold_insert(value) + } else { + self.count += 1; + true + } + } + + #[cold] + #[inline(never)] + fn cold_insert(&mut self, value: T) -> bool { + self.cache.insert(value) + } + + #[inline(always)] + pub fn contains(&self, value: &T) -> bool { + !self.cache.is_empty() && self.cold_contains(value) + } + + #[cold] + #[inline(never)] + fn cold_contains(&self, value: &T) -> bool { + self.cache.contains(value) + } +} diff --git a/compiler/rustc_type_ir/src/data_structures.rs b/compiler/rustc_type_ir/src/data_structures/mod.rs similarity index 92% rename from compiler/rustc_type_ir/src/data_structures.rs rename to compiler/rustc_type_ir/src/data_structures/mod.rs index 96036e53b0abb..d9766d91845cb 100644 --- a/compiler/rustc_type_ir/src/data_structures.rs +++ b/compiler/rustc_type_ir/src/data_structures/mod.rs @@ -6,6 +6,8 @@ pub use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet}; pub type IndexMap = indexmap::IndexMap>; pub type IndexSet = indexmap::IndexSet>; +mod delayed_map; + #[cfg(feature = "nightly")] mod impl_ { pub use rustc_data_structures::sso::{SsoHashMap, SsoHashSet}; @@ -24,4 +26,5 @@ mod impl_ { } } +pub use delayed_map::{DelayedMap, DelayedSet}; pub use impl_::*; From a7b114420c70a1c0bc082d078d5284af6ff4af23 Mon Sep 17 00:00:00 2001 From: lcnr Date: Mon, 30 Sep 2024 10:19:00 +0200 Subject: [PATCH 067/120] add tests --- .../coherence-alias-hang-with-region.rs | 30 ++++++++++++++++ .../overflow}/coherence-alias-hang.rs | 11 ++++-- .../next-solver/overflow/nalgebra-hang.rs | 35 +++++++++++++++++++ 3 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 tests/ui/traits/next-solver/overflow/coherence-alias-hang-with-region.rs rename tests/ui/traits/{ => next-solver/overflow}/coherence-alias-hang.rs (57%) create mode 100644 tests/ui/traits/next-solver/overflow/nalgebra-hang.rs diff --git a/tests/ui/traits/next-solver/overflow/coherence-alias-hang-with-region.rs b/tests/ui/traits/next-solver/overflow/coherence-alias-hang-with-region.rs new file mode 100644 index 0000000000000..4ade8a13ca914 --- /dev/null +++ b/tests/ui/traits/next-solver/overflow/coherence-alias-hang-with-region.rs @@ -0,0 +1,30 @@ +//@ check-pass +//@ revisions: ai ia ii +//@ compile-flags: -Znext-solver=coherence + +// Regression test for nalgebra hang . + +#![feature(lazy_type_alias)] +#![allow(incomplete_features)] + +type Id = T; +trait NotImplemented {} + +struct W<'a, T: ?Sized, U: ?Sized>(&'a (), *const T, *const U); +trait Trait { + type Assoc: ?Sized; +} +impl<'a, T: ?Sized + Trait> Trait for W<'a, T, T> { + #[cfg(ai)] + type Assoc = W<'a, T::Assoc, Id>; + #[cfg(ia)] + type Assoc = W<'a, Id, T::Assoc>; + #[cfg(ii)] + type Assoc = W<'a, Id, Id>; +} + +trait Overlap {} +impl<'a, T: ?Sized> Overlap for W<'a, T, T> {} +impl Overlap for T {} + +fn main() {} diff --git a/tests/ui/traits/coherence-alias-hang.rs b/tests/ui/traits/next-solver/overflow/coherence-alias-hang.rs similarity index 57% rename from tests/ui/traits/coherence-alias-hang.rs rename to tests/ui/traits/next-solver/overflow/coherence-alias-hang.rs index c2b4d2e42d214..0d5f42231e4ad 100644 --- a/tests/ui/traits/coherence-alias-hang.rs +++ b/tests/ui/traits/next-solver/overflow/coherence-alias-hang.rs @@ -1,6 +1,8 @@ //@ check-pass -//@ revisions: current next -//[next]@ compile-flags: -Znext-solver +//@ revisions: ai_current ai_next ia_current ia_next ii_current ii_next +//@[ai_next] compile-flags: -Znext-solver +//@[ia_next] compile-flags: -Znext-solver +//@[ii_next] compile-flags: -Znext-solver // Regression test for nalgebra hang . @@ -15,7 +17,12 @@ trait Trait { type Assoc: ?Sized; } impl Trait for W { + #[cfg(any(ai_current, ai_next))] type Assoc = W>; + #[cfg(any(ia_current, ia_next))] + type Assoc = W, T::Assoc>; + #[cfg(any(ii_current, ii_next))] + type Assoc = W, Id>; } trait Overlap {} diff --git a/tests/ui/traits/next-solver/overflow/nalgebra-hang.rs b/tests/ui/traits/next-solver/overflow/nalgebra-hang.rs new file mode 100644 index 0000000000000..4bc6039c57dfd --- /dev/null +++ b/tests/ui/traits/next-solver/overflow/nalgebra-hang.rs @@ -0,0 +1,35 @@ +//@ check-pass +//@ revisions: current next +//@[next] compile-flags: -Znext-solver + +// Regression test for nalgebra hang from +// https://github.com/rust-lang/rust/pull/130654#issuecomment-2365465354 +trait HasAlias {} + +struct Dummy; +trait DummyTrait { + type DummyType; +} +impl DummyTrait for Dummy { + type DummyType = T; +} +type AliasOf = ::DummyType; + +struct Matrix(T, S); +type OMatrix = Matrix>; + +impl HasAlias for OMatrix {} + +trait SimdValue { + type Element; +} +impl> SimdValue for OMatrix { + type Element = OMatrix; +} + +trait Unimplemented {} +pub trait MyFrom {} +impl MyFrom for T {} +impl> MyFrom for OMatrix {} + +fn main() {} From f06a46ee4dc7587c5f353dc2e396e996068cebac Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Thu, 26 Sep 2024 14:08:22 +0200 Subject: [PATCH 068/120] consider `wait()` calls in nested bodies --- clippy_lints/src/zombie_processes.rs | 7 +++++++ tests/ui/zombie_processes.rs | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/clippy_lints/src/zombie_processes.rs b/clippy_lints/src/zombie_processes.rs index 58d71bee299a4..8d9241cc7d9a2 100644 --- a/clippy_lints/src/zombie_processes.rs +++ b/clippy_lints/src/zombie_processes.rs @@ -6,6 +6,7 @@ use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_local}; use rustc_hir::{Expr, ExprKind, HirId, LetStmt, Node, PatKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::nested_filter; use rustc_session::declare_lint_pass; use rustc_span::sym; use std::ops::ControlFlow; @@ -119,6 +120,7 @@ enum WaitFinder<'a, 'tcx> { } impl<'tcx> Visitor<'tcx> for WaitFinder<'_, 'tcx> { + type NestedFilter = nested_filter::OnlyBodies; type Result = ControlFlow; fn visit_local(&mut self, l: &'tcx LetStmt<'tcx>) -> Self::Result { @@ -204,6 +206,11 @@ impl<'tcx> Visitor<'tcx> for WaitFinder<'_, 'tcx> { walk_expr(self, ex) } + + fn nested_visit_map(&mut self) -> Self::Map { + let (Self::Found(cx, _) | Self::WalkUpTo(cx, _)) = self; + cx.tcx.hir() + } } /// This function has shared logic between the different kinds of nodes that can trigger the lint. diff --git a/tests/ui/zombie_processes.rs b/tests/ui/zombie_processes.rs index a2abc7fc3a179..b41bcce3f7f7c 100644 --- a/tests/ui/zombie_processes.rs +++ b/tests/ui/zombie_processes.rs @@ -131,6 +131,13 @@ fn main() { } x.wait().unwrap(); } + + { + let mut x = Command::new("").spawn().unwrap(); + std::thread::spawn(move || { + x.wait().unwrap(); + }); + } } fn process_child(c: Child) { From 1c7e8246d53da3b68589c9fe2aa2c830fae3f164 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?= Date: Tue, 1 Oct 2024 18:35:26 +0000 Subject: [PATCH 069/120] Revert "Drop conditionally applied cargo `-Zon-broken-pipe=kill` flags" This reverts commit 5a7058c5a542ec42d1fa9b524f7b4f7d6845d1e9. In [#131059] we found out that `-Zon-broken-pipe=kill` is actually **load-bearing** [1] for (at least) `rustc` and `rustdoc` to have the kill-process-on-broken-pipe behavior, e.g. `rustc --print=sysroot | false` will ICE and `rustdoc --print=sysroot | false` will panic on a broken pipe. [#131059]: https://github.com/rust-lang/rust/issues/131059 [1]: https://github.com/rust-lang/rust/issues/131059#issuecomment-2385822033 --- src/bootstrap/src/core/build_steps/compile.rs | 4 ++++ src/bootstrap/src/core/build_steps/tool.rs | 11 +++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index bb1d8d279288d..eaa982d4e2bbd 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -1053,6 +1053,10 @@ pub fn rustc_cargo( cargo.rustdocflag("-Zcrate-attr=warn(rust_2018_idioms)"); + // If the rustc output is piped to e.g. `head -n1` we want the process to be + // killed, rather than having an error bubble up and cause a panic. + cargo.rustflag("-Zon-broken-pipe=kill"); + if builder.config.llvm_enzyme { cargo.rustflag("-l").rustflag("Enzyme-19"); } diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index fa2c1b8360f4c..64dfe054d9c77 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -200,10 +200,6 @@ pub fn prepare_tool_cargo( cargo.arg("--features").arg(features.join(", ")); } - // Warning: be very careful with RUSTFLAGS or command-line options, as conditionally applied - // RUSTFLAGS or cli flags can lead to hard-to-diagnose rebuilds due to flag differences, causing - // previous tool build artifacts to get invalidated. - // Enable internal lints for clippy and rustdoc // NOTE: this doesn't enable lints for any other tools unless they explicitly add `#![warn(rustc::internal)]` // See https://github.com/rust-lang/rust/pull/80573#issuecomment-754010776 @@ -213,6 +209,13 @@ pub fn prepare_tool_cargo( // See https://github.com/rust-lang/rust/issues/116538 cargo.rustflag("-Zunstable-options"); + // `-Zon-broken-pipe=kill` breaks cargo tests + if !path.ends_with("cargo") { + // If the output is piped to e.g. `head -n1` we want the process to be killed, + // rather than having an error bubble up and cause a panic. + cargo.rustflag("-Zon-broken-pipe=kill"); + } + cargo } From c4ce8c114b06840c3521a189ee44958b713fb33a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 29 Sep 2024 11:53:23 +0200 Subject: [PATCH 070/120] make InterpResult a dedicated type to avoid accidentally discarding the error --- .../src/const_eval/dummy_machine.rs | 7 +- .../src/const_eval/eval_queries.rs | 53 ++-- .../src/const_eval/machine.rs | 68 ++--- .../rustc_const_eval/src/const_eval/mod.rs | 20 +- .../src/const_eval/valtrees.rs | 8 +- .../rustc_const_eval/src/interpret/call.rs | 58 ++--- .../rustc_const_eval/src/interpret/cast.rs | 24 +- .../src/interpret/discriminant.rs | 19 +- .../src/interpret/eval_context.rs | 49 ++-- .../rustc_const_eval/src/interpret/intern.rs | 10 +- .../src/interpret/intrinsics.rs | 27 +- .../rustc_const_eval/src/interpret/machine.rs | 49 ++-- .../rustc_const_eval/src/interpret/memory.rs | 148 +++++------ .../rustc_const_eval/src/interpret/operand.rs | 51 ++-- .../src/interpret/operator.rs | 37 +-- .../rustc_const_eval/src/interpret/place.rs | 76 +++--- .../src/interpret/projection.rs | 20 +- .../rustc_const_eval/src/interpret/stack.rs | 26 +- .../rustc_const_eval/src/interpret/step.rs | 28 +- .../rustc_const_eval/src/interpret/traits.rs | 14 +- .../rustc_const_eval/src/interpret/util.rs | 8 +- .../src/interpret/validity.rs | 182 +++++++------ .../rustc_const_eval/src/interpret/visitor.rs | 10 +- .../src/util/check_validity_requirement.rs | 3 +- compiler/rustc_middle/src/lib.rs | 2 + compiler/rustc_middle/src/mir/consts.rs | 4 +- .../src/mir/interpret/allocation.rs | 15 +- .../rustc_middle/src/mir/interpret/error.rs | 239 ++++++++++++++---- .../rustc_middle/src/mir/interpret/mod.rs | 12 +- .../rustc_middle/src/mir/interpret/value.rs | 38 +-- compiler/rustc_middle/src/ty/consts.rs | 2 +- .../src/dataflow_const_prop.rs | 64 +++-- compiler/rustc_mir_transform/src/gvn.rs | 115 ++++----- .../rustc_mir_transform/src/jump_threading.rs | 44 ++-- .../src/known_panics_lint.rs | 56 ++-- src/tools/clippy/clippy_utils/src/consts.rs | 10 +- src/tools/miri/src/alloc_addresses/mod.rs | 24 +- src/tools/miri/src/borrow_tracker/mod.rs | 8 +- .../src/borrow_tracker/stacked_borrows/mod.rs | 52 ++-- .../borrow_tracker/stacked_borrows/stack.rs | 14 +- .../tree_borrows/diagnostics.rs | 4 +- .../src/borrow_tracker/tree_borrows/mod.rs | 34 +-- .../src/borrow_tracker/tree_borrows/tree.rs | 6 +- src/tools/miri/src/concurrency/data_race.rs | 36 +-- src/tools/miri/src/concurrency/init_once.rs | 8 +- src/tools/miri/src/concurrency/sync.rs | 46 ++-- src/tools/miri/src/concurrency/thread.rs | 34 +-- src/tools/miri/src/concurrency/weak_memory.rs | 22 +- src/tools/miri/src/diagnostics.rs | 6 +- src/tools/miri/src/eval.rs | 10 +- src/tools/miri/src/helpers.rs | 92 +++---- src/tools/miri/src/intrinsics/atomic.rs | 22 +- src/tools/miri/src/intrinsics/mod.rs | 18 +- src/tools/miri/src/intrinsics/simd.rs | 7 +- src/tools/miri/src/machine.rs | 48 ++-- src/tools/miri/src/operator.rs | 2 +- src/tools/miri/src/shims/alloc.rs | 20 +- src/tools/miri/src/shims/backtrace.rs | 8 +- src/tools/miri/src/shims/env.rs | 8 +- src/tools/miri/src/shims/extern_static.rs | 8 +- src/tools/miri/src/shims/foreign_items.rs | 35 +-- src/tools/miri/src/shims/native_lib.rs | 10 +- src/tools/miri/src/shims/os_str.rs | 25 +- src/tools/miri/src/shims/panic.rs | 10 +- src/tools/miri/src/shims/time.rs | 32 +-- src/tools/miri/src/shims/tls.rs | 32 +-- .../src/shims/unix/android/foreign_items.rs | 4 +- src/tools/miri/src/shims/unix/env.rs | 40 +-- src/tools/miri/src/shims/unix/fd.rs | 46 ++-- .../miri/src/shims/unix/foreign_items.rs | 6 +- .../src/shims/unix/freebsd/foreign_items.rs | 4 +- src/tools/miri/src/shims/unix/fs.rs | 225 +++++++++-------- src/tools/miri/src/shims/unix/linux/epoll.rs | 40 +-- .../miri/src/shims/unix/linux/eventfd.rs | 12 +- .../src/shims/unix/linux/foreign_items.rs | 8 +- src/tools/miri/src/shims/unix/linux/mem.rs | 6 +- src/tools/miri/src/shims/unix/linux/sync.rs | 8 +- .../src/shims/unix/macos/foreign_items.rs | 4 +- src/tools/miri/src/shims/unix/macos/sync.rs | 12 +- src/tools/miri/src/shims/unix/mem.rs | 20 +- .../src/shims/unix/solarish/foreign_items.rs | 4 +- src/tools/miri/src/shims/unix/sync.rs | 92 +++---- src/tools/miri/src/shims/unix/thread.rs | 16 +- .../miri/src/shims/unix/unnamed_socket.rs | 8 +- .../miri/src/shims/wasi/foreign_items.rs | 4 +- src/tools/miri/src/shims/windows/env.rs | 30 +-- .../miri/src/shims/windows/foreign_items.rs | 14 +- src/tools/miri/src/shims/windows/handle.rs | 6 +- src/tools/miri/src/shims/windows/sync.rs | 18 +- src/tools/miri/src/shims/windows/thread.rs | 2 +- src/tools/miri/src/shims/x86/aesni.rs | 6 +- src/tools/miri/src/shims/x86/avx.rs | 4 +- src/tools/miri/src/shims/x86/avx2.rs | 4 +- src/tools/miri/src/shims/x86/bmi.rs | 6 +- src/tools/miri/src/shims/x86/mod.rs | 88 +++---- src/tools/miri/src/shims/x86/sha.rs | 8 +- src/tools/miri/src/shims/x86/sse.rs | 4 +- src/tools/miri/src/shims/x86/sse2.rs | 4 +- src/tools/miri/src/shims/x86/sse3.rs | 4 +- src/tools/miri/src/shims/x86/sse41.rs | 4 +- src/tools/miri/src/shims/x86/sse42.rs | 16 +- src/tools/miri/src/shims/x86/ssse3.rs | 4 +- 102 files changed, 1590 insertions(+), 1468 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs b/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs index ed1c912e6cb03..743924faa21d8 100644 --- a/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs @@ -6,7 +6,8 @@ use rustc_middle::{bug, span_bug, ty}; use rustc_span::def_id::DefId; use crate::interpret::{ - self, HasStaticRootDefId, ImmTy, Immediate, InterpCx, PointerArithmetic, throw_machine_stop, + self, HasStaticRootDefId, ImmTy, Immediate, InterpCx, PointerArithmetic, interp_ok, + throw_machine_stop, }; /// Macro for machine-specific `InterpError` without allocation. @@ -79,7 +80,7 @@ impl<'tcx> interpret::Machine<'tcx> for DummyMachine { throw_machine_stop_str!("can't access mutable globals in ConstProp"); } - Ok(()) + interp_ok(()) } fn find_mir_or_eval_fn( @@ -127,7 +128,7 @@ impl<'tcx> interpret::Machine<'tcx> for DummyMachine { right: &interpret::ImmTy<'tcx, Self::Provenance>, ) -> interpret::InterpResult<'tcx, ImmTy<'tcx, Self::Provenance>> { use rustc_middle::mir::BinOp::*; - Ok(match bin_op { + interp_ok(match bin_op { Eq | Ne | Lt | Le | Gt | Ge => { // Types can differ, e.g. fn ptrs with different `for`. assert_eq!(left.layout.abi, right.layout.abi); diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index a241edbd77ad9..672353e629d44 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -20,7 +20,7 @@ use crate::const_eval::CheckAlignment; use crate::interpret::{ CtfeValidationMode, GlobalId, Immediate, InternKind, InternResult, InterpCx, InterpError, InterpResult, MPlaceTy, MemoryKind, OpTy, RefTracking, StackPopCleanup, create_static_alloc, - eval_nullary_intrinsic, intern_const_alloc_recursive, throw_exhaust, + eval_nullary_intrinsic, intern_const_alloc_recursive, interp_ok, throw_exhaust, }; use crate::{CTRL_C_RECEIVED, errors}; @@ -98,19 +98,19 @@ fn eval_body_using_ecx<'tcx, R: InterpretationResult<'tcx>>( return Err(ecx .tcx .dcx() - .emit_err(errors::DanglingPtrInFinal { span: ecx.tcx.span, kind: intern_kind }) - .into()); + .emit_err(errors::DanglingPtrInFinal { span: ecx.tcx.span, kind: intern_kind })) + .into(); } Err(InternResult::FoundBadMutablePointer) => { return Err(ecx .tcx .dcx() - .emit_err(errors::MutablePtrInFinal { span: ecx.tcx.span, kind: intern_kind }) - .into()); + .emit_err(errors::MutablePtrInFinal { span: ecx.tcx.span, kind: intern_kind })) + .into(); } } - Ok(R::make_result(ret, ecx)) + interp_ok(R::make_result(ret, ecx)) } /// The `InterpCx` is only meant to be used to do field and index projections into constants for @@ -147,7 +147,8 @@ pub fn mk_eval_cx_for_const_val<'tcx>( ty: Ty<'tcx>, ) -> Option<(CompileTimeInterpCx<'tcx>, OpTy<'tcx>)> { let ecx = mk_eval_cx_to_read_const_val(tcx.tcx, tcx.span, param_env, CanAccessMutGlobal::No); - let op = ecx.const_val_to_op(val, ty, None).ok()?; + // FIXME: is it a problem to discard the error here? + let op = ecx.const_val_to_op(val, ty, None).discard_err()?; Some((ecx, op)) } @@ -185,12 +186,16 @@ pub(super) fn op_to_const<'tcx>( _ => false, }; let immediate = if force_as_immediate { - match ecx.read_immediate(op) { + match ecx.read_immediate(op).report_err() { Ok(imm) => Right(imm), - Err(err) if !for_diagnostics => { - panic!("normalization works on validated constants: {err:?}") + Err(err) => { + if for_diagnostics { + // This discard the error, but for diagnostics that's okay. + op.as_mplace_or_imm() + } else { + panic!("normalization works on validated constants: {err:?}") + } } - _ => op.as_mplace_or_imm(), } } else { op.as_mplace_or_imm() @@ -283,17 +288,19 @@ pub fn eval_to_const_value_raw_provider<'tcx>( let ty::FnDef(_, args) = ty.kind() else { bug!("intrinsic with type {:?}", ty); }; - return eval_nullary_intrinsic(tcx, key.param_env, def_id, args).map_err(|error| { - let span = tcx.def_span(def_id); - - super::report( - tcx, - error.into_kind(), - span, - || (span, vec![]), - |span, _| errors::NullaryIntrinsicError { span }, - ) - }); + return eval_nullary_intrinsic(tcx, key.param_env, def_id, args).report_err().map_err( + |error| { + let span = tcx.def_span(def_id); + + super::report( + tcx, + error.into_kind(), + span, + || (span, vec![]), + |span, _| errors::NullaryIntrinsicError { span }, + ) + }, + ); } tcx.eval_to_allocation_raw(key).map(|val| turn_into_const_value(tcx, val, key)) @@ -376,6 +383,7 @@ fn eval_in_interpreter<'tcx, R: InterpretationResult<'tcx>>( ); let res = ecx.load_mir(cid.instance.def, cid.promoted); res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, body)) + .report_err() .map_err(|error| report_eval_error(&ecx, cid, error)) } @@ -400,6 +408,7 @@ fn const_validate_mplace<'tcx>( } }; ecx.const_validate_operand(&mplace.into(), path, &mut ref_tracking, mode) + .report_err() // Instead of just reporting the `InterpError` via the usual machinery, we give a more targeted // error about the validation failure. .map_err(|error| report_validation_error(&ecx, cid, error, alloc_id))?; diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 6d74998b7d88e..4aec74595bc86 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -24,8 +24,8 @@ use crate::fluent_generated as fluent; use crate::interpret::{ self, AllocId, AllocRange, ConstAllocation, CtfeProvenance, FnArg, Frame, GlobalAlloc, ImmTy, InterpCx, InterpResult, MPlaceTy, OpTy, Pointer, PointerArithmetic, RangeSet, Scalar, - StackPopCleanup, compile_time_machine, err_ub, throw_exhaust, throw_inval, throw_ub_custom, - throw_unsup, throw_unsup_format, + StackPopCleanup, compile_time_machine, interp_ok, throw_exhaust, throw_inval, throw_ub, + throw_ub_custom, throw_unsup, throw_unsup_format, }; /// When hitting this many interpreted terminators we emit a deny by default lint @@ -247,7 +247,7 @@ impl<'tcx> CompileTimeInterpCx<'tcx> { let msg = Symbol::intern(self.read_str(&msg_place)?); let span = self.find_closest_untracked_caller_location(); let (file, line, col) = self.location_triple_for_span(span); - return Err(ConstEvalErrKind::Panic { msg, file, line, col }.into()); + return Err(ConstEvalErrKind::Panic { msg, file, line, col }).into(); } else if self.tcx.is_lang_item(def_id, LangItem::PanicFmt) { // For panic_fmt, call const_panic_fmt instead. let const_def_id = self.tcx.require_lang_item(LangItem::ConstPanicFmt, None); @@ -259,16 +259,16 @@ impl<'tcx> CompileTimeInterpCx<'tcx> { self.cur_span(), ); - return Ok(Some(new_instance)); + return interp_ok(Some(new_instance)); } else if self.tcx.is_lang_item(def_id, LangItem::AlignOffset) { let args = self.copy_fn_args(args); // For align_offset, we replace the function call if the pointer has no address. match self.align_offset(instance, &args, dest, ret)? { - ControlFlow::Continue(()) => return Ok(Some(instance)), - ControlFlow::Break(()) => return Ok(None), + ControlFlow::Continue(()) => return interp_ok(Some(instance)), + ControlFlow::Break(()) => return interp_ok(None), } } - Ok(Some(instance)) + interp_ok(Some(instance)) } /// `align_offset(ptr, target_align)` needs special handling in const eval, because the pointer @@ -323,25 +323,25 @@ impl<'tcx> CompileTimeInterpCx<'tcx> { dest, StackPopCleanup::Goto { ret, unwind: mir::UnwindAction::Unreachable }, )?; - Ok(ControlFlow::Break(())) + interp_ok(ControlFlow::Break(())) } else { // Not alignable in const, return `usize::MAX`. let usize_max = Scalar::from_target_usize(self.target_usize_max(), self); self.write_scalar(usize_max, dest)?; self.return_to_block(ret)?; - Ok(ControlFlow::Break(())) + interp_ok(ControlFlow::Break(())) } } Err(_addr) => { // The pointer has an address, continue with function call. - Ok(ControlFlow::Continue(())) + interp_ok(ControlFlow::Continue(())) } } } /// See documentation on the `ptr_guaranteed_cmp` intrinsic. fn guaranteed_cmp(&mut self, a: Scalar, b: Scalar) -> InterpResult<'tcx, u8> { - Ok(match (a, b) { + interp_ok(match (a, b) { // Comparisons between integers are always known. (Scalar::Int { .. }, Scalar::Int { .. }) => { if a == b { @@ -403,8 +403,8 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { instance: ty::InstanceKind<'tcx>, ) -> InterpResult<'tcx, &'tcx mir::Body<'tcx>> { match instance { - ty::InstanceKind::Item(def) => Ok(ecx.tcx.mir_for_ctfe(def)), - _ => Ok(ecx.tcx.instance_mir(instance)), + ty::InstanceKind::Item(def) => interp_ok(ecx.tcx.mir_for_ctfe(def)), + _ => interp_ok(ecx.tcx.instance_mir(instance)), } } @@ -422,7 +422,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { // Replace some functions. let Some(instance) = ecx.hook_special_const_fn(orig_instance, args, dest, ret)? else { // Call has already been handled. - return Ok(None); + return interp_ok(None); }; // Only check non-glue functions @@ -444,14 +444,14 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { // This is a const fn. Call it. // In case of replacement, we return the *original* instance to make backtraces work out // (and we hope this does not confuse the FnAbi checks too much). - Ok(Some((ecx.load_mir(instance.def, None)?, orig_instance))) + interp_ok(Some((ecx.load_mir(instance.def, None)?, orig_instance))) } fn panic_nounwind(ecx: &mut InterpCx<'tcx, Self>, msg: &str) -> InterpResult<'tcx> { let msg = Symbol::intern(msg); let span = ecx.find_closest_untracked_caller_location(); let (file, line, col) = ecx.location_triple_for_span(span); - Err(ConstEvalErrKind::Panic { msg, file, line, col }.into()) + Err(ConstEvalErrKind::Panic { msg, file, line, col }).into() } fn call_intrinsic( @@ -464,7 +464,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { ) -> InterpResult<'tcx, Option>> { // Shared intrinsics. if ecx.eval_intrinsic(instance, args, dest, target)? { - return Ok(None); + return interp_ok(None); } let intrinsic_name = ecx.tcx.item_name(instance.def_id()); @@ -541,7 +541,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { "intrinsic `{intrinsic_name}` is not supported at compile-time" ); } - return Ok(Some(ty::Instance { + return interp_ok(Some(ty::Instance { def: ty::InstanceKind::Item(instance.def_id()), args: instance.args, })); @@ -550,7 +550,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { // Intrinsic is done, jump to next block. ecx.return_to_block(target)?; - Ok(None) + interp_ok(None) } fn assert_panic( @@ -581,7 +581,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { } } }; - Err(ConstEvalErrKind::AssertFailure(err).into()) + Err(ConstEvalErrKind::AssertFailure(err)).into() } fn binary_ptr_op( @@ -652,7 +652,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { } } - Ok(()) + interp_ok(()) } #[inline(always)] @@ -670,7 +670,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { if !ecx.recursion_limit.value_within_limit(ecx.stack().len() + 1) { throw_exhaust!(StackFrameLimitReached) } else { - Ok(frame) + interp_ok(frame) } } @@ -700,22 +700,22 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { if is_write { // Write access. These are never allowed, but we give a targeted error message. match alloc.mutability { - Mutability::Not => Err(err_ub!(WriteToReadOnly(alloc_id)).into()), - Mutability::Mut => Err(ConstEvalErrKind::ModifiedGlobal.into()), + Mutability::Not => throw_ub!(WriteToReadOnly(alloc_id)), + Mutability::Mut => Err(ConstEvalErrKind::ModifiedGlobal).into(), } } else { // Read access. These are usually allowed, with some exceptions. if machine.can_access_mut_global == CanAccessMutGlobal::Yes { // Machine configuration allows us read from anything (e.g., `static` initializer). - Ok(()) + interp_ok(()) } else if alloc.mutability == Mutability::Mut { // Machine configuration does not allow us to read statics (e.g., `const` // initializer). - Err(ConstEvalErrKind::ConstAccessesMutGlobal.into()) + Err(ConstEvalErrKind::ConstAccessesMutGlobal).into() } else { // Immutable global, this read is fine. assert_eq!(alloc.mutability, Mutability::Not); - Ok(()) + interp_ok(()) } } } @@ -748,9 +748,9 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { // even when there is interior mutability.) place.map_provenance(CtfeProvenance::as_shared_ref) }; - Ok(ImmTy::from_immediate(new_place.to_ref(ecx), val.layout)) + interp_ok(ImmTy::from_immediate(new_place.to_ref(ecx), val.layout)) } else { - Ok(val.clone()) + interp_ok(val.clone()) } } @@ -763,20 +763,20 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { ) -> InterpResult<'tcx> { if range.size == Size::ZERO { // Nothing to check. - return Ok(()); + return interp_ok(()); } // Reject writes through immutable pointers. if immutable { - return Err(ConstEvalErrKind::WriteThroughImmutablePointer.into()); + return Err(ConstEvalErrKind::WriteThroughImmutablePointer).into(); } // Everything else is fine. - Ok(()) + interp_ok(()) } fn before_alloc_read(ecx: &InterpCx<'tcx, Self>, alloc_id: AllocId) -> InterpResult<'tcx> { // Check if this is the currently evaluated static. if Some(alloc_id) == ecx.machine.static_root_ids.map(|(id, _)| id) { - return Err(ConstEvalErrKind::RecursiveStatic.into()); + return Err(ConstEvalErrKind::RecursiveStatic).into(); } // If this is another static, make sure we fire off the query to detect cycles. // But only do that when checks for static recursion are enabled. @@ -788,7 +788,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { ecx.ctfe_query(|tcx| tcx.eval_static_initializer(def_id))?; } } - Ok(()) + interp_ok(()) } fn cached_union_data_range<'e>( diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs index 48aeee2e6f05e..ba19f642795a9 100644 --- a/compiler/rustc_const_eval/src/const_eval/mod.rs +++ b/compiler/rustc_const_eval/src/const_eval/mod.rs @@ -1,13 +1,12 @@ // Not in interpret to make sure we do not use private implementation details -use rustc_middle::mir::interpret::InterpErrorInfo; use rustc_middle::query::{Key, TyCtxtAt}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::{bug, mir}; use rustc_target::abi::VariantIdx; use tracing::instrument; -use crate::interpret::{InterpCx, format_interp_error}; +use crate::interpret::InterpCx; mod dummy_machine; mod error; @@ -33,17 +32,6 @@ pub(crate) enum ValTreeCreationError<'tcx> { } pub(crate) type ValTreeCreationResult<'tcx> = Result, ValTreeCreationError<'tcx>>; -impl<'tcx> From> for ValTreeCreationError<'tcx> { - fn from(err: InterpErrorInfo<'tcx>) -> Self { - ty::tls::with(|tcx| { - bug!( - "Unexpected Undefined Behavior error during valtree construction: {}", - format_interp_error(tcx.dcx(), err), - ) - }) - } -} - #[instrument(skip(tcx), level = "debug")] pub(crate) fn try_destructure_mir_constant_for_user_output<'tcx>( tcx: TyCtxtAt<'tcx>, @@ -60,8 +48,8 @@ pub(crate) fn try_destructure_mir_constant_for_user_output<'tcx>( return None; } ty::Adt(def, _) => { - let variant = ecx.read_discriminant(&op).ok()?; - let down = ecx.project_downcast(&op, variant).ok()?; + let variant = ecx.read_discriminant(&op).discard_err()?; + let down = ecx.project_downcast(&op, variant).discard_err()?; (def.variants()[variant].fields.len(), Some(variant), down) } ty::Tuple(args) => (args.len(), None, op), @@ -70,7 +58,7 @@ pub(crate) fn try_destructure_mir_constant_for_user_output<'tcx>( let fields_iter = (0..field_count) .map(|i| { - let field_op = ecx.project_field(&down, i).ok()?; + let field_op = ecx.project_field(&down, i).discard_err()?; let val = op_to_const(&ecx, &field_op, /* for diagnostics */ true); Some((val, field_op.layout.ty)) }) diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs index a11d491bdd2c9..9e80e666ba91b 100644 --- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs +++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs @@ -92,7 +92,7 @@ fn const_to_valtree_inner<'tcx>( Ok(ty::ValTree::zst()) } ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => { - let val = ecx.read_immediate(place)?; + let val = ecx.read_immediate(place).unwrap(); let val = val.to_scalar_int().unwrap(); *num_nodes += 1; @@ -114,7 +114,7 @@ fn const_to_valtree_inner<'tcx>( // equality at compile-time (see `ptr_guaranteed_cmp`). // However we allow those that are just integers in disguise. // First, get the pointer. Remember it might be wide! - let val = ecx.read_immediate(place)?; + let val = ecx.read_immediate(place).unwrap(); // We could allow wide raw pointers where both sides are integers in the future, // but for now we reject them. if matches!(val.layout.abi, Abi::ScalarPair(..)) { @@ -135,7 +135,7 @@ fn const_to_valtree_inner<'tcx>( ty::FnPtr(..) => Err(ValTreeCreationError::NonSupportedType(ty)), ty::Ref(_, _, _) => { - let derefd_place = ecx.deref_pointer(place)?; + let derefd_place = ecx.deref_pointer(place).unwrap(); const_to_valtree_inner(ecx, &derefd_place, num_nodes) } @@ -159,7 +159,7 @@ fn const_to_valtree_inner<'tcx>( bug!("uninhabited types should have errored and never gotten converted to valtree") } - let variant = ecx.read_discriminant(place)?; + let variant = ecx.read_discriminant(place).unwrap(); branches(ecx, place, def.variant(variant).fields.len(), def.is_enum().then_some(variant), num_nodes) } diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index 19aa76bf1eae1..4945563f4a4ee 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -15,8 +15,8 @@ use tracing::{info, instrument, trace}; use super::{ CtfeProvenance, FnVal, ImmTy, InterpCx, InterpResult, MPlaceTy, Machine, OpTy, PlaceTy, - Projectable, Provenance, ReturnAction, Scalar, StackPopCleanup, StackPopInfo, throw_ub, - throw_ub_custom, throw_unsup_format, + Projectable, Provenance, ReturnAction, Scalar, StackPopCleanup, StackPopInfo, interp_ok, + throw_ub, throw_ub_custom, throw_unsup_format, }; use crate::fluent_generated as fluent; @@ -64,7 +64,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { arg: &FnArg<'tcx, M::Provenance>, field: usize, ) -> InterpResult<'tcx, FnArg<'tcx, M::Provenance>> { - Ok(match arg { + interp_ok(match arg { FnArg::Copy(op) => FnArg::Copy(self.project_field(op, field)?), FnArg::InPlace(mplace) => FnArg::InPlace(self.project_field(mplace, field)?), }) @@ -97,7 +97,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // another type. let ty::Adt(def, args) = layout.ty.kind() else { // Not an ADT, so definitely no NPO. - return Ok(layout); + return interp_ok(layout); }; let inner = if self.tcx.is_diagnostic_item(sym::Option, def.did()) { // The wrapped type is the only arg. @@ -111,10 +111,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } else if rhs.is_1zst() { lhs } else { - return Ok(layout); // no NPO + return interp_ok(layout); // no NPO } } else { - return Ok(layout); // no NPO + return interp_ok(layout); // no NPO }; // Check if the inner type is one of the NPO-guaranteed ones. @@ -126,7 +126,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // Stop at NPO types so that we don't miss that attribute in the check below! def.is_struct() && !is_npo(def) }); - Ok(match inner.ty.kind() { + interp_ok(match inner.ty.kind() { ty::Ref(..) | ty::FnPtr(..) => { // Option<&T> behaves like &T, and same for fn() inner @@ -153,11 +153,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ) -> InterpResult<'tcx, bool> { // Fast path: equal types are definitely compatible. if caller.ty == callee.ty { - return Ok(true); + return interp_ok(true); } // 1-ZST are compatible with all 1-ZST (and with nothing else). if caller.is_1zst() || callee.is_1zst() { - return Ok(caller.is_1zst() && callee.is_1zst()); + return interp_ok(caller.is_1zst() && callee.is_1zst()); } // Unfold newtypes and NPO optimizations. let unfold = |layout: TyAndLayout<'tcx>| { @@ -180,17 +180,17 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { _ => None, }; if let (Some(caller), Some(callee)) = (thin_pointer(caller), thin_pointer(callee)) { - return Ok(caller == callee); + return interp_ok(caller == callee); } // For wide pointers we have to get the pointee type. let pointee_ty = |ty: Ty<'tcx>| -> InterpResult<'tcx, Option>> { // We cannot use `builtin_deref` here since we need to reject `Box`. - Ok(Some(match ty.kind() { + interp_ok(Some(match ty.kind() { ty::Ref(_, ty, _) => *ty, ty::RawPtr(ty, _) => *ty, // We only accept `Box` with the default allocator. _ if ty.is_box_global(*self.tcx) => ty.expect_boxed_ty(), - _ => return Ok(None), + _ => return interp_ok(None), })) }; if let (Some(caller), Some(callee)) = (pointee_ty(caller.ty)?, pointee_ty(callee.ty)?) { @@ -202,7 +202,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let normalize = |ty| self.tcx.normalize_erasing_regions(self.param_env, ty); ty.ptr_metadata_ty(*self.tcx, normalize) }; - return Ok(meta_ty(caller) == meta_ty(callee)); + return interp_ok(meta_ty(caller) == meta_ty(callee)); } // Compatible integer types (in particular, usize vs ptr-sized-u32/u64). @@ -217,11 +217,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { }; if let (Some(caller), Some(callee)) = (int_ty(caller.ty), int_ty(callee.ty)) { // This is okay if they are the same integer type. - return Ok(caller == callee); + return interp_ok(caller == callee); } // Fall back to exact equality. - Ok(caller == callee) + interp_ok(caller == callee) } fn check_argument_compat( @@ -235,13 +235,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // Ensure that our checks imply actual ABI compatibility for this concrete call. // (This can fail e.g. if `#[rustc_nonnull_optimization_guaranteed]` is used incorrectly.) assert!(caller_abi.eq_abi(callee_abi)); - Ok(true) + interp_ok(true) } else { trace!( "check_argument_compat: incompatible ABIs:\ncaller: {:?}\ncallee: {:?}", caller_abi, callee_abi ); - Ok(false) + interp_ok(false) } } @@ -266,7 +266,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if !already_live { self.storage_live(callee_arg.as_local().unwrap())?; } - return Ok(()); + return interp_ok(()); } // Find next caller arg. let Some((caller_arg, caller_abi)) = caller_args.next() else { @@ -308,7 +308,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if let FnArg::InPlace(mplace) = caller_arg { M::protect_in_place_function_argument(self, mplace)?; } - Ok(()) + interp_ok(()) } /// The main entry point for creating a new stack frame: performs ABI checks and initializes @@ -536,7 +536,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { unwind, ); } else { - Ok(()) + interp_ok(()) } } ty::InstanceKind::VTableShim(..) @@ -561,7 +561,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { unwind, )? else { - return Ok(()); + return interp_ok(()); }; // Special handling for the closure ABI: untuple the last argument. @@ -572,7 +572,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { trace!("init_fn_call: Will pass last argument by untupling"); Cow::from( args.iter() - .map(|a| Ok(a.clone())) + .map(|a| interp_ok(a.clone())) .chain( (0..untuple_arg.layout().fields.count()) .map(|i| self.fn_arg_field(untuple_arg, i)), @@ -886,27 +886,25 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // this transmute. res } else { - Ok(()) + interp_ok(()) }; // All right, now it is time to actually pop the frame. - let stack_pop_info = self.pop_stack_frame_raw(unwinding)?; - - // Report error from return value copy, if any. - copy_ret_result?; + // An error here takes precedence over the copy error. + let (stack_pop_info, ()) = self.pop_stack_frame_raw(unwinding).and(copy_ret_result)?; match stack_pop_info.return_action { ReturnAction::Normal => {} ReturnAction::NoJump => { // The hook already did everything. - return Ok(()); + return interp_ok(()); } ReturnAction::NoCleanup => { // If we are not doing cleanup, also skip everything else. assert!(self.stack().is_empty(), "only the topmost frame should ever be leaked"); assert!(!unwinding, "tried to skip cleanup during unwinding"); // Skip machine hook. - return Ok(()); + return interp_ok(()); } } @@ -931,7 +929,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.stack().is_empty(), "only the bottommost frame can have StackPopCleanup::Root" ); - Ok(()) + interp_ok(()) } } } diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index 85e7d91c2ad50..565a7d1624249 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -14,7 +14,8 @@ use tracing::trace; use super::util::ensure_monomorphic_enough; use super::{ - FnVal, ImmTy, Immediate, InterpCx, Machine, OpTy, PlaceTy, err_inval, throw_ub, throw_ub_custom, + FnVal, ImmTy, Immediate, InterpCx, Machine, OpTy, PlaceTy, err_inval, interp_ok, throw_ub, + throw_ub_custom, }; use crate::fluent_generated as fluent; @@ -157,7 +158,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.copy_op_allow_transmute(src, dest)?; } } - Ok(()) + interp_ok(()) } /// Handles 'IntToInt' and 'IntToFloat' casts. @@ -169,7 +170,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { assert!(src.layout.ty.is_integral() || src.layout.ty.is_char() || src.layout.ty.is_bool()); assert!(cast_to.ty.is_floating_point() || cast_to.ty.is_integral() || cast_to.ty.is_char()); - Ok(ImmTy::from_scalar( + interp_ok(ImmTy::from_scalar( self.cast_from_int_like(src.to_scalar(), src.layout, cast_to.ty)?, cast_to, )) @@ -192,7 +193,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { FloatTy::F64 => self.cast_from_float(src.to_scalar().to_f64()?, cast_to.ty), FloatTy::F128 => self.cast_from_float(src.to_scalar().to_f128()?, cast_to.ty), }; - Ok(ImmTy::from_scalar(val, cast_to)) + interp_ok(ImmTy::from_scalar(val, cast_to)) } /// Handles 'FnPtrToPtr' and 'PtrToPtr' casts. @@ -206,14 +207,14 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // Handle casting any ptr to raw ptr (might be a fat ptr). if cast_to.size == src.layout.size { // Thin or fat pointer that just has the ptr kind of target type changed. - return Ok(ImmTy::from_immediate(**src, cast_to)); + return interp_ok(ImmTy::from_immediate(**src, cast_to)); } else { // Casting the metadata away from a fat ptr. assert_eq!(src.layout.size, 2 * self.pointer_size()); assert_eq!(cast_to.size, self.pointer_size()); assert!(src.layout.ty.is_unsafe_ptr()); return match **src { - Immediate::ScalarPair(data, _) => Ok(ImmTy::from_scalar(data, cast_to)), + Immediate::ScalarPair(data, _) => interp_ok(ImmTy::from_scalar(data, cast_to)), Immediate::Scalar(..) => span_bug!( self.cur_span(), "{:?} input to a fat-to-thin cast ({} -> {})", @@ -240,7 +241,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { Ok(ptr) => M::expose_ptr(self, ptr)?, Err(_) => {} // Do nothing, exposing an invalid pointer (`None` provenance) is a NOP. }; - Ok(ImmTy::from_scalar(self.cast_from_int_like(scalar, src.layout, cast_to.ty)?, cast_to)) + interp_ok(ImmTy::from_scalar( + self.cast_from_int_like(scalar, src.layout, cast_to.ty)?, + cast_to, + )) } pub fn pointer_with_exposed_provenance_cast( @@ -258,7 +262,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // Then turn address into pointer. let ptr = M::ptr_from_addr_cast(self, addr)?; - Ok(ImmTy::from_scalar(Scalar::from_maybe_pointer(ptr, self), cast_to)) + interp_ok(ImmTy::from_scalar(Scalar::from_maybe_pointer(ptr, self), cast_to)) } /// Low-level cast helper function. This works directly on scalars and can take 'int-like' input @@ -280,7 +284,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { _ => span_bug!(self.cur_span(), "invalid int-like cast from {}", src_layout.ty), }; - Ok(match *cast_ty.kind() { + interp_ok(match *cast_ty.kind() { // int -> int Int(_) | Uint(_) => { let size = match *cast_ty.kind() { @@ -505,7 +509,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.unsize_into(&src_field, cast_ty_field, &dst_field)?; } } - Ok(()) + interp_ok(()) } _ => { // Do not ICE if we are not monomorphic enough. diff --git a/compiler/rustc_const_eval/src/interpret/discriminant.rs b/compiler/rustc_const_eval/src/interpret/discriminant.rs index 3bab2b3261e1e..81e0b1e12caf4 100644 --- a/compiler/rustc_const_eval/src/interpret/discriminant.rs +++ b/compiler/rustc_const_eval/src/interpret/discriminant.rs @@ -7,7 +7,8 @@ use rustc_target::abi::{self, TagEncoding, VariantIdx, Variants}; use tracing::{instrument, trace}; use super::{ - ImmTy, InterpCx, InterpResult, Machine, Projectable, Scalar, Writeable, err_ub, throw_ub, + ImmTy, InterpCx, InterpResult, Machine, Projectable, Scalar, Writeable, err_ub, interp_ok, + throw_ub, }; impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { @@ -48,7 +49,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if actual_variant != variant_index { throw_ub!(InvalidNichedEnumVariantWritten { enum_ty: dest.layout().ty }); } - Ok(()) + interp_ok(()) } } } @@ -89,7 +90,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { throw_ub!(UninhabitedEnumVariantRead(index)) } } - return Ok(index); + return interp_ok(index); } Variants::Multiple { tag, ref tag_encoding, tag_field, .. } => { (tag, tag_encoding, tag_field) @@ -205,7 +206,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if op.layout().for_variant(self, index).abi.is_uninhabited() { throw_ub!(UninhabitedEnumVariantRead(index)) } - Ok(index) + interp_ok(index) } pub fn discriminant_for_variant( @@ -226,7 +227,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { Scalar::from_uint(variant.as_u32(), discr_layout.size) } }; - Ok(ImmTy::from_scalar(discr_value, discr_layout)) + interp_ok(ImmTy::from_scalar(discr_value, discr_layout)) } /// Computes how to write the tag of a given variant of enum `ty`: @@ -247,7 +248,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // discriminant is encoded implicitly, so any attempt to write // the wrong discriminant for a `Single` enum will reliably // result in UB. - Ok(None) + interp_ok(None) } abi::Variants::Multiple { @@ -265,7 +266,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let tag_size = tag_layout.size(self); let tag_val = tag_size.truncate(discr_val); let tag = ScalarInt::try_from_uint(tag_val, tag_size).unwrap(); - Ok(Some((tag, tag_field))) + interp_ok(Some((tag, tag_field))) } abi::Variants::Multiple { @@ -274,7 +275,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } if untagged_variant == variant_index => { // The untagged variant is implicitly encoded simply by having a // value that is outside the niche variants. - Ok(None) + interp_ok(None) } abi::Variants::Multiple { @@ -299,7 +300,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let tag = self .binary_op(mir::BinOp::Add, &variant_index_relative_val, &niche_start_val)? .to_scalar_int()?; - Ok(Some((tag, tag_field))) + interp_ok(Some((tag, tag_field))) } } } diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index f4c4a6fb0bf5b..5165f95afd5b7 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -19,9 +19,9 @@ use rustc_trait_selection::traits::ObligationCtxt; use tracing::{debug, instrument, trace}; use super::{ - Frame, FrameInfo, GlobalId, InterpErrorInfo, InterpResult, MPlaceTy, Machine, MemPlaceMeta, - Memory, OpTy, Place, PlaceTy, PointerArithmetic, Projectable, Provenance, err_inval, - throw_inval, throw_ub, throw_ub_custom, + Frame, FrameInfo, GlobalId, InterpError, InterpErrorInfo, InterpResult, MPlaceTy, Machine, + MemPlaceMeta, Memory, OpTy, Place, PlaceTy, PointerArithmetic, Projectable, Provenance, + err_inval, interp_ok, throw_inval, throw_ub, throw_ub_custom, }; use crate::{ReportErrorExt, fluent_generated as fluent, util}; @@ -73,7 +73,7 @@ where } impl<'tcx, M: Machine<'tcx>> LayoutOfHelpers<'tcx> for InterpCx<'tcx, M> { - type LayoutOfResult = InterpResult<'tcx, TyAndLayout<'tcx>>; + type LayoutOfResult = Result, InterpError<'tcx>>; #[inline] fn layout_tcx_at_span(&self) -> Span { @@ -82,29 +82,24 @@ impl<'tcx, M: Machine<'tcx>> LayoutOfHelpers<'tcx> for InterpCx<'tcx, M> { } #[inline] - fn handle_layout_err( - &self, - err: LayoutError<'tcx>, - _: Span, - _: Ty<'tcx>, - ) -> InterpErrorInfo<'tcx> { - err_inval!(Layout(err)).into() + fn handle_layout_err(&self, err: LayoutError<'tcx>, _: Span, _: Ty<'tcx>) -> InterpError<'tcx> { + err_inval!(Layout(err)) } } impl<'tcx, M: Machine<'tcx>> FnAbiOfHelpers<'tcx> for InterpCx<'tcx, M> { - type FnAbiOfResult = InterpResult<'tcx, &'tcx FnAbi<'tcx, Ty<'tcx>>>; + type FnAbiOfResult = Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, InterpError<'tcx>>; fn handle_fn_abi_err( &self, err: FnAbiError<'tcx>, _span: Span, _fn_abi_request: FnAbiRequest<'tcx>, - ) -> InterpErrorInfo<'tcx> { + ) -> InterpError<'tcx> { match err { - FnAbiError::Layout(err) => err_inval!(Layout(err)).into(), + FnAbiError::Layout(err) => err_inval!(Layout(err)), FnAbiError::AdjustForForeignAbi(err) => { - err_inval!(FnAbiAdjustForForeignAbi(err)).into() + err_inval!(FnAbiAdjustForForeignAbi(err)) } } } @@ -160,7 +155,7 @@ pub(super) fn from_known_layout<'tcx>( ); } } - Ok(known_layout) + interp_ok(known_layout) } } } @@ -262,7 +257,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if let Some(err) = body.tainted_by_errors { throw_inval!(AlreadyReported(ReportedErrorInfo::tainted_by_errors(err))); } - Ok(body) + interp_ok(body) } /// Call this on things you got out of the MIR (so it is as generic as the current @@ -305,7 +300,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { trace!("param_env: {:#?}", self.param_env); trace!("args: {:#?}", args); match ty::Instance::try_resolve(*self.tcx, self.param_env, def, args) { - Ok(Some(instance)) => Ok(instance), + Ok(Some(instance)) => interp_ok(instance), Ok(None) => throw_inval!(TooGeneric), // FIXME(eddyb) this could be a bit more specific than `AlreadyReported`. @@ -401,7 +396,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { layout: &TyAndLayout<'tcx>, ) -> InterpResult<'tcx, Option<(Size, Align)>> { if layout.is_sized() { - return Ok(Some((layout.size, layout.align.abi))); + return interp_ok(Some((layout.size, layout.align.abi))); } match layout.ty.kind() { ty::Adt(..) | ty::Tuple(..) => { @@ -425,7 +420,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { else { // A field with an extern type. We don't know the actual dynamic size // or the alignment. - return Ok(None); + return interp_ok(None); }; // # First compute the dynamic alignment @@ -456,12 +451,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if full_size > self.max_size_of_val() { throw_ub!(InvalidMeta(InvalidMetaKind::TooBig)); } - Ok(Some((full_size, full_align))) + interp_ok(Some((full_size, full_align))) } ty::Dynamic(expected_trait, _, ty::Dyn) => { let vtable = metadata.unwrap_meta().to_pointer(self)?; // Read size and align from vtable (already checks size). - Ok(Some(self.get_vtable_size_and_align(vtable, Some(expected_trait))?)) + interp_ok(Some(self.get_vtable_size_and_align(vtable, Some(expected_trait))?)) } ty::Slice(_) | ty::Str => { @@ -474,10 +469,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if size > self.max_size_of_val() { throw_ub!(InvalidMeta(InvalidMetaKind::SliceTooBig)); } - Ok(Some((size, elem.align.abi))) + interp_ok(Some((size, elem.align.abi))) } - ty::Foreign(_) => Ok(None), + ty::Foreign(_) => interp_ok(None), _ => span_bug!(self.cur_span(), "size_and_align_of::<{}> not supported", layout.ty), } @@ -503,7 +498,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { pub fn return_to_block(&mut self, target: Option) -> InterpResult<'tcx> { if let Some(target) = target { self.go_to_block(target); - Ok(()) + interp_ok(()) } else { throw_ub!(Unreachable) } @@ -530,10 +525,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { M::unwind_terminate(self, reason)?; // This might have pushed a new stack frame, or it terminated execution. // Either way, `loc` will not be updated. - return Ok(()); + return interp_ok(()); } }; - Ok(()) + interp_ok(()) } /// Call a query that can return `ErrorHandled`. Should be used for statics and other globals. diff --git a/compiler/rustc_const_eval/src/interpret/intern.rs b/compiler/rustc_const_eval/src/interpret/intern.rs index 81659df7c533b..7a1b92601a441 100644 --- a/compiler/rustc_const_eval/src/interpret/intern.rs +++ b/compiler/rustc_const_eval/src/interpret/intern.rs @@ -26,7 +26,9 @@ use rustc_span::def_id::LocalDefId; use rustc_span::sym; use tracing::{instrument, trace}; -use super::{AllocId, Allocation, InterpCx, MPlaceTy, Machine, MemoryKind, PlaceTy, err_ub}; +use super::{ + AllocId, Allocation, InterpCx, MPlaceTy, Machine, MemoryKind, PlaceTy, err_ub, interp_ok, +}; use crate::const_eval; use crate::errors::NestedStaticInThreadLocal; @@ -307,7 +309,7 @@ pub fn intern_const_alloc_for_constprop<'tcx, T, M: CompileTimeMachine<'tcx, T>> ) -> InterpResult<'tcx, ()> { if ecx.tcx.try_get_global_alloc(alloc_id).is_some() { // The constant is already in global memory. Do nothing. - return Ok(()); + return interp_ok(()); } // Move allocation to `tcx`. if let Some(_) = @@ -318,7 +320,7 @@ pub fn intern_const_alloc_for_constprop<'tcx, T, M: CompileTimeMachine<'tcx, T>> // proper recursive interning loop -- or just call `intern_const_alloc_recursive`. panic!("`intern_const_alloc_for_constprop` called on allocation with nested provenance") } - Ok(()) + interp_ok(()) } impl<'tcx, M: super::intern::CompileTimeMachine<'tcx, !>> InterpCx<'tcx, M> { @@ -342,6 +344,6 @@ impl<'tcx, M: super::intern::CompileTimeMachine<'tcx, !>> InterpCx<'tcx, M> { panic!("`intern_with_temp_alloc` with nested allocations"); } } - Ok(alloc_id) + interp_ok(alloc_id) } } diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 4c34ffc5d4ec1..52780cc6a3a6f 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -18,7 +18,7 @@ use super::util::ensure_monomorphic_enough; use super::{ Allocation, CheckInAllocMsg, ConstAllocation, GlobalId, ImmTy, InterpCx, InterpResult, MPlaceTy, Machine, OpTy, Pointer, PointerArithmetic, Provenance, Scalar, err_inval, - err_ub_custom, err_unsup_format, throw_inval, throw_ub_custom, throw_ub_format, + err_ub_custom, err_unsup_format, interp_ok, throw_inval, throw_ub_custom, throw_ub_format, }; use crate::fluent_generated as fluent; @@ -39,7 +39,7 @@ pub(crate) fn eval_nullary_intrinsic<'tcx>( ) -> InterpResult<'tcx, ConstValue<'tcx>> { let tp_ty = args.type_at(0); let name = tcx.item_name(def_id); - Ok(match name { + interp_ok(match name { sym::type_name => { ensure_monomorphic_enough(tcx, tp_ty)?; let alloc = alloc_type_name(tcx, tp_ty); @@ -329,6 +329,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { fluent::const_eval_offset_from_different_allocations, name = intrinsic_name, ) + .into() })?; // Perform division by size to compute return value. @@ -378,7 +379,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { M::panic_nounwind(self, &msg)?; // Skip the `return_to_block` at the end (we panicked, we do not return). - return Ok(true); + return interp_ok(true); } } sym::simd_insert => { @@ -438,12 +439,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } // Unsupported intrinsic: skip the return_to_block below. - _ => return Ok(false), + _ => return interp_ok(false), } trace!("{:?}", self.dump_place(&dest.clone().into())); self.return_to_block(ret)?; - Ok(true) + interp_ok(true) } pub(super) fn eval_nondiverging_intrinsic( @@ -457,7 +458,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if !cond { throw_ub_custom!(fluent::const_eval_assume_false); } - Ok(()) + interp_ok(()) } NonDivergingIntrinsic::CopyNonOverlapping(mir::CopyNonOverlapping { count, @@ -499,7 +500,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } _ => bug!("not a numeric intrinsic: {}", name), }; - Ok(Scalar::from_uint(bits_out, ret_layout.size)) + interp_ok(Scalar::from_uint(bits_out, ret_layout.size)) } pub fn exact_div( @@ -540,7 +541,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let (val, overflowed) = self.binary_op(mir_op.wrapping_to_overflowing().unwrap(), l, r)?.to_scalar_pair(); - Ok(if overflowed.to_bool()? { + interp_ok(if overflowed.to_bool()? { let size = l.layout.size; if l.layout.abi.is_signed() { // For signed ints the saturated value depends on the sign of the first @@ -582,7 +583,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // The offset must be in bounds starting from `ptr`. self.check_ptr_access_signed(ptr, offset_bytes, CheckInAllocMsg::PointerArithmeticTest)?; // This also implies that there is no overflow, so we are done. - Ok(ptr.wrapping_signed_offset(offset_bytes, self)) + interp_ok(ptr.wrapping_signed_offset(offset_bytes, self)) } /// Copy `count*size_of::()` many bytes from `*src` to `*dst`. @@ -628,7 +629,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.copy_op(&right, &left)?; self.copy_op(&temp, &right)?; self.deallocate_ptr(temp.ptr(), None, kind)?; - Ok(()) + interp_ok(()) } pub fn write_bytes_intrinsic( @@ -669,7 +670,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // `Ordering`'s discriminants are -1/0/+1, so casting does the right thing. let result = Ord::cmp(left_bytes, right_bytes) as i32; - Ok(Scalar::from_i32(result)) + interp_ok(Scalar::from_i32(result)) } pub(crate) fn raw_eq_intrinsic( @@ -687,13 +688,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { this.check_ptr_align(ptr, layout.align.abi)?; let Some(alloc_ref) = self.get_ptr_alloc(ptr, layout.size)? else { // zero-sized access - return Ok(&[]); + return interp_ok(&[]); }; alloc_ref.get_bytes_strip_provenance() }; let lhs_bytes = get_bytes(self, lhs)?; let rhs_bytes = get_bytes(self, rhs)?; - Ok(Scalar::from_bool(lhs_bytes == rhs_bytes)) + interp_ok(Scalar::from_bool(lhs_bytes == rhs_bytes)) } } diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index a809f74e8eb90..0f796c3122210 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -20,7 +20,8 @@ use rustc_target::spec::abi::Abi as CallAbi; use super::{ AllocBytes, AllocId, AllocKind, AllocRange, Allocation, CTFE_ALLOC_SALT, ConstAllocation, CtfeProvenance, FnArg, Frame, ImmTy, InterpCx, InterpResult, MPlaceTy, MemoryKind, - Misalignment, OpTy, PlaceTy, Pointer, Provenance, RangeSet, throw_unsup, throw_unsup_format, + Misalignment, OpTy, PlaceTy, Pointer, Provenance, RangeSet, interp_ok, throw_unsup, + throw_unsup_format, }; /// Data returned by [`Machine::after_stack_pop`], and consumed by @@ -185,7 +186,7 @@ pub trait Machine<'tcx>: Sized { ecx: &InterpCx<'tcx, Self>, instance: ty::InstanceKind<'tcx>, ) -> InterpResult<'tcx, &'tcx mir::Body<'tcx>> { - Ok(ecx.tcx.instance_mir(instance)) + interp_ok(ecx.tcx.instance_mir(instance)) } /// Entry point to all function calls. @@ -280,7 +281,7 @@ pub trait Machine<'tcx>: Sized { /// Called before a basic block terminator is executed. #[inline] fn before_terminator(_ecx: &mut InterpCx<'tcx, Self>) -> InterpResult<'tcx> { - Ok(()) + interp_ok(()) } /// Determines the result of a `NullaryOp::UbChecks` invocation. @@ -290,7 +291,7 @@ pub trait Machine<'tcx>: Sized { /// You can use this to detect long or endlessly running programs. #[inline] fn increment_const_eval_counter(_ecx: &mut InterpCx<'tcx, Self>) -> InterpResult<'tcx> { - Ok(()) + interp_ok(()) } /// Called before a global allocation is accessed. @@ -304,7 +305,7 @@ pub trait Machine<'tcx>: Sized { _static_def_id: Option, _is_write: bool, ) -> InterpResult<'tcx> { - Ok(()) + interp_ok(()) } /// Return the `AllocId` for the given thread-local static in the current thread. @@ -422,7 +423,7 @@ pub trait Machine<'tcx>: Sized { _prov: (AllocId, Self::ProvenanceExtra), _range: AllocRange, ) -> InterpResult<'tcx> { - Ok(()) + interp_ok(()) } /// Hook for performing extra checks on any memory read access, @@ -433,7 +434,7 @@ pub trait Machine<'tcx>: Sized { /// Used to prevent statics from self-initializing by reading from their own memory /// as it is being initialized. fn before_alloc_read(_ecx: &InterpCx<'tcx, Self>, _alloc_id: AllocId) -> InterpResult<'tcx> { - Ok(()) + interp_ok(()) } /// Hook for performing extra checks on a memory write access. @@ -446,7 +447,7 @@ pub trait Machine<'tcx>: Sized { _prov: (AllocId, Self::ProvenanceExtra), _range: AllocRange, ) -> InterpResult<'tcx> { - Ok(()) + interp_ok(()) } /// Hook for performing extra operations on a memory deallocation. @@ -460,7 +461,7 @@ pub trait Machine<'tcx>: Sized { _align: Align, _kind: MemoryKind, ) -> InterpResult<'tcx> { - Ok(()) + interp_ok(()) } /// Executes a retagging operation for a single pointer. @@ -471,7 +472,7 @@ pub trait Machine<'tcx>: Sized { _kind: mir::RetagKind, val: &ImmTy<'tcx, Self::Provenance>, ) -> InterpResult<'tcx, ImmTy<'tcx, Self::Provenance>> { - Ok(val.clone()) + interp_ok(val.clone()) } /// Executes a retagging operation on a compound value. @@ -482,7 +483,7 @@ pub trait Machine<'tcx>: Sized { _kind: mir::RetagKind, _place: &PlaceTy<'tcx, Self::Provenance>, ) -> InterpResult<'tcx> { - Ok(()) + interp_ok(()) } /// Called on places used for in-place function argument and return value handling. @@ -516,7 +517,7 @@ pub trait Machine<'tcx>: Sized { /// Called immediately after a stack frame got pushed and its locals got initialized. fn after_stack_push(_ecx: &mut InterpCx<'tcx, Self>) -> InterpResult<'tcx> { - Ok(()) + interp_ok(()) } /// Called just before the return value is copied to the caller-provided return place. @@ -524,7 +525,7 @@ pub trait Machine<'tcx>: Sized { _ecx: &InterpCx<'tcx, Self>, _frame: &Frame<'tcx, Self::Provenance, Self::FrameExtra>, ) -> InterpResult<'tcx> { - Ok(()) + interp_ok(()) } /// Called immediately after a stack frame got popped, but before jumping back to the caller. @@ -537,14 +538,14 @@ pub trait Machine<'tcx>: Sized { ) -> InterpResult<'tcx, ReturnAction> { // By default, we do not support unwinding from panics assert!(!unwinding); - Ok(ReturnAction::Normal) + interp_ok(ReturnAction::Normal) } /// Called immediately after an "immediate" local variable is read /// (i.e., this is called for reads that do not end up accessing addressable memory). #[inline(always)] fn after_local_read(_ecx: &InterpCx<'tcx, Self>, _local: mir::Local) -> InterpResult<'tcx> { - Ok(()) + interp_ok(()) } /// Called immediately after an "immediate" local variable is assigned a new value @@ -556,7 +557,7 @@ pub trait Machine<'tcx>: Sized { _local: mir::Local, _storage_live: bool, ) -> InterpResult<'tcx> { - Ok(()) + interp_ok(()) } /// Called immediately after actual memory was allocated for a local @@ -567,7 +568,7 @@ pub trait Machine<'tcx>: Sized { _local: mir::Local, _mplace: &MPlaceTy<'tcx, Self::Provenance>, ) -> InterpResult<'tcx> { - Ok(()) + interp_ok(()) } /// Evaluate the given constant. The `eval` function will do all the required evaluation, @@ -645,7 +646,7 @@ pub macro compile_time_machine(<$tcx: lifetime>) { ) -> InterpResult<$tcx> { // For now we don't do any checking here. We can't use `tcx.sess` because that can differ // between crates, and we need to ensure that const-eval always behaves the same. - Ok(()) + interp_ok(()) } #[inline(always)] @@ -665,7 +666,7 @@ pub macro compile_time_machine(<$tcx: lifetime>) { fn ub_checks(_ecx: &InterpCx<$tcx, Self>) -> InterpResult<$tcx, bool> { // We can't look at `tcx.sess` here as that can differ across crates, which can lead to // unsound differences in evaluating the same constant at different instantiation sites. - Ok(true) + interp_ok(true) } #[inline(always)] @@ -675,7 +676,7 @@ pub macro compile_time_machine(<$tcx: lifetime>) { alloc: &'b Allocation, ) -> InterpResult<$tcx, Cow<'b, Allocation>> { // Overwrite default implementation: no need to adjust anything. - Ok(Cow::Borrowed(alloc)) + interp_ok(Cow::Borrowed(alloc)) } fn init_alloc_extra( @@ -685,7 +686,7 @@ pub macro compile_time_machine(<$tcx: lifetime>) { _size: Size, _align: Align, ) -> InterpResult<$tcx, Self::AllocExtra> { - Ok(()) + interp_ok(()) } fn extern_static_pointer( @@ -693,7 +694,7 @@ pub macro compile_time_machine(<$tcx: lifetime>) { def_id: DefId, ) -> InterpResult<$tcx, Pointer> { // Use the `AllocId` associated with the `DefId`. Any actual *access* will fail. - Ok(Pointer::new(ecx.tcx.reserve_and_set_static_alloc(def_id).into(), Size::ZERO)) + interp_ok(Pointer::new(ecx.tcx.reserve_and_set_static_alloc(def_id).into(), Size::ZERO)) } #[inline(always)] @@ -702,7 +703,7 @@ pub macro compile_time_machine(<$tcx: lifetime>) { ptr: Pointer, _kind: Option>, ) -> InterpResult<$tcx, Pointer> { - Ok(ptr) + interp_ok(ptr) } #[inline(always)] @@ -713,7 +714,7 @@ pub macro compile_time_machine(<$tcx: lifetime>) { // Allow these casts, but make the pointer not dereferenceable. // (I.e., they behave like transmutation.) // This is correct because no pointers can ever be exposed in compile-time evaluation. - Ok(Pointer::from_addr_invalid(addr)) + interp_ok(Pointer::from_addr_invalid(addr)) } #[inline(always)] diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index c3b506d848cdb..e6ab8ca12a886 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -23,7 +23,7 @@ use tracing::{debug, instrument, trace}; use super::{ AllocBytes, AllocId, AllocMap, AllocRange, Allocation, CheckAlignMsg, CheckInAllocMsg, CtfeProvenance, GlobalAlloc, InterpCx, InterpResult, Machine, MayLeak, Misalignment, Pointer, - PointerArithmetic, Provenance, Scalar, alloc_range, err_ub, err_ub_custom, throw_ub, + PointerArithmetic, Provenance, Scalar, alloc_range, err_ub, err_ub_custom, interp_ok, throw_ub, throw_ub_custom, throw_unsup, throw_unsup_format, }; use crate::fluent_generated as fluent; @@ -82,7 +82,7 @@ pub enum FnVal<'tcx, Other> { impl<'tcx, Other> FnVal<'tcx, Other> { pub fn as_instance(self) -> InterpResult<'tcx, Instance<'tcx>> { match self { - FnVal::Instance(instance) => Ok(instance), + FnVal::Instance(instance) => interp_ok(instance), FnVal::Other(_) => { throw_unsup_format!("'foreign' function pointers are not supported in this context") } @@ -284,7 +284,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.mem_copy(ptr, new_ptr.into(), old_size.min(new_size), /*nonoverlapping*/ true)?; self.deallocate_ptr(ptr, old_size_and_align, kind)?; - Ok(new_ptr) + interp_ok(new_ptr) } #[instrument(skip(self), level = "debug")] @@ -330,8 +330,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ) } None => err_ub!(PointerUseAfterFree(alloc_id, CheckInAllocMsg::MemoryAccessTest)), - } - .into()); + }) + .into(); }; if alloc.mutability.is_not() { @@ -376,7 +376,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { bug!("Nothing can be deallocated twice"); } - Ok(()) + interp_ok(()) } /// Internal helper function to determine the allocation and offset of a pointer (if any). @@ -395,7 +395,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { |this, alloc_id, offset, prov| { let (size, align) = this .get_live_alloc_size_and_align(alloc_id, CheckInAllocMsg::MemoryAccessTest)?; - Ok((size, align, (alloc_id, offset, prov))) + interp_ok((size, align, (alloc_id, offset, prov))) }, ) } @@ -412,9 +412,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let size = i64::try_from(size.bytes()).unwrap(); // it would be an error to even ask for more than isize::MAX bytes Self::check_and_deref_ptr(self, ptr, size, msg, |this, alloc_id, _, _| { let (size, align) = this.get_live_alloc_size_and_align(alloc_id, msg)?; - Ok((size, align, ())) + interp_ok((size, align, ())) })?; - Ok(()) + interp_ok(()) } /// Check whether the given pointer points to live memory for a signed amount of bytes. @@ -428,9 +428,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ) -> InterpResult<'tcx> { Self::check_and_deref_ptr(self, ptr, size, msg, |this, alloc_id, _, _| { let (size, align) = this.get_live_alloc_size_and_align(alloc_id, msg)?; - Ok((size, align, ())) + interp_ok((size, align, ())) })?; - Ok(()) + interp_ok(()) } /// Low-level helper function to check if a ptr is in-bounds and potentially return a reference @@ -455,10 +455,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ) -> InterpResult<'tcx, Option> { // Everything is okay with size 0. if size == 0 { - return Ok(None); + return interp_ok(None); } - Ok(match this.borrow().ptr_try_get_alloc_id(ptr, size) { + interp_ok(match this.borrow().ptr_try_get_alloc_id(ptr, size) { Err(addr) => { // We couldn't get a proper allocation. throw_ub!(DanglingIntPointer { addr, inbounds_size: size, msg }); @@ -498,7 +498,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if let Some(misaligned) = misaligned { throw_ub!(AlignmentCheckFailed(misaligned, msg)) } - Ok(()) + interp_ok(()) } pub(super) fn is_ptr_misaligned( @@ -634,7 +634,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // `get_global_alloc` that we can actually use directly without inserting anything anywhere. // So the error type is `InterpResult<'tcx, &Allocation>`. let a = self.memory.alloc_map.get_or(id, || { - let alloc = self.get_global_alloc(id, /*is_write*/ false).map_err(Err)?; + // We have to funnel the `InterpErrorInfo` through a `Result` to match the `get_or` API, + // so we use `report_err` for that. + let alloc = self.get_global_alloc(id, /*is_write*/ false).report_err().map_err(Err)?; match alloc { Cow::Borrowed(alloc) => { // We got a ref, cheaply return that as an "error" so that the @@ -653,8 +655,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { }); // Now unpack that funny error type match a { - Ok(a) => Ok(&a.1), - Err(a) => a, + Ok(a) => interp_ok(&a.1), + Err(a) => a.into(), } } @@ -662,7 +664,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// The caller is responsible for calling the access hooks! pub fn get_alloc_bytes_unchecked_raw(&self, id: AllocId) -> InterpResult<'tcx, *const u8> { let alloc = self.get_alloc_raw(id)?; - Ok(alloc.get_bytes_unchecked_raw()) + interp_ok(alloc.get_bytes_unchecked_raw()) } /// Bounds-checked *but not align-checked* allocation access. @@ -680,7 +682,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { CheckInAllocMsg::MemoryAccessTest, |this, alloc_id, offset, prov| { let alloc = this.get_alloc_raw(alloc_id)?; - Ok((alloc.size(), alloc.align, (alloc_id, offset, prov, alloc))) + interp_ok((alloc.size(), alloc.align, (alloc_id, offset, prov, alloc))) }, )?; // We want to call the hook on *all* accesses that involve an AllocId, including zero-sized @@ -703,20 +705,20 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { range, )?; } - Ok(Some(AllocRef { alloc, range, tcx: *self.tcx, alloc_id })) + interp_ok(Some(AllocRef { alloc, range, tcx: *self.tcx, alloc_id })) } else { - Ok(None) + interp_ok(None) } } /// Return the `extra` field of the given allocation. pub fn get_alloc_extra<'a>(&'a self, id: AllocId) -> InterpResult<'tcx, &'a M::AllocExtra> { - Ok(&self.get_alloc_raw(id)?.extra) + interp_ok(&self.get_alloc_raw(id)?.extra) } /// Return the `mutability` field of the given allocation. pub fn get_alloc_mutability<'a>(&'a self, id: AllocId) -> InterpResult<'tcx, Mutability> { - Ok(self.get_alloc_raw(id)?.mutability) + interp_ok(self.get_alloc_raw(id)?.mutability) } /// Gives raw mutable access to the `Allocation`, without bounds or alignment checks. @@ -750,7 +752,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if alloc.mutability.is_not() { throw_ub!(WriteToReadOnly(id)) } - Ok((alloc, &mut self.machine)) + interp_ok((alloc, &mut self.machine)) } /// Gives raw, mutable access to the `Allocation` address, without bounds or alignment checks. @@ -760,7 +762,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { id: AllocId, ) -> InterpResult<'tcx, *mut u8> { let alloc = self.get_alloc_raw_mut(id)?.0; - Ok(alloc.get_bytes_unchecked_raw_mut()) + interp_ok(alloc.get_bytes_unchecked_raw_mut()) } /// Bounds-checked *but not align-checked* allocation access. @@ -781,7 +783,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { CheckInAllocMsg::MemoryAccessTest, |this, alloc_id, offset, prov| { let (alloc, machine) = this.get_alloc_raw_mut(alloc_id)?; - Ok((alloc.size(), alloc.align, (alloc_id, offset, prov, alloc, machine))) + interp_ok((alloc.size(), alloc.align, (alloc_id, offset, prov, alloc, machine))) }, )?; @@ -790,9 +792,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if !validation_in_progress { M::before_memory_write(tcx, machine, &mut alloc.extra, (alloc_id, prov), range)?; } - Ok(Some(AllocRefMut { alloc, range, tcx: *tcx, alloc_id })) + interp_ok(Some(AllocRefMut { alloc, range, tcx: *tcx, alloc_id })) } else { - Ok(None) + interp_ok(None) } } @@ -802,7 +804,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { id: AllocId, ) -> InterpResult<'tcx, (&'a mut M::AllocExtra, &'a mut M)> { let (alloc, machine) = self.get_alloc_raw_mut(id)?; - Ok((&mut alloc.extra, machine)) + interp_ok((&mut alloc.extra, machine)) } /// Check whether an allocation is live. This is faster than calling @@ -904,7 +906,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if matches!(kind, AllocKind::Dead) { throw_ub!(PointerUseAfterFree(id, msg)) } - Ok((size, align)) + interp_ok((size, align)) } fn get_fn_alloc(&self, id: AllocId) -> Option> { @@ -928,7 +930,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { throw_ub!(InvalidFunctionPointer(Pointer::new(alloc_id, offset))) } self.get_fn_alloc(alloc_id) - .ok_or_else(|| err_ub!(InvalidFunctionPointer(Pointer::new(alloc_id, offset))).into()) + .ok_or_else(|| err_ub!(InvalidFunctionPointer(Pointer::new(alloc_id, offset)))) + .into() } /// Get the dynamic type of the given vtable pointer. @@ -951,12 +954,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if let Some(expected_dyn_type) = expected_trait { self.check_vtable_for_type(vtable_dyn_type, expected_dyn_type)?; } - Ok(ty) + interp_ok(ty) } pub fn alloc_mark_immutable(&mut self, id: AllocId) -> InterpResult<'tcx> { self.get_alloc_raw_mut(id)?.0.mutability = Mutability::Not; - Ok(()) + interp_ok(()) } /// Create a lazy debug printer that prints the given allocation and all allocations it points @@ -1144,10 +1147,11 @@ impl<'a, 'tcx, Prov: Provenance, Extra, Bytes: AllocBytes> pub fn write_scalar(&mut self, range: AllocRange, val: Scalar) -> InterpResult<'tcx> { let range = self.range.subrange(range); debug!("write_scalar at {:?}{range:?}: {val:?}", self.alloc_id); - Ok(self - .alloc + + self.alloc .write_scalar(&self.tcx, range, val) - .map_err(|e| e.to_interp_error(self.alloc_id))?) + .map_err(|e| e.to_interp_error(self.alloc_id)) + .into() } /// `offset` is relative to this allocation reference, not the base of the allocation. @@ -1158,26 +1162,27 @@ impl<'a, 'tcx, Prov: Provenance, Extra, Bytes: AllocBytes> /// Mark the given sub-range (relative to this allocation reference) as uninitialized. pub fn write_uninit(&mut self, range: AllocRange) -> InterpResult<'tcx> { let range = self.range.subrange(range); - Ok(self - .alloc + + self.alloc .write_uninit(&self.tcx, range) - .map_err(|e| e.to_interp_error(self.alloc_id))?) + .map_err(|e| e.to_interp_error(self.alloc_id)) + .into() } /// Mark the entire referenced range as uninitialized pub fn write_uninit_full(&mut self) -> InterpResult<'tcx> { - Ok(self - .alloc + self.alloc .write_uninit(&self.tcx, self.range) - .map_err(|e| e.to_interp_error(self.alloc_id))?) + .map_err(|e| e.to_interp_error(self.alloc_id)) + .into() } /// Remove all provenance in the reference range. pub fn clear_provenance(&mut self) -> InterpResult<'tcx> { - Ok(self - .alloc + self.alloc .clear_provenance(&self.tcx, self.range) - .map_err(|e| e.to_interp_error(self.alloc_id))?) + .map_err(|e| e.to_interp_error(self.alloc_id)) + .into() } } @@ -1189,12 +1194,10 @@ impl<'a, 'tcx, Prov: Provenance, Extra, Bytes: AllocBytes> AllocRef<'a, 'tcx, Pr read_provenance: bool, ) -> InterpResult<'tcx, Scalar> { let range = self.range.subrange(range); - let res = self - .alloc + self.alloc .read_scalar(&self.tcx, range, read_provenance) - .map_err(|e| e.to_interp_error(self.alloc_id))?; - debug!("read_scalar at {:?}{range:?}: {res:?}", self.alloc_id); - Ok(res) + .map_err(|e| e.to_interp_error(self.alloc_id)) + .into() } /// `range` is relative to this allocation reference, not the base of the allocation. @@ -1212,10 +1215,10 @@ impl<'a, 'tcx, Prov: Provenance, Extra, Bytes: AllocBytes> AllocRef<'a, 'tcx, Pr /// `range` is relative to this allocation reference, not the base of the allocation. pub fn get_bytes_strip_provenance<'b>(&'b self) -> InterpResult<'tcx, &'a [u8]> { - Ok(self - .alloc + self.alloc .get_bytes_strip_provenance(&self.tcx, self.range) - .map_err(|e| e.to_interp_error(self.alloc_id))?) + .map_err(|e| e.to_interp_error(self.alloc_id)) + .into() } /// Returns whether the allocation has provenance anywhere in the range of the `AllocRef`. @@ -1236,14 +1239,16 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ) -> InterpResult<'tcx, &[u8]> { let Some(alloc_ref) = self.get_ptr_alloc(ptr, size)? else { // zero-sized access - return Ok(&[]); + return interp_ok(&[]); }; // Side-step AllocRef and directly access the underlying bytes more efficiently. // (We are staying inside the bounds here so all is good.) - Ok(alloc_ref - .alloc - .get_bytes_strip_provenance(&alloc_ref.tcx, alloc_ref.range) - .map_err(|e| e.to_interp_error(alloc_ref.alloc_id))?) + interp_ok( + alloc_ref + .alloc + .get_bytes_strip_provenance(&alloc_ref.tcx, alloc_ref.range) + .map_err(|e| e.to_interp_error(alloc_ref.alloc_id))?, + ) } /// Writes the given stream of bytes into memory. @@ -1263,7 +1268,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let Some(alloc_ref) = self.get_ptr_alloc_mut(ptr, size)? else { // zero-sized access assert_matches!(src.next(), None, "iterator said it was empty but returned an element"); - return Ok(()); + return interp_ok(()); }; // Side-step AllocRef and directly access the underlying bytes more efficiently. @@ -1279,7 +1284,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { *dest = src.next().expect("iterator was shorter than it said it would be"); } assert_matches!(src.next(), None, "iterator was longer than it said it would be"); - Ok(()) + interp_ok(()) } pub fn mem_copy( @@ -1316,7 +1321,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // Source alloc preparations and access hooks. let Some((src_alloc_id, src_offset, src_prov)) = src_parts else { // Zero-sized *source*, that means dest is also zero-sized and we have nothing to do. - return Ok(()); + return interp_ok(()); }; let src_alloc = self.get_alloc_raw(src_alloc_id)?; let src_range = alloc_range(src_offset, size); @@ -1332,7 +1337,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // We already did the source checks and called the hooks so we are good to return early. let Some((dest_alloc_id, dest_offset, dest_prov)) = dest_parts else { // Zero-sized *destination*. - return Ok(()); + return interp_ok(()); }; // Prepare getting source provenance. @@ -1375,7 +1380,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { .write_uninit(&tcx, dest_range) .map_err(|e| e.to_interp_error(dest_alloc_id))?; // We can forget about the provenance, this is all not initialized anyway. - return Ok(()); + return interp_ok(()); } // SAFE: The above indexing would have panicked if there weren't at least `size` bytes @@ -1432,7 +1437,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // copy the provenance to the destination dest_alloc.provenance_apply_copy(provenance); - Ok(()) + interp_ok(()) } } @@ -1441,7 +1446,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// Test if this value might be null. /// If the machine does not support ptr-to-int casts, this is conservative. pub fn scalar_may_be_null(&self, scalar: Scalar) -> InterpResult<'tcx, bool> { - Ok(match scalar.try_to_scalar_int() { + interp_ok(match scalar.try_to_scalar_int() { Ok(int) => int.is_null(), Err(_) => { // Can only happen during CTFE. @@ -1508,13 +1513,14 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ptr: Pointer>, size: i64, ) -> InterpResult<'tcx, (AllocId, Size, M::ProvenanceExtra)> { - self.ptr_try_get_alloc_id(ptr, size).map_err(|offset| { - err_ub!(DanglingIntPointer { - addr: offset, - inbounds_size: size, - msg: CheckInAllocMsg::InboundsTest + self.ptr_try_get_alloc_id(ptr, size) + .map_err(|offset| { + err_ub!(DanglingIntPointer { + addr: offset, + inbounds_size: size, + msg: CheckInAllocMsg::InboundsTest + }) }) .into() - }) } } diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index 31ee3e6519afc..7e6600060b40e 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -16,7 +16,7 @@ use tracing::trace; use super::{ CtfeProvenance, InterpCx, InterpResult, MPlaceTy, Machine, MemPlace, MemPlaceMeta, OffsetMode, PlaceTy, Pointer, Projectable, Provenance, Scalar, alloc_range, err_ub, from_known_layout, - mir_assign_valid_types, throw_ub, + interp_ok, mir_assign_valid_types, throw_ub, }; /// An `Immediate` represents a single immediate self-contained Rust value. @@ -149,7 +149,7 @@ impl Immediate { } Immediate::Uninit => {} } - Ok(()) + interp_ok(()) } } @@ -307,7 +307,7 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> { data_size: s.size().bytes(), })); } - Ok(s) + interp_ok(s) } #[inline] @@ -430,7 +430,7 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for ImmTy<'tcx, Prov> { ecx: &InterpCx<'tcx, M>, ) -> InterpResult<'tcx, Self> { assert_matches!(meta, MemPlaceMeta::None); // we can't store this anywhere anyway - Ok(self.offset_(offset, layout, ecx)) + interp_ok(self.offset_(offset, layout, ecx)) } #[inline(always)] @@ -438,7 +438,7 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for ImmTy<'tcx, Prov> { &self, _ecx: &InterpCx<'tcx, M>, ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { - Ok(self.clone().into()) + interp_ok(self.clone().into()) } } @@ -514,11 +514,13 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for OpTy<'tcx, Prov> { ecx: &InterpCx<'tcx, M>, ) -> InterpResult<'tcx, Self> { match self.as_mplace_or_imm() { - Left(mplace) => Ok(mplace.offset_with_meta(offset, mode, meta, layout, ecx)?.into()), + Left(mplace) => { + interp_ok(mplace.offset_with_meta(offset, mode, meta, layout, ecx)?.into()) + } Right(imm) => { assert_matches!(meta, MemPlaceMeta::None); // no place to store metadata here // Every part of an uninit is uninit. - Ok(imm.offset_(offset, layout, ecx).into()) + interp_ok(imm.offset_(offset, layout, ecx).into()) } } } @@ -528,7 +530,7 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for OpTy<'tcx, Prov> { &self, _ecx: &InterpCx<'tcx, M>, ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { - Ok(self.clone()) + interp_ok(self.clone()) } } @@ -543,12 +545,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ) -> InterpResult<'tcx, Option>> { if mplace.layout.is_unsized() { // Don't touch unsized - return Ok(None); + return interp_ok(None); } let Some(alloc) = self.get_place_alloc(mplace)? else { // zero-sized type can be left uninit - return Ok(Some(ImmTy::uninit(mplace.layout))); + return interp_ok(Some(ImmTy::uninit(mplace.layout))); }; // It may seem like all types with `Scalar` or `ScalarPair` ABI are fair game at this point. @@ -557,7 +559,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // case where some of the bytes are initialized and others are not. So, we need an extra // check that walks over the type of `mplace` to make sure it is truly correct to treat this // like a `Scalar` (or `ScalarPair`). - Ok(match mplace.layout.abi { + interp_ok(match mplace.layout.abi { Abi::Scalar(abi::Scalar::Initialized { value: s, .. }) => { let size = s.size(self); assert_eq!(size, mplace.layout.size, "abi::Scalar size does not match layout size"); @@ -606,7 +608,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { &self, src: &impl Projectable<'tcx, M::Provenance>, ) -> InterpResult<'tcx, Either, ImmTy<'tcx, M::Provenance>>> { - Ok(match src.to_op(self)?.as_mplace_or_imm() { + interp_ok(match src.to_op(self)?.as_mplace_or_imm() { Left(ref mplace) => { if let Some(val) = self.read_immediate_from_mplace_raw(mplace)? { Right(val) @@ -637,7 +639,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if matches!(*imm, Immediate::Uninit) { throw_ub!(InvalidUninitBytes(None)); } - Ok(imm) + interp_ok(imm) } /// Read a scalar from a place @@ -645,7 +647,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { &self, op: &impl Projectable<'tcx, M::Provenance>, ) -> InterpResult<'tcx, Scalar> { - Ok(self.read_immediate(op)?.to_scalar()) + interp_ok(self.read_immediate(op)?.to_scalar()) } // Pointer-sized reads are fairly common and need target layout access, so we wrap them in @@ -678,7 +680,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let len = mplace.len(self)?; let bytes = self.read_bytes_ptr_strip_provenance(mplace.ptr(), Size::from_bytes(len))?; let str = std::str::from_utf8(bytes).map_err(|err| err_ub!(InvalidStr(err)))?; - Ok(str) + interp_ok(str) } /// Read from a local of the current frame. @@ -698,7 +700,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { assert!(!layout.is_unsized()); } M::after_local_read(self, local)?; - Ok(OpTy { op, layout }) + interp_ok(OpTy { op, layout }) } /// Every place can be read from, so we can turn them into an operand. @@ -709,12 +711,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { place: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { match place.as_mplace_or_local() { - Left(mplace) => Ok(mplace.into()), + Left(mplace) => interp_ok(mplace.into()), Right((local, offset, locals_addr, _)) => { debug_assert!(place.layout.is_sized()); // only sized locals can ever be `Place::Local`. debug_assert_eq!(locals_addr, self.frame().locals_addr()); let base = self.local_to_op(local, None)?; - Ok(match offset { + interp_ok(match offset { Some(offset) => base.offset(offset, place.layout, self)?, None => { // In the common case this hasn't been projected. @@ -764,7 +766,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ) } } - Ok(op) + interp_ok(op) } /// Evaluate the operand, returning a place where you can then find the data. @@ -794,7 +796,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } }; trace!("{:?}: {:?}", mir_op, op); - Ok(op) + interp_ok(op) } pub(crate) fn const_val_to_op( @@ -805,12 +807,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { // Other cases need layout. let adjust_scalar = |scalar| -> InterpResult<'tcx, _> { - Ok(match scalar { + interp_ok(match scalar { Scalar::Ptr(ptr, size) => Scalar::Ptr(self.global_root_pointer(ptr)?, size), Scalar::Int(int) => Scalar::Int(int), }) }; - let layout = from_known_layout(self.tcx, self.param_env, layout, || self.layout_of(ty))?; + let layout = + from_known_layout(self.tcx, self.param_env, layout, || self.layout_of(ty).into())?; let imm = match val_val { mir::ConstValue::Indirect { alloc_id, offset } => { // This is const data, no mutation allowed. @@ -818,7 +821,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { CtfeProvenance::from(alloc_id).as_immutable(), offset, ))?; - return Ok(self.ptr_to_mplace(ptr.into(), layout).into()); + return interp_ok(self.ptr_to_mplace(ptr.into(), layout).into()); } mir::ConstValue::Scalar(x) => adjust_scalar(x)?.into(), mir::ConstValue::ZeroSized => Immediate::Uninit, @@ -829,7 +832,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { Immediate::new_slice(self.global_root_pointer(ptr)?.into(), meta, self) } }; - Ok(OpTy { op: Operand::Immediate(imm), layout }) + interp_ok(OpTy { op: Operand::Immediate(imm), layout }) } } diff --git a/compiler/rustc_const_eval/src/interpret/operator.rs b/compiler/rustc_const_eval/src/interpret/operator.rs index 863b330a74cd5..52cd9b898bb72 100644 --- a/compiler/rustc_const_eval/src/interpret/operator.rs +++ b/compiler/rustc_const_eval/src/interpret/operator.rs @@ -9,7 +9,7 @@ use rustc_span::symbol::sym; use rustc_target::abi::Size; use tracing::trace; -use super::{ImmTy, InterpCx, Machine, MemPlaceMeta, throw_ub}; +use super::{ImmTy, InterpCx, Machine, MemPlaceMeta, interp_ok, throw_ub}; impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { fn three_way_compare(&self, lhs: T, rhs: T) -> ImmTy<'tcx, M::Provenance> { @@ -156,7 +156,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { }); } - return Ok(ImmTy::from_scalar_int(result, left.layout)); + return interp_ok(ImmTy::from_scalar_int(result, left.layout)); } // For the remaining ops, the types must be the same on both sides @@ -181,10 +181,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { _ => None, }; if let Some(op) = op { - return Ok(ImmTy::from_bool(op(&l_signed(), &r_signed()), *self.tcx)); + return interp_ok(ImmTy::from_bool(op(&l_signed(), &r_signed()), *self.tcx)); } if bin_op == Cmp { - return Ok(self.three_way_compare(l_signed(), r_signed())); + return interp_ok(self.three_way_compare(l_signed(), r_signed())); } let op: Option (i128, bool)> = match bin_op { Div if r.is_null() => throw_ub!(DivisionByZero), @@ -221,7 +221,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { throw_ub!(ArithOverflow { intrinsic }); } let res = ImmTy::from_scalar_int(result, left.layout); - return Ok(if with_overflow { + return interp_ok(if with_overflow { let overflow = ImmTy::from_bool(overflow, *self.tcx); ImmTy::from_pair(res, overflow, *self.tcx) } else { @@ -234,10 +234,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let r = r_unsigned(); if bin_op == Cmp { - return Ok(self.three_way_compare(l, r)); + return interp_ok(self.three_way_compare(l, r)); } - Ok(match bin_op { + interp_ok(match bin_op { Eq => ImmTy::from_bool(l == r, *self.tcx), Ne => ImmTy::from_bool(l != r, *self.tcx), @@ -339,7 +339,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { throw_ub!(PointerArithOverflow) } let offset_ptr = self.ptr_offset_inbounds(ptr, offset_bytes)?; - Ok(ImmTy::from_scalar(Scalar::from_maybe_pointer(offset_ptr, self), left.layout)) + interp_ok(ImmTy::from_scalar( + Scalar::from_maybe_pointer(offset_ptr, self), + left.layout, + )) } // Fall back to machine hook so Miri can support more pointer ops. @@ -366,20 +369,20 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { assert_eq!(left.layout.ty, right.layout.ty); let left = left.to_scalar(); let right = right.to_scalar(); - Ok(self.binary_char_op(bin_op, left.to_char()?, right.to_char()?)) + interp_ok(self.binary_char_op(bin_op, left.to_char()?, right.to_char()?)) } ty::Bool => { assert_eq!(left.layout.ty, right.layout.ty); let left = left.to_scalar(); let right = right.to_scalar(); - Ok(self.binary_bool_op(bin_op, left.to_bool()?, right.to_bool()?)) + interp_ok(self.binary_bool_op(bin_op, left.to_bool()?, right.to_bool()?)) } ty::Float(fty) => { assert_eq!(left.layout.ty, right.layout.ty); let layout = left.layout; let left = left.to_scalar(); let right = right.to_scalar(); - Ok(match fty { + interp_ok(match fty { FloatTy::F16 => { self.binary_float_op(bin_op, layout, left.to_f16()?, right.to_f16()?) } @@ -447,7 +450,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { Not => !val, _ => span_bug!(self.cur_span(), "Invalid bool op {:?}", un_op), }; - Ok(ImmTy::from_bool(res, *self.tcx)) + interp_ok(ImmTy::from_bool(res, *self.tcx)) } ty::Float(fty) => { let val = val.to_scalar(); @@ -462,7 +465,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { FloatTy::F64 => Scalar::from_f64(-val.to_f64()?), FloatTy::F128 => Scalar::from_f128(-val.to_f128()?), }; - Ok(ImmTy::from_scalar(res, layout)) + interp_ok(ImmTy::from_scalar(res, layout)) } ty::Int(..) => { let val = val.to_scalar().to_int(layout.size)?; @@ -472,7 +475,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { _ => span_bug!(self.cur_span(), "Invalid integer op {:?}", un_op), }; let res = ScalarInt::truncate_from_int(res, layout.size).0; - Ok(ImmTy::from_scalar(res.into(), layout)) + interp_ok(ImmTy::from_scalar(res.into(), layout)) } ty::Uint(..) => { let val = val.to_scalar().to_uint(layout.size)?; @@ -481,12 +484,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { _ => span_bug!(self.cur_span(), "Invalid unsigned integer op {:?}", un_op), }; let res = ScalarInt::truncate_from_uint(res, layout.size).0; - Ok(ImmTy::from_scalar(res.into(), layout)) + interp_ok(ImmTy::from_scalar(res.into(), layout)) } ty::RawPtr(..) | ty::Ref(..) => { assert_eq!(un_op, PtrMetadata); let (_, meta) = val.to_scalar_and_meta(); - Ok(match meta { + interp_ok(match meta { MemPlaceMeta::Meta(scalar) => { let ty = un_op.ty(*self.tcx, val.layout.ty); let layout = self.layout_of(ty)?; @@ -514,7 +517,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let layout = self.layout_of(arg_ty)?; let usize_layout = || self.layout_of(self.tcx.types.usize).unwrap(); - Ok(match null_op { + interp_ok(match null_op { SizeOf => { if !layout.abi.is_sized() { span_bug!(self.cur_span(), "unsized type for `NullaryOp::SizeOf`"); diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index 05264d32c6ba8..449d4c6bd7dea 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -13,9 +13,9 @@ use rustc_target::abi::{Abi, Align, HasDataLayout, Size}; use tracing::{instrument, trace}; use super::{ - AllocRef, AllocRefMut, CheckAlignMsg, CtfeProvenance, DiscardInterpError, ImmTy, Immediate, - InterpCx, InterpResult, Machine, MemoryKind, Misalignment, OffsetMode, OpTy, Operand, Pointer, - Projectable, Provenance, Scalar, alloc_range, mir_assign_valid_types, + AllocRef, AllocRefMut, CheckAlignMsg, CtfeProvenance, ImmTy, Immediate, InterpCx, InterpResult, + Machine, MemoryKind, Misalignment, OffsetMode, OpTy, Operand, Pointer, Projectable, Provenance, + Scalar, alloc_range, interp_ok, mir_assign_valid_types, }; #[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)] @@ -90,7 +90,7 @@ impl MemPlace { } OffsetMode::Wrapping => self.ptr.wrapping_offset(offset, ecx), }; - Ok(MemPlace { ptr, meta, misaligned: self.misaligned }) + interp_ok(MemPlace { ptr, meta, misaligned: self.misaligned }) } } @@ -163,7 +163,10 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for MPlaceTy<'tcx, Prov> { layout: TyAndLayout<'tcx>, ecx: &InterpCx<'tcx, M>, ) -> InterpResult<'tcx, Self> { - Ok(MPlaceTy { mplace: self.mplace.offset_with_meta_(offset, mode, meta, ecx)?, layout }) + interp_ok(MPlaceTy { + mplace: self.mplace.offset_with_meta_(offset, mode, meta, ecx)?, + layout, + }) } #[inline(always)] @@ -171,7 +174,7 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for MPlaceTy<'tcx, Prov> { &self, _ecx: &InterpCx<'tcx, M>, ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { - Ok(self.clone().into()) + interp_ok(self.clone().into()) } } @@ -279,7 +282,7 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for PlaceTy<'tcx, Prov> { layout: TyAndLayout<'tcx>, ecx: &InterpCx<'tcx, M>, ) -> InterpResult<'tcx, Self> { - Ok(match self.as_mplace_or_local() { + interp_ok(match self.as_mplace_or_local() { Left(mplace) => mplace.offset_with_meta(offset, mode, meta, layout, ecx)?.into(), Right((local, old_offset, locals_addr, _)) => { debug_assert!(layout.is_sized(), "unsized locals should live in memory"); @@ -367,7 +370,7 @@ impl<'tcx, Prov: Provenance> Writeable<'tcx, Prov> for MPlaceTy<'tcx, Prov> { &self, _ecx: &mut InterpCx<'tcx, M>, ) -> InterpResult<'tcx, MPlaceTy<'tcx, Prov>> { - Ok(self.clone()) + interp_ok(self.clone()) } } @@ -425,7 +428,7 @@ where // `ref_to_mplace` is called on raw pointers even if they don't actually get dereferenced; // we hence can't call `size_and_align_of` since that asserts more validity than we want. let ptr = ptr.to_pointer(self)?; - Ok(self.ptr_with_meta_to_mplace(ptr, meta, layout, /*unaligned*/ false)) + interp_ok(self.ptr_with_meta_to_mplace(ptr, meta, layout, /*unaligned*/ false)) } /// Turn a mplace into a (thin or wide) mutable raw pointer, pointing to the same space. @@ -437,7 +440,7 @@ where ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> { let imm = mplace.mplace.to_ref(self); let layout = self.layout_of(Ty::new_mut_ptr(self.tcx.tcx, mplace.layout.ty))?; - Ok(ImmTy::from_immediate(imm, layout)) + interp_ok(ImmTy::from_immediate(imm, layout)) } /// Take an operand, representing a pointer, and dereference it to a place. @@ -458,7 +461,7 @@ where trace!("deref to {} on {:?}", val.layout.ty, *val); let mplace = self.ref_to_mplace(&val)?; - Ok(mplace) + interp_ok(mplace) } #[inline] @@ -474,7 +477,7 @@ where // If an access is both OOB and misaligned, we want to see the bounds error. let a = self.get_ptr_alloc(mplace.ptr(), size)?; self.check_misalign(mplace.mplace.misaligned, CheckAlignMsg::BasedOn)?; - Ok(a) + interp_ok(a) } #[inline] @@ -489,17 +492,10 @@ where // We check alignment separately, and raise that error *after* checking everything else. // If an access is both OOB and misaligned, we want to see the bounds error. // However we have to call `check_misalign` first to make the borrow checker happy. - let misalign_err = self.check_misalign(mplace.mplace.misaligned, CheckAlignMsg::BasedOn); - match self.get_ptr_alloc_mut(mplace.ptr(), size) { - Ok(a) => { - misalign_err?; - Ok(a) - } - Err(e) => { - misalign_err.discard_interp_err(); - Err(e) - } - } + let misalign_res = self.check_misalign(mplace.mplace.misaligned, CheckAlignMsg::BasedOn); + // An error from get_ptr_alloc_mut takes precedence. + let (a, ()) = self.get_ptr_alloc_mut(mplace.ptr(), size).and(misalign_res)?; + interp_ok(a) } /// Turn a local in the current frame into a place. @@ -519,7 +515,7 @@ where Operand::Indirect(mplace) => Place::Ptr(*mplace), } }; - Ok(PlaceTy { place, layout }) + interp_ok(PlaceTy { place, layout }) } /// Computes a place. You should only use this if you intend to write into this @@ -556,7 +552,7 @@ where ) } } - Ok(place) + interp_ok(place) } /// Given a place, returns either the underlying mplace or a reference to where the value of @@ -572,7 +568,7 @@ where (&mut Immediate, TyAndLayout<'tcx>, mir::Local), >, > { - Ok(match place.to_place().as_mplace_or_local() { + interp_ok(match place.to_place().as_mplace_or_local() { Left(mplace) => Left(mplace), Right((local, offset, locals_addr, layout)) => { if offset.is_some() { @@ -617,7 +613,7 @@ where )?; } - Ok(()) + interp_ok(()) } /// Write a scalar to a place @@ -667,7 +663,7 @@ where self.write_immediate_to_mplace_no_validate(src, mplace.layout, mplace.mplace)?; } } - Ok(()) + interp_ok(()) } /// Write an immediate to memory. @@ -690,7 +686,7 @@ where let tcx = *self.tcx; let Some(mut alloc) = self.get_place_alloc_mut(&MPlaceTy { mplace: dest, layout })? else { // zero-sized access - return Ok(()); + return interp_ok(()); }; match value { @@ -715,7 +711,7 @@ where alloc.write_scalar(alloc_range(Size::ZERO, a_val.size()), a_val)?; alloc.write_scalar(alloc_range(b_offset, b_val.size()), b_val)?; // We don't have to reset padding here, `write_immediate` will anyway do a validation run. - Ok(()) + interp_ok(()) } Immediate::Uninit => alloc.write_uninit_full(), } @@ -736,12 +732,12 @@ where Left(mplace) => { let Some(mut alloc) = self.get_place_alloc_mut(&mplace)? else { // Zero-sized access - return Ok(()); + return interp_ok(()); }; alloc.write_uninit_full()?; } } - Ok(()) + interp_ok(()) } /// Remove all provenance in the given place. @@ -760,12 +756,12 @@ where Left(mplace) => { let Some(mut alloc) = self.get_place_alloc_mut(&mplace)? else { // Zero-sized access - return Ok(()); + return interp_ok(()); }; alloc.clear_provenance()?; } } - Ok(()) + interp_ok(()) } /// Copies the data from an operand to a place. @@ -848,7 +844,7 @@ where )?; } - Ok(()) + interp_ok(()) } /// Copies the data from an operand to a place. @@ -925,7 +921,7 @@ where self.mem_copy(src.ptr(), dest.ptr(), dest_size, /*nonoverlapping*/ true)?; self.check_misalign(src.mplace.misaligned, CheckAlignMsg::BasedOn)?; self.check_misalign(dest.mplace.misaligned, CheckAlignMsg::BasedOn)?; - Ok(()) + interp_ok(()) } /// Ensures that a place is in memory, and returns where it is. @@ -987,7 +983,7 @@ where Place::Ptr(mplace) => mplace, }; // Return with the original layout and align, so that the caller can go on - Ok(MPlaceTy { mplace, layout: place.layout }) + interp_ok(MPlaceTy { mplace, layout: place.layout }) } pub fn allocate_dyn( @@ -1000,7 +996,7 @@ where span_bug!(self.cur_span(), "cannot allocate space for `extern` type, size is not known") }; let ptr = self.allocate_ptr(size, align, kind)?; - Ok(self.ptr_with_meta_to_mplace(ptr.into(), meta, layout, /*unaligned*/ false)) + interp_ok(self.ptr_with_meta_to_mplace(ptr.into(), meta, layout, /*unaligned*/ false)) } pub fn allocate( @@ -1035,7 +1031,7 @@ where }; let meta = Scalar::from_target_usize(u64::try_from(str.len()).unwrap(), self); let layout = self.layout_of(self.tcx.types.str_).unwrap(); - Ok(self.ptr_with_meta_to_mplace( + interp_ok(self.ptr_with_meta_to_mplace( ptr.into(), MemPlaceMeta::Meta(meta), layout, @@ -1051,7 +1047,7 @@ where let _ = self.tcx.global_alloc(raw.alloc_id); let ptr = self.global_root_pointer(Pointer::from(raw.alloc_id))?; let layout = self.layout_of(raw.ty)?; - Ok(self.ptr_to_mplace(ptr.into(), layout)) + interp_ok(self.ptr_to_mplace(ptr.into(), layout)) } } diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs index 621afdb1050a2..cc49d7cebf450 100644 --- a/compiler/rustc_const_eval/src/interpret/projection.rs +++ b/compiler/rustc_const_eval/src/interpret/projection.rs @@ -18,7 +18,7 @@ use tracing::{debug, instrument}; use super::{ InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy, Provenance, Scalar, err_ub, - throw_ub, throw_unsup, + interp_ok, throw_ub, throw_unsup, }; /// Describes the constraints placed on offset-projections. @@ -54,7 +54,7 @@ pub trait Projectable<'tcx, Prov: Provenance>: Sized + std::fmt::Debug { // Go through the layout. There are lots of types that support a length, // e.g., SIMD types. (But not all repr(simd) types even have FieldsShape::Array!) match layout.fields { - abi::FieldsShape::Array { count, .. } => Ok(count), + abi::FieldsShape::Array { count, .. } => interp_ok(count), _ => bug!("len not supported on sized type {:?}", layout.ty), } } @@ -115,9 +115,9 @@ impl<'a, 'tcx, Prov: Provenance, P: Projectable<'tcx, Prov>> ArrayIterator<'a, ' &mut self, ecx: &InterpCx<'tcx, M>, ) -> InterpResult<'tcx, Option<(u64, P)>> { - let Some(idx) = self.range.next() else { return Ok(None) }; + let Some(idx) = self.range.next() else { return interp_ok(None) }; // We use `Wrapping` here since the offset has already been checked when the iterator was created. - Ok(Some(( + interp_ok(Some(( idx, self.base.offset_with_meta( self.stride * idx, @@ -258,7 +258,7 @@ where // SIMD types must be newtypes around arrays, so all we have to do is project to their only field. let array = self.project_field(base, 0)?; let len = array.len(self)?; - Ok((array, len)) + interp_ok((array, len)) } fn project_constant_index>( @@ -300,7 +300,13 @@ where debug!("project_array_fields: {base:?} {len}"); base.offset(len * stride, self.layout_of(self.tcx.types.unit).unwrap(), self)?; // Create the iterator. - Ok(ArrayIterator { base, range: 0..len, stride, field_layout, _phantom: PhantomData }) + interp_ok(ArrayIterator { + base, + range: 0..len, + stride, + field_layout, + _phantom: PhantomData, + }) } /// Subslicing @@ -367,7 +373,7 @@ where P: Projectable<'tcx, M::Provenance> + From> + std::fmt::Debug, { use rustc_middle::mir::ProjectionElem::*; - Ok(match proj_elem { + interp_ok(match proj_elem { OpaqueCast(ty) => { span_bug!(self.cur_span(), "OpaqueCast({ty}) encountered after borrowck") } diff --git a/compiler/rustc_const_eval/src/interpret/stack.rs b/compiler/rustc_const_eval/src/interpret/stack.rs index 15868f1b02d92..3bc9f46aea066 100644 --- a/compiler/rustc_const_eval/src/interpret/stack.rs +++ b/compiler/rustc_const_eval/src/interpret/stack.rs @@ -17,7 +17,7 @@ use tracing::{info_span, instrument, trace}; use super::{ AllocId, CtfeProvenance, Immediate, InterpCx, InterpResult, MPlaceTy, Machine, MemPlace, MemPlaceMeta, MemoryKind, Operand, Pointer, Provenance, ReturnAction, Scalar, - from_known_layout, throw_ub, throw_unsup, + from_known_layout, interp_ok, throw_ub, throw_unsup, }; use crate::errors; @@ -189,7 +189,7 @@ impl<'tcx, Prov: Provenance> LocalState<'tcx, Prov> { pub(super) fn access(&self) -> InterpResult<'tcx, &Operand> { match &self.value { LocalValue::Dead => throw_ub!(DeadLocal), // could even be "invalid program"? - LocalValue::Live(val) => Ok(val), + LocalValue::Live(val) => interp_ok(val), } } @@ -199,7 +199,7 @@ impl<'tcx, Prov: Provenance> LocalState<'tcx, Prov> { pub(super) fn access_mut(&mut self) -> InterpResult<'tcx, &mut Operand> { match &mut self.value { LocalValue::Dead => throw_ub!(DeadLocal), // could even be "invalid program"? - LocalValue::Live(val) => Ok(val), + LocalValue::Live(val) => interp_ok(val), } } } @@ -391,7 +391,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let span = info_span!("frame", "{}", instance); self.frame_mut().tracing_span.enter(span); - Ok(()) + interp_ok(()) } /// Low-level helper that pops a stack frame from the stack and returns some information about @@ -426,7 +426,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { return_action = ReturnAction::NoCleanup; }; - Ok(StackPopInfo { return_action, return_to_block, return_place }) + interp_ok(StackPopInfo { return_action, return_to_block, return_place }) } /// A private helper for [`pop_stack_frame_raw`](InterpCx::pop_stack_frame_raw). @@ -449,7 +449,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } } - Ok(cleanup) + interp_ok(cleanup) } /// In the current stack frame, mark all locals as live that are not arguments and don't have @@ -464,7 +464,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.storage_live(local)?; } } - Ok(()) + interp_ok(()) } pub fn storage_live_dyn( @@ -550,7 +550,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // If the local is already live, deallocate its old memory. let old = mem::replace(&mut self.frame_mut().locals[local].value, local_val); self.deallocate_local(old)?; - Ok(()) + interp_ok(()) } /// Mark a storage as live, killing the previous content. @@ -566,7 +566,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // If the local is already dead, this is a NOP. let old = mem::replace(&mut self.frame_mut().locals[local].value, LocalValue::Dead); self.deallocate_local(old)?; - Ok(()) + interp_ok(()) } fn deallocate_local(&mut self, local: LocalValue) -> InterpResult<'tcx> { @@ -581,7 +581,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ); self.deallocate_ptr(ptr, None, MemoryKind::Stack)?; }; - Ok(()) + interp_ok(()) } #[inline(always)] @@ -593,19 +593,19 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ) -> InterpResult<'tcx, TyAndLayout<'tcx>> { let state = &frame.locals[local]; if let Some(layout) = state.layout.get() { - return Ok(layout); + return interp_ok(layout); } let layout = from_known_layout(self.tcx, self.param_env, layout, || { let local_ty = frame.body.local_decls[local].ty; let local_ty = self.instantiate_from_frame_and_normalize_erasing_regions(frame, local_ty)?; - self.layout_of(local_ty) + self.layout_of(local_ty).into() })?; // Layouts of locals are requested a lot, so we cache them. state.layout.set(Some(layout)); - Ok(layout) + interp_ok(layout) } } diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index 8e2367e0d214c..aa7529556754b 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -14,7 +14,7 @@ use tracing::{info, instrument, trace}; use super::{ FnArg, FnVal, ImmTy, Immediate, InterpCx, InterpResult, Machine, MemPlaceMeta, PlaceTy, - Projectable, Scalar, throw_ub, + Projectable, Scalar, interp_ok, throw_ub, }; use crate::util; @@ -36,7 +36,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { #[inline(always)] pub fn step(&mut self) -> InterpResult<'tcx, bool> { if self.stack().is_empty() { - return Ok(false); + return interp_ok(false); } let Either::Left(loc) = self.frame().loc else { @@ -44,7 +44,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // Just go on unwinding. trace!("unwinding: skipping frame"); self.return_from_current_stack_frame(/* unwinding */ true)?; - return Ok(true); + return interp_ok(true); }; let basic_block = &self.body().basic_blocks[loc.block]; @@ -55,7 +55,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { assert_eq!(old_frames, self.frame_idx()); // Advance the program counter. self.frame_mut().loc.as_mut().left().unwrap().statement_index += 1; - return Ok(true); + return interp_ok(true); } M::before_terminator(self)?; @@ -67,7 +67,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { info!("// executing {:?}", loc.block); } } - Ok(true) + interp_ok(true) } /// Runs the interpretation logic for the given `mir::Statement` at the current frame and @@ -145,7 +145,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { Nop => {} } - Ok(()) + interp_ok(()) } /// Evaluate an assignment statement. @@ -277,7 +277,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { trace!("{:?}", self.dump_place(&dest)); - Ok(()) + interp_ok(()) } /// Writes the aggregate to the destination. @@ -313,7 +313,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let ptr_imm = Immediate::new_pointer_with_meta(data, meta, self); let ptr = ImmTy::from_immediate(ptr_imm, dest.layout); self.copy_op(&ptr, dest)?; - return Ok(()); + return interp_ok(()); } _ => (FIRST_VARIANT, dest.clone(), None), }; @@ -365,7 +365,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { )?; } - Ok(()) + interp_ok(()) } /// Evaluate the arguments of a function call @@ -373,7 +373,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { &self, op: &mir::Operand<'tcx>, ) -> InterpResult<'tcx, FnArg<'tcx, M::Provenance>> { - Ok(match op { + interp_ok(match op { mir::Operand::Copy(_) | mir::Operand::Constant(_) => { // Make a regular copy. let op = self.eval_operand(op, None)?; @@ -442,7 +442,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } }; - Ok(EvaluatedCalleeAndArgs { callee, args, fn_sig, fn_abi, with_caller_location }) + interp_ok(EvaluatedCalleeAndArgs { callee, args, fn_sig, fn_abi, with_caller_location }) } fn eval_terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> InterpResult<'tcx> { @@ -537,7 +537,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // generic. In order to make sure that generic and non-generic code behaves // roughly the same (and in keeping with Mir semantics) we do nothing here. self.go_to_block(target); - return Ok(()); + return interp_ok(()); } trace!("TerminatorKind::drop: {:?}, type {}", place, place.layout.ty); self.init_drop_in_place_call(&place, instance, target, unwind)?; @@ -566,7 +566,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // By definition, a Resume terminator means // that we're unwinding self.return_from_current_stack_frame(/* unwinding */ true)?; - return Ok(()); + return interp_ok(()); } // It is UB to ever encounter this. @@ -584,6 +584,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } } - Ok(()) + interp_ok(()) } } diff --git a/compiler/rustc_const_eval/src/interpret/traits.rs b/compiler/rustc_const_eval/src/interpret/traits.rs index 8eead6018ac4a..da7d6853c0ea7 100644 --- a/compiler/rustc_const_eval/src/interpret/traits.rs +++ b/compiler/rustc_const_eval/src/interpret/traits.rs @@ -5,7 +5,9 @@ use rustc_target::abi::{Align, Size}; use tracing::trace; use super::util::ensure_monomorphic_enough; -use super::{InterpCx, MPlaceTy, Machine, MemPlaceMeta, OffsetMode, Projectable, throw_ub}; +use super::{ + InterpCx, MPlaceTy, Machine, MemPlaceMeta, OffsetMode, Projectable, interp_ok, throw_ub, +}; impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// Creates a dynamic vtable for the given type and vtable origin. This is used only for @@ -31,7 +33,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let salt = M::get_global_alloc_salt(self, None); let vtable_symbolic_allocation = self.tcx.reserve_and_set_vtable_alloc(ty, dyn_ty, salt); let vtable_ptr = self.global_root_pointer(Pointer::from(vtable_symbolic_allocation))?; - Ok(vtable_ptr.into()) + interp_ok(vtable_ptr.into()) } pub fn get_vtable_size_and_align( @@ -42,7 +44,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let ty = self.get_ptr_vtable_ty(vtable, expected_trait)?; let layout = self.layout_of(ty)?; assert!(layout.is_sized(), "there are no vtables for unsized types"); - Ok((layout.size, layout.align.abi)) + interp_ok((layout.size, layout.align.abi)) } pub(super) fn vtable_entries( @@ -102,7 +104,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } } - Ok(()) + interp_ok(()) } /// Turn a place with a `dyn Trait` type into a place with the actual dynamic type. @@ -127,7 +129,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { layout, self, )?; - Ok(mplace) + interp_ok(mplace) } /// Turn a `dyn* Trait` type into an value with the actual dynamic type. @@ -147,6 +149,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // `data` is already the right thing but has the wrong type. So we transmute it. let layout = self.layout_of(ty)?; let data = data.transmute(layout, self)?; - Ok(data) + interp_ok(data) } } diff --git a/compiler/rustc_const_eval/src/interpret/util.rs b/compiler/rustc_const_eval/src/interpret/util.rs index 2d53badf0f967..8bb5f173a5645 100644 --- a/compiler/rustc_const_eval/src/interpret/util.rs +++ b/compiler/rustc_const_eval/src/interpret/util.rs @@ -9,7 +9,7 @@ use rustc_middle::ty::{ }; use tracing::debug; -use super::{InterpCx, MPlaceTy, MemoryKind, throw_inval}; +use super::{InterpCx, MPlaceTy, MemoryKind, interp_ok, throw_inval}; use crate::const_eval::{CompileTimeInterpCx, CompileTimeMachine, InterpretationResult}; /// Checks whether a type contains generic parameters which must be instantiated. @@ -23,7 +23,7 @@ where { debug!("ensure_monomorphic_enough: ty={:?}", ty); if !ty.has_param() { - return Ok(()); + return interp_ok(()); } struct FoundParam; @@ -78,7 +78,7 @@ where if matches!(ty.visit_with(&mut vis), ControlFlow::Break(FoundParam)) { throw_inval!(TooGeneric); } else { - Ok(()) + interp_ok(()) } } @@ -103,5 +103,5 @@ pub(crate) fn create_static_alloc<'tcx>( assert_eq!(ecx.machine.static_root_ids, None); ecx.machine.static_root_ids = Some((alloc_id, static_def_id)); assert!(ecx.memory.alloc_map.insert(alloc_id, (MemoryKind::Stack, alloc)).is_none()); - Ok(ecx.ptr_to_mplace(Pointer::from(alloc_id).into(), layout)) + interp_ok(ecx.ptr_to_mplace(Pointer::from(alloc_id).into(), layout)) } diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 3c9fdd73100c6..13641ef2bd3f6 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -18,7 +18,7 @@ use rustc_middle::bug; use rustc_middle::mir::interpret::ValidationErrorKind::{self, *}; use rustc_middle::mir::interpret::{ ExpectedKind, InterpError, InterpErrorInfo, InvalidMetaKind, Misalignment, PointerKind, - Provenance, UnsupportedOpInfo, ValidationErrorInfo, alloc_range, + Provenance, UnsupportedOpInfo, ValidationErrorInfo, alloc_range, interp_ok, }; use rustc_middle::ty::layout::{LayoutCx, LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, Ty}; @@ -32,7 +32,7 @@ use super::machine::AllocMap; use super::{ AllocId, AllocKind, CheckInAllocMsg, GlobalAlloc, ImmTy, Immediate, InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, PlaceTy, Pointer, Projectable, Scalar, ValueVisitor, err_ub, - format_interp_error, throw_ub, + format_interp_error, }; // for the validation errors @@ -42,7 +42,7 @@ use super::InterpError::Unsupported as Unsup; use super::UndefinedBehaviorInfo::*; use super::UnsupportedOpInfo::*; -macro_rules! throw_validation_failure { +macro_rules! err_validation_failure { ($where:expr, $kind: expr) => {{ let where_ = &$where; let path = if !where_.is_empty() { @@ -53,10 +53,16 @@ macro_rules! throw_validation_failure { None }; - throw_ub!(ValidationError(ValidationErrorInfo { path, kind: $kind })) + err_ub!(ValidationError(ValidationErrorInfo { path, kind: $kind })) }}; } +macro_rules! throw_validation_failure { + ($where:expr, $kind: expr) => { + do yeet err_validation_failure!($where, $kind) + }; +} + /// If $e throws an error matching the pattern, throw a validation failure. /// Other errors are passed back to the caller, unchanged -- and if they reach the root of /// the visitor, we make sure only validation errors and `InvalidProgram` errors are left. @@ -91,25 +97,22 @@ macro_rules! try_validation { ($e:expr, $where:expr, $( $( $p:pat_param )|+ => $kind: expr ),+ $(,)? ) => {{ - match $e { - Ok(x) => x, + $e.map_err(|e| { // We catch the error and turn it into a validation failure. We are okay with // allocation here as this can only slow down builds that fail anyway. - Err(e) => { - let (kind, backtrace) = e.into_parts(); - match kind { - $( - $($p)|+ => { - throw_validation_failure!( - $where, - $kind - ) - } - ),+, - _ => Err::(InterpErrorInfo::from_parts(kind, backtrace))?, - } + let (kind, backtrace) = e.into_parts(); + match kind { + $( + $($p)|+ => { + err_validation_failure!( + $where, + $kind + ).into() + } + ),+, + _ => InterpErrorInfo::from_parts(kind, backtrace), } - } + })? }}; } @@ -381,7 +384,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { // Undo changes self.path.truncate(path_len); // Done - Ok(r) + interp_ok(r) } fn read_immediate( @@ -389,7 +392,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { val: &PlaceTy<'tcx, M::Provenance>, expected: ExpectedKind, ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> { - Ok(try_validation!( + interp_ok(try_validation!( self.ecx.read_immediate(val), self.path, Ub(InvalidUninitBytes(None)) => @@ -407,7 +410,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { val: &PlaceTy<'tcx, M::Provenance>, expected: ExpectedKind, ) -> InterpResult<'tcx, Scalar> { - Ok(self.read_immediate(val, expected)?.to_scalar()) + interp_ok(self.read_immediate(val, expected)?.to_scalar()) } fn deref_pointer( @@ -472,7 +475,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { _ => bug!("Unexpected unsized type tail: {:?}", tail), } - Ok(()) + interp_ok(()) } /// Check a reference or `Box`. @@ -635,7 +638,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { } // Potentially skip recursive check. if skip_recursive_check { - return Ok(()); + return interp_ok(()); } } else { // This is not CTFE, so it's Miri with recursive checking. @@ -644,7 +647,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { // FIXME: should we also skip `UnsafeCell` behind shared references? Currently that is not // needed since validation reads bypass Stacked Borrows and data race checks. if matches!(ptr_kind, PointerKind::Box) { - return Ok(()); + return interp_ok(()); } } let path = &self.path; @@ -657,7 +660,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { new_path }); } - Ok(()) + interp_ok(()) } /// Check if this is a value of primitive type, and if yes check the validity of the value @@ -684,7 +687,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { self.ecx.clear_provenance(value)?; self.add_data_range_place(value); } - Ok(true) + interp_ok(true) } ty::Char => { let scalar = self.read_scalar(value, ExpectedKind::Char)?; @@ -699,7 +702,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { self.ecx.clear_provenance(value)?; self.add_data_range_place(value); } - Ok(true) + interp_ok(true) } ty::Float(_) | ty::Int(_) | ty::Uint(_) => { // NOTE: Keep this in sync with the array optimization for int/float @@ -716,18 +719,18 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { self.ecx.clear_provenance(value)?; self.add_data_range_place(value); } - Ok(true) + interp_ok(true) } ty::RawPtr(..) => { let place = self.deref_pointer(value, ExpectedKind::RawPtr)?; if place.layout.is_unsized() { self.check_wide_ptr_meta(place.meta(), place.layout)?; } - Ok(true) + interp_ok(true) } ty::Ref(_, _ty, mutbl) => { self.check_safe_pointer(value, PointerKind::Ref(*mutbl))?; - Ok(true) + interp_ok(true) } ty::FnPtr(..) => { let scalar = self.read_scalar(value, ExpectedKind::FnPtr)?; @@ -756,12 +759,12 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { } self.add_data_range_place(value); } - Ok(true) + interp_ok(true) } ty::Never => throw_validation_failure!(self.path, NeverVal), ty::Foreign(..) | ty::FnDef(..) => { // Nothing to check. - Ok(true) + interp_ok(true) } // The above should be all the primitive types. The rest is compound, we // check them by visiting their fields/variants. @@ -774,7 +777,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { | ty::Closure(..) | ty::Pat(..) | ty::CoroutineClosure(..) - | ty::Coroutine(..) => Ok(false), + | ty::Coroutine(..) => interp_ok(false), // Some types only occur during typechecking, they have no layout. // We should not see them here and we could not check them anyway. ty::Error(_) @@ -811,11 +814,11 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { max_value }) } else { - return Ok(()); + return interp_ok(()); } } else if scalar_layout.is_always_valid(self.ecx) { // Easy. (This is reachable if `enforce_number_validity` is set.) - return Ok(()); + return interp_ok(()); } else { // Conservatively, we reject, because the pointer *could* have a bad // value. @@ -828,7 +831,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { }; // Now compare. if valid_range.contains(bits) { - Ok(()) + interp_ok(()) } else { throw_validation_failure!(self.path, OutOfRange { value: format!("{bits}"), @@ -887,7 +890,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { } fn reset_padding(&mut self, place: &PlaceTy<'tcx, M::Provenance>) -> InterpResult<'tcx> { - let Some(data_bytes) = self.data_bytes.as_mut() else { return Ok(()) }; + let Some(data_bytes) = self.data_bytes.as_mut() else { return interp_ok(()) }; // Our value must be in memory, otherwise we would not have set up `data_bytes`. let mplace = self.ecx.force_allocation(place)?; // Determine starting offset and size. @@ -899,14 +902,14 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { // If there is no padding at all, we can skip the rest: check for // a single data range covering the entire value. if data_bytes.0 == &[(start_offset, size)] { - return Ok(()); + return interp_ok(()); } // Get a handle for the allocation. Do this only once, to avoid looking up the same // allocation over and over again. (Though to be fair, iterating the value already does // exactly that.) let Some(mut alloc) = self.ecx.get_ptr_alloc_mut(mplace.ptr(), size)? else { // A ZST, no padding to clear. - return Ok(()); + return interp_ok(()); }; // Add a "finalizer" data range at the end, so that the iteration below finds all gaps // between ranges. @@ -933,7 +936,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { padding_cleared_until = offset + size; } assert!(padding_cleared_until == start_offset + size); - Ok(()) + interp_ok(()) } /// Computes the data range of this union type: @@ -1073,7 +1076,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, val: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx, VariantIdx> { self.with_elem(PathElem::EnumTag, move |this| { - Ok(try_validation!( + interp_ok(try_validation!( this.ecx.read_discriminant(val), this.path, Ub(InvalidTag(val)) => InvalidEnumTag { @@ -1137,7 +1140,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, data_bytes.add_range(base_offset + offset, size); } } - Ok(()) + interp_ok(()) } #[inline] @@ -1147,7 +1150,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, val: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx> { self.check_safe_pointer(val, PointerKind::Box)?; - Ok(()) + interp_ok(()) } #[inline] @@ -1160,7 +1163,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, // We assume that the Scalar validity range does not restrict these values // any further than `try_visit_primitive` does! if self.try_visit_primitive(val)? { - return Ok(()); + return interp_ok(()); } // Special check preventing `UnsafeCell` in the inner part of constants @@ -1207,7 +1210,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, // If the size is 0, there is nothing to check. // (`size` can only be 0 if `len` is 0, and empty arrays are always valid.) if size == Size::ZERO { - return Ok(()); + return interp_ok(()); } // Now that we definitely have a non-ZST array, we know it lives in memory -- except it may // be an uninitialized local variable, those are also "immediate". @@ -1227,37 +1230,33 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, // No need for an alignment check here, this is not an actual memory access. let alloc = self.ecx.get_ptr_alloc(mplace.ptr(), size)?.expect("we already excluded size 0"); - match alloc.get_bytes_strip_provenance() { - // In the happy case, we needn't check anything else. - Ok(_) => {} + alloc.get_bytes_strip_provenance().map_err(|err| { // Some error happened, try to provide a more detailed description. - Err(err) => { - // For some errors we might be able to provide extra information. - // (This custom logic does not fit the `try_validation!` macro.) - let (kind, backtrace) = err.into_parts(); - match kind { - Ub(InvalidUninitBytes(Some((_alloc_id, access)))) | Unsup(ReadPointerAsInt(Some((_alloc_id, access)))) => { - // Some byte was uninitialized, determine which - // element that byte belongs to so we can - // provide an index. - let i = usize::try_from( - access.bad.start.bytes() / layout.size.bytes(), - ) - .unwrap(); - self.path.push(PathElem::ArrayElem(i)); - - if matches!(kind, Ub(InvalidUninitBytes(_))) { - throw_validation_failure!(self.path, Uninit { expected }) - } else { - throw_validation_failure!(self.path, PointerAsInt { expected }) - } - } + // For some errors we might be able to provide extra information. + // (This custom logic does not fit the `try_validation!` macro.) + let (kind, backtrace) = err.into_parts(); + match kind { + Ub(InvalidUninitBytes(Some((_alloc_id, access)))) | Unsup(ReadPointerAsInt(Some((_alloc_id, access)))) => { + // Some byte was uninitialized, determine which + // element that byte belongs to so we can + // provide an index. + let i = usize::try_from( + access.bad.start.bytes() / layout.size.bytes(), + ) + .unwrap(); + self.path.push(PathElem::ArrayElem(i)); - // Propagate upwards (that will also check for unexpected errors). - _ => return Err(InterpErrorInfo::from_parts(kind, backtrace)), + if matches!(kind, Ub(InvalidUninitBytes(_))) { + err_validation_failure!(self.path, Uninit { expected }).into() + } else { + err_validation_failure!(self.path, PointerAsInt { expected }).into() + } } + + // Propagate upwards (that will also check for unexpected errors). + _ => return InterpErrorInfo::from_parts(kind, backtrace), } - } + })?; // Don't forget that these are all non-pointer types, and thus do not preserve // provenance. @@ -1335,7 +1334,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, } } - Ok(()) + interp_ok(()) } } @@ -1351,7 +1350,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { trace!("validate_operand_internal: {:?}, {:?}", *val, val.layout.ty); // Run the visitor. - match self.run_for_validation(|ecx| { + self.run_for_validation(|ecx| { let reset_padding = reset_provenance_and_padding && { // Check if `val` is actually stored in memory. If not, padding is not even // represented and we need not reset it. @@ -1367,29 +1366,22 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { }; v.visit_value(val)?; v.reset_padding(val)?; - InterpResult::Ok(()) - }) { - Ok(()) => Ok(()), - // Pass through validation failures and "invalid program" issues. - Err(err) - if matches!( - err.kind(), - err_ub!(ValidationError { .. }) - | InterpError::InvalidProgram(_) - | InterpError::Unsupported(UnsupportedOpInfo::ExternTypeField) - ) => - { - Err(err) - } - // Complain about any other kind of error -- those are bad because we'd like to - // report them in a way that shows *where* in the value the issue lies. - Err(err) => { + interp_ok(()) + }) + .map_err(|err| { + if !matches!( + err.kind(), + err_ub!(ValidationError { .. }) + | InterpError::InvalidProgram(_) + | InterpError::Unsupported(UnsupportedOpInfo::ExternTypeField) + ) { bug!( "Unexpected error during validation: {}", format_interp_error(self.tcx.dcx(), err) ); } - } + err + }) } /// This function checks the data at `op` to be const-valid. @@ -1460,6 +1452,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /*reset_provenance_and_padding*/ false, )?; } - Ok(()) + interp_ok(()) } } diff --git a/compiler/rustc_const_eval/src/interpret/visitor.rs b/compiler/rustc_const_eval/src/interpret/visitor.rs index d004a3f08929f..647917dbb67ed 100644 --- a/compiler/rustc_const_eval/src/interpret/visitor.rs +++ b/compiler/rustc_const_eval/src/interpret/visitor.rs @@ -10,7 +10,7 @@ use rustc_middle::ty::{self, Ty}; use rustc_target::abi::{FieldIdx, FieldsShape, VariantIdx, Variants}; use tracing::trace; -use super::{InterpCx, MPlaceTy, Machine, Projectable, throw_inval}; +use super::{InterpCx, MPlaceTy, Machine, Projectable, interp_ok, throw_inval}; /// How to traverse a value and what to do when we are at the leaves. pub trait ValueVisitor<'tcx, M: Machine<'tcx>>: Sized { @@ -46,14 +46,14 @@ pub trait ValueVisitor<'tcx, M: Machine<'tcx>>: Sized { /// Visits the given value as a union. No automatic recursion can happen here. #[inline(always)] fn visit_union(&mut self, _v: &Self::V, _fields: NonZero) -> InterpResult<'tcx> { - Ok(()) + interp_ok(()) } /// Visits the given value as the pointer of a `Box`. There is nothing to recurse into. /// The type of `v` will be a raw pointer to `T`, but this is a field of `Box` and the /// pointee type is the actual `T`. `box_ty` provides the full type of the `Box` itself. #[inline(always)] fn visit_box(&mut self, _box_ty: Ty<'tcx>, _v: &Self::V) -> InterpResult<'tcx> { - Ok(()) + interp_ok(()) } /// Called each time we recurse down to a field of a "product-like" aggregate @@ -165,7 +165,7 @@ pub trait ValueVisitor<'tcx, M: Machine<'tcx>>: Sized { self.visit_field(v, 1, &alloc)?; // We visited all parts of this one. - return Ok(()); + return interp_ok(()); } // Non-normalized types should never show up here. @@ -222,6 +222,6 @@ pub trait ValueVisitor<'tcx, M: Machine<'tcx>>: Sized { Variants::Single { .. } => {} } - Ok(()) + interp_ok(()) } } diff --git a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs index 421648d9e7b2a..649179d5b1e08 100644 --- a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs +++ b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs @@ -1,5 +1,4 @@ use rustc_middle::bug; -use rustc_middle::mir::interpret::DiscardInterpError; use rustc_middle::ty::layout::{ HasTyCtxt, LayoutCx, LayoutError, LayoutOf, TyAndLayout, ValidityRequirement, }; @@ -76,7 +75,7 @@ fn check_validity_requirement_strict<'tcx>( /*recursive*/ false, /*reset_provenance_and_padding*/ false, ) - .discard_interp_err() + .discard_err() .is_some()) } diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index a32b19b067a55..70e61df1ab4d1 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -61,6 +61,8 @@ #![feature(trait_upcasting)] #![feature(trusted_len)] #![feature(try_blocks)] +#![feature(try_trait_v2)] +#![feature(try_trait_v2_yeet)] #![feature(type_alias_impl_trait)] #![feature(yeet_expr)] #![warn(unreachable_pub)] diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs index b34f5b48b782a..4262460d928b6 100644 --- a/compiler/rustc_middle/src/mir/consts.rs +++ b/compiler/rustc_middle/src/mir/consts.rs @@ -148,7 +148,7 @@ impl<'tcx> ConstValue<'tcx> { /* read_provenance */ true, ) .ok()?; - let ptr = ptr.to_pointer(&tcx).ok()?; + let ptr = ptr.to_pointer(&tcx).discard_err()?; let len = a .read_scalar( &tcx, @@ -156,7 +156,7 @@ impl<'tcx> ConstValue<'tcx> { /* read_provenance */ false, ) .ok()?; - let len = len.to_target_usize(&tcx).ok()?; + let len = len.to_target_usize(&tcx).discard_err()?; if len == 0 { return Some(&[]); } diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index b8ecfa3c3f946..04d035e27ba3e 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -20,7 +20,7 @@ use rustc_target::abi::{Align, HasDataLayout, Size}; use super::{ AllocId, BadBytesAccess, CtfeProvenance, InterpError, InterpResult, Pointer, PointerArithmetic, Provenance, ResourceExhaustionInfo, Scalar, ScalarSizeMismatch, UndefinedBehaviorInfo, - UnsupportedOpInfo, read_target_uint, write_target_uint, + UnsupportedOpInfo, interp_ok, read_target_uint, write_target_uint, }; use crate::ty; @@ -318,8 +318,9 @@ impl Allocation { pub fn try_uninit<'tcx>(size: Size, align: Align) -> InterpResult<'tcx, Self> { Self::uninit_inner(size, align, || { ty::tls::with(|tcx| tcx.dcx().delayed_bug("exhausted memory during interpretation")); - InterpError::ResourceExhaustion(ResourceExhaustionInfo::MemoryExhausted).into() + InterpError::ResourceExhaustion(ResourceExhaustionInfo::MemoryExhausted) }) + .into() } /// Try to create an Allocation of `size` bytes, panics if there is not enough memory @@ -355,12 +356,12 @@ impl Allocation { impl Allocation { /// Adjust allocation from the ones in `tcx` to a custom Machine instance /// with a different `Provenance` and `Byte` type. - pub fn adjust_from_tcx( + pub fn adjust_from_tcx<'tcx, Prov: Provenance, Bytes: AllocBytes>( &self, cx: &impl HasDataLayout, - mut alloc_bytes: impl FnMut(&[u8], Align) -> Result, - mut adjust_ptr: impl FnMut(Pointer) -> Result, Err>, - ) -> Result, Err> { + mut alloc_bytes: impl FnMut(&[u8], Align) -> InterpResult<'tcx, Bytes>, + mut adjust_ptr: impl FnMut(Pointer) -> InterpResult<'tcx, Pointer>, + ) -> InterpResult<'tcx, Allocation> { // Copy the data. let mut bytes = alloc_bytes(&*self.bytes, self.align)?; // Adjust provenance of pointers stored in this allocation. @@ -377,7 +378,7 @@ impl Allocation { new_provenance.push((offset, ptr_prov)); } // Create allocation. - Ok(Allocation { + interp_ok(Allocation { bytes, provenance: ProvenanceMap::from_presorted_ptrs(new_provenance), init_mask: self.init_mask.clone(), diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 06fdb9e1ecdee..431043b043192 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -1,7 +1,7 @@ use std::any::Any; use std::backtrace::Backtrace; use std::borrow::Cow; -use std::{fmt, mem}; +use std::{convert, fmt, mem, ops}; use either::Either; use rustc_ast_ir::Mutability; @@ -106,55 +106,15 @@ rustc_data_structures::static_assert_size!(InterpErrorInfo<'_>, 8); /// macros for this. /// /// Interpreter errors must *not* be silently discarded (that will lead to a panic). Instead, -/// explicitly call `discard_interp_err` if this is really the right thing to do. Note that if +/// explicitly call `discard_err` if this is really the right thing to do. Note that if /// this happens during const-eval or in Miri, it could lead to a UB error being lost! #[derive(Debug)] pub struct InterpErrorInfo<'tcx>(Box>); -/// Calling `.ok()` on an `InterpResult` leads to a panic because of the guard. -/// To still let people opt-in to discarding interpreter errors, we have this extension trait. -pub trait DiscardInterpError { - type Output; - - fn discard_interp_err(self) -> Option; -} - -impl<'tcx, T> DiscardInterpError for InterpResult<'tcx, T> { - type Output = T; - - fn discard_interp_err(self) -> Option { - match self { - Ok(v) => Some(v), - Err(e) => { - // Disarm the guard. - mem::forget(e.0.guard); - None - } - } - } -} - -#[derive(Debug)] -struct Guard; - -impl Drop for Guard { - fn drop(&mut self) { - // We silence the guard if we are already panicking, to avoid double-panics. - if !std::thread::panicking() { - panic!( - "an interpreter error got improperly discarded; use `discard_interp_err()` instead of `ok()` if this is intentional" - ); - } - } -} - #[derive(Debug)] struct InterpErrorInfoInner<'tcx> { kind: InterpError<'tcx>, backtrace: InterpErrorBacktrace, - /// This makes us panic on drop. This is used to catch - /// accidentally discarding an interpreter error. - guard: Guard, } #[derive(Debug)] @@ -195,23 +155,16 @@ impl InterpErrorBacktrace { impl<'tcx> InterpErrorInfo<'tcx> { pub fn into_parts(self) -> (InterpError<'tcx>, InterpErrorBacktrace) { - let InterpErrorInfo(box InterpErrorInfoInner { kind, backtrace, guard }) = self; - mem::forget(guard); // The error got explicitly discarded right here. + let InterpErrorInfo(box InterpErrorInfoInner { kind, backtrace }) = self; (kind, backtrace) } pub fn into_kind(self) -> InterpError<'tcx> { - let InterpErrorInfo(box InterpErrorInfoInner { kind, guard, .. }) = self; - mem::forget(guard); // The error got explicitly discarded right here. - kind - } - - pub fn discard_interp_err(self) { - mem::forget(self.0.guard); // The error got explicitly discarded right here. + self.0.kind } pub fn from_parts(kind: InterpError<'tcx>, backtrace: InterpErrorBacktrace) -> Self { - Self(Box::new(InterpErrorInfoInner { kind, backtrace, guard: Guard })) + Self(Box::new(InterpErrorInfoInner { kind, backtrace })) } #[inline] @@ -245,7 +198,6 @@ impl<'tcx> From> for InterpErrorInfo<'tcx> { InterpErrorInfo(Box::new(InterpErrorInfoInner { kind, backtrace: InterpErrorBacktrace::new(), - guard: Guard, })) } } @@ -654,8 +606,6 @@ pub enum InterpError<'tcx> { MachineStop(Box), } -pub type InterpResult<'tcx, T = ()> = Result>; - impl InterpError<'_> { /// Some errors do string formatting even if the error is never printed. /// To avoid performance issues, there are places where we want to be sure to never raise these formatting errors, @@ -783,3 +733,182 @@ macro_rules! throw_exhaust { macro_rules! throw_machine_stop { ($($tt:tt)*) => { do yeet $crate::err_machine_stop!($($tt)*) }; } + +/// Guard type that panics on drop. +#[derive(Debug)] +struct Guard; + +impl Drop for Guard { + fn drop(&mut self) { + // We silence the guard if we are already panicking, to avoid double-panics. + if !std::thread::panicking() { + panic!( + "an interpreter error got improperly discarded; use `discard_err()` if this is intentional" + ); + } + } +} + +/// The result type used by the interpreter. This is a newtype around `Result` +/// to block access to operations like `ok()` that discard UB errors. +/// +/// We also make things panic if this type is ever implicitly dropped. +#[derive(Debug)] +pub struct InterpResult_<'tcx, T> { + res: Result>, + guard: Guard, +} + +// Type alias to be able to set a default type argument. +pub type InterpResult<'tcx, T = ()> = InterpResult_<'tcx, T>; + +impl<'tcx, T> ops::Try for InterpResult_<'tcx, T> { + type Output = T; + type Residual = InterpResult_<'tcx, convert::Infallible>; + + #[inline] + fn from_output(output: Self::Output) -> Self { + InterpResult_::new(Ok(output)) + } + + #[inline] + fn branch(self) -> ops::ControlFlow { + match self.disarm() { + Ok(v) => ops::ControlFlow::Continue(v), + Err(e) => ops::ControlFlow::Break(InterpResult_::new(Err(e))), + } + } +} + +impl<'tcx, T> ops::FromResidual for InterpResult_<'tcx, T> { + #[inline] + #[track_caller] + fn from_residual(residual: InterpResult_<'tcx, convert::Infallible>) -> Self { + match residual.disarm() { + Err(e) => Self::new(Err(e)), + } + } +} + +// Allow `yeet`ing `InterpError` in functions returning `InterpResult_`. +impl<'tcx, T> ops::FromResidual>> for InterpResult_<'tcx, T> { + #[inline] + fn from_residual(ops::Yeet(e): ops::Yeet>) -> Self { + Self::new(Err(e.into())) + } +} + +// Allow `?` on `Result<_, InterpError>` in functions returning `InterpResult_`. +// This is useful e.g. for `option.ok_or_else(|| err_ub!(...))`. +impl<'tcx, T, E: Into>> ops::FromResidual> + for InterpResult_<'tcx, T> +{ + #[inline] + fn from_residual(residual: Result) -> Self { + match residual { + Err(e) => Self::new(Err(e.into())), + } + } +} + +impl<'tcx, T, E: Into>> From> for InterpResult<'tcx, T> { + #[inline] + fn from(value: Result) -> Self { + Self::new(value.map_err(|e| e.into())) + } +} + +impl<'tcx, T, V: FromIterator> FromIterator> for InterpResult<'tcx, V> { + fn from_iter>>(iter: I) -> Self { + Self::new(iter.into_iter().map(|x| x.disarm()).collect()) + } +} + +impl<'tcx, T> InterpResult_<'tcx, T> { + #[inline(always)] + fn new(res: Result>) -> Self { + Self { res, guard: Guard } + } + + #[inline(always)] + fn disarm(self) -> Result> { + mem::forget(self.guard); + self.res + } + + /// Discard the error information in this result. Only use this if ignoring Undefined Behavior is okay! + #[inline] + pub fn discard_err(self) -> Option { + self.disarm().ok() + } + + /// Look at the `Result` wrapped inside of this. + /// Must only be used to report the error! + #[inline] + pub fn report_err(self) -> Result> { + self.disarm() + } + + #[inline] + pub fn map(self, f: impl FnOnce(T) -> U) -> InterpResult<'tcx, U> { + InterpResult_::new(self.disarm().map(f)) + } + + #[inline] + pub fn map_err( + self, + f: impl FnOnce(InterpErrorInfo<'tcx>) -> InterpErrorInfo<'tcx>, + ) -> InterpResult<'tcx, T> { + InterpResult_::new(self.disarm().map_err(f)) + } + + #[inline] + pub fn inspect_err(self, f: impl FnOnce(&InterpErrorInfo<'tcx>)) -> InterpResult<'tcx, T> { + InterpResult_::new(self.disarm().inspect_err(f)) + } + + #[inline] + #[track_caller] + pub fn unwrap(self) -> T { + self.disarm().unwrap() + } + + #[inline] + #[track_caller] + pub fn unwrap_or_else(self, f: impl FnOnce(InterpErrorInfo<'tcx>) -> T) -> T { + self.disarm().unwrap_or_else(f) + } + + #[inline] + #[track_caller] + pub fn expect(self, msg: &str) -> T { + self.disarm().expect(msg) + } + + #[inline] + pub fn and_then(self, f: impl FnOnce(T) -> InterpResult<'tcx, U>) -> InterpResult<'tcx, U> { + InterpResult_::new(self.disarm().and_then(|t| f(t).disarm())) + } + + /// Returns success if both `self` and `other` succeed, while ensuring we don't + /// accidentally drop an error. + /// + /// If both are an error, `self` will be reported. + #[inline] + pub fn and(self, other: InterpResult<'tcx, U>) -> InterpResult<'tcx, (T, U)> { + match self.disarm() { + Ok(t) => interp_ok((t, other?)), + Err(e) => { + // Discard the other error. + drop(other.disarm()); + // Return `self`. + InterpResult_::new(Err(e)) + } + } + } +} + +#[inline(always)] +pub fn interp_ok<'tcx, T>(x: T) -> InterpResult<'tcx, T> { + InterpResult_::new(Ok(x)) +} diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index 8ce6f9d230052..115bcdbc58987 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -34,12 +34,12 @@ pub use self::allocation::{ InitChunkIter, alloc_range, }; pub use self::error::{ - BadBytesAccess, CheckAlignMsg, CheckInAllocMsg, DiscardInterpError, ErrorHandled, - EvalStaticInitializerRawResult, EvalToAllocationRawResult, EvalToConstValueResult, - EvalToValTreeResult, ExpectedKind, InterpError, InterpErrorInfo, InterpResult, InvalidMetaKind, - InvalidProgramInfo, MachineStopType, Misalignment, PointerKind, ReportedErrorInfo, - ResourceExhaustionInfo, ScalarSizeMismatch, UndefinedBehaviorInfo, UnsupportedOpInfo, - ValidationErrorInfo, ValidationErrorKind, + BadBytesAccess, CheckAlignMsg, CheckInAllocMsg, ErrorHandled, EvalStaticInitializerRawResult, + EvalToAllocationRawResult, EvalToConstValueResult, EvalToValTreeResult, ExpectedKind, + InterpError, InterpErrorInfo, InterpResult, InvalidMetaKind, InvalidProgramInfo, + MachineStopType, Misalignment, PointerKind, ReportedErrorInfo, ResourceExhaustionInfo, + ScalarSizeMismatch, UndefinedBehaviorInfo, UnsupportedOpInfo, ValidationErrorInfo, + ValidationErrorKind, interp_ok, }; pub use self::pointer::{CtfeProvenance, Pointer, PointerArithmetic, Provenance}; pub use self::value::Scalar; diff --git a/compiler/rustc_middle/src/mir/interpret/value.rs b/compiler/rustc_middle/src/mir/interpret/value.rs index c6c2ab414ed41..061a55bfda8d6 100644 --- a/compiler/rustc_middle/src/mir/interpret/value.rs +++ b/compiler/rustc_middle/src/mir/interpret/value.rs @@ -8,7 +8,7 @@ use rustc_target::abi::{HasDataLayout, Size}; use super::{ AllocId, CtfeProvenance, InterpResult, Pointer, PointerArithmetic, Provenance, - ScalarSizeMismatch, + ScalarSizeMismatch, interp_ok, }; use crate::ty::ScalarInt; @@ -273,10 +273,10 @@ impl<'tcx, Prov: Provenance> Scalar { .to_bits_or_ptr_internal(cx.pointer_size()) .map_err(|s| err_ub!(ScalarSizeMismatch(s)))? { - Right(ptr) => Ok(ptr.into()), + Right(ptr) => interp_ok(ptr.into()), Left(bits) => { let addr = u64::try_from(bits).unwrap(); - Ok(Pointer::from_addr_invalid(addr)) + interp_ok(Pointer::from_addr_invalid(addr)) } } } @@ -311,12 +311,12 @@ impl<'tcx, Prov: Provenance> Scalar { if matches!(self, Scalar::Ptr(..)) { *self = self.to_scalar_int()?.into(); } - Ok(()) + interp_ok(()) } #[inline(always)] pub fn to_scalar_int(self) -> InterpResult<'tcx, ScalarInt> { - self.try_to_scalar_int().map_err(|_| err_unsup!(ReadPointerAsInt(None)).into()) + self.try_to_scalar_int().map_err(|_| err_unsup!(ReadPointerAsInt(None))).into() } #[inline(always)] @@ -330,20 +330,22 @@ impl<'tcx, Prov: Provenance> Scalar { #[inline] pub fn to_bits(self, target_size: Size) -> InterpResult<'tcx, u128> { assert_ne!(target_size.bytes(), 0, "you should never look at the bits of a ZST"); - self.to_scalar_int()?.try_to_bits(target_size).map_err(|size| { - err_ub!(ScalarSizeMismatch(ScalarSizeMismatch { - target_size: target_size.bytes(), - data_size: size.bytes(), - })) + self.to_scalar_int()? + .try_to_bits(target_size) + .map_err(|size| { + err_ub!(ScalarSizeMismatch(ScalarSizeMismatch { + target_size: target_size.bytes(), + data_size: size.bytes(), + })) + }) .into() - }) } pub fn to_bool(self) -> InterpResult<'tcx, bool> { let val = self.to_u8()?; match val { - 0 => Ok(false), - 1 => Ok(true), + 0 => interp_ok(false), + 1 => interp_ok(true), _ => throw_ub!(InvalidBool(val)), } } @@ -351,7 +353,7 @@ impl<'tcx, Prov: Provenance> Scalar { pub fn to_char(self) -> InterpResult<'tcx, char> { let val = self.to_u32()?; match std::char::from_u32(val) { - Some(c) => Ok(c), + Some(c) => interp_ok(c), None => throw_ub!(InvalidChar(val)), } } @@ -392,7 +394,7 @@ impl<'tcx, Prov: Provenance> Scalar { /// Fails if the scalar is a pointer. pub fn to_target_usize(self, cx: &impl HasDataLayout) -> InterpResult<'tcx, u64> { let b = self.to_uint(cx.data_layout().pointer_size)?; - Ok(u64::try_from(b).unwrap()) + interp_ok(u64::try_from(b).unwrap()) } /// Converts the scalar to produce a signed integer of the given size. @@ -400,7 +402,7 @@ impl<'tcx, Prov: Provenance> Scalar { #[inline] pub fn to_int(self, size: Size) -> InterpResult<'tcx, i128> { let b = self.to_bits(size)?; - Ok(size.sign_extend(b)) + interp_ok(size.sign_extend(b)) } /// Converts the scalar to produce an `i8`. Fails if the scalar is a pointer. @@ -432,13 +434,13 @@ impl<'tcx, Prov: Provenance> Scalar { /// Fails if the scalar is a pointer. pub fn to_target_isize(self, cx: &impl HasDataLayout) -> InterpResult<'tcx, i64> { let b = self.to_int(cx.data_layout().pointer_size)?; - Ok(i64::try_from(b).unwrap()) + interp_ok(i64::try_from(b).unwrap()) } #[inline] pub fn to_float(self) -> InterpResult<'tcx, F> { // Going through `to_bits` to check size and truncation. - Ok(F::from_bits(self.to_bits(Size::from_bits(F::BITS))?)) + interp_ok(F::from_bits(self.to_bits(Size::from_bits(F::BITS))?)) } #[inline] diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index 73d0acf95f4f5..389d20f315ff0 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -519,7 +519,7 @@ impl<'tcx> Const<'tcx> { } pub fn try_to_bool(self) -> Option { - self.try_to_scalar()?.to_bool().ok() + self.try_to_valtree()?.try_to_scalar_int()?.try_to_bool().ok() } #[inline] diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index 905143b2efbc2..002216f50f2d1 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -4,7 +4,7 @@ use rustc_const_eval::const_eval::{DummyMachine, throw_machine_stop_str}; use rustc_const_eval::interpret::{ - DiscardInterpError, ImmTy, Immediate, InterpCx, OpTy, PlaceTy, Projectable, + ImmTy, Immediate, InterpCx, OpTy, PlaceTy, Projectable, interp_ok, }; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::DefKind; @@ -238,6 +238,7 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> { FlatSet::Elem(op) => self .ecx .int_to_int_or_float(&op, layout) + .discard_err() .map_or(FlatSet::Top, |result| self.wrap_immediate(*result)), FlatSet::Bottom => FlatSet::Bottom, FlatSet::Top => FlatSet::Top, @@ -251,6 +252,7 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> { FlatSet::Elem(op) => self .ecx .float_to_float_or_int(&op, layout) + .discard_err() .map_or(FlatSet::Top, |result| self.wrap_immediate(*result)), FlatSet::Bottom => FlatSet::Bottom, FlatSet::Top => FlatSet::Top, @@ -273,6 +275,7 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> { FlatSet::Elem(value) => self .ecx .unary_op(*op, &value) + .discard_err() .map_or(FlatSet::Top, |val| self.wrap_immediate(*val)), FlatSet::Bottom => FlatSet::Bottom, FlatSet::Top => FlatSet::Top, @@ -366,10 +369,8 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { } } Operand::Constant(box constant) => { - if let Some(constant) = self - .ecx - .eval_mir_constant(&constant.const_, constant.span, None) - .discard_interp_err() + if let Some(constant) = + self.ecx.eval_mir_constant(&constant.const_, constant.span, None).discard_err() { self.assign_constant(state, place, constant, &[]); } @@ -391,7 +392,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { for &(mut proj_elem) in projection { if let PlaceElem::Index(index) = proj_elem { if let FlatSet::Elem(index) = state.get(index.into(), &self.map) - && let Some(offset) = index.to_target_usize(&self.tcx).discard_interp_err() + && let Some(offset) = index.to_target_usize(&self.tcx).discard_err() && let Some(min_length) = offset.checked_add(1) { proj_elem = PlaceElem::ConstantIndex { offset, min_length, from_end: false }; @@ -399,40 +400,35 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { return; } } - operand = - if let Some(operand) = self.ecx.project(&operand, proj_elem).discard_interp_err() { - operand - } else { - return; - } + operand = if let Some(operand) = self.ecx.project(&operand, proj_elem).discard_err() { + operand + } else { + return; + } } self.map.for_each_projection_value( place, operand, &mut |elem, op| match elem { - TrackElem::Field(idx) => { - self.ecx.project_field(op, idx.as_usize()).discard_interp_err() - } - TrackElem::Variant(idx) => self.ecx.project_downcast(op, idx).discard_interp_err(), + TrackElem::Field(idx) => self.ecx.project_field(op, idx.as_usize()).discard_err(), + TrackElem::Variant(idx) => self.ecx.project_downcast(op, idx).discard_err(), TrackElem::Discriminant => { - let variant = self.ecx.read_discriminant(op).discard_interp_err()?; - let discr_value = self - .ecx - .discriminant_for_variant(op.layout.ty, variant) - .discard_interp_err()?; + let variant = self.ecx.read_discriminant(op).discard_err()?; + let discr_value = + self.ecx.discriminant_for_variant(op.layout.ty, variant).discard_err()?; Some(discr_value.into()) } TrackElem::DerefLen => { - let op: OpTy<'_> = self.ecx.deref_pointer(op).discard_interp_err()?.into(); - let len_usize = op.len(&self.ecx).discard_interp_err()?; + let op: OpTy<'_> = self.ecx.deref_pointer(op).discard_err()?.into(); + let len_usize = op.len(&self.ecx).discard_err()?; let layout = self.tcx.layout_of(self.param_env.and(self.tcx.types.usize)).unwrap(); Some(ImmTy::from_uint(len_usize, layout).into()) } }, &mut |place, op| { - if let Some(imm) = self.ecx.read_immediate_raw(op).discard_interp_err() + if let Some(imm) = self.ecx.read_immediate_raw(op).discard_err() && let Some(imm) = imm.right() { let elem = self.wrap_immediate(*imm); @@ -456,7 +452,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { (FlatSet::Bottom, _) | (_, FlatSet::Bottom) => (FlatSet::Bottom, FlatSet::Bottom), // Both sides are known, do the actual computation. (FlatSet::Elem(left), FlatSet::Elem(right)) => { - match self.ecx.binary_op(op, &left, &right).discard_interp_err() { + match self.ecx.binary_op(op, &left, &right).discard_err() { // Ideally this would return an Immediate, since it's sometimes // a pair and sometimes not. But as a hack we always return a pair // and just make the 2nd component `Bottom` when it does not exist. @@ -479,7 +475,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { } let arg_scalar = const_arg.to_scalar(); - let Some(arg_value) = arg_scalar.to_bits(layout.size).discard_interp_err() else { + let Some(arg_value) = arg_scalar.to_bits(layout.size).discard_err() else { return (FlatSet::Top, FlatSet::Top); }; @@ -527,10 +523,8 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { return None; } let enum_ty_layout = self.tcx.layout_of(self.param_env.and(enum_ty)).ok()?; - let discr_value = self - .ecx - .discriminant_for_variant(enum_ty_layout.ty, variant_index) - .discard_interp_err()?; + let discr_value = + self.ecx.discriminant_for_variant(enum_ty_layout.ty, variant_index).discard_err()?; Some(discr_value.to_scalar()) } @@ -584,7 +578,7 @@ impl<'a, 'tcx> Collector<'a, 'tcx> { map: &Map<'tcx>, ) -> Option> { let ty = place.ty(self.local_decls, self.patch.tcx).ty; - let layout = ecx.layout_of(ty).discard_interp_err()?; + let layout = ecx.layout_of(ty).ok()?; if layout.is_zst() { return Some(Const::zero_sized(ty)); @@ -606,7 +600,7 @@ impl<'a, 'tcx> Collector<'a, 'tcx> { .intern_with_temp_alloc(layout, |ecx, dest| { try_write_constant(ecx, dest, place, ty, state, map) }) - .discard_interp_err()?; + .discard_err()?; return Some(Const::Val(ConstValue::Indirect { alloc_id, offset: Size::ZERO }, ty)); } @@ -643,7 +637,7 @@ fn try_write_constant<'tcx>( // Fast path for ZSTs. if layout.is_zst() { - return Ok(()); + return interp_ok(()); } // Fast path for scalars. @@ -728,7 +722,7 @@ fn try_write_constant<'tcx>( ty::Error(_) | ty::Infer(..) | ty::CoroutineWitness(..) => bug!(), } - Ok(()) + interp_ok(()) } impl<'mir, 'tcx> @@ -841,7 +835,7 @@ impl<'tcx> MutVisitor<'tcx> for Patch<'tcx> { if let PlaceElem::Index(local) = elem { let offset = self.before_effect.get(&(location, local.into()))?; let offset = offset.try_to_scalar()?; - let offset = offset.to_target_usize(&self.tcx).discard_interp_err()?; + let offset = offset.to_target_usize(&self.tcx).discard_err()?; let min_length = offset.checked_add(1)?; Some(PlaceElem::ConstantIndex { offset, min_length, from_end: false }) } else { diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 4dbcc6af9bde4..50c9702cb9b02 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -87,8 +87,8 @@ use std::borrow::Cow; use either::Either; use rustc_const_eval::const_eval::DummyMachine; use rustc_const_eval::interpret::{ - DiscardInterpError, ImmTy, Immediate, InterpCx, MemPlaceMeta, MemoryKind, OpTy, Projectable, - Scalar, intern_const_alloc_for_constprop, + ImmTy, Immediate, InterpCx, MemPlaceMeta, MemoryKind, OpTy, Projectable, Scalar, + intern_const_alloc_for_constprop, }; use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::graph::dominators::Dominators; @@ -393,7 +393,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { Repeat(..) => return None, Constant { ref value, disambiguator: _ } => { - self.ecx.eval_mir_constant(value, DUMMY_SP, None).discard_interp_err()? + self.ecx.eval_mir_constant(value, DUMMY_SP, None).discard_err()? } Aggregate(kind, variant, ref fields) => { let fields = fields @@ -414,39 +414,37 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { AggregateTy::RawPtr { output_pointer_ty, .. } => output_pointer_ty, }; let variant = if ty.is_enum() { Some(variant) } else { None }; - let ty = self.ecx.layout_of(ty).discard_interp_err()?; + let ty = self.ecx.layout_of(ty).ok()?; if ty.is_zst() { ImmTy::uninit(ty).into() } else if matches!(kind, AggregateTy::RawPtr { .. }) { // Pointers don't have fields, so don't `project_field` them. - let data = self.ecx.read_pointer(fields[0]).discard_interp_err()?; + let data = self.ecx.read_pointer(fields[0]).discard_err()?; let meta = if fields[1].layout.is_zst() { MemPlaceMeta::None } else { - MemPlaceMeta::Meta(self.ecx.read_scalar(fields[1]).discard_interp_err()?) + MemPlaceMeta::Meta(self.ecx.read_scalar(fields[1]).discard_err()?) }; let ptr_imm = Immediate::new_pointer_with_meta(data, meta, &self.ecx); ImmTy::from_immediate(ptr_imm, ty).into() } else if matches!(ty.abi, Abi::Scalar(..) | Abi::ScalarPair(..)) { - let dest = self.ecx.allocate(ty, MemoryKind::Stack).discard_interp_err()?; + let dest = self.ecx.allocate(ty, MemoryKind::Stack).discard_err()?; let variant_dest = if let Some(variant) = variant { - self.ecx.project_downcast(&dest, variant).discard_interp_err()? + self.ecx.project_downcast(&dest, variant).discard_err()? } else { dest.clone() }; for (field_index, op) in fields.into_iter().enumerate() { - let field_dest = self - .ecx - .project_field(&variant_dest, field_index) - .discard_interp_err()?; - self.ecx.copy_op(op, &field_dest).discard_interp_err()?; + let field_dest = + self.ecx.project_field(&variant_dest, field_index).discard_err()?; + self.ecx.copy_op(op, &field_dest).discard_err()?; } self.ecx .write_discriminant(variant.unwrap_or(FIRST_VARIANT), &dest) - .discard_interp_err()?; + .discard_err()?; self.ecx .alloc_mark_immutable(dest.ptr().provenance.unwrap().alloc_id()) - .discard_interp_err()?; + .discard_err()?; dest.into() } else { return None; @@ -472,7 +470,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { // This should have been replaced by a `ConstantIndex` earlier. ProjectionElem::Index(_) => return None, }; - self.ecx.project(value, elem).discard_interp_err()? + self.ecx.project(value, elem).discard_err()? } Address { place, kind, provenance: _ } => { if !place.is_indirect_first_projection() { @@ -480,14 +478,14 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } let local = self.locals[place.local]?; let pointer = self.evaluated[local].as_ref()?; - let mut mplace = self.ecx.deref_pointer(pointer).discard_interp_err()?; + let mut mplace = self.ecx.deref_pointer(pointer).discard_err()?; for proj in place.projection.iter().skip(1) { // We have no call stack to associate a local with a value, so we cannot // interpret indexing. if matches!(proj, ProjectionElem::Index(_)) { return None; } - mplace = self.ecx.project(&mplace, proj).discard_interp_err()?; + mplace = self.ecx.project(&mplace, proj).discard_err()?; } let pointer = mplace.to_ref(&self.ecx); let ty = match kind { @@ -499,28 +497,26 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { ), AddressKind::Address(mutbl) => Ty::new_ptr(self.tcx, mplace.layout.ty, mutbl), }; - let layout = self.ecx.layout_of(ty).discard_interp_err()?; + let layout = self.ecx.layout_of(ty).ok()?; ImmTy::from_immediate(pointer, layout).into() } Discriminant(base) => { let base = self.evaluated[base].as_ref()?; - let variant = self.ecx.read_discriminant(base).discard_interp_err()?; - let discr_value = self - .ecx - .discriminant_for_variant(base.layout.ty, variant) - .discard_interp_err()?; + let variant = self.ecx.read_discriminant(base).discard_err()?; + let discr_value = + self.ecx.discriminant_for_variant(base.layout.ty, variant).discard_err()?; discr_value.into() } Len(slice) => { let slice = self.evaluated[slice].as_ref()?; let usize_layout = self.ecx.layout_of(self.tcx.types.usize).unwrap(); - let len = slice.len(&self.ecx).discard_interp_err()?; + let len = slice.len(&self.ecx).discard_err()?; let imm = ImmTy::from_uint(len, usize_layout); imm.into() } NullaryOp(null_op, ty) => { - let layout = self.ecx.layout_of(ty).discard_interp_err()?; + let layout = self.ecx.layout_of(ty).ok()?; if let NullOp::SizeOf | NullOp::AlignOf = null_op && layout.is_unsized() { @@ -542,36 +538,36 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } UnaryOp(un_op, operand) => { let operand = self.evaluated[operand].as_ref()?; - let operand = self.ecx.read_immediate(operand).discard_interp_err()?; - let val = self.ecx.unary_op(un_op, &operand).discard_interp_err()?; + let operand = self.ecx.read_immediate(operand).discard_err()?; + let val = self.ecx.unary_op(un_op, &operand).discard_err()?; val.into() } BinaryOp(bin_op, lhs, rhs) => { let lhs = self.evaluated[lhs].as_ref()?; - let lhs = self.ecx.read_immediate(lhs).discard_interp_err()?; + let lhs = self.ecx.read_immediate(lhs).discard_err()?; let rhs = self.evaluated[rhs].as_ref()?; - let rhs = self.ecx.read_immediate(rhs).discard_interp_err()?; - let val = self.ecx.binary_op(bin_op, &lhs, &rhs).discard_interp_err()?; + let rhs = self.ecx.read_immediate(rhs).discard_err()?; + let val = self.ecx.binary_op(bin_op, &lhs, &rhs).discard_err()?; val.into() } Cast { kind, value, from: _, to } => match kind { CastKind::IntToInt | CastKind::IntToFloat => { let value = self.evaluated[value].as_ref()?; - let value = self.ecx.read_immediate(value).discard_interp_err()?; - let to = self.ecx.layout_of(to).discard_interp_err()?; - let res = self.ecx.int_to_int_or_float(&value, to).discard_interp_err()?; + let value = self.ecx.read_immediate(value).discard_err()?; + let to = self.ecx.layout_of(to).ok()?; + let res = self.ecx.int_to_int_or_float(&value, to).discard_err()?; res.into() } CastKind::FloatToFloat | CastKind::FloatToInt => { let value = self.evaluated[value].as_ref()?; - let value = self.ecx.read_immediate(value).discard_interp_err()?; - let to = self.ecx.layout_of(to).discard_interp_err()?; - let res = self.ecx.float_to_float_or_int(&value, to).discard_interp_err()?; + let value = self.ecx.read_immediate(value).discard_err()?; + let to = self.ecx.layout_of(to).ok()?; + let res = self.ecx.float_to_float_or_int(&value, to).discard_err()?; res.into() } CastKind::Transmute => { let value = self.evaluated[value].as_ref()?; - let to = self.ecx.layout_of(to).discard_interp_err()?; + let to = self.ecx.layout_of(to).ok()?; // `offset` for immediates only supports scalar/scalar-pair ABIs, // so bail out if the target is not one. if value.as_mplace_or_imm().is_right() { @@ -581,29 +577,29 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { _ => return None, } } - value.offset(Size::ZERO, to, &self.ecx).discard_interp_err()? + value.offset(Size::ZERO, to, &self.ecx).discard_err()? } CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize, _) => { let src = self.evaluated[value].as_ref()?; - let to = self.ecx.layout_of(to).discard_interp_err()?; - let dest = self.ecx.allocate(to, MemoryKind::Stack).discard_interp_err()?; - self.ecx.unsize_into(src, to, &dest.clone().into()).discard_interp_err()?; + let to = self.ecx.layout_of(to).ok()?; + let dest = self.ecx.allocate(to, MemoryKind::Stack).discard_err()?; + self.ecx.unsize_into(src, to, &dest.clone().into()).discard_err()?; self.ecx .alloc_mark_immutable(dest.ptr().provenance.unwrap().alloc_id()) - .discard_interp_err()?; + .discard_err()?; dest.into() } CastKind::FnPtrToPtr | CastKind::PtrToPtr => { let src = self.evaluated[value].as_ref()?; - let src = self.ecx.read_immediate(src).discard_interp_err()?; - let to = self.ecx.layout_of(to).discard_interp_err()?; - let ret = self.ecx.ptr_to_ptr(&src, to).discard_interp_err()?; + let src = self.ecx.read_immediate(src).discard_err()?; + let to = self.ecx.layout_of(to).ok()?; + let ret = self.ecx.ptr_to_ptr(&src, to).discard_err()?; ret.into() } CastKind::PointerCoercion(ty::adjustment::PointerCoercion::UnsafeFnPointer, _) => { let src = self.evaluated[value].as_ref()?; - let src = self.ecx.read_immediate(src).discard_interp_err()?; - let to = self.ecx.layout_of(to).discard_interp_err()?; + let src = self.ecx.read_immediate(src).discard_err()?; + let to = self.ecx.layout_of(to).ok()?; ImmTy::from_immediate(*src, to).into() } _ => return None, @@ -715,7 +711,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { && let Some(idx) = self.locals[idx_local] { if let Some(offset) = self.evaluated[idx].as_ref() - && let Some(offset) = self.ecx.read_target_usize(offset).discard_interp_err() + && let Some(offset) = self.ecx.read_target_usize(offset).discard_err() && let Some(min_length) = offset.checked_add(1) { projection.to_mut()[i] = @@ -875,7 +871,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { && let DefKind::Enum = self.tcx.def_kind(enum_did) { let enum_ty = self.tcx.type_of(enum_did).instantiate(self.tcx, enum_args); - let discr = self.ecx.discriminant_for_variant(enum_ty, variant).discard_interp_err()?; + let discr = self.ecx.discriminant_for_variant(enum_ty, variant).discard_err()?; return Some(self.insert_scalar(discr.to_scalar(), discr.layout.ty)); } @@ -1225,13 +1221,13 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { return None; } - let layout = self.ecx.layout_of(lhs_ty).discard_interp_err()?; + let layout = self.ecx.layout_of(lhs_ty).ok()?; let as_bits = |value| { let constant = self.evaluated[value].as_ref()?; if layout.abi.is_scalar() { - let scalar = self.ecx.read_scalar(constant).discard_interp_err()?; - scalar.to_bits(constant.layout.size).discard_interp_err() + let scalar = self.ecx.read_scalar(constant).discard_err()?; + scalar.to_bits(constant.layout.size).discard_err() } else { // `constant` is a wide pointer. Do not evaluate to bits. None @@ -1491,7 +1487,7 @@ fn op_to_prop_const<'tcx>( // If this constant has scalar ABI, return it as a `ConstValue::Scalar`. if let Abi::Scalar(abi::Scalar::Initialized { .. }) = op.layout.abi - && let Some(scalar) = ecx.read_scalar(op).discard_interp_err() + && let Some(scalar) = ecx.read_scalar(op).discard_err() { if !scalar.try_to_scalar_int().is_ok() { // Check that we do not leak a pointer. @@ -1505,12 +1501,12 @@ fn op_to_prop_const<'tcx>( // If this constant is already represented as an `Allocation`, // try putting it into global memory to return it. if let Either::Left(mplace) = op.as_mplace_or_imm() { - let (size, _align) = ecx.size_and_align_of_mplace(&mplace).discard_interp_err()??; + let (size, _align) = ecx.size_and_align_of_mplace(&mplace).discard_err()??; // Do not try interning a value that contains provenance. // Due to https://github.com/rust-lang/rust/issues/79738, doing so could lead to bugs. // FIXME: remove this hack once that issue is fixed. - let alloc_ref = ecx.get_ptr_alloc(mplace.ptr(), size).discard_interp_err()??; + let alloc_ref = ecx.get_ptr_alloc(mplace.ptr(), size).discard_err()??; if alloc_ref.has_provenance() { return None; } @@ -1518,7 +1514,7 @@ fn op_to_prop_const<'tcx>( let pointer = mplace.ptr().into_pointer_or_addr().ok()?; let (prov, offset) = pointer.into_parts(); let alloc_id = prov.alloc_id(); - intern_const_alloc_for_constprop(ecx, alloc_id).discard_interp_err()?; + intern_const_alloc_for_constprop(ecx, alloc_id).discard_err()?; // `alloc_id` may point to a static. Codegen will choke on an `Indirect` with anything // by `GlobalAlloc::Memory`, so do fall through to copying if needed. @@ -1533,9 +1529,8 @@ fn op_to_prop_const<'tcx>( } // Everything failed: create a new allocation to hold the data. - let alloc_id = ecx - .intern_with_temp_alloc(op.layout, |ecx, dest| ecx.copy_op(op, dest)) - .discard_interp_err()?; + let alloc_id = + ecx.intern_with_temp_alloc(op.layout, |ecx, dest| ecx.copy_op(op, dest)).discard_err()?; let value = ConstValue::Indirect { alloc_id, offset: Size::ZERO }; // Check that we do not leak a pointer. diff --git a/compiler/rustc_mir_transform/src/jump_threading.rs b/compiler/rustc_mir_transform/src/jump_threading.rs index e69a7e552f606..1844b97887ab9 100644 --- a/compiler/rustc_mir_transform/src/jump_threading.rs +++ b/compiler/rustc_mir_transform/src/jump_threading.rs @@ -37,9 +37,7 @@ use rustc_arena::DroplessArena; use rustc_const_eval::const_eval::DummyMachine; -use rustc_const_eval::interpret::{ - DiscardInterpError, ImmTy, Immediate, InterpCx, OpTy, Projectable, -}; +use rustc_const_eval::interpret::{ImmTy, Immediate, InterpCx, OpTy, Projectable}; use rustc_data_structures::fx::FxHashSet; use rustc_index::IndexVec; use rustc_index::bit_set::BitSet; @@ -202,7 +200,7 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { debug!(?discr, ?bb); let discr_ty = discr.ty(self.body, self.tcx).ty; - let Some(discr_layout) = self.ecx.layout_of(discr_ty).discard_interp_err() else { + let Ok(discr_layout) = self.ecx.layout_of(discr_ty) else { return; }; @@ -392,28 +390,24 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { lhs, constant, &mut |elem, op| match elem { - TrackElem::Field(idx) => { - self.ecx.project_field(op, idx.as_usize()).discard_interp_err() - } - TrackElem::Variant(idx) => self.ecx.project_downcast(op, idx).discard_interp_err(), + TrackElem::Field(idx) => self.ecx.project_field(op, idx.as_usize()).discard_err(), + TrackElem::Variant(idx) => self.ecx.project_downcast(op, idx).discard_err(), TrackElem::Discriminant => { - let variant = self.ecx.read_discriminant(op).discard_interp_err()?; - let discr_value = self - .ecx - .discriminant_for_variant(op.layout.ty, variant) - .discard_interp_err()?; + let variant = self.ecx.read_discriminant(op).discard_err()?; + let discr_value = + self.ecx.discriminant_for_variant(op.layout.ty, variant).discard_err()?; Some(discr_value.into()) } TrackElem::DerefLen => { - let op: OpTy<'_> = self.ecx.deref_pointer(op).discard_interp_err()?.into(); - let len_usize = op.len(&self.ecx).discard_interp_err()?; + let op: OpTy<'_> = self.ecx.deref_pointer(op).discard_err()?.into(); + let len_usize = op.len(&self.ecx).discard_err()?; let layout = self.ecx.layout_of(self.tcx.types.usize).unwrap(); Some(ImmTy::from_uint(len_usize, layout).into()) } }, &mut |place, op| { if let Some(conditions) = state.try_get_idx(place, &self.map) - && let Some(imm) = self.ecx.read_immediate_raw(op).discard_interp_err() + && let Some(imm) = self.ecx.read_immediate_raw(op).discard_err() && let Some(imm) = imm.right() && let Immediate::Scalar(Scalar::Int(int)) = *imm { @@ -437,10 +431,8 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { match rhs { // If we expect `lhs ?= A`, we have an opportunity if we assume `constant == A`. Operand::Constant(constant) => { - let Some(constant) = self - .ecx - .eval_mir_constant(&constant.const_, constant.span, None) - .discard_interp_err() + let Some(constant) = + self.ecx.eval_mir_constant(&constant.const_, constant.span, None).discard_err() else { return; }; @@ -482,7 +474,7 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { && let Some(discr_value) = self .ecx .discriminant_for_variant(agg_ty, *variant_index) - .discard_interp_err() + .discard_err() { self.process_immediate(bb, discr_target, discr_value, state); } @@ -567,7 +559,7 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { // `SetDiscriminant` may be a no-op if the assigned variant is the untagged variant // of a niche encoding. If we cannot ensure that we write to the discriminant, do // nothing. - let Some(enum_layout) = self.ecx.layout_of(enum_ty).discard_interp_err() else { + let Ok(enum_layout) = self.ecx.layout_of(enum_ty) else { return; }; let writes_discriminant = match enum_layout.variants { @@ -582,10 +574,8 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { } => *variant_index != untagged_variant, }; if writes_discriminant { - let Some(discr) = self - .ecx - .discriminant_for_variant(enum_ty, *variant_index) - .discard_interp_err() + let Some(discr) = + self.ecx.discriminant_for_variant(enum_ty, *variant_index).discard_err() else { return; }; @@ -662,7 +652,7 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { let Some(discr) = discr.place() else { return }; let discr_ty = discr.ty(self.body, self.tcx).ty; - let Some(discr_layout) = self.ecx.layout_of(discr_ty).discard_interp_err() else { + let Ok(discr_layout) = self.ecx.layout_of(discr_ty) else { return; }; let Some(conditions) = state.try_get(discr.as_ref(), &self.map) else { return }; diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs index b165ea53440e0..ccc029b1e281e 100644 --- a/compiler/rustc_mir_transform/src/known_panics_lint.rs +++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs @@ -6,7 +6,7 @@ use std::fmt::Debug; use rustc_const_eval::const_eval::DummyMachine; use rustc_const_eval::interpret::{ - DiscardInterpError, ImmTy, InterpCx, InterpResult, Projectable, Scalar, format_interp_error, + ImmTy, InterpCx, InterpResult, Projectable, Scalar, format_interp_error, interp_ok, }; use rustc_data_structures::fx::FxHashSet; use rustc_hir::HirId; @@ -101,8 +101,7 @@ impl<'tcx> Value<'tcx> { } (PlaceElem::Index(idx), Value::Aggregate { fields, .. }) => { let idx = prop.get_const(idx.into())?.immediate()?; - let idx = - prop.ecx.read_target_usize(idx).discard_interp_err()?.try_into().ok()?; + let idx = prop.ecx.read_target_usize(idx).discard_err()?.try_into().ok()?; if idx <= FieldIdx::MAX_AS_U32 { fields.get(FieldIdx::from_u32(idx)).unwrap_or(&Value::Uninit) } else { @@ -232,22 +231,20 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { where F: FnOnce(&mut Self) -> InterpResult<'tcx, T>, { - match f(self) { - Ok(val) => Some(val), - Err(error) => { - trace!("InterpCx operation failed: {:?}", error); + f(self) + .map_err(|err| { + trace!("InterpCx operation failed: {:?}", err); // Some errors shouldn't come up because creating them causes // an allocation, which we should avoid. When that happens, // dedicated error variants should be introduced instead. assert!( - !error.kind().formatted_string(), + !err.kind().formatted_string(), "known panics lint encountered formatting error: {}", - format_interp_error(self.ecx.tcx.dcx(), error), + format_interp_error(self.ecx.tcx.dcx(), err), ); - error.discard_interp_err(); - None - } - } + err + }) + .discard_err() } /// Returns the value, if any, of evaluating `c`. @@ -317,7 +314,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { .ecx .binary_op(BinOp::SubWithOverflow, &ImmTy::from_int(0, arg.layout), &arg)? .to_scalar_pair(); - Ok((arg, overflow.to_bool()?)) + interp_ok((arg, overflow.to_bool()?)) })?; if overflow { self.report_assert_as_lint( @@ -349,9 +346,9 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { // We need the type of the LHS. We cannot use `place_layout` as that is the type // of the result, which for checked binops is not the same! let left_ty = left.ty(self.local_decls(), self.tcx); - let left_size = self.ecx.layout_of(left_ty).discard_interp_err()?.size; + let left_size = self.ecx.layout_of(left_ty).ok()?.size; let right_size = r.layout.size; - let r_bits = r.to_scalar().to_bits(right_size).discard_interp_err(); + let r_bits = r.to_scalar().to_bits(right_size).discard_err(); if r_bits.is_some_and(|b| b >= left_size.bits() as u128) { debug!("check_binary_op: reporting assert for {:?}", location); let panic = AssertKind::Overflow( @@ -498,7 +495,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { // This can be `None` if the lhs wasn't const propagated and we just // triggered the assert on the value of the rhs. self.eval_operand(op) - .and_then(|op| self.ecx.read_immediate(&op).discard_interp_err()) + .and_then(|op| self.ecx.read_immediate(&op).discard_err()) .map_or(DbgVal::Underscore, |op| DbgVal::Val(op.to_const_int())) }; let msg = match msg { @@ -542,7 +539,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { return None; } use rustc_middle::mir::Rvalue::*; - let layout = self.ecx.layout_of(dest.ty(self.body, self.tcx).ty).discard_interp_err()?; + let layout = self.ecx.layout_of(dest.ty(self.body, self.tcx).ty).ok()?; trace!(?layout); let val: Value<'_> = match *rvalue { @@ -604,7 +601,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { Len(place) => { let len = match self.get_const(place)? { - Value::Immediate(src) => src.len(&self.ecx).discard_interp_err()?, + Value::Immediate(src) => src.len(&self.ecx).discard_err()?, Value::Aggregate { fields, .. } => fields.len() as u64, Value::Uninit => match place.ty(self.local_decls(), self.tcx).ty.kind() { ty::Array(_, n) => n.try_eval_target_usize(self.tcx, self.param_env)?, @@ -617,7 +614,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { Ref(..) | RawPtr(..) => return None, NullaryOp(ref null_op, ty) => { - let op_layout = self.use_ecx(|this| this.ecx.layout_of(ty))?; + let op_layout = self.ecx.layout_of(ty).ok()?; let val = match null_op { NullOp::SizeOf => op_layout.size.bytes(), NullOp::AlignOf => op_layout.align.abi.bytes(), @@ -635,21 +632,21 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { Cast(ref kind, ref value, to) => match kind { CastKind::IntToInt | CastKind::IntToFloat => { let value = self.eval_operand(value)?; - let value = self.ecx.read_immediate(&value).discard_interp_err()?; - let to = self.ecx.layout_of(to).discard_interp_err()?; - let res = self.ecx.int_to_int_or_float(&value, to).discard_interp_err()?; + let value = self.ecx.read_immediate(&value).discard_err()?; + let to = self.ecx.layout_of(to).ok()?; + let res = self.ecx.int_to_int_or_float(&value, to).discard_err()?; res.into() } CastKind::FloatToFloat | CastKind::FloatToInt => { let value = self.eval_operand(value)?; - let value = self.ecx.read_immediate(&value).discard_interp_err()?; - let to = self.ecx.layout_of(to).discard_interp_err()?; - let res = self.ecx.float_to_float_or_int(&value, to).discard_interp_err()?; + let value = self.ecx.read_immediate(&value).discard_err()?; + let to = self.ecx.layout_of(to).ok()?; + let res = self.ecx.float_to_float_or_int(&value, to).discard_err()?; res.into() } CastKind::Transmute => { let value = self.eval_operand(value)?; - let to = self.ecx.layout_of(to).discard_interp_err()?; + let to = self.ecx.layout_of(to).ok()?; // `offset` for immediates only supports scalar/scalar-pair ABIs, // so bail out if the target is not one. match (value.layout.abi, to.abi) { @@ -658,7 +655,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { _ => return None, } - value.offset(Size::ZERO, to, &self.ecx).discard_interp_err()?.into() + value.offset(Size::ZERO, to, &self.ecx).discard_err()?.into() } _ => return None, }, @@ -783,8 +780,7 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> { TerminatorKind::SwitchInt { ref discr, ref targets } => { if let Some(ref value) = self.eval_operand(discr) && let Some(value_const) = self.use_ecx(|this| this.ecx.read_scalar(value)) - && let Some(constant) = - value_const.to_bits(value_const.size()).discard_interp_err() + && let Some(constant) = value_const.to_bits(value_const.size()).discard_err() { // We managed to evaluate the discriminant, so we know we only need to visit // one target. diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs index bf47cf6d372d2..53a1170d6a618 100644 --- a/src/tools/clippy/clippy_utils/src/consts.rs +++ b/src/tools/clippy/clippy_utils/src/consts.rs @@ -870,10 +870,10 @@ pub fn mir_to_const<'tcx>(tcx: TyCtxt<'tcx>, result: mir::Const<'tcx>) -> Option let range = alloc_range(offset + size * idx, size); let val = alloc.read_scalar(&tcx, range, /* read_provenance */ false).ok()?; res.push(match flt { - FloatTy::F16 => Constant::F16(f16::from_bits(val.to_u16().ok()?)), - FloatTy::F32 => Constant::F32(f32::from_bits(val.to_u32().ok()?)), - FloatTy::F64 => Constant::F64(f64::from_bits(val.to_u64().ok()?)), - FloatTy::F128 => Constant::F128(f128::from_bits(val.to_u128().ok()?)), + FloatTy::F16 => Constant::F16(f16::from_bits(val.to_u16().discard_err()?)), + FloatTy::F32 => Constant::F32(f32::from_bits(val.to_u32().discard_err()?)), + FloatTy::F64 => Constant::F64(f64::from_bits(val.to_u64().discard_err()?)), + FloatTy::F128 => Constant::F128(f128::from_bits(val.to_u128().discard_err()?)), }); } Some(Constant::Vec(res)) @@ -903,7 +903,7 @@ fn mir_is_empty<'tcx>(tcx: TyCtxt<'tcx>, result: mir::Const<'tcx>) -> Option: crate::MiriInterpCxExt<'tcx> { AllocKind::Dead => unreachable!(), }; // Ensure this pointer's provenance is exposed, so that it can be used by FFI code. - return Ok(base_ptr.expose_provenance().try_into().unwrap()); + return interp_ok(base_ptr.expose_provenance().try_into().unwrap()); } // We are not in native lib mode, so we control the addresses ourselves. if let Some((reuse_addr, clock)) = @@ -209,7 +209,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { if let Some(clock) = clock { ecx.acquire_clock(&clock); } - Ok(reuse_addr) + interp_ok(reuse_addr) } else { // We have to pick a fresh address. // Leave some space to the previous allocation, to give it some chance to be less aligned. @@ -234,7 +234,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { throw_exhaust!(AddressSpaceFull); } - Ok(base_addr) + interp_ok(base_addr) } } @@ -248,7 +248,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { let global_state = &mut *global_state; match global_state.base_addr.get(&alloc_id) { - Some(&addr) => Ok(addr), + Some(&addr) => interp_ok(addr), None => { // First time we're looking for the absolute address of this allocation. let base_addr = @@ -274,7 +274,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { }; global_state.int_to_ptr_map.insert(pos, (base_addr, alloc_id)); - Ok(base_addr) + interp_ok(base_addr) } } } @@ -287,12 +287,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let global_state = ecx.machine.alloc_addresses.get_mut(); // In strict mode, we don't need this, so we can save some cycles by not tracking it. if global_state.provenance_mode == ProvenanceMode::Strict { - return Ok(()); + return interp_ok(()); } // Exposing a dead alloc is a no-op, because it's not possible to get a dead allocation // via int2ptr. if !ecx.is_alloc_live(alloc_id) { - return Ok(()); + return interp_ok(()); } trace!("Exposing allocation id {alloc_id:?}"); let global_state = ecx.machine.alloc_addresses.get_mut(); @@ -300,7 +300,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if ecx.machine.borrow_tracker.is_some() { ecx.expose_tag(alloc_id, tag)?; } - Ok(()) + interp_ok(()) } fn ptr_from_addr_cast(&self, addr: u64) -> InterpResult<'tcx, Pointer> { @@ -337,7 +337,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // cast is fairly irrelevant. Instead we generate this as a "wildcard" pointer, such that // *every time the pointer is used*, we do an `AllocId` lookup to find the (exposed) // allocation it might be referencing. - Ok(Pointer::new(Some(Provenance::Wildcard), Size::from_bytes(addr))) + interp_ok(Pointer::new(Some(Provenance::Wildcard), Size::from_bytes(addr))) } /// Convert a relative (tcx) pointer to a Miri pointer. @@ -359,7 +359,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Size::from_bytes(base_addr), ); // Add offset with the right kind of pointer-overflowing arithmetic. - Ok(base_ptr.wrapping_offset(offset, ecx)) + interp_ok(base_ptr.wrapping_offset(offset, ecx)) } // This returns some prepared `MiriAllocBytes`, either because `addr_from_alloc_id` reserved @@ -390,9 +390,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { assert_eq!(prepared_alloc_bytes.len(), bytes.len()); // Copy allocation contents into prepared memory. prepared_alloc_bytes.copy_from_slice(bytes); - Ok(prepared_alloc_bytes) + interp_ok(prepared_alloc_bytes) } else { - Ok(MiriAllocBytes::from_bytes(std::borrow::Cow::Borrowed(bytes), align)) + interp_ok(MiriAllocBytes::from_bytes(std::borrow::Cow::Borrowed(bytes), align)) } } diff --git a/src/tools/miri/src/borrow_tracker/mod.rs b/src/tools/miri/src/borrow_tracker/mod.rs index 89fd8c28af2ae..5204558f98c1f 100644 --- a/src/tools/miri/src/borrow_tracker/mod.rs +++ b/src/tools/miri/src/borrow_tracker/mod.rs @@ -322,7 +322,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { match method { BorrowTrackerMethod::StackedBorrows => { this.tcx.tcx.dcx().warn("Stacked Borrows does not support named pointers; `miri_pointer_name` is a no-op"); - Ok(()) + interp_ok(()) } BorrowTrackerMethod::TreeBorrows => this.tb_give_pointer_debug_name(ptr, nth_parent, name), @@ -333,7 +333,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let this = self.eval_context_mut(); let Some(borrow_tracker) = &this.machine.borrow_tracker else { eprintln!("attempted to print borrow state, but no borrow state is being tracked"); - return Ok(()); + return interp_ok(()); }; let method = borrow_tracker.borrow().borrow_tracker_method; match method { @@ -376,7 +376,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } } borrow_tracker.borrow_mut().end_call(&frame.extra); - Ok(()) + interp_ok(()) } } @@ -489,7 +489,7 @@ impl AllocState { alloc_id: AllocId, // diagnostics ) -> InterpResult<'tcx> { match self { - AllocState::StackedBorrows(_sb) => Ok(()), + AllocState::StackedBorrows(_sb) => interp_ok(()), AllocState::TreeBorrows(tb) => tb.borrow_mut().release_protector(machine, global, tag, alloc_id), } diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs index b270e484bdba4..fdc7a675fb713 100644 --- a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs @@ -230,7 +230,7 @@ impl<'tcx> Stack { } if !item.protected() { - return Ok(()); + return interp_ok(()); } // We store tags twice, once in global.protected_tags and once in each call frame. @@ -252,10 +252,10 @@ impl<'tcx> Stack { let allowed = matches!(cause, ItemInvalidationCause::Dealloc) && matches!(protector_kind, ProtectorKind::WeakProtector); if !allowed { - return Err(dcx.protector_error(item, protector_kind).into()); + return Err(dcx.protector_error(item, protector_kind)).into(); } } - Ok(()) + interp_ok(()) } /// Test if a memory `access` using pointer tagged `tag` is granted. @@ -295,7 +295,7 @@ impl<'tcx> Stack { self.pop_items_after(first_incompatible_idx, |item| { Stack::item_invalidated(&item, global, dcx, ItemInvalidationCause::Conflict)?; dcx.log_invalidation(item.tag()); - Ok(()) + interp_ok(()) })?; } else { // On a read, *disable* all `Unique` above the granting item. This ensures U2 for read accesses. @@ -316,7 +316,7 @@ impl<'tcx> Stack { self.disable_uniques_starting_at(first_incompatible_idx, |item| { Stack::item_invalidated(&item, global, dcx, ItemInvalidationCause::Conflict)?; dcx.log_invalidation(item.tag()); - Ok(()) + interp_ok(()) })?; } @@ -345,7 +345,7 @@ impl<'tcx> Stack { } // Done. - Ok(()) + interp_ok(()) } /// Deallocate a location: Like a write access, but also there must be no @@ -367,7 +367,7 @@ impl<'tcx> Stack { Stack::item_invalidated(&item, global, dcx, ItemInvalidationCause::Dealloc)?; } - Ok(()) + interp_ok(()) } /// Derive a new pointer from one with the given tag. @@ -418,7 +418,7 @@ impl<'tcx> Stack { "reborrow: forgetting stack entirely due to SharedReadWrite reborrow from wildcard or unknown" ); self.set_unknown_bottom(global.next_ptr_tag); - return Ok(()); + return interp_ok(()); }; // SharedReadWrite can coexist with "existing loans", meaning they don't act like a write @@ -431,7 +431,7 @@ impl<'tcx> Stack { // Put the new item there. trace!("reborrow: adding item {:?}", new); self.insert(new_idx, new); - Ok(()) + interp_ok(()) } } // # Stacked Borrows Core End @@ -491,7 +491,7 @@ impl<'tcx> Stacks { f(stack, &mut dcx, &mut self.exposed_tags)?; dcx_builder = dcx.unbuild(); } - Ok(()) + interp_ok(()) } } @@ -576,7 +576,7 @@ impl Stacks { self.for_each(alloc_range(Size::ZERO, size), dcx, |stack, dcx, exposed_tags| { stack.dealloc(tag, &state, dcx, exposed_tags) })?; - Ok(()) + interp_ok(()) } } @@ -623,7 +623,7 @@ trait EvalContextPrivExt<'tcx, 'ecx>: crate::MiriInterpCxExt<'tcx> { drop(global); // don't hold that reference any longer than we have to let Some((alloc_id, base_offset, orig_tag)) = loc else { - return Ok(()) + return interp_ok(()) }; let (_size, _align, alloc_kind) = this.get_alloc_info(alloc_id); @@ -655,7 +655,7 @@ trait EvalContextPrivExt<'tcx, 'ecx>: crate::MiriInterpCxExt<'tcx> { // No stacked borrows on these allocations. } } - Ok(()) + interp_ok(()) }; if size == Size::ZERO { @@ -676,12 +676,12 @@ trait EvalContextPrivExt<'tcx, 'ecx>: crate::MiriInterpCxExt<'tcx> { { log_creation(this, Some((alloc_id, base_offset, orig_tag)))?; // Still give it the new provenance, it got retagged after all. - return Ok(Some(Provenance::Concrete { alloc_id, tag: new_tag })); + return interp_ok(Some(Provenance::Concrete { alloc_id, tag: new_tag })); } else { // This pointer doesn't come with an AllocId. :shrug: log_creation(this, None)?; // Provenance unchanged. - return Ok(place.ptr().provenance); + return interp_ok(place.ptr().provenance); } } @@ -800,12 +800,12 @@ trait EvalContextPrivExt<'tcx, 'ecx>: crate::MiriInterpCxExt<'tcx> { )?; } } - Ok(()) + interp_ok(()) })?; } } - Ok(Some(Provenance::Concrete { alloc_id, tag: new_tag })) + interp_ok(Some(Provenance::Concrete { alloc_id, tag: new_tag })) } fn sb_retag_place( @@ -832,7 +832,7 @@ trait EvalContextPrivExt<'tcx, 'ecx>: crate::MiriInterpCxExt<'tcx> { *shown = true; this.emit_diagnostic(NonHaltingDiagnostic::ExternTypeReborrow); }); - return Ok(place.clone()); + return interp_ok(place.clone()); } }; @@ -845,7 +845,7 @@ trait EvalContextPrivExt<'tcx, 'ecx>: crate::MiriInterpCxExt<'tcx> { // Adjust place. // (If the closure gets called, that means the old provenance was `Some`, and hence the new // one must also be `Some`.) - Ok(place.clone().map_provenance(|_| new_prov.unwrap())) + interp_ok(place.clone().map_provenance(|_| new_prov.unwrap())) } /// Retags an individual pointer, returning the retagged version. @@ -859,7 +859,7 @@ trait EvalContextPrivExt<'tcx, 'ecx>: crate::MiriInterpCxExt<'tcx> { let this = self.eval_context_mut(); let place = this.ref_to_mplace(val)?; let new_place = this.sb_retag_place(&place, new_perm, info)?; - Ok(ImmTy::from_immediate(new_place.to_ref(this), val.layout)) + interp_ok(ImmTy::from_immediate(new_place.to_ref(this), val.layout)) } } @@ -917,7 +917,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { in_field: self.in_field, })?; self.ecx.write_immediate(*val, place)?; - Ok(()) + interp_ok(()) } } impl<'ecx, 'tcx> ValueVisitor<'tcx, MiriMachine<'tcx>> for RetagVisitor<'ecx, 'tcx> { @@ -935,7 +935,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let new_perm = NewPermission::from_box_ty(place.layout.ty, self.kind, self.ecx); self.retag_ptr_inplace(place, new_perm)?; } - Ok(()) + interp_ok(()) } fn visit_value(&mut self, place: &PlaceTy<'tcx>) -> InterpResult<'tcx> { @@ -944,7 +944,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // This optimization is crucial for ZSTs, because they can contain way more fields // than we can ever visit. if place.layout.is_sized() && place.layout.size < self.ecx.pointer_size() { - return Ok(()); + return interp_ok(()); } // Check the type of this value to see what to do with it (retag, or recurse). @@ -983,7 +983,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } } - Ok(()) + interp_ok(()) } } } @@ -1028,7 +1028,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // No stacked borrows on these allocations. } } - Ok(()) + interp_ok(()) } fn print_stacks(&mut self, alloc_id: AllocId) -> InterpResult<'tcx> { @@ -1046,6 +1046,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } println!(" ]"); } - Ok(()) + interp_ok(()) } } diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/stack.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/stack.rs index 6b5ef7d59c554..f024796c0a722 100644 --- a/src/tools/miri/src/borrow_tracker/stacked_borrows/stack.rs +++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/stack.rs @@ -4,9 +4,9 @@ use std::ops::Range; use rustc_data_structures::fx::FxHashSet; use tracing::trace; -use crate::ProvenanceExtra; use crate::borrow_tracker::stacked_borrows::{Item, Permission}; use crate::borrow_tracker::{AccessKind, BorTag}; +use crate::{InterpResult, ProvenanceExtra, interp_ok}; /// Exactly what cache size we should use is a difficult trade-off. There will always be some /// workload which has a `BorTag` working set which exceeds the size of the cache, and ends up @@ -380,8 +380,8 @@ impl<'tcx> Stack { pub fn disable_uniques_starting_at( &mut self, disable_start: usize, - mut visitor: impl FnMut(Item) -> crate::InterpResult<'tcx>, - ) -> crate::InterpResult<'tcx> { + mut visitor: impl FnMut(Item) -> InterpResult<'tcx>, + ) -> InterpResult<'tcx> { #[cfg(feature = "stack-cache")] let unique_range = self.unique_range.clone(); #[cfg(not(feature = "stack-cache"))] @@ -420,16 +420,16 @@ impl<'tcx> Stack { #[cfg(feature = "stack-cache-consistency-check")] self.verify_cache_consistency(); - Ok(()) + interp_ok(()) } /// Produces an iterator which iterates over `range` in reverse, and when dropped removes that /// range of `Item`s from this `Stack`. - pub fn pop_items_after crate::InterpResult<'tcx>>( + pub fn pop_items_after InterpResult<'tcx>>( &mut self, start: usize, mut visitor: V, - ) -> crate::InterpResult<'tcx> { + ) -> InterpResult<'tcx> { while self.borrows.len() > start { let item = self.borrows.pop().unwrap(); visitor(item)?; @@ -474,6 +474,6 @@ impl<'tcx> Stack { #[cfg(feature = "stack-cache-consistency-check")] self.verify_cache_consistency(); - Ok(()) + interp_ok(()) } } diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs index af9ec129c69f7..cb840f19e3b61 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs @@ -226,7 +226,7 @@ impl<'tcx> Tree { } else { eprintln!("Tag {tag:?} (to be named '{name}') not found!"); } - Ok(()) + interp_ok(()) } /// Debug helper: determines if the tree contains a tag. @@ -798,6 +798,6 @@ impl<'tcx> Tree { /* print warning message about tags not shown */ !show_unnamed, ); } - Ok(()) + interp_ok(()) } } diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs index 30e940b4439a4..acfb76030f5ff 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs @@ -56,7 +56,7 @@ impl<'tcx> Tree { // handle them as much as we can. let tag = match prov { ProvenanceExtra::Concrete(tag) => tag, - ProvenanceExtra::Wildcard => return Ok(()), + ProvenanceExtra::Wildcard => return interp_ok(()), }; let global = machine.borrow_tracker.as_ref().unwrap(); let span = machine.current_span(); @@ -81,7 +81,7 @@ impl<'tcx> Tree { // handle them as much as we can. let tag = match prov { ProvenanceExtra::Concrete(tag) => tag, - ProvenanceExtra::Wildcard => return Ok(()), + ProvenanceExtra::Wildcard => return interp_ok(()), }; let global = machine.borrow_tracker.as_ref().unwrap(); let span = machine.current_span(); @@ -213,7 +213,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { )); } drop(global); // don't hold that reference any longer than we have to - Ok(()) + interp_ok(()) }; trace!("Reborrow of size {:?}", ptr_size); @@ -235,13 +235,13 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ); log_creation(this, None)?; // Keep original provenance. - return Ok(place.ptr().provenance); + return interp_ok(place.ptr().provenance); } }; log_creation(this, Some((alloc_id, base_offset, parent_prov)))?; let orig_tag = match parent_prov { - ProvenanceExtra::Wildcard => return Ok(place.ptr().provenance), // TODO: handle wildcard pointers + ProvenanceExtra::Wildcard => return interp_ok(place.ptr().provenance), // TODO: handle wildcard pointers ProvenanceExtra::Concrete(tag) => tag, }; @@ -279,7 +279,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { assert_eq!(ptr_size, Size::ZERO); // we did the deref check above, size has to be 0 here // There's not actually any bytes here where accesses could even be tracked. // Just produce the new provenance, nothing else to do. - return Ok(Some(Provenance::Concrete { alloc_id, tag: new_tag })); + return interp_ok(Some(Provenance::Concrete { alloc_id, tag: new_tag })); } let span = this.machine.current_span(); @@ -312,7 +312,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } } - Ok(Some(Provenance::Concrete { alloc_id, tag: new_tag })) + interp_ok(Some(Provenance::Concrete { alloc_id, tag: new_tag })) } fn tb_retag_place( @@ -350,7 +350,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Adjust place. // (If the closure gets called, that means the old provenance was `Some`, and hence the new // one must also be `Some`.) - Ok(place.clone().map_provenance(|_| new_prov.unwrap())) + interp_ok(place.clone().map_provenance(|_| new_prov.unwrap())) } /// Retags an individual pointer, returning the retagged version. @@ -362,7 +362,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let this = self.eval_context_mut(); let place = this.ref_to_mplace(val)?; let new_place = this.tb_retag_place(&place, new_perm)?; - Ok(ImmTy::from_immediate(new_place.to_ref(this), val.layout)) + interp_ok(ImmTy::from_immediate(new_place.to_ref(this), val.layout)) } } @@ -384,7 +384,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let Some(new_perm) = new_perm { this.tb_retag_reference(val, new_perm) } else { - Ok(val.clone()) + interp_ok(val.clone()) } } @@ -421,7 +421,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let val = self.ecx.tb_retag_reference(&val, new_perm)?; self.ecx.write_immediate(*val, place)?; } - Ok(()) + interp_ok(()) } } impl<'ecx, 'tcx> ValueVisitor<'tcx, MiriMachine<'tcx>> for RetagVisitor<'ecx, 'tcx> { @@ -446,7 +446,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ); self.retag_ptr_inplace(place, new_perm)?; } - Ok(()) + interp_ok(()) } fn visit_value(&mut self, place: &PlaceTy<'tcx>) -> InterpResult<'tcx> { @@ -455,7 +455,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // This optimization is crucial for ZSTs, because they can contain way more fields // than we can ever visit. if place.layout.is_sized() && place.layout.size < self.ecx.pointer_size() { - return Ok(()); + return interp_ok(()); } // Check the type of this value to see what to do with it (retag, or recurse). @@ -503,7 +503,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } } } - Ok(()) + interp_ok(()) } } } @@ -549,7 +549,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // No tree borrows on these allocations. } } - Ok(()) + interp_ok(()) } /// Display the tree. @@ -575,7 +575,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Some(Provenance::Concrete { tag, alloc_id }) => (tag, alloc_id), _ => { eprintln!("Can't give the name {name} to Wildcard pointer"); - return Ok(()); + return interp_ok(()); } }; let alloc_extra = this.get_alloc_extra(alloc_id)?; @@ -605,5 +605,5 @@ fn inner_ptr_of_unique<'tcx>( assert_eq!(nonnull.layout.fields.count(), 1, "NonNull must have exactly 1 field"); let ptr = ecx.project_field(&nonnull, 0)?; // Finally a plain `*mut` - Ok(ptr) + interp_ok(ptr) } diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs index 9fe134ed34bc9..410f4a58ac530 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs @@ -637,7 +637,7 @@ impl<'tcx> Tree { { perms.insert(idx, perm); } - Ok(()) + interp_ok(()) } /// Deallocation requires @@ -688,7 +688,7 @@ impl<'tcx> Tree { }, )?; } - Ok(()) + interp_ok(()) } /// Map the per-node and per-location `LocationState::perform_access` @@ -827,7 +827,7 @@ impl<'tcx> Tree { } } } - Ok(()) + interp_ok(()) } } diff --git a/src/tools/miri/src/concurrency/data_race.rs b/src/tools/miri/src/concurrency/data_race.rs index 5910be22a690a..82c4f6d3007e6 100644 --- a/src/tools/miri/src/concurrency/data_race.rs +++ b/src/tools/miri/src/concurrency/data_race.rs @@ -624,7 +624,7 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { let buffered_scalar = this.buffered_atomic_read(place, atomic, scalar, || { this.validate_atomic_load(place, atomic) })?; - Ok(buffered_scalar.ok_or_else(|| err_ub!(InvalidUninitBytes(None)))?) + interp_ok(buffered_scalar.ok_or_else(|| err_ub!(InvalidUninitBytes(None)))?) } /// Perform an atomic write operation at the memory location. @@ -641,7 +641,7 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { // The program didn't actually do a read, so suppress the memory access hooks. // This is also a very special exception where we just ignore an error -- if this read // was UB e.g. because the memory is uninitialized, we don't want to know! - let old_val = this.run_for_validation(|this| this.read_scalar(dest)).discard_interp_err(); + let old_val = this.run_for_validation(|this| this.read_scalar(dest)).discard_err(); this.allow_data_races_mut(move |this| this.write_scalar(val, dest))?; this.validate_atomic_store(dest, atomic)?; this.buffered_atomic_write(val, dest, atomic, old_val) @@ -668,7 +668,7 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { this.validate_atomic_rmw(place, atomic)?; this.buffered_atomic_rmw(val.to_scalar(), place, atomic, old.to_scalar())?; - Ok(old) + interp_ok(old) } /// Perform an atomic exchange with a memory place and a new @@ -688,7 +688,7 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { this.validate_atomic_rmw(place, atomic)?; this.buffered_atomic_rmw(new, place, atomic, old)?; - Ok(old) + interp_ok(old) } /// Perform an conditional atomic exchange with a memory place and a new @@ -720,7 +720,7 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { this.buffered_atomic_rmw(new_val.to_scalar(), place, atomic, old.to_scalar())?; // Return the old value. - Ok(old) + interp_ok(old) } /// Perform an atomic compare and exchange at a given memory location. @@ -777,7 +777,7 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { } // Return the old value. - Ok(res) + interp_ok(res) } /// Update the data-race detector for an atomic fence on the current thread. @@ -809,11 +809,11 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { } // Increment timestamp in case of release semantics. - Ok(atomic != AtomicFenceOrd::Acquire) + interp_ok(atomic != AtomicFenceOrd::Acquire) }, ) } else { - Ok(()) + interp_ok(()) } } @@ -1047,7 +1047,7 @@ impl VClockAlloc { let current_span = machine.current_span(); let global = machine.data_race.as_ref().unwrap(); if !global.race_detecting() { - return Ok(()); + return interp_ok(()); } let (index, mut thread_clocks) = global.active_thread_state_mut(&machine.threads); let mut alloc_ranges = self.alloc_ranges.borrow_mut(); @@ -1070,7 +1070,7 @@ impl VClockAlloc { ); } } - Ok(()) + interp_ok(()) } /// Detect data-races for an unsynchronized write operation. It will not perform @@ -1089,7 +1089,7 @@ impl VClockAlloc { let current_span = machine.current_span(); let global = machine.data_race.as_mut().unwrap(); if !global.race_detecting() { - return Ok(()); + return interp_ok(()); } let (index, mut thread_clocks) = global.active_thread_state_mut(&machine.threads); for (mem_clocks_range, mem_clocks) in @@ -1111,7 +1111,7 @@ impl VClockAlloc { ); } } - Ok(()) + interp_ok(()) } } @@ -1307,7 +1307,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { } } } - Ok(()) + interp_ok(()) } /// Update the data-race detector for an atomic read occurring at the @@ -1399,9 +1399,9 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { ) -> InterpResult<'tcx> { let this = self.eval_context_ref(); assert!(access.is_atomic()); - let Some(data_race) = &this.machine.data_race else { return Ok(()) }; + let Some(data_race) = &this.machine.data_race else { return interp_ok(()) }; if !data_race.race_detecting() { - return Ok(()); + return interp_ok(()); } let size = place.layout.size; let (alloc_id, base_offset, _prov) = this.ptr_get_alloc_id(place.ptr(), 0)?; @@ -1444,7 +1444,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { } // This conservatively assumes all operations have release semantics - Ok(true) + interp_ok(true) }, )?; @@ -1460,7 +1460,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { } } - Ok(()) + interp_ok(()) } } @@ -1757,7 +1757,7 @@ impl GlobalState { clocks.increment_clock(index, current_span); } } - Ok(()) + interp_ok(()) } /// Internal utility to identify a thread stored internally diff --git a/src/tools/miri/src/concurrency/init_once.rs b/src/tools/miri/src/concurrency/init_once.rs index 9c2c6ae133023..8985135f4e892 100644 --- a/src/tools/miri/src/concurrency/init_once.rs +++ b/src/tools/miri/src/concurrency/init_once.rs @@ -37,9 +37,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { lock, offset, |ecx| &mut ecx.machine.sync.init_onces, - |_| Ok(Default::default()), + |_| interp_ok(Default::default()), )? - .ok_or_else(|| err_ub_format!("init_once has invalid ID").into()) + .ok_or_else(|| err_ub_format!("init_once has invalid ID")).into() } #[inline] @@ -101,7 +101,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.unblock_thread(waiter, BlockReason::InitOnce(id))?; } - Ok(()) + interp_ok(()) } #[inline] @@ -126,7 +126,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.unblock_thread(waiter, BlockReason::InitOnce(id))?; } - Ok(()) + interp_ok(()) } /// Synchronize with the previous completion of an InitOnce. diff --git a/src/tools/miri/src/concurrency/sync.rs b/src/tools/miri/src/concurrency/sync.rs index 7fe67e5c6727e..3b57af641b589 100644 --- a/src/tools/miri/src/concurrency/sync.rs +++ b/src/tools/miri/src/concurrency/sync.rs @@ -206,7 +206,7 @@ pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { )? .to_scalar_pair(); - Ok(if success.to_bool().expect("compare_exchange's second return value is a bool") { + interp_ok(if success.to_bool().expect("compare_exchange's second return value is a bool") { // We set the in-memory ID to `next_index`, now also create this object in the machine // state. let obj = create_obj(this)?; @@ -247,7 +247,7 @@ pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { let new_index = get_objs(this).push(obj); this.write_scalar(Scalar::from_u32(new_index.to_u32()), &id_place)?; - Ok(new_index) + interp_ok(new_index) } fn condvar_reacquire_mutex( @@ -266,7 +266,7 @@ pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // Don't forget to write the return value. this.write_scalar(retval, &dest)?; } - Ok(()) + interp_ok(()) } } @@ -307,7 +307,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |ecx| &mut ecx.machine.sync.mutexes, |ecx| initialize_data(ecx).map(|data| Mutex { data, ..Default::default() }), )? - .ok_or_else(|| err_ub_format!("mutex has invalid ID").into()) + .ok_or_else(|| err_ub_format!("mutex has invalid ID")).into() } /// Retrieve the additional data stored for a mutex. @@ -334,7 +334,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |ecx| &mut ecx.machine.sync.rwlocks, |ecx| initialize_data(ecx).map(|data| RwLock { data, ..Default::default() }), )? - .ok_or_else(|| err_ub_format!("rwlock has invalid ID").into()) + .ok_or_else(|| err_ub_format!("rwlock has invalid ID")).into() } /// Retrieve the additional data stored for a rwlock. @@ -375,7 +375,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |ecx| &mut ecx.machine.sync.condvars, |ecx| initialize_data(ecx).map(|data| Condvar { data, ..Default::default() }), )? - .ok_or_else(|| err_ub_format!("condvar has invalid ID").into()) + .ok_or_else(|| err_ub_format!("condvar has invalid ID")).into() } /// Retrieve the additional data stored for a condvar. @@ -428,11 +428,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn mutex_unlock(&mut self, id: MutexId) -> InterpResult<'tcx, Option> { let this = self.eval_context_mut(); let mutex = &mut this.machine.sync.mutexes[id]; - Ok(if let Some(current_owner) = mutex.owner { + interp_ok(if let Some(current_owner) = mutex.owner { // Mutex is locked. if current_owner != this.machine.threads.active_thread() { // Only the owner can unlock the mutex. - return Ok(None); + return interp_ok(None); } let old_lock_count = mutex.lock_count; mutex.lock_count = old_lock_count.strict_sub(1); @@ -484,7 +484,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(retval, &dest)?; } - Ok(()) + interp_ok(()) } ), ); @@ -546,7 +546,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { trace!("rwlock_reader_unlock: {:?} held one less time by {:?}", id, thread); } } - Entry::Vacant(_) => return Ok(false), // we did not even own this lock + Entry::Vacant(_) => return interp_ok(false), // we did not even own this lock } if let Some(data_race) = &this.machine.data_race { // Add this to the shared-release clock of all concurrent readers. @@ -565,7 +565,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.unblock_thread(writer, BlockReason::RwLock(id))?; } } - Ok(true) + interp_ok(true) } /// Put the reader in the queue waiting for the lock and block it. @@ -593,7 +593,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { @unblock = |this| { this.rwlock_reader_lock(id); this.write_scalar(retval, &dest)?; - Ok(()) + interp_ok(()) } ), ); @@ -620,10 +620,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let this = self.eval_context_mut(); let thread = this.active_thread(); let rwlock = &mut this.machine.sync.rwlocks[id]; - Ok(if let Some(current_writer) = rwlock.writer { + interp_ok(if let Some(current_writer) = rwlock.writer { if current_writer != thread { // Only the owner can unlock the rwlock. - return Ok(false); + return interp_ok(false); } rwlock.writer = None; trace!("rwlock_writer_unlock: {:?} unlocked by {:?}", id, thread); @@ -676,7 +676,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { @unblock = |this| { this.rwlock_writer_lock(id); this.write_scalar(retval, &dest)?; - Ok(()) + interp_ok(()) } ), ); @@ -749,7 +749,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } ), ); - Ok(()) + interp_ok(()) } /// Wake up some thread (if there is any) sleeping on the conditional @@ -764,10 +764,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { condvar.clock.clone_from(&*data_race.release_clock(&this.machine.threads)); } let Some(waiter) = condvar.waiters.pop_front() else { - return Ok(false); + return interp_ok(false); }; this.unblock_thread(waiter, BlockReason::Condvar(id))?; - Ok(true) + interp_ok(true) } /// Wait for the futex to be signaled, or a timeout. @@ -808,7 +808,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } // Write the return value. this.write_scalar(retval_succ, &dest)?; - Ok(()) + interp_ok(()) } @timeout = |this| { // Remove the waiter from the futex. @@ -818,7 +818,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Set errno and write return value. this.set_last_error(errno_timeout)?; this.write_scalar(retval_timeout, &dest)?; - Ok(()) + interp_ok(()) } ), ); @@ -828,7 +828,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn futex_wake(&mut self, addr: u64, bitset: u32) -> InterpResult<'tcx, bool> { let this = self.eval_context_mut(); let Some(futex) = this.machine.sync.futexes.get_mut(&addr) else { - return Ok(false); + return interp_ok(false); }; let data_race = &this.machine.data_race; @@ -839,10 +839,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Wake up the first thread in the queue that matches any of the bits in the bitset. let Some(i) = futex.waiters.iter().position(|w| w.bitset & bitset != 0) else { - return Ok(false); + return interp_ok(false); }; let waiter = futex.waiters.remove(i).unwrap(); this.unblock_thread(waiter.thread, BlockReason::Futex { addr })?; - Ok(true) + interp_ok(true) } } diff --git a/src/tools/miri/src/concurrency/thread.rs b/src/tools/miri/src/concurrency/thread.rs index f6b453efcbeb7..dcae85109a5bf 100644 --- a/src/tools/miri/src/concurrency/thread.rs +++ b/src/tools/miri/src/concurrency/thread.rs @@ -622,7 +622,7 @@ impl<'tcx> ThreadManager<'tcx> { } self.threads[id].join_status = ThreadJoinStatus::Detached; - Ok(()) + interp_ok(()) } /// Mark that the active thread tries to join the thread with `joined_thread_id`. @@ -657,7 +657,7 @@ impl<'tcx> ThreadManager<'tcx> { if let Some(data_race) = &mut this.machine.data_race { data_race.thread_joined(&this.machine.threads, joined_thread_id); } - Ok(()) + interp_ok(()) } ), ); @@ -667,7 +667,7 @@ impl<'tcx> ThreadManager<'tcx> { data_race.thread_joined(self, joined_thread_id); } } - Ok(()) + interp_ok(()) } /// Mark that the active thread tries to exclusively join the thread with `joined_thread_id`. @@ -754,7 +754,7 @@ impl<'tcx> ThreadManager<'tcx> { // This thread and the program can keep going. if self.threads[self.active_thread].state.is_enabled() && !self.yield_active_thread { // The currently active thread is still enabled, just continue with it. - return Ok(SchedulingAction::ExecuteStep); + return interp_ok(SchedulingAction::ExecuteStep); } // The active thread yielded or got terminated. Let's see if there are any timeouts to take // care of. We do this *before* running any other thread, to ensure that timeouts "in the @@ -764,7 +764,7 @@ impl<'tcx> ThreadManager<'tcx> { // let potential_sleep_time = self.next_callback_wait_time(clock); if potential_sleep_time == Some(Duration::ZERO) { - return Ok(SchedulingAction::ExecuteTimeoutCallback); + return interp_ok(SchedulingAction::ExecuteTimeoutCallback); } // No callbacks immediately scheduled, pick a regular thread to execute. // The active thread blocked or yielded. So we go search for another enabled thread. @@ -793,7 +793,7 @@ impl<'tcx> ThreadManager<'tcx> { } self.yield_active_thread = false; if self.threads[self.active_thread].state.is_enabled() { - return Ok(SchedulingAction::ExecuteStep); + return interp_ok(SchedulingAction::ExecuteStep); } // We have not found a thread to execute. if self.threads.iter().all(|thread| thread.state.is_terminated()) { @@ -802,7 +802,7 @@ impl<'tcx> ThreadManager<'tcx> { // All threads are currently blocked, but we have unexecuted // timeout_callbacks, which may unblock some of the threads. Hence, // sleep until the first callback. - Ok(SchedulingAction::Sleep(sleep_time)) + interp_ok(SchedulingAction::Sleep(sleep_time)) } else { throw_machine_stop!(TerminationInfo::Deadlock); } @@ -848,7 +848,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { // https://github.com/rust-lang/miri/issues/1763). In this case, // just do nothing, which effectively just returns to the // scheduler. - Ok(()) + interp_ok(()) } #[inline] @@ -861,7 +861,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { .expect("`on_stack_empty` not set up, or already running"); let res = callback(this)?; this.active_thread_mut().on_stack_empty = Some(callback); - Ok(res) + interp_ok(res) } } @@ -879,7 +879,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let Some(old_alloc) = this.machine.threads.get_thread_local_alloc_id(def_id) { // We already have a thread-specific allocation id for this // thread-local static. - Ok(old_alloc) + interp_ok(old_alloc) } else { // We need to allocate a thread-specific allocation id for this // thread-local static. @@ -892,7 +892,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let mut alloc = alloc.inner().adjust_from_tcx( &this.tcx, |bytes, align| { - Ok(MiriAllocBytes::from_bytes(std::borrow::Cow::Borrowed(bytes), align)) + interp_ok(MiriAllocBytes::from_bytes(std::borrow::Cow::Borrowed(bytes), align)) }, |ptr| this.global_root_pointer(ptr), )?; @@ -901,7 +901,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Create a fresh allocation with this content. let ptr = this.insert_allocation(alloc, MiriMemoryKind::Tls.into())?; this.machine.threads.set_thread_local_alloc(def_id, ptr); - Ok(ptr) + interp_ok(ptr) } } @@ -964,7 +964,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Restore the old active thread frame. this.machine.threads.set_active_thread_id(old_thread_id); - Ok(new_thread_id) + interp_ok(new_thread_id) } /// Handles thread termination of the active thread: wakes up threads joining on this one, @@ -1022,7 +1022,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.unblock_thread(thread, unblock_reason)?; } - Ok(()) + interp_ok(()) } /// Block the current thread, with an optional timeout. @@ -1078,7 +1078,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let old_thread = this.machine.threads.set_active_thread_id(thread); callback.unblock(this)?; this.machine.threads.set_active_thread_id(old_thread); - Ok(()) + interp_ok(()) } #[inline] @@ -1095,7 +1095,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn join_thread(&mut self, joined_thread_id: ThreadId) -> InterpResult<'tcx> { let this = self.eval_context_mut(); this.machine.threads.join_thread(joined_thread_id, this.machine.data_race.as_mut())?; - Ok(()) + interp_ok(()) } #[inline] @@ -1104,7 +1104,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.machine .threads .join_thread_exclusive(joined_thread_id, this.machine.data_race.as_mut())?; - Ok(()) + interp_ok(()) } #[inline] diff --git a/src/tools/miri/src/concurrency/weak_memory.rs b/src/tools/miri/src/concurrency/weak_memory.rs index 307695436c61e..800c301a82175 100644 --- a/src/tools/miri/src/concurrency/weak_memory.rs +++ b/src/tools/miri/src/concurrency/weak_memory.rs @@ -195,10 +195,10 @@ impl StoreBufferAlloc { AccessType::PerfectlyOverlapping(pos) => pos, // If there is nothing here yet, that means there wasn't an atomic write yet so // we can't return anything outdated. - _ => return Ok(None), + _ => return interp_ok(None), }; let store_buffer = Ref::map(self.store_buffers.borrow(), |buffer| &buffer[pos]); - Ok(Some(store_buffer)) + interp_ok(Some(store_buffer)) } /// Gets a mutable store buffer associated with an atomic object in this allocation, @@ -223,7 +223,7 @@ impl StoreBufferAlloc { pos_range.start } }; - Ok(&mut buffers[pos]) + interp_ok(&mut buffers[pos]) } } @@ -284,7 +284,7 @@ impl<'tcx> StoreBuffer { let (index, clocks) = global.active_thread_state(thread_mgr); let loaded = store_elem.load_impl(index, &clocks, is_seqcst); - Ok((loaded, recency)) + interp_ok((loaded, recency)) } fn buffered_write( @@ -297,7 +297,7 @@ impl<'tcx> StoreBuffer { let (index, clocks) = global.active_thread_state(thread_mgr); self.store_impl(val, index, &clocks.clock, is_seqcst); - Ok(()) + interp_ok(()) } #[allow(clippy::if_same_then_else, clippy::needless_bool)] @@ -470,7 +470,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { buffer.read_from_last_store(global, threads, atomic == AtomicRwOrd::SeqCst); buffer.buffered_write(new_val, global, threads, atomic == AtomicRwOrd::SeqCst)?; } - Ok(()) + interp_ok(()) } fn buffered_atomic_read( @@ -508,14 +508,14 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }); } - return Ok(loaded); + return interp_ok(loaded); } } } // Race detector or weak memory disabled, simply read the latest value validate()?; - Ok(Some(latest_in_mo)) + interp_ok(Some(latest_in_mo)) } /// Add the given write to the store buffer. (Does not change machine memory.) @@ -546,7 +546,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } // Caller should've written to dest with the vanilla scalar write, we do nothing here - Ok(()) + interp_ok(()) } /// Caller should never need to consult the store buffer for the latest value. @@ -570,7 +570,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { alloc_buffers.get_store_buffer(alloc_range(base_offset, size))? else { // No store buffer, nothing to do. - return Ok(()); + return interp_ok(()); }; buffer.read_from_last_store( global, @@ -579,6 +579,6 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ); } } - Ok(()) + interp_ok(()) } } diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs index ee087ece9d833..5b1bad28c07c7 100644 --- a/src/tools/miri/src/diagnostics.rs +++ b/src/tools/miri/src/diagnostics.rs @@ -223,10 +223,7 @@ pub fn report_error<'tcx>( let info = info.downcast_ref::().expect("invalid MachineStop payload"); use TerminationInfo::*; let title = match info { - &Exit { code, leak_check } => { - e.discard_interp_err(); - return Some((code, leak_check)); - } + &Exit { code, leak_check } => return Some((code, leak_check)), Abort(_) => Some("abnormal termination"), UnsupportedInIsolation(_) | Int2PtrWithStrictProvenance | UnsupportedForeignItem(_) => Some("unsupported operation"), @@ -378,7 +375,6 @@ pub fn report_error<'tcx>( InvalidProgramInfo::AlreadyReported(_) ) => { // This got already reported. No point in reporting it again. - e.discard_interp_err(); return None; } _ => diff --git a/src/tools/miri/src/eval.rs b/src/tools/miri/src/eval.rs index 3739d46bd60f0..ece76e581f248 100644 --- a/src/tools/miri/src/eval.rs +++ b/src/tools/miri/src/eval.rs @@ -259,7 +259,7 @@ impl<'tcx> MainThreadState<'tcx> { throw_machine_stop!(TerminationInfo::Exit { code: exit_code, leak_check: true }); } } - Ok(Poll::Pending) + interp_ok(Poll::Pending) } } @@ -420,7 +420,7 @@ pub fn create_ecx<'tcx>( } } - Ok(ecx) + interp_ok(ecx) } /// Evaluates the entry function specified by `entry_id`. @@ -436,7 +436,7 @@ pub fn eval_entry<'tcx>( // Copy setting before we move `config`. let ignore_leaks = config.ignore_leaks; - let mut ecx = match create_ecx(tcx, entry_id, entry_type, &config) { + let mut ecx = match create_ecx(tcx, entry_id, entry_type, &config).report_err() { Ok(v) => v, Err(err) => { let (kind, backtrace) = err.into_parts(); @@ -453,7 +453,7 @@ pub fn eval_entry<'tcx>( panic::resume_unwind(panic_payload) }); // `Ok` can never happen. - let Err(res) = res; + let Err(err) = res.report_err(); // Machine cleanup. Only do this if all threads have terminated; threads that are still running // might cause Stacked Borrows errors (https://github.com/rust-lang/miri/issues/2396). @@ -466,7 +466,7 @@ pub fn eval_entry<'tcx>( } // Process the result. - let (return_code, leak_check) = report_error(&ecx, res)?; + let (return_code, leak_check) = report_error(&ecx, err)?; if leak_check && !ignore_leaks { // Check for thread leaks. if !ecx.have_all_terminated() { diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index 0cd6ba69aac9d..70ebca0f32961 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -228,7 +228,7 @@ pub fn iter_exported_symbols<'tcx>( } } } - Ok(()) + interp_ok(()) } /// Convert a softfloat type to its corresponding hostfloat type. @@ -431,7 +431,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let field = this.project_field(dest, idx)?; this.write_int(val, &field)?; } - Ok(()) + interp_ok(()) } /// Write the given fields of the given place. @@ -445,7 +445,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let field = this.project_field_named(dest, name)?; this.write_int(val, &field)?; } - Ok(()) + interp_ok(()) } /// Write a 0 of the appropriate size to `dest`. @@ -455,7 +455,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { /// Test if this pointer equals 0. fn ptr_is_null(&self, ptr: Pointer) -> InterpResult<'tcx, bool> { - Ok(ptr.addr().bytes() == 0) + interp_ok(ptr.addr().bytes() == 0) } /// Generate some random bytes, and write them to `dest`. @@ -466,7 +466,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // any additional checks - it's okay if the pointer is invalid, // since we wouldn't actually be writing to it. if len == 0 { - return Ok(()); + return interp_ok(()); } let this = self.eval_context_mut(); @@ -571,7 +571,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } cur_addr += unsafe_cell_size; // Done - Ok(()) + interp_ok(()) }; // Run a visitor { @@ -589,7 +589,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if unsafe_cell_size != Size::ZERO { unsafe_cell_action(&place.ptr(), unsafe_cell_size) } else { - Ok(()) + interp_ok(()) } }, }; @@ -599,7 +599,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // So pretend there is a 0-sized `UnsafeCell` at the end. unsafe_cell_action(&place.ptr().wrapping_offset(size, this), Size::ZERO)?; // Done! - return Ok(()); + return interp_ok(()); /// Visiting the memory covered by a `MemPlace`, being aware of /// whether we are inside an `UnsafeCell` or not. @@ -642,7 +642,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { (self.unsafe_cell_action)(v) } else if self.ecx.type_is_freeze(v.layout.ty) { // This is `Freeze`, there cannot be an `UnsafeCell` - Ok(()) + interp_ok(()) } else if matches!(v.layout.fields, FieldsShape::Union(..)) { // A (non-frozen) union. We fall back to whatever the type says. (self.unsafe_cell_action)(v) @@ -691,7 +691,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if !self.eval_context_ref().machine.communicate() { self.reject_in_isolation(name, RejectOpWith::Abort)?; } - Ok(()) + interp_ok(()) } /// Helper function used inside the shims of foreign functions which reject the op @@ -713,13 +713,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { .dcx() .warn(format!("{op_name} was made to return an error due to isolation")); } - Ok(()) + interp_ok(()) } RejectOpWith::Warning => { this.emit_diagnostic(NonHaltingDiagnostic::RejectedIsolatedOp(op_name.to_string())); - Ok(()) + interp_ok(()) } - RejectOpWith::NoWarning => Ok(()), // no warning + RejectOpWith::NoWarning => interp_ok(()), // no warning } } @@ -750,14 +750,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn last_error_place(&mut self) -> InterpResult<'tcx, MPlaceTy<'tcx>> { let this = self.eval_context_mut(); if let Some(errno_place) = this.active_thread_ref().last_error.as_ref() { - Ok(errno_place.clone()) + interp_ok(errno_place.clone()) } else { // Allocate new place, set initial value to 0. let errno_layout = this.machine.layouts.u32; let errno_place = this.allocate(errno_layout, MiriMemoryKind::Machine.into())?; this.write_scalar(Scalar::from_u32(0), &errno_place)?; this.active_thread_mut().last_error = Some(errno_place.clone()); - Ok(errno_place) + interp_ok(errno_place) } } @@ -783,14 +783,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if target.families.iter().any(|f| f == "unix") { for &(name, kind) in UNIX_IO_ERROR_TABLE { if err.kind() == kind { - return Ok(this.eval_libc(name)); + return interp_ok(this.eval_libc(name)); } } throw_unsup_format!("unsupported io error: {err}") } else if target.families.iter().any(|f| f == "windows") { for &(name, kind) in WINDOWS_IO_ERROR_TABLE { if err.kind() == kind { - return Ok(this.eval_windows("c", name)); + return interp_ok(this.eval_windows("c", name)); } } throw_unsup_format!("unsupported io error: {err}"); @@ -814,18 +814,18 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let errnum = errnum.to_i32()?; for &(name, kind) in UNIX_IO_ERROR_TABLE { if errnum == this.eval_libc_i32(name) { - return Ok(Some(kind)); + return interp_ok(Some(kind)); } } - return Ok(None); + return interp_ok(None); } else if target.families.iter().any(|f| f == "windows") { let errnum = errnum.to_u32()?; for &(name, kind) in WINDOWS_IO_ERROR_TABLE { if errnum == this.eval_windows("c", name).to_u32()? { - return Ok(Some(kind)); + return interp_ok(Some(kind)); } } - return Ok(None); + return interp_ok(None); } else { throw_unsup_format!( "converting errnum into io::Error is unsupported for OS {}", @@ -839,8 +839,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { self.set_last_error(self.io_error_to_errnum(err)?) } - /// Helper function that consumes an `std::io::Result` and returns an - /// `InterpResult<'tcx,T>::Ok` instead. In case the result is an error, this function returns + /// Helper function that consumes a `std::io::Result` and returns a + /// `InterpResult<'tcx, T>` instead. In case the result is an error, this function returns /// `Ok(-1)` and sets the last OS error accordingly. /// /// This function uses `T: From` instead of `i32` directly because some IO related @@ -850,10 +850,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { result: std::io::Result, ) -> InterpResult<'tcx, T> { match result { - Ok(ok) => Ok(ok), + Ok(ok) => interp_ok(ok), Err(e) => { self.eval_context_mut().set_last_error_from_io_error(e)?; - Ok((-1).into()) + interp_ok((-1).into()) } } } @@ -866,7 +866,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ) -> InterpResult<'tcx, MPlaceTy<'tcx>> { let this = self.eval_context_ref(); let ptr = this.read_pointer(op)?; - Ok(this.ptr_to_mplace(ptr, layout)) + interp_ok(this.ptr_to_mplace(ptr, layout)) } /// Calculates the MPlaceTy given the offset and layout of an access on an operand @@ -884,7 +884,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Ensure that the access is within bounds. assert!(base_layout.size >= offset + value_layout.size); let value_place = op_place.offset(offset, value_layout, this)?; - Ok(value_place) + interp_ok(value_place) } fn deref_pointer_and_read( @@ -924,7 +924,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let nanoseconds_scalar = this.read_scalar(&nanoseconds_place)?; let nanoseconds = nanoseconds_scalar.to_target_isize(this)?; - Ok(try { + interp_ok(try { // tv_sec must be non-negative. let seconds: u64 = seconds.try_into().ok()?; // tv_nsec must be non-negative. @@ -947,7 +947,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let ptr = ptr.to_pointer(this)?; let len = len.to_target_usize(this)?; let bytes = this.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?; - Ok(bytes) + interp_ok(bytes) } /// Read a sequence of bytes until the first null terminator. @@ -992,11 +992,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let string_length = u64::try_from(c_str.len()).unwrap(); let string_length = string_length.strict_add(1); if size < string_length { - return Ok((false, string_length)); + return interp_ok((false, string_length)); } self.eval_context_mut() .write_bytes_ptr(ptr, c_str.iter().copied().chain(iter::once(0u8)))?; - Ok((true, string_length)) + interp_ok((true, string_length)) } /// Helper function to read a sequence of unsigned integers of the given size and alignment @@ -1031,7 +1031,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } } - Ok(wchars) + interp_ok(wchars) } /// Read a sequence of u16 until the first null terminator. @@ -1056,7 +1056,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let string_length = u64::try_from(wide_str.len()).unwrap(); let string_length = string_length.strict_add(1); if size < string_length { - return Ok((false, string_length)); + return interp_ok((false, string_length)); } // Store the UTF-16 string. @@ -1068,7 +1068,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let offset = u64::try_from(offset).unwrap(); alloc.write_scalar(alloc_range(size2 * offset, size2), Scalar::from_u16(wchar))?; } - Ok((true, string_length)) + interp_ok((true, string_length)) } /// Read a sequence of wchar_t until the first null terminator. @@ -1093,7 +1093,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { abi.name() ) } - Ok(()) + interp_ok(()) } fn frame_in_std(&self) -> bool { @@ -1128,7 +1128,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // message is slightly different here to make automated analysis easier let error_msg = format!("unsupported Miri functionality: {error_msg}"); this.start_panic(error_msg.as_ref(), mir::UnwindAction::Continue)?; - Ok(()) + interp_ok(()) } else { throw_machine_stop!(TerminationInfo::UnsupportedForeignItem(error_msg)); } @@ -1148,7 +1148,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // compiler-builtins when running other crates, but Miri can still be run on // compiler-builtins itself (or any crate that uses it as a normal dependency) if self.eval_context_ref().tcx.is_compiler_builtins(instance.def_id().krate) { - return Ok(()); + return interp_ok(()); } throw_machine_stop!(TerminationInfo::SymbolShimClashing { @@ -1156,7 +1156,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { span: body.span.data(), }) } - Ok(()) + interp_ok(()) } fn check_shim<'a, const N: usize>( @@ -1242,11 +1242,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ) { // Floating point value is NaN (flagged with INVALID_OP) or outside the range // of values of the integer type (flagged with OVERFLOW or UNDERFLOW). - Ok(None) + interp_ok(None) } else { // Floating point value can be represented by the integer type after rounding. // The INEXACT flag is ignored on purpose to allow rounding. - Ok(Some(ImmTy::from_scalar(val, cast_to))) + interp_ok(Some(ImmTy::from_scalar(val, cast_to))) } } @@ -1283,7 +1283,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "attempted to call intrinsic `{intrinsic}` that requires missing target feature {target_feature}" ); } - Ok(()) + interp_ok(()) } /// Lookup an array of immediates stored as a linker section of name `name`. @@ -1296,7 +1296,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { iter_exported_symbols(tcx, |_cnum, def_id| { let attrs = tcx.codegen_fn_attrs(def_id); let Some(link_section) = attrs.link_section else { - return Ok(()); + return interp_ok(()); }; if link_section.as_str() == name { let instance = ty::Instance::mono(tcx, def_id); @@ -1308,10 +1308,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let val = this.read_immediate(&const_val)?; array.push(val); } - Ok(()) + interp_ok(()) })?; - Ok(array) + interp_ok(array) } } @@ -1361,7 +1361,7 @@ where &'a [OpTy<'tcx>; N]: TryFrom<&'a [OpTy<'tcx>]>, { if let Ok(ops) = args.try_into() { - return Ok(ops); + return interp_ok(ops); } throw_ub_format!("incorrect number of arguments: got {}, expected {}", args.len(), N) } @@ -1401,7 +1401,7 @@ pub(crate) fn bool_to_simd_element(b: bool, size: Size) -> Scalar { pub(crate) fn simd_element_to_bool(elem: ImmTy<'_>) -> InterpResult<'_, bool> { let val = elem.to_scalar().to_int(elem.layout.size)?; - Ok(match val { + interp_ok(match val { 0 => false, -1 => true, _ => throw_ub_format!("each element of a SIMD mask must be all-0-bits or all-1-bits"), diff --git a/src/tools/miri/src/intrinsics/atomic.rs b/src/tools/miri/src/intrinsics/atomic.rs index b6fd9ba1e825c..8507b0f49ded8 100644 --- a/src/tools/miri/src/intrinsics/atomic.rs +++ b/src/tools/miri/src/intrinsics/atomic.rs @@ -115,9 +115,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.atomic_rmw_op(args, dest, AtomicOp::Max, rw_ord(ord))?; } - _ => return Ok(EmulateItemResult::NotSupported), + _ => return interp_ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsReturn) + interp_ok(EmulateItemResult::NeedsReturn) } } @@ -138,7 +138,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { let val = this.read_scalar_atomic(&place, atomic)?; // Perform regular store. this.write_scalar(val, dest)?; - Ok(()) + interp_ok(()) } fn atomic_store(&mut self, args: &[OpTy<'tcx>], atomic: AtomicWriteOrd) -> InterpResult<'tcx> { @@ -151,7 +151,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { let val = this.read_scalar(val)?; // Perform atomic store this.write_scalar_atomic(val, &place, atomic)?; - Ok(()) + interp_ok(()) } fn compiler_fence_intrinsic( @@ -162,7 +162,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { let [] = check_arg_count(args)?; let _ = atomic; //FIXME: compiler fences are currently ignored - Ok(()) + interp_ok(()) } fn atomic_fence_intrinsic( @@ -173,7 +173,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { let this = self.eval_context_mut(); let [] = check_arg_count(args)?; this.atomic_fence(atomic)?; - Ok(()) + interp_ok(()) } fn atomic_rmw_op( @@ -203,17 +203,17 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { AtomicOp::Min => { let old = this.atomic_min_max_scalar(&place, rhs, true, atomic)?; this.write_immediate(*old, dest)?; // old value is returned - Ok(()) + interp_ok(()) } AtomicOp::Max => { let old = this.atomic_min_max_scalar(&place, rhs, false, atomic)?; this.write_immediate(*old, dest)?; // old value is returned - Ok(()) + interp_ok(()) } AtomicOp::MirOp(op, not) => { let old = this.atomic_rmw_op_immediate(&place, &rhs, op, not, atomic)?; this.write_immediate(*old, dest)?; // old value is returned - Ok(()) + interp_ok(()) } } } @@ -232,7 +232,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { let old = this.atomic_exchange_scalar(&place, new, atomic)?; this.write_scalar(old, dest)?; // old value is returned - Ok(()) + interp_ok(()) } fn atomic_compare_exchange_impl( @@ -261,7 +261,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { // Return old value. this.write_immediate(old, dest)?; - Ok(()) + interp_ok(()) } fn atomic_compare_exchange( diff --git a/src/tools/miri/src/intrinsics/mod.rs b/src/tools/miri/src/intrinsics/mod.rs index 23ce2f8ed1f91..665dd7c441a55 100644 --- a/src/tools/miri/src/intrinsics/mod.rs +++ b/src/tools/miri/src/intrinsics/mod.rs @@ -29,7 +29,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // See if the core engine can handle this intrinsic. if this.eval_intrinsic(instance, args, dest, ret)? { - return Ok(None); + return interp_ok(None); } let intrinsic_name = this.tcx.item_name(instance.def_id()); let intrinsic_name = intrinsic_name.as_str(); @@ -51,7 +51,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "Miri can only use intrinsic fallback bodies that exactly reflect the specification: they fully check for UB and are as non-deterministic as possible. After verifying that `{intrinsic_name}` does so, add the `#[miri::intrinsic_fallback_is_spec]` attribute to it; also ping @rust-lang/miri when you do that" ); } - Ok(Some(ty::Instance { + interp_ok(Some(ty::Instance { def: ty::InstanceKind::Item(instance.def_id()), args: instance.args, })) @@ -59,14 +59,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { EmulateItemResult::NeedsReturn => { trace!("{:?}", this.dump_place(&dest.clone().into())); this.return_to_block(ret)?; - Ok(None) + interp_ok(None) } EmulateItemResult::NeedsUnwind => { // Jump to the unwind block to begin unwinding. this.unwind_to_block(unwind)?; - Ok(None) + interp_ok(None) } - EmulateItemResult::AlreadyJumped => Ok(None), + EmulateItemResult::AlreadyJumped => interp_ok(None), } } @@ -99,7 +99,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "catch_unwind" => { this.handle_catch_unwind(args, dest, ret)?; // This pushed a stack frame, don't jump to `ret`. - return Ok(EmulateItemResult::AlreadyJumped); + return interp_ok(EmulateItemResult::AlreadyJumped); } // Raw memory accesses @@ -378,7 +378,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let ty::Float(fty) = x.layout.ty.kind() else { bug!("float_finite: non-float input type {}", x.layout.ty) }; - Ok(match fty { + interp_ok(match fty { FloatTy::F16 => x.to_scalar().to_f16()?.is_finite(), FloatTy::F32 => x.to_scalar().to_f32()?.is_finite(), FloatTy::F64 => x.to_scalar().to_f64()?.is_finite(), @@ -429,9 +429,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { throw_machine_stop!(TerminationInfo::Abort(format!("trace/breakpoint trap"))) } - _ => return Ok(EmulateItemResult::NotSupported), + _ => return interp_ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsReturn) + interp_ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/intrinsics/simd.rs b/src/tools/miri/src/intrinsics/simd.rs index 7f43c61cd66d8..de293495e8618 100644 --- a/src/tools/miri/src/intrinsics/simd.rs +++ b/src/tools/miri/src/intrinsics/simd.rs @@ -248,7 +248,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let val = this.binary_op(mir_op, &left, &right).map_err(|err| { match err.kind() { &InterpError::UndefinedBehavior(UndefinedBehaviorInfo::ShiftOverflow { shift_amount, .. }) => { - err.discard_interp_err(); // This resets the interpreter backtrace, but it's not worth avoiding that. let shift_amount = match shift_amount { Either::Left(v) => v.to_string(), @@ -787,9 +786,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } } - _ => return Ok(EmulateItemResult::NotSupported), + _ => return interp_ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsReturn) + interp_ok(EmulateItemResult::NeedsReturn) } fn fminmax_op( @@ -805,7 +804,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }; let left = left.to_scalar(); let right = right.to_scalar(); - Ok(match float_ty { + interp_ok(match float_ty { FloatTy::F16 => unimplemented!("f16_f128"), FloatTy::F32 => { let left = left.to_f32()?; diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index fb5ccbcd4fe22..b9cebcfe9cd82 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -730,7 +730,7 @@ impl<'tcx> MiriMachine<'tcx> { EnvVars::init(this, config)?; MiriMachine::init_extern_statics(this)?; ThreadManager::init(this, on_main_stack_empty); - Ok(()) + interp_ok(()) } pub(crate) fn add_extern_static(this: &mut MiriInterpCx<'tcx>, name: &str, ptr: Pointer) { @@ -992,7 +992,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { throw_ub_format!("{msg}"); } } - Ok(()) + interp_ok(()) } #[inline(always)] @@ -1019,7 +1019,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { } // Otherwise, load the MIR. - Ok(Some((ecx.load_mir(instance.def, None)?, instance))) + interp_ok(Some((ecx.load_mir(instance.def, None)?, instance))) } #[inline(always)] @@ -1072,7 +1072,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { ret: None, unwind: mir::UnwindAction::Unreachable, })?; - Ok(()) + interp_ok(()) } #[inline(always)] @@ -1097,7 +1097,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { } fn ub_checks(ecx: &InterpCx<'tcx, Self>) -> InterpResult<'tcx, bool> { - Ok(ecx.tcx.sess.ub_checks()) + interp_ok(ecx.tcx.sess.ub_checks()) } fn thread_local_static_pointer( @@ -1136,7 +1136,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { shim_align = shim_align.bytes(), ) } - Ok(ptr) + interp_ok(ptr) } else { throw_unsup_format!("extern static `{link_name}` is not supported by Miri",) } @@ -1186,7 +1186,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { .insert(id, (ecx.machine.current_span(), None)); } - Ok(AllocExtra { borrow_tracker, data_race, weak_memory, backtrace }) + interp_ok(AllocExtra { borrow_tracker, data_race, weak_memory, backtrace }) } fn adjust_alloc_root_pointer( @@ -1233,7 +1233,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { Provenance::Wildcard => { // No need to do anything for wildcard pointers as // their provenances have already been previously exposed. - Ok(()) + interp_ok(()) } } } @@ -1286,7 +1286,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { |ptr| ecx.global_root_pointer(ptr), )?; let extra = Self::init_alloc_extra(ecx, id, kind, alloc.size(), alloc.align)?; - Ok(Cow::Owned(alloc.with_extra(extra))) + interp_ok(Cow::Owned(alloc.with_extra(extra))) } #[inline(always)] @@ -1310,7 +1310,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { if let Some(weak_memory) = &alloc_extra.weak_memory { weak_memory.memory_accessed(range, machine.data_race.as_ref().unwrap()); } - Ok(()) + interp_ok(()) } #[inline(always)] @@ -1334,7 +1334,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { if let Some(weak_memory) = &alloc_extra.weak_memory { weak_memory.memory_accessed(range, machine.data_race.as_ref().unwrap()); } - Ok(()) + interp_ok(()) } #[inline(always)] @@ -1367,7 +1367,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { *deallocated_at = Some(machine.current_span()); } machine.free_alloc_id(alloc_id, size, align, kind); - Ok(()) + interp_ok(()) } #[inline(always)] @@ -1379,7 +1379,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { if ecx.machine.borrow_tracker.is_some() { ecx.retag_ptr_value(kind, val) } else { - Ok(val.clone()) + interp_ok(val.clone()) } } @@ -1392,7 +1392,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { if ecx.machine.borrow_tracker.is_some() { ecx.retag_place_contents(kind, place)?; } - Ok(()) + interp_ok(()) } fn protect_in_place_function_argument( @@ -1413,7 +1413,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { // Conveniently this also ensures that the place actually points to suitable memory. ecx.write_uninit(&protected_place)?; // Now we throw away the protected place, ensuring its tag is never used again. - Ok(()) + interp_ok(()) } #[inline(always)] @@ -1447,7 +1447,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { data_race: ecx.machine.data_race.as_ref().map(|_| data_race::FrameState::default()), }; - Ok(frame.with_extra(extra)) + interp_ok(frame.with_extra(extra)) } fn stack<'a>( @@ -1489,7 +1489,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { // Make sure some time passes. ecx.machine.clock.tick(); - Ok(()) + interp_ok(()) } #[inline(always)] @@ -1500,7 +1500,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { let stack_len = ecx.active_thread_stack().len(); ecx.active_thread_mut().set_top_user_relevant_frame(stack_len - 1); } - Ok(()) + interp_ok(()) } fn before_stack_pop( @@ -1516,7 +1516,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { // concurrency and what it prints is just plain wrong. So we print our own information // instead. (Cc https://github.com/rust-lang/miri/issues/2266) info!("Leaving {}", ecx.frame().instance()); - Ok(()) + interp_ok(()) } #[inline(always)] @@ -1554,7 +1554,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { if let Some(data_race) = &ecx.frame().extra.data_race { data_race.local_read(local, &ecx.machine); } - Ok(()) + interp_ok(()) } fn after_local_write( @@ -1565,7 +1565,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { if let Some(data_race) = &ecx.frame().extra.data_race { data_race.local_write(local, storage_live, &ecx.machine); } - Ok(()) + interp_ok(()) } fn after_local_moved_to_memory( @@ -1587,7 +1587,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { { data_race.local_moved_to_memory(local, alloc_info.data_race.as_mut().unwrap(), machine); } - Ok(()) + interp_ok(()) } fn eval_mir_constant( @@ -1611,9 +1611,9 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { Entry::Vacant(ve) => { let op = eval(ecx, val, span, layout)?; ve.insert(op.clone()); - Ok(op) + interp_ok(op) } - Entry::Occupied(oe) => Ok(oe.get().clone()), + Entry::Occupied(oe) => interp_ok(oe.get().clone()), } } diff --git a/src/tools/miri/src/operator.rs b/src/tools/miri/src/operator.rs index 6b8c0fe87adb3..c0911fa717f50 100644 --- a/src/tools/miri/src/operator.rs +++ b/src/tools/miri/src/operator.rs @@ -21,7 +21,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let this = self.eval_context_ref(); trace!("ptr_op: {:?} {:?} {:?}", *left, bin_op, *right); - Ok(match bin_op { + interp_ok(match bin_op { Eq | Ne | Lt | Le | Gt | Ge => { assert_eq!(left.layout.abi, right.layout.abi); // types can differ, e.g. fn ptrs with different `for` let size = this.pointer_size(); diff --git a/src/tools/miri/src/shims/alloc.rs b/src/tools/miri/src/shims/alloc.rs index 057d7ef5e67b9..e73344367ec3f 100644 --- a/src/tools/miri/src/shims/alloc.rs +++ b/src/tools/miri/src/shims/alloc.rs @@ -61,7 +61,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let Some(allocator_kind) = this.tcx.allocator_kind(()) else { // in real code, this symbol does not exist without an allocator - return Ok(EmulateItemResult::NotSupported); + return interp_ok(EmulateItemResult::NotSupported); }; match allocator_kind { @@ -71,11 +71,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // and not execute any Miri shim. Somewhat unintuitively doing so is done // by returning `NotSupported`, which triggers the `lookup_exported_symbol` // fallback case in `emulate_foreign_item`. - Ok(EmulateItemResult::NotSupported) + interp_ok(EmulateItemResult::NotSupported) } AllocatorKind::Default => { default(this)?; - Ok(EmulateItemResult::NeedsReturn) + interp_ok(EmulateItemResult::NeedsReturn) } } } @@ -92,7 +92,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ) .unwrap(); } - Ok(ptr.into()) + interp_ok(ptr.into()) } fn posix_memalign( @@ -109,7 +109,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Align must be power of 2, and also at least ptr-sized (POSIX rules). // But failure to adhere to this is not UB, it's an error condition. if !align.is_power_of_two() || align < this.pointer_size().bytes() { - Ok(this.eval_libc("EINVAL")) + interp_ok(this.eval_libc("EINVAL")) } else { let ptr = this.allocate_ptr( Size::from_bytes(size), @@ -117,7 +117,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { MiriMemoryKind::C.into(), )?; this.write_pointer(ptr, &memptr)?; - Ok(Scalar::from_i32(0)) + interp_ok(Scalar::from_i32(0)) } } @@ -126,7 +126,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if !this.ptr_is_null(ptr)? { this.deallocate_ptr(ptr, None, MiriMemoryKind::C.into())?; } - Ok(()) + interp_ok(()) } fn realloc(&mut self, old_ptr: Pointer, new_size: u64) -> InterpResult<'tcx, Pointer> { @@ -148,7 +148,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { new_align, MiriMemoryKind::C.into(), )?; - Ok(new_ptr.into()) + interp_ok(new_ptr.into()) } } } @@ -188,9 +188,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Align::from_bytes(align).unwrap(), MiriMemoryKind::C.into(), )?; - Ok(ptr.into()) + interp_ok(ptr.into()) } - _ => Ok(Pointer::null()), + _ => interp_ok(Pointer::null()), } } } diff --git a/src/tools/miri/src/shims/backtrace.rs b/src/tools/miri/src/shims/backtrace.rs index 1d20189b6fd8f..25afda4edc853 100644 --- a/src/tools/miri/src/shims/backtrace.rs +++ b/src/tools/miri/src/shims/backtrace.rs @@ -107,7 +107,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { _ => throw_unsup_format!("unknown `miri_get_backtrace` flags {}", flags), }; - Ok(()) + interp_ok(()) } fn resolve_frame_pointer( @@ -135,7 +135,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let name = fn_instance.to_string(); let filename = lo.file.name.prefer_remapped_unconditionaly().to_string(); - Ok((fn_instance, lo, name, filename)) + interp_ok((fn_instance, lo, name, filename)) } fn handle_miri_resolve_frame( @@ -213,7 +213,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_pointer(fn_ptr, &this.project_field(dest, 4)?)?; } - Ok(()) + interp_ok(()) } fn handle_miri_resolve_frame_names( @@ -237,6 +237,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_bytes_ptr(this.read_pointer(name_ptr)?, name.bytes())?; this.write_bytes_ptr(this.read_pointer(filename_ptr)?, filename.bytes())?; - Ok(()) + interp_ok(()) } } diff --git a/src/tools/miri/src/shims/env.rs b/src/tools/miri/src/shims/env.rs index 557d648682c7c..e99a8fd6e8c09 100644 --- a/src/tools/miri/src/shims/env.rs +++ b/src/tools/miri/src/shims/env.rs @@ -56,15 +56,15 @@ impl<'tcx> EnvVars<'tcx> { }; ecx.machine.env_vars = env_vars; - Ok(()) + interp_ok(()) } pub(crate) fn cleanup(ecx: &mut InterpCx<'tcx, MiriMachine<'tcx>>) -> InterpResult<'tcx> { let this = ecx.eval_context_mut(); match this.machine.env_vars { EnvVars::Unix(_) => UnixEnvVars::cleanup(this), - EnvVars::Windows(_) => Ok(()), // no cleanup needed - EnvVars::Uninit => Ok(()), + EnvVars::Windows(_) => interp_ok(()), // no cleanup needed + EnvVars::Uninit => interp_ok(()), } } @@ -104,7 +104,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn get_env_var(&mut self, name: &OsStr) -> InterpResult<'tcx, Option> { let this = self.eval_context_ref(); match &this.machine.env_vars { - EnvVars::Uninit => Ok(None), + EnvVars::Uninit => interp_ok(None), EnvVars::Unix(vars) => vars.get(this, name), EnvVars::Windows(vars) => vars.get(name), } diff --git a/src/tools/miri/src/shims/extern_static.rs b/src/tools/miri/src/shims/extern_static.rs index 788de8162cb70..5559ea2750b3b 100644 --- a/src/tools/miri/src/shims/extern_static.rs +++ b/src/tools/miri/src/shims/extern_static.rs @@ -11,7 +11,7 @@ impl<'tcx> MiriMachine<'tcx> { let place = this.allocate(val.layout, MiriMemoryKind::ExternStatic.into())?; this.write_immediate(*val, &place)?; Self::add_extern_static(this, name, place.ptr()); - Ok(()) + interp_ok(()) } /// Zero-initialized pointer-sized extern statics are pretty common. @@ -26,7 +26,7 @@ impl<'tcx> MiriMachine<'tcx> { let val = ImmTy::from_int(0, this.machine.layouts.usize); Self::alloc_extern_static(this, name, val)?; } - Ok(()) + interp_ok(()) } /// Extern statics that are initialized with function pointers to the symbols of the same name. @@ -41,7 +41,7 @@ impl<'tcx> MiriMachine<'tcx> { let val = ImmTy::from_scalar(Scalar::from_pointer(ptr, this), layout); Self::alloc_extern_static(this, name, val)?; } - Ok(()) + interp_ok(()) } /// Sets up the "extern statics" for this machine. @@ -87,6 +87,6 @@ impl<'tcx> MiriMachine<'tcx> { } _ => {} // No "extern statics" supported on this target } - Ok(()) + interp_ok(()) } } diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index 6a835098e6048..78b07f68b44f6 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -62,7 +62,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let handler = this .lookup_exported_symbol(Symbol::intern(name))? .expect("missing alloc error handler symbol"); - return Ok(Some(handler)); + return interp_ok(Some(handler)); } _ => {} } @@ -80,18 +80,18 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { EmulateItemResult::AlreadyJumped => (), EmulateItemResult::NotSupported => { if let Some(body) = this.lookup_exported_symbol(link_name)? { - return Ok(Some(body)); + return interp_ok(Some(body)); } this.handle_unsupported_foreign_item(format!( "can't call foreign function `{link_name}` on OS `{os}`", os = this.tcx.sess.target.os, ))?; - return Ok(None); + return interp_ok(None); } } - Ok(None) + interp_ok(None) } fn is_dyn_sym(&self, name: &str) -> bool { @@ -116,7 +116,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ) -> InterpResult<'tcx> { let res = self.emulate_foreign_item(sym.0, abi, args, dest, ret, unwind)?; assert!(res.is_none(), "DynSyms that delegate are not supported"); - Ok(()) + interp_ok(()) } /// Lookup the body of a function that has `link_name` as the symbol name. @@ -143,7 +143,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { tcx.item_name(def_id) } else { // Skip over items without an explicitly defined symbol name. - return Ok(()); + return interp_ok(()); }; if symbol_name == link_name { if let Some((original_instance, original_cnum)) = instance_and_crate { @@ -175,15 +175,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } instance_and_crate = Some((ty::Instance::mono(tcx, def_id), cnum)); } - Ok(()) + interp_ok(()) })?; e.insert(instance_and_crate.map(|ic| ic.0)) } }; match instance { - None => Ok(None), // no symbol with this name - Some(instance) => Ok(Some((this.load_mir(instance.def, None)?, instance))), + None => interp_ok(None), // no symbol with this name + Some(instance) => interp_ok(Some((this.load_mir(instance.def, None)?, instance))), } } } @@ -214,7 +214,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { } } - Ok(()) + interp_ok(()) } fn emulate_foreign_item_inner( @@ -234,7 +234,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // by the specified `.so` file; we should continue and check if it corresponds to // a provided shim. if this.call_native_fn(link_name, dest, args)? { - return Ok(EmulateItemResult::NeedsReturn); + return interp_ok(EmulateItemResult::NeedsReturn); } } @@ -268,7 +268,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // // // ... // - // Ok(Scalar::from_u32(42)) + // interp_ok(Scalar::from_u32(42)) // } // ``` // You might find existing shims not following this pattern, most @@ -281,7 +281,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { "miri_start_unwind" => { let [payload] = this.check_shim(abi, Abi::Rust, link_name, args)?; this.handle_miri_start_unwind(payload)?; - return Ok(EmulateItemResult::NeedsUnwind); + return interp_ok(EmulateItemResult::NeedsUnwind); } "miri_run_provenance_gc" => { let [] = this.check_shim(abi, Abi::Rust, link_name, args)?; @@ -294,6 +294,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { err_machine_stop!(TerminationInfo::Abort(format!( "pointer passed to `miri_get_alloc_id` must not be dangling, got {ptr:?}" ))) + .into() })?; this.write_scalar(Scalar::from_u64(alloc_id.0.get()), dest)?; } @@ -524,7 +525,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { "__rust_alloc" => return this.emulate_allocator(default), "miri_alloc" => { default(this)?; - return Ok(EmulateItemResult::NeedsReturn); + return interp_ok(EmulateItemResult::NeedsReturn); } _ => unreachable!(), } @@ -584,7 +585,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { } "miri_dealloc" => { default(this)?; - return Ok(EmulateItemResult::NeedsReturn); + return interp_ok(EmulateItemResult::NeedsReturn); } _ => unreachable!(), } @@ -1000,11 +1001,11 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { shims::windows::foreign_items::EvalContextExt::emulate_foreign_item_inner( this, link_name, abi, args, dest, ), - _ => Ok(EmulateItemResult::NotSupported), + _ => interp_ok(EmulateItemResult::NotSupported), }, }; // We only fall through to here if we did *not* hit the `_` arm above, // i.e., if we actually emulated the function with one of the shims. - Ok(EmulateItemResult::NeedsReturn) + interp_ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/native_lib.rs b/src/tools/miri/src/shims/native_lib.rs index 2b5acee244cba..3f282017bb79d 100644 --- a/src/tools/miri/src/shims/native_lib.rs +++ b/src/tools/miri/src/shims/native_lib.rs @@ -73,11 +73,11 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // have the output_type `Tuple([])`. ty::Tuple(t_list) if t_list.len() == 0 => { unsafe { ffi::call::<()>(ptr, libffi_args.as_slice()) }; - return Ok(ImmTy::uninit(dest.layout)); + return interp_ok(ImmTy::uninit(dest.layout)); } _ => throw_unsup_format!("unsupported return type for native call: {:?}", link_name), }; - Ok(ImmTy::from_scalar(scalar, dest.layout)) + interp_ok(ImmTy::from_scalar(scalar, dest.layout)) } /// Get the pointer to the function of the specified name in the shared object file, @@ -142,7 +142,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Some(ptr) => ptr, None => { // Shared object file does not export this function -- try the shims next. - return Ok(false); + return interp_ok(false); } }; @@ -164,7 +164,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Call the function and store output, depending on return type in the function signature. let ret = this.call_native_with_args(link_name, dest, code_ptr, libffi_args)?; this.write_immediate(*ret, dest)?; - Ok(true) + interp_ok(true) } } @@ -221,7 +221,7 @@ impl<'a> CArg { /// Extract the scalar value from the result of reading a scalar from the machine, /// and convert it to a `CArg`. fn imm_to_carg<'tcx>(v: ImmTy<'tcx>, cx: &impl HasDataLayout) -> InterpResult<'tcx, CArg> { - Ok(match v.layout.ty.kind() { + interp_ok(match v.layout.ty.kind() { // If the primitive provided can be converted to a type matching the type pattern // then create a `CArg` of this primitive value with the corresponding `CArg` constructor. // the ints diff --git a/src/tools/miri/src/shims/os_str.rs b/src/tools/miri/src/shims/os_str.rs index 17c451059208f..7080edb26a5ff 100644 --- a/src/tools/miri/src/shims/os_str.rs +++ b/src/tools/miri/src/shims/os_str.rs @@ -19,14 +19,14 @@ pub enum PathConversion { #[cfg(unix)] pub fn bytes_to_os_str<'tcx>(bytes: &[u8]) -> InterpResult<'tcx, &OsStr> { - Ok(OsStr::from_bytes(bytes)) + interp_ok(OsStr::from_bytes(bytes)) } #[cfg(not(unix))] pub fn bytes_to_os_str<'tcx>(bytes: &[u8]) -> InterpResult<'tcx, &OsStr> { // We cannot use `from_encoded_bytes_unchecked` here since we can't trust `bytes`. let s = std::str::from_utf8(bytes) .map_err(|_| err_unsup_format!("{:?} is not a valid utf-8 string", bytes))?; - Ok(OsStr::new(s)) + interp_ok(OsStr::new(s)) } impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} @@ -50,13 +50,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { { #[cfg(windows)] pub fn u16vec_to_osstring<'tcx>(u16_vec: Vec) -> InterpResult<'tcx, OsString> { - Ok(OsString::from_wide(&u16_vec[..])) + interp_ok(OsString::from_wide(&u16_vec[..])) } #[cfg(not(windows))] pub fn u16vec_to_osstring<'tcx>(u16_vec: Vec) -> InterpResult<'tcx, OsString> { let s = String::from_utf16(&u16_vec[..]) .map_err(|_| err_unsup_format!("{:?} is not a valid utf-16 string", u16_vec))?; - Ok(s.into()) + interp_ok(s.into()) } let u16_vec = self.eval_context_ref().read_wide_str(ptr)?; @@ -87,7 +87,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ) -> InterpResult<'tcx, (bool, u64)> { #[cfg(windows)] fn os_str_to_u16vec<'tcx>(os_str: &OsStr) -> InterpResult<'tcx, Vec> { - Ok(os_str.encode_wide().collect()) + interp_ok(os_str.encode_wide().collect()) } #[cfg(not(windows))] fn os_str_to_u16vec<'tcx>(os_str: &OsStr) -> InterpResult<'tcx, Vec> { @@ -97,7 +97,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { os_str .to_str() .map(|s| s.encode_utf16().collect()) - .ok_or_else(|| err_unsup_format!("{:?} is not a valid utf-8 string", os_str).into()) + .ok_or_else(|| err_unsup_format!("{:?} is not a valid utf-8 string", os_str)) + .into() } let u16_vec = os_str_to_u16vec(os_str)?; @@ -109,7 +110,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { self.eval_context_mut().write_wide_str(truncated_data, ptr, size)?; assert!(written && written_len == size); } - Ok((written, size_needed)) + interp_ok((written, size_needed)) } /// Helper function to write an OsStr as a 0x0000-terminated u16-sequence, which is what the @@ -148,7 +149,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let arg_place = this.allocate(this.layout_of(arg_type).unwrap(), memkind)?; let (written, _) = self.write_os_str_to_c_str(os_str, arg_place.ptr(), size).unwrap(); assert!(written); - Ok(arg_place.ptr()) + interp_ok(arg_place.ptr()) } /// Allocate enough memory to store the given `OsStr` as a null-terminated sequence of `u16`. @@ -164,7 +165,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let arg_place = this.allocate(this.layout_of(arg_type).unwrap(), memkind)?; let (written, _) = self.write_os_str_to_wide_str(os_str, arg_place.ptr(), size).unwrap(); assert!(written); - Ok(arg_place.ptr()) + interp_ok(arg_place.ptr()) } /// Read a null-terminated sequence of bytes, and perform path separator conversion if needed. @@ -175,7 +176,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let this = self.eval_context_ref(); let os_str = this.read_os_str_from_c_str(ptr)?; - Ok(match this.convert_path(Cow::Borrowed(os_str), PathConversion::TargetToHost) { + interp_ok(match this.convert_path(Cow::Borrowed(os_str), PathConversion::TargetToHost) { Cow::Borrowed(x) => Cow::Borrowed(Path::new(x)), Cow::Owned(y) => Cow::Owned(PathBuf::from(y)), }) @@ -186,7 +187,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let this = self.eval_context_ref(); let os_str = this.read_os_str_from_wide_str(ptr)?; - Ok(this.convert_path(Cow::Owned(os_str), PathConversion::TargetToHost).into_owned().into()) + interp_ok( + this.convert_path(Cow::Owned(os_str), PathConversion::TargetToHost).into_owned().into(), + ) } /// Write a Path to the machine memory (as a null-terminated sequence of bytes), diff --git a/src/tools/miri/src/shims/panic.rs b/src/tools/miri/src/shims/panic.rs index 55b814a09fa01..9bb0d7d0ce6f6 100644 --- a/src/tools/miri/src/shims/panic.rs +++ b/src/tools/miri/src/shims/panic.rs @@ -54,7 +54,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let thread = this.active_thread_mut(); thread.panic_payloads.push(payload); - Ok(()) + interp_ok(()) } /// Handles the `try` intrinsic, the underlying implementation of `std::panicking::try`. @@ -106,7 +106,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Some(CatchUnwindData { catch_fn, data, dest: dest.clone(), ret }); } - Ok(()) + interp_ok(()) } fn handle_stack_pop_unwind( @@ -150,9 +150,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { )?; // We pushed a new stack frame, the engine should not do any jumping now! - Ok(ReturnAction::NoJump) + interp_ok(ReturnAction::NoJump) } else { - Ok(ReturnAction::Normal) + interp_ok(ReturnAction::Normal) } } @@ -254,6 +254,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { })?; } } - Ok(()) + interp_ok(()) } } diff --git a/src/tools/miri/src/shims/time.rs b/src/tools/miri/src/shims/time.rs index 694c52b54657a..21c5421f10941 100644 --- a/src/tools/miri/src/shims/time.rs +++ b/src/tools/miri/src/shims/time.rs @@ -11,7 +11,7 @@ use crate::*; /// Returns the time elapsed between the provided time and the unix epoch as a `Duration`. pub fn system_time_to_duration<'tcx>(time: &SystemTime) -> InterpResult<'tcx, Duration> { time.duration_since(SystemTime::UNIX_EPOCH) - .map_err(|_| err_unsup_format!("times before the Unix epoch are not supported").into()) + .map_err(|_| err_unsup_format!("times before the Unix epoch are not supported")).into() } impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} @@ -82,7 +82,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } else { let einval = this.eval_libc("EINVAL"); this.set_last_error(einval)?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); }; let tv_sec = duration.as_secs(); @@ -90,7 +90,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_int_fields(&[tv_sec.into(), tv_nsec.into()], &tp)?; - Ok(Scalar::from_i32(0)) + interp_ok(Scalar::from_i32(0)) } fn gettimeofday( @@ -110,7 +110,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if !this.ptr_is_null(tz)? { let einval = this.eval_libc("EINVAL"); this.set_last_error(einval)?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); } let duration = system_time_to_duration(&SystemTime::now())?; @@ -119,7 +119,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_int_fields(&[tv_sec.into(), tv_usec.into()], &tv)?; - Ok(Scalar::from_i32(0)) + interp_ok(Scalar::from_i32(0)) } // The localtime() function shall convert the time in seconds since the Epoch pointed to by @@ -206,7 +206,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_pointer(tm_zone_ptr, &this.project_field_named(&result, "tm_zone")?)?; this.write_int_fields_named(&[("tm_gmtoff", tm_gmtoff.into())], &result)?; } - Ok(result.ptr()) + interp_ok(result.ptr()) } #[allow(non_snake_case, clippy::arithmetic_side_effects)] fn GetSystemTimeAsFileTime( @@ -236,7 +236,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let dwHighDateTime = u32::try_from((duration_ticks & 0xFFFFFFFF00000000) >> 32).unwrap(); this.write_int_fields(&[dwLowDateTime.into(), dwHighDateTime.into()], &filetime)?; - Ok(()) + interp_ok(()) } #[allow(non_snake_case)] @@ -255,7 +255,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { err_unsup_format!("programs running longer than 2^63 nanoseconds are not supported") })?; this.write_scalar(Scalar::from_i64(qpc), &this.deref_pointer(lpPerformanceCount_op)?)?; - Ok(Scalar::from_i32(-1)) // return non-zero on success + interp_ok(Scalar::from_i32(-1)) // return non-zero on success } #[allow(non_snake_case)] @@ -276,7 +276,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Scalar::from_i64(1_000_000_000), &this.deref_pointer_as(lpFrequency_op, this.machine.layouts.u64)?, )?; - Ok(Scalar::from_i32(-1)) // Return non-zero on success + interp_ok(Scalar::from_i32(-1)) // Return non-zero on success } fn mach_absolute_time(&self) -> InterpResult<'tcx, Scalar> { @@ -290,7 +290,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let res = u64::try_from(duration.as_nanos()).map_err(|_| { err_unsup_format!("programs running longer than 2^64 nanoseconds are not supported") })?; - Ok(Scalar::from_u64(res)) + interp_ok(Scalar::from_u64(res)) } fn mach_timebase_info(&mut self, info_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { @@ -305,7 +305,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let (numer, denom) = (1, 1); this.write_int_fields(&[numer.into(), denom.into()], &info)?; - Ok(Scalar::from_i32(0)) // KERN_SUCCESS + interp_ok(Scalar::from_i32(0)) // KERN_SUCCESS } fn nanosleep( @@ -324,7 +324,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { None => { let einval = this.eval_libc("EINVAL"); this.set_last_error(einval)?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); } }; @@ -334,10 +334,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { callback!( @capture<'tcx> {} @unblock = |_this| { panic!("sleeping thread unblocked before time is up") } - @timeout = |_this| { Ok(()) } + @timeout = |_this| { interp_ok(()) } ), ); - Ok(Scalar::from_i32(0)) + interp_ok(Scalar::from_i32(0)) } #[allow(non_snake_case)] @@ -356,9 +356,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { callback!( @capture<'tcx> {} @unblock = |_this| { panic!("sleeping thread unblocked before time is up") } - @timeout = |_this| { Ok(()) } + @timeout = |_this| { interp_ok(()) } ), ); - Ok(()) + interp_ok(()) } } diff --git a/src/tools/miri/src/shims/tls.rs b/src/tools/miri/src/shims/tls.rs index b3ea7098dfe81..94b2d1bc782aa 100644 --- a/src/tools/miri/src/shims/tls.rs +++ b/src/tools/miri/src/shims/tls.rs @@ -68,14 +68,14 @@ impl<'tcx> TlsData<'tcx> { if max_size.bits() < 128 && new_key >= (1u128 << max_size.bits()) { throw_unsup_format!("we ran out of TLS key space"); } - Ok(new_key) + interp_ok(new_key) } pub fn delete_tls_key(&mut self, key: TlsKey) -> InterpResult<'tcx> { match self.keys.remove(&key) { Some(_) => { trace!("TLS key {} removed", key); - Ok(()) + interp_ok(()) } None => throw_ub_format!("removing a nonexistent TLS key: {}", key), } @@ -91,7 +91,7 @@ impl<'tcx> TlsData<'tcx> { Some(TlsEntry { data, .. }) => { let value = data.get(&thread_id).copied(); trace!("TLS key {} for thread {:?} loaded: {:?}", key, thread_id, value); - Ok(value.unwrap_or_else(|| Scalar::null_ptr(cx))) + interp_ok(value.unwrap_or_else(|| Scalar::null_ptr(cx))) } None => throw_ub_format!("loading from a non-existing TLS key: {}", key), } @@ -113,7 +113,7 @@ impl<'tcx> TlsData<'tcx> { trace!("TLS key {} for thread {:?} removed", key, thread_id); data.remove(&thread_id); } - Ok(()) + interp_ok(()) } None => throw_ub_format!("storing to a non-existing TLS key: {}", key), } @@ -128,7 +128,7 @@ impl<'tcx> TlsData<'tcx> { data: Scalar, ) -> InterpResult<'tcx> { self.macos_thread_dtors.entry(thread).or_default().push((dtor, data)); - Ok(()) + interp_ok(()) } /// Returns a dtor, its argument and its index, if one is supposed to run. @@ -261,7 +261,7 @@ impl<'tcx> TlsDtorsState<'tcx> { } MacOsDtors => { match this.schedule_macos_tls_dtor()? { - Poll::Pending => return Ok(Poll::Pending), + Poll::Pending => return interp_ok(Poll::Pending), // After all macOS destructors are run, the system switches // to destroying the pthread destructors. Poll::Ready(()) => break 'new_state PthreadDtors(Default::default()), @@ -269,14 +269,14 @@ impl<'tcx> TlsDtorsState<'tcx> { } PthreadDtors(state) => { match this.schedule_next_pthread_tls_dtor(state)? { - Poll::Pending => return Ok(Poll::Pending), // just keep going + Poll::Pending => return interp_ok(Poll::Pending), // just keep going Poll::Ready(()) => break 'new_state Done, } } WindowsDtors(dtors) => { if let Some(dtor) = dtors.pop() { this.schedule_windows_tls_dtor(dtor)?; - return Ok(Poll::Pending); // we stay in this state (but `dtors` got shorter) + return interp_ok(Poll::Pending); // we stay in this state (but `dtors` got shorter) } else { // No more destructors to run. break 'new_state Done; @@ -284,13 +284,13 @@ impl<'tcx> TlsDtorsState<'tcx> { } Done => { this.machine.tls.delete_all_thread_tls(this.active_thread()); - return Ok(Poll::Ready(())); + return interp_ok(Poll::Ready(())); } } }; self.0 = new_state; - Ok(Poll::Pending) + interp_ok(Poll::Pending) } } @@ -303,7 +303,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Windows has a special magic linker section that is run on certain events. // We don't support most of that, but just enough to make thread-local dtors in `std` work. - Ok(this.lookup_link_section(".CRT$XLB")?) + interp_ok(this.lookup_link_section(".CRT$XLB")?) } fn schedule_windows_tls_dtor(&mut self, dtor: ImmTy<'tcx>) -> InterpResult<'tcx> { @@ -328,7 +328,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { None, StackPopCleanup::Root { cleanup: true }, )?; - Ok(()) + interp_ok(()) } /// Schedule the macOS thread local storage destructors to be executed. @@ -350,10 +350,10 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { StackPopCleanup::Root { cleanup: true }, )?; - return Ok(Poll::Pending); + return interp_ok(Poll::Pending); } - Ok(Poll::Ready(())) + interp_ok(Poll::Ready(())) } /// Schedule a pthread TLS destructor. Returns `true` if found @@ -387,9 +387,9 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { StackPopCleanup::Root { cleanup: true }, )?; - return Ok(Poll::Pending); + return interp_ok(Poll::Pending); } - Ok(Poll::Ready(())) + interp_ok(Poll::Ready(())) } } diff --git a/src/tools/miri/src/shims/unix/android/foreign_items.rs b/src/tools/miri/src/shims/unix/android/foreign_items.rs index 42552a51edae3..583a1f6500963 100644 --- a/src/tools/miri/src/shims/unix/android/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/android/foreign_items.rs @@ -25,8 +25,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?; } - _ => return Ok(EmulateItemResult::NotSupported), + _ => return interp_ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsReturn) + interp_ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/unix/env.rs b/src/tools/miri/src/shims/unix/env.rs index 184a6c238b392..75721814c9948 100644 --- a/src/tools/miri/src/shims/unix/env.rs +++ b/src/tools/miri/src/shims/unix/env.rs @@ -47,7 +47,7 @@ impl<'tcx> UnixEnvVars<'tcx> { let environ_block = alloc_environ_block(ecx, env_vars_machine.values().copied().collect())?; ecx.write_pointer(environ_block, &environ)?; - Ok(UnixEnvVars { map: env_vars_machine, environ }) + interp_ok(UnixEnvVars { map: env_vars_machine, environ }) } pub(crate) fn cleanup(ecx: &mut InterpCx<'tcx, MiriMachine<'tcx>>) -> InterpResult<'tcx> { @@ -61,7 +61,7 @@ impl<'tcx> UnixEnvVars<'tcx> { let old_vars_ptr = ecx.read_pointer(environ)?; ecx.deallocate_ptr(old_vars_ptr, None, MiriMemoryKind::Runtime.into())?; - Ok(()) + interp_ok(()) } pub(crate) fn environ(&self) -> Pointer { @@ -77,14 +77,14 @@ impl<'tcx> UnixEnvVars<'tcx> { // but we do want to do this read so it shows up as a data race. let _vars_ptr = ecx.read_pointer(&self.environ)?; let Some(var_ptr) = self.map.get(name) else { - return Ok(None); + return interp_ok(None); }; // The offset is used to strip the "{name}=" part of the string. let var_ptr = var_ptr.wrapping_offset( Size::from_bytes(u64::try_from(name.len()).unwrap().strict_add(1)), ecx, ); - Ok(Some(var_ptr)) + interp_ok(Some(var_ptr)) } /// Implementation detail for [`InterpCx::get_env_var`]. This basically does `getenv`, complete @@ -97,9 +97,9 @@ impl<'tcx> UnixEnvVars<'tcx> { let var_ptr = self.get_ptr(ecx, name)?; if let Some(ptr) = var_ptr { let var = ecx.read_os_str_from_c_str(ptr)?; - Ok(Some(var.to_owned())) + interp_ok(Some(var.to_owned())) } else { - Ok(None) + interp_ok(None) } } } @@ -133,7 +133,7 @@ fn alloc_environ_block<'tcx>( let place = ecx.project_field(&vars_place, idx)?; ecx.write_pointer(var, &place)?; } - Ok(vars_place.ptr()) + interp_ok(vars_place.ptr()) } impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} @@ -146,7 +146,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let name = this.read_os_str_from_c_str(name_ptr)?; let var_ptr = this.machine.env_vars.unix().get_ptr(this, name)?; - Ok(var_ptr.unwrap_or_else(Pointer::null)) + interp_ok(var_ptr.unwrap_or_else(Pointer::null)) } fn setenv( @@ -174,12 +174,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.deallocate_ptr(var, None, MiriMemoryKind::Runtime.into())?; } this.update_environ()?; - Ok(Scalar::from_i32(0)) // return zero on success + interp_ok(Scalar::from_i32(0)) // return zero on success } else { // name argument is a null pointer, points to an empty string, or points to a string containing an '=' character. let einval = this.eval_libc("EINVAL"); this.set_last_error(einval)?; - Ok(Scalar::from_i32(-1)) + interp_ok(Scalar::from_i32(-1)) } } @@ -200,12 +200,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.deallocate_ptr(var, None, MiriMemoryKind::Runtime.into())?; } this.update_environ()?; - Ok(Scalar::from_i32(0)) + interp_ok(Scalar::from_i32(0)) } else { // name argument is a null pointer, points to an empty string, or points to a string containing an '=' character. let einval = this.eval_libc("EINVAL"); this.set_last_error(einval)?; - Ok(Scalar::from_i32(-1)) + interp_ok(Scalar::from_i32(-1)) } } @@ -219,14 +219,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`getcwd`", reject_with)?; this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; - return Ok(Pointer::null()); + return interp_ok(Pointer::null()); } // If we cannot get the current directory, we return null match env::current_dir() { Ok(cwd) => { if this.write_path_to_c_str(&cwd, buf, size)?.0 { - return Ok(buf); + return interp_ok(buf); } let erange = this.eval_libc("ERANGE"); this.set_last_error(erange)?; @@ -234,7 +234,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Err(e) => this.set_last_error_from_io_error(e)?, } - Ok(Pointer::null()) + interp_ok(Pointer::null()) } fn chdir(&mut self, path_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { @@ -247,11 +247,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.reject_in_isolation("`chdir`", reject_with)?; this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); } let result = env::set_current_dir(path).map(|()| 0); - Ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) + interp_ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) } /// Updates the `environ` static. @@ -267,7 +267,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let environ_block = alloc_environ_block(this, vals)?; this.write_pointer(environ_block, &environ)?; - Ok(()) + interp_ok(()) } fn getpid(&mut self) -> InterpResult<'tcx, Scalar> { @@ -278,7 +278,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // `libc::getpid` returns an i32, however, `std::process::id()` return an u32. // So we un-do the conversion that stdlib does and turn it back into an i32. // In `Scalar` representation, these are the same, so we don't need to anything else. - Ok(Scalar::from_u32(this.get_pid())) + interp_ok(Scalar::from_u32(this.get_pid())) } fn linux_gettid(&mut self) -> InterpResult<'tcx, Scalar> { @@ -290,6 +290,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Compute a TID for this thread, ensuring that the main thread has PID == TID. let tid = this.get_pid().strict_add(index); - Ok(Scalar::from_u32(tid)) + interp_ok(Scalar::from_u32(tid)) } } diff --git a/src/tools/miri/src/shims/unix/fd.rs b/src/tools/miri/src/shims/unix/fd.rs index 6bd753d0d6bee..24e5d5579c3f1 100644 --- a/src/tools/miri/src/shims/unix/fd.rs +++ b/src/tools/miri/src/shims/unix/fd.rs @@ -277,7 +277,7 @@ impl FileDescriptionRef { fd.file_description.close(communicate_allowed, ecx) } - None => Ok(Ok(())), + None => interp_ok(Ok(())), } } @@ -414,16 +414,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let this = self.eval_context_mut(); let Some(fd) = this.machine.fds.get(old_fd_num) else { - return Ok(Scalar::from_i32(this.fd_not_found()?)); + return interp_ok(Scalar::from_i32(this.fd_not_found()?)); }; - Ok(Scalar::from_i32(this.machine.fds.insert(fd))) + interp_ok(Scalar::from_i32(this.machine.fds.insert(fd))) } fn dup2(&mut self, old_fd_num: i32, new_fd_num: i32) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); let Some(fd) = this.machine.fds.get(old_fd_num) else { - return Ok(Scalar::from_i32(this.fd_not_found()?)); + return interp_ok(Scalar::from_i32(this.fd_not_found()?)); }; if new_fd_num != old_fd_num { // Close new_fd if it is previously opened. @@ -433,13 +433,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { old_new_fd.close(this.machine.communicate(), this)?.ok(); } } - Ok(Scalar::from_i32(new_fd_num)) + interp_ok(Scalar::from_i32(new_fd_num)) } fn flock(&mut self, fd_num: i32, op: i32) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); let Some(fd) = this.machine.fds.get(fd_num) else { - return Ok(Scalar::from_i32(this.fd_not_found()?)); + return interp_ok(Scalar::from_i32(this.fd_not_found()?)); }; // We need to check that there aren't unsupported options in `op`. @@ -467,7 +467,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { drop(fd); // return `0` if flock is successful let result = result.map(|()| 0i32); - Ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) + interp_ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) } fn fcntl(&mut self, args: &[OpTy<'tcx>]) -> InterpResult<'tcx, Scalar> { @@ -488,7 +488,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // `FD_CLOEXEC` value without checking if the flag is set for the file because `std` // always sets this flag when opening a file. However we still need to check that the // file itself is open. - Ok(Scalar::from_i32(if this.machine.fds.is_fd_num(fd_num) { + interp_ok(Scalar::from_i32(if this.machine.fds.is_fd_num(fd_num) { this.eval_libc_i32("FD_CLOEXEC") } else { this.fd_not_found()? @@ -509,15 +509,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let start = this.read_scalar(&args[2])?.to_i32()?; match this.machine.fds.get(fd_num) { - Some(fd) => Ok(Scalar::from_i32(this.machine.fds.insert_with_min_num(fd, start))), - None => Ok(Scalar::from_i32(this.fd_not_found()?)), + Some(fd) => interp_ok(Scalar::from_i32(this.machine.fds.insert_with_min_num(fd, start))), + None => interp_ok(Scalar::from_i32(this.fd_not_found()?)), } } else if this.tcx.sess.target.os == "macos" && cmd == this.eval_libc_i32("F_FULLFSYNC") { // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`fcntl`", reject_with)?; this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); } this.ffullsync_fd(fd_num) @@ -532,12 +532,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let fd_num = this.read_scalar(fd_op)?.to_i32()?; let Some(fd) = this.machine.fds.remove(fd_num) else { - return Ok(Scalar::from_i32(this.fd_not_found()?)); + return interp_ok(Scalar::from_i32(this.fd_not_found()?)); }; let result = fd.close(this.machine.communicate(), this)?; // return `0` if close is successful let result = result.map(|()| 0i32); - Ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) + interp_ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) } /// Function used when a file descriptor does not exist. It returns `Ok(-1)`and sets @@ -548,7 +548,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let this = self.eval_context_mut(); let ebadf = this.eval_libc("EBADF"); this.set_last_error(ebadf)?; - Ok((-1).into()) + interp_ok((-1).into()) } /// Read data from `fd` into buffer specified by `buf` and `count`. @@ -586,7 +586,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { trace!("read: FD not found"); let res: i32 = this.fd_not_found()?; this.write_int(res, dest)?; - return Ok(()); + return interp_ok(()); }; trace!("read: FD mapped to {fd:?}"); @@ -601,12 +601,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let einval = this.eval_libc("EINVAL"); this.set_last_error(einval)?; this.write_int(-1, dest)?; - return Ok(()); + return interp_ok(()); }; fd.pread(communicate, offset, buf, count, dest, this)? } }; - Ok(()) + interp_ok(()) } fn write( @@ -636,7 +636,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let Some(fd) = this.machine.fds.get(fd_num) else { let res: i32 = this.fd_not_found()?; this.write_int(res, dest)?; - return Ok(()); + return interp_ok(()); }; match offset { @@ -646,12 +646,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let einval = this.eval_libc("EINVAL"); this.set_last_error(einval)?; this.write_int(-1, dest)?; - return Ok(()); + return interp_ok(()); }; fd.pwrite(communicate, buf, count, offset, dest, this)? } }; - Ok(()) + interp_ok(()) } /// Helper to implement `FileDescription::read`: @@ -675,12 +675,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_bytes_ptr(buf, bytes[..read_bytes].iter().copied())?; // The actual read size is always less than what got originally requested so this cannot fail. this.write_int(u64::try_from(read_bytes).unwrap(), dest)?; - Ok(()) + interp_ok(()) } Err(e) => { this.set_last_error_from_io_error(e)?; this.write_int(-1, dest)?; - Ok(()) + interp_ok(()) } } } @@ -695,6 +695,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let this = self.eval_context_mut(); let result = this.try_unwrap_io_result(result.map(|c| i64::try_from(c).unwrap()))?; this.write_int(result, dest)?; - Ok(()) + interp_ok(()) } } diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs index 528d068ea92c0..b35fe4487b27d 100644 --- a/src/tools/miri/src/shims/unix/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/foreign_items.rs @@ -828,7 +828,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // This function looks and behaves excatly like miri_start_unwind. let [payload] = this.check_shim(abi, Abi::C { unwind: true }, link_name, args)?; this.handle_miri_start_unwind(payload)?; - return Ok(EmulateItemResult::NeedsUnwind); + return interp_ok(EmulateItemResult::NeedsUnwind); } "getuid" => { let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; @@ -944,11 +944,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "linux" => linux::EvalContextExt::emulate_foreign_item_inner(this, link_name, abi, args, dest), "macos" => macos::EvalContextExt::emulate_foreign_item_inner(this, link_name, abi, args, dest), "solaris" | "illumos" => solarish::EvalContextExt::emulate_foreign_item_inner(this, link_name, abi, args, dest), - _ => Ok(EmulateItemResult::NotSupported), + _ => interp_ok(EmulateItemResult::NotSupported), }; } }; - Ok(EmulateItemResult::NeedsReturn) + interp_ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs b/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs index 36f25767a8ea0..e89dd488a2fec 100644 --- a/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs @@ -84,8 +84,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_null(dest)?; } - _ => return Ok(EmulateItemResult::NotSupported), + _ => return interp_ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsReturn) + interp_ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index 92514b67beab1..56280982e6ece 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -121,7 +121,7 @@ impl FileDescription for FileHandle { offset: SeekFrom, ) -> InterpResult<'tcx, io::Result> { assert!(communicate_allowed, "isolation should have prevented even opening a file"); - Ok((&mut &self.file).seek(offset)) + interp_ok((&mut &self.file).seek(offset)) } fn close<'tcx>( @@ -136,7 +136,7 @@ impl FileDescription for FileHandle { // to handle possible errors correctly. let result = self.file.sync_all(); // Now we actually close the file and return the result. - Ok(result) + interp_ok(result) } else { // We drop the file, this closes it but ignores any errors // produced when closing it. This is done because @@ -144,7 +144,7 @@ impl FileDescription for FileHandle { // `/dev/urandom` which are read-only. Check // https://github.com/rust-lang/miri/issues/999#issuecomment-568920439 // for a deeper discussion. - Ok(Ok(())) + interp_ok(Ok(())) } } @@ -179,7 +179,7 @@ impl FileDescription for FileHandle { } ret => panic!("Unexpected return value from flock: {ret}"), }; - Ok(res) + interp_ok(res) } #[cfg(target_family = "windows")] @@ -231,7 +231,7 @@ impl FileDescription for FileHandle { } _ => panic!("Unexpected return value: {ret}"), }; - Ok(res) + interp_ok(res) } #[cfg(not(any(target_family = "unix", target_family = "windows")))] @@ -289,7 +289,7 @@ trait EvalContextExtPrivate<'tcx>: crate::MiriInterpCxExt<'tcx> { &buf, )?; - Ok(0) + interp_ok(0) } fn file_type_to_d_type( @@ -303,26 +303,30 @@ trait EvalContextExtPrivate<'tcx>: crate::MiriInterpCxExt<'tcx> { match file_type { Ok(file_type) => { match () { - _ if file_type.is_dir() => Ok(this.eval_libc("DT_DIR").to_u8()?.into()), - _ if file_type.is_file() => Ok(this.eval_libc("DT_REG").to_u8()?.into()), - _ if file_type.is_symlink() => Ok(this.eval_libc("DT_LNK").to_u8()?.into()), + _ if file_type.is_dir() => interp_ok(this.eval_libc("DT_DIR").to_u8()?.into()), + _ if file_type.is_file() => interp_ok(this.eval_libc("DT_REG").to_u8()?.into()), + _ if file_type.is_symlink() => + interp_ok(this.eval_libc("DT_LNK").to_u8()?.into()), // Certain file types are only supported when the host is a Unix system. #[cfg(unix)] _ if file_type.is_block_device() => - Ok(this.eval_libc("DT_BLK").to_u8()?.into()), + interp_ok(this.eval_libc("DT_BLK").to_u8()?.into()), #[cfg(unix)] - _ if file_type.is_char_device() => Ok(this.eval_libc("DT_CHR").to_u8()?.into()), + _ if file_type.is_char_device() => + interp_ok(this.eval_libc("DT_CHR").to_u8()?.into()), #[cfg(unix)] - _ if file_type.is_fifo() => Ok(this.eval_libc("DT_FIFO").to_u8()?.into()), + _ if file_type.is_fifo() => + interp_ok(this.eval_libc("DT_FIFO").to_u8()?.into()), #[cfg(unix)] - _ if file_type.is_socket() => Ok(this.eval_libc("DT_SOCK").to_u8()?.into()), + _ if file_type.is_socket() => + interp_ok(this.eval_libc("DT_SOCK").to_u8()?.into()), // Fallback - _ => Ok(this.eval_libc("DT_UNKNOWN").to_u8()?.into()), + _ => interp_ok(this.eval_libc("DT_UNKNOWN").to_u8()?.into()), } } Err(e) => match e.raw_os_error() { - Some(error) => Ok(error), + Some(error) => interp_ok(error), None => throw_unsup_format!( "the error {} couldn't be converted to a return value", @@ -524,7 +528,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // if the flag contains `O_TMPFILE` then we return a graceful error let eopnotsupp = this.eval_libc("EOPNOTSUPP"); this.set_last_error(eopnotsupp)?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); } } @@ -545,7 +549,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if path.is_symlink() { let eloop = this.eval_libc("ELOOP"); this.set_last_error(eloop)?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); } } mirror |= o_nofollow; @@ -561,14 +565,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`open`", reject_with)?; this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); } let fd = options .open(path) .map(|file| this.machine.fds.insert_new(FileHandle { file, writable })); - Ok(Scalar::from_i32(this.try_unwrap_io_result(fd)?)) + interp_ok(Scalar::from_i32(this.try_unwrap_io_result(fd)?)) } fn lseek64(&mut self, fd_num: i32, offset: i128, whence: i32) -> InterpResult<'tcx, Scalar> { @@ -581,7 +585,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Negative offsets return `EINVAL`. let einval = this.eval_libc("EINVAL"); this.set_last_error(einval)?; - return Ok(Scalar::from_i64(-1)); + return interp_ok(Scalar::from_i64(-1)); } else { SeekFrom::Start(u64::try_from(offset).unwrap()) } @@ -592,19 +596,19 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } else { let einval = this.eval_libc("EINVAL"); this.set_last_error(einval)?; - return Ok(Scalar::from_i64(-1)); + return interp_ok(Scalar::from_i64(-1)); }; let communicate = this.machine.communicate(); let Some(fd) = this.machine.fds.get(fd_num) else { - return Ok(Scalar::from_i64(this.fd_not_found()?)); + return interp_ok(Scalar::from_i64(this.fd_not_found()?)); }; let result = fd.seek(communicate, seek_from)?.map(|offset| i64::try_from(offset).unwrap()); drop(fd); let result = this.try_unwrap_io_result(result)?; - Ok(Scalar::from_i64(result)) + interp_ok(Scalar::from_i64(result)) } fn unlink(&mut self, path_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { @@ -616,11 +620,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`unlink`", reject_with)?; this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); } let result = remove_file(path).map(|_| 0); - Ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) + interp_ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) } fn symlink( @@ -647,11 +651,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`symlink`", reject_with)?; this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); } let result = create_link(&target, &linkpath).map(|_| 0); - Ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) + interp_ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) } fn macos_fbsd_stat( @@ -673,16 +677,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.reject_in_isolation("`stat`", reject_with)?; let eacc = this.eval_libc("EACCES"); this.set_last_error(eacc)?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); } // `stat` always follows symlinks. let metadata = match FileMetadata::from_path(this, &path, true)? { Some(metadata) => metadata, - None => return Ok(Scalar::from_i32(-1)), // `FileMetadata` has set errno + None => return interp_ok(Scalar::from_i32(-1)), // `FileMetadata` has set errno }; - Ok(Scalar::from_i32(this.macos_stat_write_buf(metadata, buf_op)?)) + interp_ok(Scalar::from_i32(this.macos_stat_write_buf(metadata, buf_op)?)) } // `lstat` is used to get symlink metadata. @@ -705,15 +709,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.reject_in_isolation("`lstat`", reject_with)?; let eacc = this.eval_libc("EACCES"); this.set_last_error(eacc)?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); } let metadata = match FileMetadata::from_path(this, &path, false)? { Some(metadata) => metadata, - None => return Ok(Scalar::from_i32(-1)), // `FileMetadata` has set errno + None => return interp_ok(Scalar::from_i32(-1)), // `FileMetadata` has set errno }; - Ok(Scalar::from_i32(this.macos_stat_write_buf(metadata, buf_op)?)) + interp_ok(Scalar::from_i32(this.macos_stat_write_buf(metadata, buf_op)?)) } fn macos_fbsd_fstat( @@ -733,14 +737,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`fstat`", reject_with)?; // Set error code as "EBADF" (bad fd) - return Ok(Scalar::from_i32(this.fd_not_found()?)); + return interp_ok(Scalar::from_i32(this.fd_not_found()?)); } let metadata = match FileMetadata::from_fd_num(this, fd)? { Some(metadata) => metadata, - None => return Ok(Scalar::from_i32(-1)), + None => return interp_ok(Scalar::from_i32(-1)), }; - Ok(Scalar::from_i32(this.macos_stat_write_buf(metadata, buf_op)?)) + interp_ok(Scalar::from_i32(this.macos_stat_write_buf(metadata, buf_op)?)) } fn linux_statx( @@ -765,7 +769,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if this.ptr_is_null(statxbuf_ptr)? || this.ptr_is_null(pathname_ptr)? { let efault = this.eval_libc("EFAULT"); this.set_last_error(efault)?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); } let statxbuf = this.deref_pointer_as(statxbuf_op, this.libc_ty_layout("statx"))?; @@ -807,7 +811,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.eval_libc("EBADF") }; this.set_last_error(ecode)?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); } // the `_mask_op` parameter specifies the file information that the caller requested. @@ -829,7 +833,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }; let metadata = match metadata { Some(metadata) => metadata, - None => return Ok(Scalar::from_i32(-1)), + None => return interp_ok(Scalar::from_i32(-1)), }; // The `mode` field specifies the type of the file and the permissions over the file for @@ -848,25 +852,25 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { .accessed .map(|tup| { mask |= this.eval_libc_u32("STATX_ATIME"); - InterpResult::Ok(tup) + interp_ok(tup) }) - .unwrap_or_else(|| Ok((0, 0)))?; + .unwrap_or_else(|| interp_ok((0, 0)))?; let (created_sec, created_nsec) = metadata .created .map(|tup| { mask |= this.eval_libc_u32("STATX_BTIME"); - InterpResult::Ok(tup) + interp_ok(tup) }) - .unwrap_or_else(|| Ok((0, 0)))?; + .unwrap_or_else(|| interp_ok((0, 0)))?; let (modified_sec, modified_nsec) = metadata .modified .map(|tup| { mask |= this.eval_libc_u32("STATX_MTIME"); - InterpResult::Ok(tup) + interp_ok(tup) }) - .unwrap_or_else(|| Ok((0, 0)))?; + .unwrap_or_else(|| interp_ok((0, 0)))?; // Now we write everything to `statxbuf`. We write a zero for the unavailable fields. this.write_int_fields_named( @@ -922,7 +926,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { &this.project_field_named(&statxbuf, "stx_mtime")?, )?; - Ok(Scalar::from_i32(0)) + interp_ok(Scalar::from_i32(0)) } fn rename( @@ -938,7 +942,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if this.ptr_is_null(oldpath_ptr)? || this.ptr_is_null(newpath_ptr)? { let efault = this.eval_libc("EFAULT"); this.set_last_error(efault)?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); } let oldpath = this.read_path_from_c_str(oldpath_ptr)?; @@ -948,12 +952,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`rename`", reject_with)?; this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); } let result = rename(oldpath, newpath).map(|_| 0); - Ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) + interp_ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) } fn mkdir(&mut self, path_op: &OpTy<'tcx>, mode_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { @@ -972,7 +976,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`mkdir`", reject_with)?; this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); } #[cfg_attr(not(unix), allow(unused_mut))] @@ -988,7 +992,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let result = builder.create(path).map(|_| 0i32); - Ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) + interp_ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) } fn rmdir(&mut self, path_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { @@ -1000,12 +1004,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`rmdir`", reject_with)?; this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); } let result = remove_dir(path).map(|_| 0i32); - Ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) + interp_ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) } fn opendir(&mut self, name_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { @@ -1018,7 +1022,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.reject_in_isolation("`opendir`", reject_with)?; let eacc = this.eval_libc("EACCES"); this.set_last_error(eacc)?; - return Ok(Scalar::null_ptr(this)); + return interp_ok(Scalar::null_ptr(this)); } let result = read_dir(name); @@ -1030,11 +1034,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // The libc API for opendir says that this method returns a pointer to an opaque // structure, but we are returning an ID number. Thus, pass it as a scalar of // pointer width. - Ok(Scalar::from_target_usize(id, this)) + interp_ok(Scalar::from_target_usize(id, this)) } Err(e) => { this.set_last_error_from_io_error(e)?; - Ok(Scalar::null_ptr(this)) + interp_ok(Scalar::null_ptr(this)) } } } @@ -1051,7 +1055,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.reject_in_isolation("`readdir`", reject_with)?; let eacc = this.eval_libc("EBADF"); this.set_last_error(eacc)?; - return Ok(Scalar::null_ptr(this)); + return interp_ok(Scalar::null_ptr(this)); } let open_dir = this.machine.dirs.streams.get_mut(&dirp).ok_or_else(|| { @@ -1129,7 +1133,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.deallocate_ptr(old_entry, None, MiriMemoryKind::Runtime.into())?; } - Ok(Scalar::from_maybe_pointer(entry.unwrap_or_else(Pointer::null), this)) + interp_ok(Scalar::from_maybe_pointer(entry.unwrap_or_else(Pointer::null), this)) } fn macos_fbsd_readdir_r( @@ -1150,13 +1154,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`readdir_r`", reject_with)?; // Set error code as "EBADF" (bad fd) - return Ok(Scalar::from_i32(this.fd_not_found()?)); + return interp_ok(Scalar::from_i32(this.fd_not_found()?)); } let open_dir = this.machine.dirs.streams.get_mut(&dirp).ok_or_else(|| { err_unsup_format!("the DIR pointer passed to readdir_r did not come from opendir") })?; - Ok(Scalar::from_i32(match open_dir.read_dir.next() { + interp_ok(Scalar::from_i32(match open_dir.read_dir.next() { Some(Ok(dir_entry)) => { // Write into entry, write pointer to result, return 0 on success. // The name is written with write_os_str_to_c_str, while the rest of the @@ -1261,18 +1265,20 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let dirp = this.read_target_usize(dirp_op)?; // Reject if isolation is enabled. - Ok(Scalar::from_i32(if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { - this.reject_in_isolation("`closedir`", reject_with)?; - this.fd_not_found()? - } else if let Some(open_dir) = this.machine.dirs.streams.remove(&dirp) { - if let Some(entry) = open_dir.entry { - this.deallocate_ptr(entry, None, MiriMemoryKind::Runtime.into())?; - } - drop(open_dir); - 0 - } else { - this.fd_not_found()? - })) + interp_ok(Scalar::from_i32( + if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { + this.reject_in_isolation("`closedir`", reject_with)?; + this.fd_not_found()? + } else if let Some(open_dir) = this.machine.dirs.streams.remove(&dirp) { + if let Some(entry) = open_dir.entry { + this.deallocate_ptr(entry, None, MiriMemoryKind::Runtime.into())?; + } + drop(open_dir); + 0 + } else { + this.fd_not_found()? + }, + )) } fn ftruncate64(&mut self, fd_num: i32, length: i128) -> InterpResult<'tcx, Scalar> { @@ -1282,11 +1288,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`ftruncate64`", reject_with)?; // Set error code as "EBADF" (bad fd) - return Ok(Scalar::from_i32(this.fd_not_found()?)); + return interp_ok(Scalar::from_i32(this.fd_not_found()?)); } let Some(fd) = this.machine.fds.get(fd_num) else { - return Ok(Scalar::from_i32(this.fd_not_found()?)); + return interp_ok(Scalar::from_i32(this.fd_not_found()?)); }; // FIXME: Support ftruncate64 for all FDs @@ -1299,19 +1305,19 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let result = file.set_len(length); drop(fd); let result = this.try_unwrap_io_result(result.map(|_| 0i32))?; - Ok(Scalar::from_i32(result)) + interp_ok(Scalar::from_i32(result)) } else { drop(fd); let einval = this.eval_libc("EINVAL"); this.set_last_error(einval)?; - Ok(Scalar::from_i32(-1)) + interp_ok(Scalar::from_i32(-1)) } } else { drop(fd); // The file is not writable let einval = this.eval_libc("EINVAL"); this.set_last_error(einval)?; - Ok(Scalar::from_i32(-1)) + interp_ok(Scalar::from_i32(-1)) } } @@ -1329,7 +1335,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`fsync`", reject_with)?; // Set error code as "EBADF" (bad fd) - return Ok(Scalar::from_i32(this.fd_not_found()?)); + return interp_ok(Scalar::from_i32(this.fd_not_found()?)); } self.ffullsync_fd(fd) @@ -1338,7 +1344,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn ffullsync_fd(&mut self, fd_num: i32) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); let Some(fd) = this.machine.fds.get(fd_num) else { - return Ok(Scalar::from_i32(this.fd_not_found()?)); + return interp_ok(Scalar::from_i32(this.fd_not_found()?)); }; // Only regular files support synchronization. let FileHandle { file, writable } = fd.downcast::().ok_or_else(|| { @@ -1346,7 +1352,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { })?; let io_result = maybe_sync_file(file, *writable, File::sync_all); drop(fd); - Ok(Scalar::from_i32(this.try_unwrap_io_result(io_result)?)) + interp_ok(Scalar::from_i32(this.try_unwrap_io_result(io_result)?)) } fn fdatasync(&mut self, fd_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { @@ -1358,11 +1364,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`fdatasync`", reject_with)?; // Set error code as "EBADF" (bad fd) - return Ok(Scalar::from_i32(this.fd_not_found()?)); + return interp_ok(Scalar::from_i32(this.fd_not_found()?)); } let Some(fd) = this.machine.fds.get(fd) else { - return Ok(Scalar::from_i32(this.fd_not_found()?)); + return interp_ok(Scalar::from_i32(this.fd_not_found()?)); }; // Only regular files support synchronization. let FileHandle { file, writable } = fd.downcast::().ok_or_else(|| { @@ -1370,7 +1376,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { })?; let io_result = maybe_sync_file(file, *writable, File::sync_data); drop(fd); - Ok(Scalar::from_i32(this.try_unwrap_io_result(io_result)?)) + interp_ok(Scalar::from_i32(this.try_unwrap_io_result(io_result)?)) } fn sync_file_range( @@ -1390,7 +1396,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if offset < 0 || nbytes < 0 { let einval = this.eval_libc("EINVAL"); this.set_last_error(einval)?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); } let allowed_flags = this.eval_libc_i32("SYNC_FILE_RANGE_WAIT_BEFORE") | this.eval_libc_i32("SYNC_FILE_RANGE_WRITE") @@ -1398,18 +1404,18 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if flags & allowed_flags != flags { let einval = this.eval_libc("EINVAL"); this.set_last_error(einval)?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); } // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`sync_file_range`", reject_with)?; // Set error code as "EBADF" (bad fd) - return Ok(Scalar::from_i32(this.fd_not_found()?)); + return interp_ok(Scalar::from_i32(this.fd_not_found()?)); } let Some(fd) = this.machine.fds.get(fd) else { - return Ok(Scalar::from_i32(this.fd_not_found()?)); + return interp_ok(Scalar::from_i32(this.fd_not_found()?)); }; // Only regular files support synchronization. let FileHandle { file, writable } = fd.downcast::().ok_or_else(|| { @@ -1417,7 +1423,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { })?; let io_result = maybe_sync_file(file, *writable, File::sync_data); drop(fd); - Ok(Scalar::from_i32(this.try_unwrap_io_result(io_result)?)) + interp_ok(Scalar::from_i32(this.try_unwrap_io_result(io_result)?)) } fn readlink( @@ -1437,7 +1443,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.reject_in_isolation("`readlink`", reject_with)?; let eacc = this.eval_libc("EACCES"); this.set_last_error(eacc)?; - return Ok(-1); + return interp_ok(-1); } let result = std::fs::read_link(pathname); @@ -1456,11 +1462,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { path_bytes = &path_bytes[..bufsize] } this.write_bytes_ptr(buf, path_bytes.iter().copied())?; - Ok(path_bytes.len().try_into().unwrap()) + interp_ok(path_bytes.len().try_into().unwrap()) } Err(e) => { this.set_last_error_from_io_error(e)?; - Ok(-1) + interp_ok(-1) } } } @@ -1472,7 +1478,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let fd = this.read_scalar(miri_fd)?.to_i32()?; let error = if let Some(fd) = this.machine.fds.get(fd) { if fd.is_tty(this.machine.communicate()) { - return Ok(Scalar::from_i32(1)); + return interp_ok(Scalar::from_i32(1)); } else { this.eval_libc("ENOTTY") } @@ -1481,7 +1487,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.eval_libc("EBADF") }; this.set_last_error(error)?; - Ok(Scalar::from_i32(0)) + interp_ok(Scalar::from_i32(0)) } fn realpath( @@ -1500,7 +1506,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.reject_in_isolation("`realpath`", reject_with)?; let eacc = this.eval_libc("EACCES"); this.set_last_error(eacc)?; - return Ok(Scalar::from_target_usize(0, this)); + return interp_ok(Scalar::from_target_usize(0, this)); } let result = std::fs::canonicalize(pathname); @@ -1531,16 +1537,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // seems like a bit of a mess anyway: . let enametoolong = this.eval_libc("ENAMETOOLONG"); this.set_last_error(enametoolong)?; - return Ok(Scalar::from_target_usize(0, this)); + return interp_ok(Scalar::from_target_usize(0, this)); } processed_ptr }; - Ok(Scalar::from_maybe_pointer(dest, this)) + interp_ok(Scalar::from_maybe_pointer(dest, this)) } Err(e) => { this.set_last_error_from_io_error(e)?; - Ok(Scalar::from_target_usize(0, this)) + interp_ok(Scalar::from_target_usize(0, this)) } } } @@ -1575,7 +1581,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.reject_in_isolation("`mkstemp`", reject_with)?; let eacc = this.eval_libc("EACCES"); this.set_last_error(eacc)?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); } // Get the bytes of the suffix we expect in _target_ encoding. @@ -1593,7 +1599,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if last_six_char_bytes != suffix_bytes { let einval = this.eval_libc("EINVAL"); this.set_last_error(einval)?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); } // At this point we know we have 6 ASCII 'X' characters as a suffix. @@ -1648,7 +1654,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { match file { Ok(f) => { let fd = this.machine.fds.insert_new(FileHandle { file: f, writable: true }); - return Ok(Scalar::from_i32(fd)); + return interp_ok(Scalar::from_i32(fd)); } Err(e) => match e.kind() { @@ -1659,7 +1665,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // "On error, -1 is returned, and errno is set to // indicate the error" this.set_last_error_from_io_error(e)?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); } }, } @@ -1668,7 +1674,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // We ran out of attempts to create the file, return an error. let eexist = this.eval_libc("EEXIST"); this.set_last_error(eexist)?; - Ok(Scalar::from_i32(-1)) + interp_ok(Scalar::from_i32(-1)) } } @@ -1678,12 +1684,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn extract_sec_and_nsec<'tcx>( time: std::io::Result, ) -> InterpResult<'tcx, Option<(u64, u32)>> { - time.ok() - .map(|time| { + match time.ok() { + Some(time) => { let duration = system_time_to_duration(&time)?; - Ok((duration.as_secs(), duration.subsec_nanos())) - }) - .transpose() + interp_ok(Some((duration.as_secs(), duration.subsec_nanos()))) + } + None => interp_ok(None), + } } /// Stores a file's metadata in order to avoid code duplication in the different metadata related @@ -1738,7 +1745,7 @@ impl FileMetadata { Ok(metadata) => metadata, Err(e) => { ecx.set_last_error_from_io_error(e)?; - return Ok(None); + return interp_ok(None); } }; @@ -1761,6 +1768,6 @@ impl FileMetadata { let modified = extract_sec_and_nsec(metadata.modified())?; // FIXME: Provide more fields using platform specific methods. - Ok(Some(FileMetadata { mode, size, created, accessed, modified })) + interp_ok(Some(FileMetadata { mode, size, created, accessed, modified })) } } diff --git a/src/tools/miri/src/shims/unix/linux/epoll.rs b/src/tools/miri/src/shims/unix/linux/epoll.rs index 675a404ae117c..08f1381caef72 100644 --- a/src/tools/miri/src/shims/unix/linux/epoll.rs +++ b/src/tools/miri/src/shims/unix/linux/epoll.rs @@ -142,7 +142,7 @@ impl FileDescription for Epoll { _communicate_allowed: bool, _ecx: &mut MiriInterpCx<'tcx>, ) -> InterpResult<'tcx, io::Result<()>> { - Ok(Ok(())) + interp_ok(Ok(())) } } @@ -211,7 +211,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { epoll_instance.ready_list = Rc::new(RefCell::new(BTreeMap::new())); let fd = this.machine.fds.insert_new(Epoll::default()); - Ok(Scalar::from_i32(fd)) + interp_ok(Scalar::from_i32(fd)) } /// This function performs control operations on the `Epoll` instance referred to by the file @@ -263,12 +263,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if epfd_value == fd { let einval = this.eval_libc("EINVAL"); this.set_last_error(einval)?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); } // Check if epfd is a valid epoll file descriptor. let Some(epfd) = this.machine.fds.get(epfd_value) else { - return Ok(Scalar::from_i32(this.fd_not_found()?)); + return interp_ok(Scalar::from_i32(this.fd_not_found()?)); }; let epoll_file_description = epfd .downcast::() @@ -278,7 +278,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let ready_list = &epoll_file_description.ready_list; let Some(fd_ref) = this.machine.fds.get(fd) else { - return Ok(Scalar::from_i32(this.fd_not_found()?)); + return interp_ok(Scalar::from_i32(this.fd_not_found()?)); }; let id = fd_ref.get_id(); @@ -330,13 +330,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if interest_list.contains_key(&epoll_key) { let eexist = this.eval_libc("EEXIST"); this.set_last_error(eexist)?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); } } else { if !interest_list.contains_key(&epoll_key) { let enoent = this.eval_libc("ENOENT"); this.set_last_error(enoent)?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); } } @@ -364,7 +364,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Notification will be returned for current epfd if there is event in the file // descriptor we registered. check_and_update_one_event_interest(&fd_ref, interest, id, this)?; - return Ok(Scalar::from_i32(0)); + return interp_ok(Scalar::from_i32(0)); } else if op == epoll_ctl_del { let epoll_key = (id, fd); @@ -372,7 +372,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let Some(epoll_interest) = interest_list.remove(&epoll_key) else { let enoent = this.eval_libc("ENOENT"); this.set_last_error(enoent)?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); }; // All related Weak will fail to upgrade after the drop. drop(epoll_interest); @@ -390,9 +390,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { .unwrap() .retain(|event| event.upgrade().is_some()); - return Ok(Scalar::from_i32(0)); + return interp_ok(Scalar::from_i32(0)); } - Ok(Scalar::from_i32(-1)) + interp_ok(Scalar::from_i32(-1)) } /// The `epoll_wait()` system call waits for events on the `Epoll` @@ -446,7 +446,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let einval = this.eval_libc("EINVAL"); this.set_last_error(einval)?; this.write_int(-1, dest)?; - return Ok(()); + return interp_ok(()); } // This needs to come after the maxevents value check, or else maxevents.try_into().unwrap() @@ -459,7 +459,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let Some(epfd) = this.machine.fds.get(epfd_value) else { let result_value: i32 = this.fd_not_found()?; this.write_int(result_value, dest)?; - return Ok(()); + return interp_ok(()); }; // Create a weak ref of epfd and pass it to callback so we will make sure that epfd // is not close after the thread unblocks. @@ -508,7 +508,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } @unblock = |this| { blocking_epoll_callback(epfd_value, weak_epfd, &dest, &event, this)?; - Ok(()) + interp_ok(()) } @timeout = |this| { // No notification after blocking timeout. @@ -521,12 +521,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { .thread_id.borrow_mut() .retain(|&id| id != this.active_thread()); this.write_int(0, &dest)?; - Ok(()) + interp_ok(()) } ), ); } - Ok(()) + interp_ok(()) } /// For a specific file description, get its ready events and update the corresponding ready @@ -577,7 +577,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { for thread_id in waiter { this.unblock_thread(thread_id, BlockReason::Epoll)?; } - Ok(()) + interp_ok(()) } } @@ -623,9 +623,9 @@ fn check_and_update_one_event_interest<'tcx>( let event_instance = EpollEventInstance::new(flags, epoll_event_interest.data); // Triggers the notification by inserting it to the ready list. ready_list.insert(epoll_key, event_instance); - Ok(true) + interp_ok(true) } else { - Ok(false) + interp_ok(false) } } @@ -665,5 +665,5 @@ fn blocking_epoll_callback<'tcx>( } } ecx.write_int(num_of_events, dest)?; - Ok(()) + interp_ok(()) } diff --git a/src/tools/miri/src/shims/unix/linux/eventfd.rs b/src/tools/miri/src/shims/unix/linux/eventfd.rs index ab7652ca721ca..12e99644357a6 100644 --- a/src/tools/miri/src/shims/unix/linux/eventfd.rs +++ b/src/tools/miri/src/shims/unix/linux/eventfd.rs @@ -37,7 +37,7 @@ impl FileDescription for Event { // We only check the status of EPOLLIN and EPOLLOUT flags for eventfd. If other event flags // need to be supported in the future, the check should be added here. - Ok(EpollReadyEvents { + interp_ok(EpollReadyEvents { epollin: self.counter.get() != 0, epollout: self.counter.get() != MAX_COUNTER, ..EpollReadyEvents::new() @@ -49,7 +49,7 @@ impl FileDescription for Event { _communicate_allowed: bool, _ecx: &mut MiriInterpCx<'tcx>, ) -> InterpResult<'tcx, io::Result<()>> { - Ok(Ok(())) + interp_ok(Ok(())) } /// Read the counter in the buffer and return the counter if succeeded. @@ -68,7 +68,7 @@ impl FileDescription for Event { if len < ty.size.bytes_usize() { ecx.set_last_error_from_io_error(Error::from(ErrorKind::InvalidInput))?; ecx.write_int(-1, dest)?; - return Ok(()); + return interp_ok(()); } // eventfd read at the size of u64. @@ -80,7 +80,7 @@ impl FileDescription for Event { if self.is_nonblock { ecx.set_last_error_from_io_error(Error::from(ErrorKind::WouldBlock))?; ecx.write_int(-1, dest)?; - return Ok(()); + return interp_ok(()); } throw_unsup_format!("eventfd: blocking is unsupported"); @@ -100,7 +100,7 @@ impl FileDescription for Event { ecx.write_int(buf_place.layout.size.bytes(), dest)?; } - Ok(()) + interp_ok(()) } /// A write call adds the 8-byte integer value supplied in @@ -226,6 +226,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { clock: RefCell::new(VClock::default()), }); - Ok(Scalar::from_i32(fd_value)) + interp_ok(Scalar::from_i32(fd_value)) } } diff --git a/src/tools/miri/src/shims/unix/linux/foreign_items.rs b/src/tools/miri/src/shims/unix/linux/foreign_items.rs index e2e76aad7cd6e..9726dac7e516b 100644 --- a/src/tools/miri/src/shims/unix/linux/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/linux/foreign_items.rs @@ -134,7 +134,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // The only supported flags are GRND_RANDOM and GRND_NONBLOCK, // neither of which have any effect on our current PRNG. // See for a discussion of argument sizes. - let _flags = this.read_scalar(&args[3])?.to_i32(); + let _flags = this.read_scalar(&args[3])?.to_i32()?; this.gen_random(ptr, len)?; this.write_scalar(Scalar::from_target_usize(len, this), dest)?; @@ -147,7 +147,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.handle_unsupported_foreign_item(format!( "can't execute syscall with ID {id}" ))?; - return Ok(EmulateItemResult::AlreadyJumped); + return interp_ok(EmulateItemResult::AlreadyJumped); } } } @@ -190,9 +190,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_null(dest)?; } - _ => return Ok(EmulateItemResult::NotSupported), + _ => return interp_ok(EmulateItemResult::NotSupported), }; - Ok(EmulateItemResult::NeedsReturn) + interp_ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/unix/linux/mem.rs b/src/tools/miri/src/shims/unix/linux/mem.rs index b11f60048a070..4f2e17d50c8b4 100644 --- a/src/tools/miri/src/shims/unix/linux/mem.rs +++ b/src/tools/miri/src/shims/unix/linux/mem.rs @@ -25,7 +25,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { #[allow(clippy::arithmetic_side_effects)] // PAGE_SIZE is nonzero if old_address.addr().bytes() % this.machine.page_size != 0 || new_size == 0 { this.set_last_error(this.eval_libc("EINVAL"))?; - return Ok(this.eval_libc("MAP_FAILED")); + return interp_ok(this.eval_libc("MAP_FAILED")); } if flags & this.eval_libc_i32("MREMAP_FIXED") != 0 { @@ -39,7 +39,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if flags & this.eval_libc_i32("MREMAP_MAYMOVE") == 0 { // We only support MREMAP_MAYMOVE, so not passing the flag is just a failure this.set_last_error(this.eval_libc("EINVAL"))?; - return Ok(this.eval_libc("MAP_FAILED")); + return interp_ok(this.eval_libc("MAP_FAILED")); } let align = this.machine.page_align(); @@ -60,6 +60,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { .unwrap(); } - Ok(Scalar::from_pointer(ptr, this)) + interp_ok(Scalar::from_pointer(ptr, this)) } } diff --git a/src/tools/miri/src/shims/unix/linux/sync.rs b/src/tools/miri/src/shims/unix/linux/sync.rs index bd21203907419..5108bc8299cbb 100644 --- a/src/tools/miri/src/shims/unix/linux/sync.rs +++ b/src/tools/miri/src/shims/unix/linux/sync.rs @@ -78,7 +78,7 @@ pub fn futex<'tcx>( let einval = this.eval_libc("EINVAL"); this.set_last_error(einval)?; this.write_scalar(Scalar::from_target_isize(-1, this), dest)?; - return Ok(()); + return interp_ok(()); } let timeout = this.deref_pointer_as(&args[3], this.libc_ty_layout("timespec"))?; @@ -91,7 +91,7 @@ pub fn futex<'tcx>( let einval = this.eval_libc("EINVAL"); this.set_last_error(einval)?; this.write_scalar(Scalar::from_target_isize(-1, this), dest)?; - return Ok(()); + return interp_ok(()); } }; let timeout_clock = if op & futex_realtime == futex_realtime { @@ -201,7 +201,7 @@ pub fn futex<'tcx>( let einval = this.eval_libc("EINVAL"); this.set_last_error(einval)?; this.write_scalar(Scalar::from_target_isize(-1, this), dest)?; - return Ok(()); + return interp_ok(()); } // Together with the SeqCst fence in futex_wait, this makes sure that futex_wait // will see the latest value on addr which could be changed by our caller @@ -221,5 +221,5 @@ pub fn futex<'tcx>( op => throw_unsup_format!("Miri does not support `futex` syscall with op={}", op), } - Ok(()) + interp_ok(()) } diff --git a/src/tools/miri/src/shims/unix/macos/foreign_items.rs b/src/tools/miri/src/shims/unix/macos/foreign_items.rs index ce4ea0816f016..2751d379dc006 100644 --- a/src/tools/miri/src/shims/unix/macos/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/macos/foreign_items.rs @@ -207,9 +207,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.os_unfair_lock_assert_not_owner(lock_op)?; } - _ => return Ok(EmulateItemResult::NotSupported), + _ => return interp_ok(EmulateItemResult::NotSupported), }; - Ok(EmulateItemResult::NeedsReturn) + interp_ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/unix/macos/sync.rs b/src/tools/miri/src/shims/unix/macos/sync.rs index 8f9237a1d46ea..2f96849d0d2d9 100644 --- a/src/tools/miri/src/shims/unix/macos/sync.rs +++ b/src/tools/miri/src/shims/unix/macos/sync.rs @@ -20,7 +20,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // os_unfair_lock holds a 32-bit value, is initialized with zero and // must be assumed to be opaque. Therefore, we can just store our // internal mutex ID in the structure without anyone noticing. - this.mutex_get_or_create_id(&lock, 0, |_| Ok(None)) + this.mutex_get_or_create_id(&lock, 0, |_| interp_ok(None)) } } @@ -43,7 +43,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.mutex_lock(id); } - Ok(()) + interp_ok(()) } fn os_unfair_lock_trylock( @@ -63,7 +63,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(Scalar::from_bool(true), dest)?; } - Ok(()) + interp_ok(()) } fn os_unfair_lock_unlock(&mut self, lock_op: &OpTy<'tcx>) -> InterpResult<'tcx> { @@ -77,7 +77,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { )); } - Ok(()) + interp_ok(()) } fn os_unfair_lock_assert_owner(&mut self, lock_op: &OpTy<'tcx>) -> InterpResult<'tcx> { @@ -90,7 +90,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { )); } - Ok(()) + interp_ok(()) } fn os_unfair_lock_assert_not_owner(&mut self, lock_op: &OpTy<'tcx>) -> InterpResult<'tcx> { @@ -103,6 +103,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { )); } - Ok(()) + interp_ok(()) } } diff --git a/src/tools/miri/src/shims/unix/mem.rs b/src/tools/miri/src/shims/unix/mem.rs index 0397ba3b91ad9..9273748ef3bfd 100644 --- a/src/tools/miri/src/shims/unix/mem.rs +++ b/src/tools/miri/src/shims/unix/mem.rs @@ -49,7 +49,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { && matches!(&*this.tcx.sess.target.os, "macos" | "solaris" | "illumos") && (flags & map_fixed) != 0 { - return Ok(Scalar::from_maybe_pointer(Pointer::from_addr_invalid(addr), this)); + return interp_ok(Scalar::from_maybe_pointer(Pointer::from_addr_invalid(addr), this)); } let prot_read = this.eval_libc_i32("PROT_READ"); @@ -58,11 +58,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // First, we do some basic argument validation as required by mmap if (flags & (map_private | map_shared)).count_ones() != 1 { this.set_last_error(this.eval_libc("EINVAL"))?; - return Ok(this.eval_libc("MAP_FAILED")); + return interp_ok(this.eval_libc("MAP_FAILED")); } if length == 0 { this.set_last_error(this.eval_libc("EINVAL"))?; - return Ok(this.eval_libc("MAP_FAILED")); + return interp_ok(this.eval_libc("MAP_FAILED")); } // If a user tries to map a file, we want to loudly inform them that this is not going @@ -104,11 +104,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let align = this.machine.page_align(); let Some(map_length) = length.checked_next_multiple_of(this.machine.page_size) else { this.set_last_error(this.eval_libc("EINVAL"))?; - return Ok(this.eval_libc("MAP_FAILED")); + return interp_ok(this.eval_libc("MAP_FAILED")); }; if map_length > this.target_usize_max() { this.set_last_error(this.eval_libc("EINVAL"))?; - return Ok(this.eval_libc("MAP_FAILED")); + return interp_ok(this.eval_libc("MAP_FAILED")); } let ptr = @@ -121,7 +121,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ) .unwrap(); - Ok(Scalar::from_pointer(ptr, this)) + interp_ok(Scalar::from_pointer(ptr, this)) } fn munmap(&mut self, addr: &OpTy<'tcx>, length: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { @@ -135,16 +135,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { #[allow(clippy::arithmetic_side_effects)] // PAGE_SIZE is nonzero if addr.addr().bytes() % this.machine.page_size != 0 { this.set_last_error(this.eval_libc("EINVAL"))?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); } let Some(length) = length.checked_next_multiple_of(this.machine.page_size) else { this.set_last_error(this.eval_libc("EINVAL"))?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); }; if length > this.target_usize_max() { this.set_last_error(this.eval_libc("EINVAL"))?; - return Ok(this.eval_libc("MAP_FAILED")); + return interp_ok(this.eval_libc("MAP_FAILED")); } let length = Size::from_bytes(length); @@ -154,6 +154,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { MemoryKind::Machine(MiriMemoryKind::Mmap), )?; - Ok(Scalar::from_i32(0)) + interp_ok(Scalar::from_i32(0)) } } diff --git a/src/tools/miri/src/shims/unix/solarish/foreign_items.rs b/src/tools/miri/src/shims/unix/solarish/foreign_items.rs index a0cc4a62bfd2b..c10098f2733ac 100644 --- a/src/tools/miri/src/shims/unix/solarish/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/solarish/foreign_items.rs @@ -99,8 +99,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_null(dest)?; } - _ => return Ok(EmulateItemResult::NotSupported), + _ => return interp_ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsReturn) + interp_ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/unix/sync.rs b/src/tools/miri/src/shims/unix/sync.rs index fea994663c076..017291f81a22e 100644 --- a/src/tools/miri/src/shims/unix/sync.rs +++ b/src/tools/miri/src/shims/unix/sync.rs @@ -10,7 +10,7 @@ use crate::*; #[inline] fn mutexattr_kind_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> { - Ok(match &*ecx.tcx.sess.target.os { + interp_ok(match &*ecx.tcx.sess.target.os { "linux" | "illumos" | "solaris" | "macos" | "freebsd" | "android" => 0, os => throw_unsup_format!("`pthread_mutexattr` is not supported on {os}"), }) @@ -112,7 +112,7 @@ fn mutex_id_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> { } } - Ok(offset) + interp_ok(offset) } /// Eagerly create and initialize a new mutex. @@ -125,7 +125,7 @@ fn mutex_create<'tcx>( let address = mutex.ptr().addr().bytes(); let data = Box::new(AdditionalMutexData { address, kind }); ecx.mutex_create(&mutex, mutex_id_offset(ecx)?, Some(data))?; - Ok(()) + interp_ok(()) } /// Returns the `MutexId` of the mutex stored at `mutex_op`. @@ -144,7 +144,7 @@ fn mutex_get_id<'tcx>( // an ID yet. We have to determine the mutex kind from the static initializer. let kind = mutex_kind_from_static_initializer(ecx, &mutex)?; - Ok(Some(Box::new(AdditionalMutexData { kind, address }))) + interp_ok(Some(Box::new(AdditionalMutexData { kind, address }))) })?; // Check that the mutex has not been moved since last use. @@ -155,7 +155,7 @@ fn mutex_get_id<'tcx>( throw_ub_format!("pthread_mutex_t can't be moved after first use") } - Ok(id) + interp_ok(id) } /// Returns the kind of a static initializer. @@ -163,7 +163,7 @@ fn mutex_kind_from_static_initializer<'tcx>( ecx: &MiriInterpCx<'tcx>, mutex: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx, MutexKind> { - Ok(match &*ecx.tcx.sess.target.os { + interp_ok(match &*ecx.tcx.sess.target.os { // Only linux has static initializers other than PTHREAD_MUTEX_DEFAULT. "linux" => { let offset = if ecx.pointer_size().bytes() == 8 { 16 } else { 12 }; @@ -186,7 +186,7 @@ fn mutex_translate_kind<'tcx>( ecx: &MiriInterpCx<'tcx>, kind: i32, ) -> InterpResult<'tcx, MutexKind> { - Ok(if kind == (ecx.eval_libc_i32("PTHREAD_MUTEX_NORMAL")) { + interp_ok(if kind == (ecx.eval_libc_i32("PTHREAD_MUTEX_NORMAL")) { MutexKind::Normal } else if kind == ecx.eval_libc_i32("PTHREAD_MUTEX_ERRORCHECK") { MutexKind::ErrorCheck @@ -237,7 +237,7 @@ fn rwlock_id_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> { ); } - Ok(offset) + interp_ok(offset) } fn rwlock_get_id<'tcx>( @@ -248,7 +248,7 @@ fn rwlock_get_id<'tcx>( let address = rwlock.ptr().addr().bytes(); let id = ecx.rwlock_get_or_create_id(&rwlock, rwlock_id_offset(ecx)?, |_| { - Ok(Some(Box::new(AdditionalRwLockData { address }))) + interp_ok(Some(Box::new(AdditionalRwLockData { address }))) })?; // Check that the rwlock has not been moved since last use. @@ -259,7 +259,7 @@ fn rwlock_get_id<'tcx>( throw_ub_format!("pthread_rwlock_t can't be moved after first use") } - Ok(id) + interp_ok(id) } // pthread_condattr_t. @@ -268,7 +268,7 @@ fn rwlock_get_id<'tcx>( #[inline] fn condattr_clock_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> { - Ok(match &*ecx.tcx.sess.target.os { + interp_ok(match &*ecx.tcx.sess.target.os { "linux" | "illumos" | "solaris" | "freebsd" | "android" => 0, // macOS does not have a clock attribute. os => throw_unsup_format!("`pthread_condattr` clock field is not supported on {os}"), @@ -292,7 +292,7 @@ fn cond_translate_clock_id<'tcx>( ecx: &MiriInterpCx<'tcx>, raw_id: i32, ) -> InterpResult<'tcx, ClockId> { - Ok(if raw_id == ecx.eval_libc_i32("CLOCK_REALTIME") { + interp_ok(if raw_id == ecx.eval_libc_i32("CLOCK_REALTIME") { ClockId::Realtime } else if raw_id == ecx.eval_libc_i32("CLOCK_MONOTONIC") { ClockId::Monotonic @@ -342,7 +342,7 @@ fn cond_id_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> { ); } - Ok(offset) + interp_ok(offset) } #[derive(Debug, Clone, Copy)] @@ -369,7 +369,7 @@ fn cond_get_id<'tcx>( let address = cond.ptr().addr().bytes(); let id = ecx.condvar_get_or_create_id(&cond, cond_id_offset(ecx)?, |_ecx| { // This used the static initializer. The clock there is always CLOCK_REALTIME. - Ok(Some(Box::new(AdditionalCondData { address, clock_id: ClockId::Realtime }))) + interp_ok(Some(Box::new(AdditionalCondData { address, clock_id: ClockId::Realtime }))) })?; // Check that the mutex has not been moved since last use. @@ -380,7 +380,7 @@ fn cond_get_id<'tcx>( throw_ub_format!("pthread_cond_t can't be moved after first use") } - Ok(id) + interp_ok(id) } impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} @@ -390,7 +390,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { mutexattr_set_kind(this, attr_op, PTHREAD_MUTEX_KIND_UNCHANGED)?; - Ok(()) + interp_ok(()) } fn pthread_mutexattr_settype( @@ -411,10 +411,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { mutexattr_set_kind(this, attr_op, kind)?; } else { let einval = this.eval_libc_i32("EINVAL"); - return Ok(Scalar::from_i32(einval)); + return interp_ok(Scalar::from_i32(einval)); } - Ok(Scalar::from_i32(0)) + interp_ok(Scalar::from_i32(0)) } fn pthread_mutexattr_destroy(&mut self, attr_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> { @@ -439,7 +439,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { &this.deref_pointer_as(attr_op, this.libc_ty_layout("pthread_mutexattr_t"))?, )?; - Ok(()) + interp_ok(()) } fn pthread_mutex_init( @@ -458,7 +458,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { mutex_create(this, mutex_op, kind)?; - Ok(()) + interp_ok(()) } fn pthread_mutex_lock( @@ -478,7 +478,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let owner_thread = this.mutex_get_owner(id); if owner_thread != this.active_thread() { this.mutex_enqueue_and_block(id, Some((Scalar::from_i32(0), dest.clone()))); - return Ok(()); + return interp_ok(()); } else { // Trying to acquire the same mutex again. match kind { @@ -498,7 +498,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { 0 }; this.write_scalar(Scalar::from_i32(ret), dest)?; - Ok(()) + interp_ok(()) } fn pthread_mutex_trylock(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { @@ -510,7 +510,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { .expect("data should always exist for pthread mutexes") .kind; - Ok(Scalar::from_i32(if this.mutex_is_locked(id) { + interp_ok(Scalar::from_i32(if this.mutex_is_locked(id) { let owner_thread = this.mutex_get_owner(id); if owner_thread != this.active_thread() { this.eval_libc_i32("EBUSY") @@ -542,7 +542,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let Some(_old_locked_count) = this.mutex_unlock(id)? { // The mutex was locked by the current thread. - Ok(Scalar::from_i32(0)) + interp_ok(Scalar::from_i32(0)) } else { // The mutex was locked by another thread or not locked at all. See // the “Unlock When Not Owner” column in @@ -557,7 +557,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "unlocked a PTHREAD_MUTEX_NORMAL mutex that was not locked by the current thread" ), MutexKind::ErrorCheck | MutexKind::Recursive => - Ok(Scalar::from_i32(this.eval_libc_i32("EPERM"))), + interp_ok(Scalar::from_i32(this.eval_libc_i32("EPERM"))), } } } @@ -579,7 +579,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { )?; // FIXME: delete interpreter state associated with this mutex. - Ok(()) + interp_ok(()) } fn pthread_rwlock_rdlock( @@ -598,7 +598,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_null(dest)?; } - Ok(()) + interp_ok(()) } fn pthread_rwlock_tryrdlock(&mut self, rwlock_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { @@ -607,10 +607,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let id = rwlock_get_id(this, rwlock_op)?; if this.rwlock_is_write_locked(id) { - Ok(Scalar::from_i32(this.eval_libc_i32("EBUSY"))) + interp_ok(Scalar::from_i32(this.eval_libc_i32("EBUSY"))) } else { this.rwlock_reader_lock(id); - Ok(Scalar::from_i32(0)) + interp_ok(Scalar::from_i32(0)) } } @@ -642,7 +642,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_null(dest)?; } - Ok(()) + interp_ok(()) } fn pthread_rwlock_trywrlock(&mut self, rwlock_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { @@ -651,10 +651,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let id = rwlock_get_id(this, rwlock_op)?; if this.rwlock_is_locked(id) { - Ok(Scalar::from_i32(this.eval_libc_i32("EBUSY"))) + interp_ok(Scalar::from_i32(this.eval_libc_i32("EBUSY"))) } else { this.rwlock_writer_lock(id); - Ok(Scalar::from_i32(0)) + interp_ok(Scalar::from_i32(0)) } } @@ -665,7 +665,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { #[allow(clippy::if_same_then_else)] if this.rwlock_reader_unlock(id)? || this.rwlock_writer_unlock(id)? { - Ok(()) + interp_ok(()) } else { throw_ub_format!("unlocked an rwlock that was not locked by the active thread"); } @@ -688,7 +688,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { )?; // FIXME: delete interpreter state associated with this rwlock. - Ok(()) + interp_ok(()) } fn pthread_condattr_init(&mut self, attr_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> { @@ -703,7 +703,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { condattr_set_clock_id(this, attr_op, default_clock_id)?; } - Ok(()) + interp_ok(()) } fn pthread_condattr_setclock( @@ -720,10 +720,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { condattr_set_clock_id(this, attr_op, clock_id)?; } else { let einval = this.eval_libc_i32("EINVAL"); - return Ok(Scalar::from_i32(einval)); + return interp_ok(Scalar::from_i32(einval)); } - Ok(Scalar::from_i32(0)) + interp_ok(Scalar::from_i32(0)) } fn pthread_condattr_getclock( @@ -736,7 +736,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let clock_id = condattr_get_clock_id(this, attr_op)?; this.write_scalar(Scalar::from_i32(clock_id), &this.deref_pointer(clk_id_op)?)?; - Ok(()) + interp_ok(()) } fn pthread_condattr_destroy(&mut self, attr_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> { @@ -754,7 +754,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { &this.deref_pointer_as(attr_op, this.libc_ty_layout("pthread_condattr_t"))?, )?; - Ok(()) + interp_ok(()) } fn pthread_cond_init( @@ -781,21 +781,21 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Some(Box::new(AdditionalCondData { address, clock_id })), )?; - Ok(()) + interp_ok(()) } fn pthread_cond_signal(&mut self, cond_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> { let this = self.eval_context_mut(); let id = cond_get_id(this, cond_op)?; this.condvar_signal(id)?; - Ok(()) + interp_ok(()) } fn pthread_cond_broadcast(&mut self, cond_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> { let this = self.eval_context_mut(); let id = cond_get_id(this, cond_op)?; while this.condvar_signal(id)? {} - Ok(()) + interp_ok(()) } fn pthread_cond_wait( @@ -818,7 +818,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { dest.clone(), )?; - Ok(()) + interp_ok(()) } fn pthread_cond_timedwait( @@ -845,7 +845,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { None => { let einval = this.eval_libc("EINVAL"); this.write_scalar(einval, dest)?; - return Ok(()); + return interp_ok(()); } }; let timeout_clock = match clock_id { @@ -865,7 +865,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { dest.clone(), )?; - Ok(()) + interp_ok(()) } fn pthread_cond_destroy(&mut self, cond_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> { @@ -882,6 +882,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_uninit(&this.deref_pointer_as(cond_op, this.libc_ty_layout("pthread_cond_t"))?)?; // FIXME: delete interpreter state associated with this condvar. - Ok(()) + interp_ok(()) } } diff --git a/src/tools/miri/src/shims/unix/thread.rs b/src/tools/miri/src/shims/unix/thread.rs index 96af4ae51efaa..5515524f2f187 100644 --- a/src/tools/miri/src/shims/unix/thread.rs +++ b/src/tools/miri/src/shims/unix/thread.rs @@ -27,7 +27,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.machine.layouts.mut_raw_ptr, )?; - Ok(()) + interp_ok(()) } fn pthread_join(&mut self, thread: &OpTy<'tcx>, retval: &OpTy<'tcx>) -> InterpResult<'tcx, ()> { @@ -41,7 +41,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let thread_id = this.read_scalar(thread)?.to_int(this.libc_ty_layout("pthread_t").size)?; this.join_thread_exclusive(thread_id.try_into().expect("thread ID should fit in u32"))?; - Ok(()) + interp_ok(()) } fn pthread_detach(&mut self, thread: &OpTy<'tcx>) -> InterpResult<'tcx, ()> { @@ -53,14 +53,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { /*allow_terminated_joined*/ false, )?; - Ok(()) + interp_ok(()) } fn pthread_self(&mut self) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); let thread_id = this.active_thread(); - Ok(Scalar::from_uint(thread_id.to_u32(), this.libc_ty_layout("pthread_t").size)) + interp_ok(Scalar::from_uint(thread_id.to_u32(), this.libc_ty_layout("pthread_t").size)) } /// Set the name of the current thread. `max_name_len` is the maximal length of the name @@ -81,12 +81,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Comparing with `>=` to account for null terminator. if name.len() >= max_name_len { - return Ok(this.eval_libc("ERANGE")); + return interp_ok(this.eval_libc("ERANGE")); } this.set_thread_name(thread, name); - Ok(Scalar::from_u32(0)) + interp_ok(Scalar::from_u32(0)) } fn pthread_getname_np( @@ -106,7 +106,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let name = this.get_thread_name(thread).unwrap_or(b"").to_owned(); let (success, _written) = this.write_c_str(&name, name_out, len)?; - Ok(if success { Scalar::from_u32(0) } else { this.eval_libc("ERANGE") }) + interp_ok(if success { Scalar::from_u32(0) } else { this.eval_libc("ERANGE") }) } fn sched_yield(&mut self) -> InterpResult<'tcx, ()> { @@ -114,6 +114,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.yield_active_thread(); - Ok(()) + interp_ok(()) } } diff --git a/src/tools/miri/src/shims/unix/unnamed_socket.rs b/src/tools/miri/src/shims/unix/unnamed_socket.rs index d1bfa563387dd..763f9f2402768 100644 --- a/src/tools/miri/src/shims/unix/unnamed_socket.rs +++ b/src/tools/miri/src/shims/unix/unnamed_socket.rs @@ -103,7 +103,7 @@ impl FileDescription for AnonSocket { epoll_ready_events.epollerr = true; } } - Ok(epoll_ready_events) + interp_ok(epoll_ready_events) } fn close<'tcx>( @@ -122,7 +122,7 @@ impl FileDescription for AnonSocket { // Notify peer fd that close has happened, since that can unblock reads and writes. ecx.check_and_update_readiness(&peer_fd)?; } - Ok(Ok(())) + interp_ok(Ok(())) } fn read<'tcx>( @@ -344,7 +344,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(sv0, &sv)?; this.write_scalar(sv1, &sv.offset(sv.layout.size, sv.layout, this)?)?; - Ok(Scalar::from_i32(0)) + interp_ok(Scalar::from_i32(0)) } fn pipe2( @@ -396,6 +396,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(pipefd0, &pipefd)?; this.write_scalar(pipefd1, &pipefd.offset(pipefd.layout.size, pipefd.layout, this)?)?; - Ok(Scalar::from_i32(0)) + interp_ok(Scalar::from_i32(0)) } } diff --git a/src/tools/miri/src/shims/wasi/foreign_items.rs b/src/tools/miri/src/shims/wasi/foreign_items.rs index e9fe90081d09c..b5de950de9adc 100644 --- a/src/tools/miri/src/shims/wasi/foreign_items.rs +++ b/src/tools/miri/src/shims/wasi/foreign_items.rs @@ -33,8 +33,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_pointer(res, dest)?; } - _ => return Ok(EmulateItemResult::NotSupported), + _ => return interp_ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsReturn) + interp_ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/windows/env.rs b/src/tools/miri/src/shims/windows/env.rs index 9707482b1e2c2..a6ace6f9bdc4c 100644 --- a/src/tools/miri/src/shims/windows/env.rs +++ b/src/tools/miri/src/shims/windows/env.rs @@ -24,12 +24,12 @@ impl WindowsEnvVars { _ecx: &mut InterpCx<'tcx, MiriMachine<'tcx>>, env_vars: FxHashMap, ) -> InterpResult<'tcx, Self> { - Ok(Self { map: env_vars }) + interp_ok(Self { map: env_vars }) } /// Implementation detail for [`InterpCx::get_env_var`]. pub(crate) fn get<'tcx>(&self, name: &OsStr) -> InterpResult<'tcx, Option> { - Ok(self.map.get(name).cloned()) + interp_ok(self.map.get(name).cloned()) } } @@ -52,7 +52,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let buf_size = this.read_scalar(size_op)?.to_u32()?; // in characters let name = this.read_os_str_from_wide_str(name_ptr)?; - Ok(match this.machine.env_vars.windows().map.get(&name).cloned() { + interp_ok(match this.machine.env_vars.windows().map.get(&name).cloned() { Some(val) => { Scalar::from_u32(windows_check_buffer_size(this.write_os_str_to_wide_str( &val, @@ -89,7 +89,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let envblock_ptr = this.alloc_os_str_as_wide_str(&env_vars, MiriMemoryKind::Runtime.into())?; // If the function succeeds, the return value is a pointer to the environment block of the current process. - Ok(envblock_ptr) + interp_ok(envblock_ptr) } #[allow(non_snake_case)] @@ -100,7 +100,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let env_block_ptr = this.read_pointer(env_block_op)?; this.deallocate_ptr(env_block_ptr, None, MiriMemoryKind::Runtime.into())?; // If the function succeeds, the return value is nonzero. - Ok(Scalar::from_i32(1)) + interp_ok(Scalar::from_i32(1)) } #[allow(non_snake_case)] @@ -128,11 +128,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } else if this.ptr_is_null(value_ptr)? { // Delete environment variable `{name}` if it exists. this.machine.env_vars.windows_mut().map.remove(&name); - Ok(this.eval_windows("c", "TRUE")) + interp_ok(this.eval_windows("c", "TRUE")) } else { let value = this.read_os_str_from_wide_str(value_ptr)?; this.machine.env_vars.windows_mut().map.insert(name, value); - Ok(this.eval_windows("c", "TRUE")) + interp_ok(this.eval_windows("c", "TRUE")) } } @@ -151,7 +151,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`GetCurrentDirectoryW`", reject_with)?; this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; - return Ok(Scalar::from_u32(0)); + return interp_ok(Scalar::from_u32(0)); } // If we cannot get the current directory, we return 0 @@ -159,13 +159,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Ok(cwd) => { // This can in fact return 0. It is up to the caller to set last_error to 0 // beforehand and check it afterwards to exclude that case. - return Ok(Scalar::from_u32(windows_check_buffer_size( + return interp_ok(Scalar::from_u32(windows_check_buffer_size( this.write_path_to_wide_str(&cwd, buf, size)?, ))); } Err(e) => this.set_last_error_from_io_error(e)?, } - Ok(Scalar::from_u32(0)) + interp_ok(Scalar::from_u32(0)) } #[allow(non_snake_case)] @@ -184,14 +184,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.reject_in_isolation("`SetCurrentDirectoryW`", reject_with)?; this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; - return Ok(this.eval_windows("c", "FALSE")); + return interp_ok(this.eval_windows("c", "FALSE")); } match env::set_current_dir(path) { - Ok(()) => Ok(this.eval_windows("c", "TRUE")), + Ok(()) => interp_ok(this.eval_windows("c", "TRUE")), Err(e) => { this.set_last_error_from_io_error(e)?; - Ok(this.eval_windows("c", "FALSE")) + interp_ok(this.eval_windows("c", "FALSE")) } } } @@ -201,7 +201,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let this = self.eval_context_mut(); this.assert_target_os("windows", "GetCurrentProcessId"); - Ok(Scalar::from_u32(this.get_pid())) + interp_ok(Scalar::from_u32(this.get_pid())) } #[allow(non_snake_case)] @@ -227,7 +227,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } // See for docs. - Ok(match directories::UserDirs::new() { + interp_ok(match directories::UserDirs::new() { Some(dirs) => { let home = dirs.home_dir(); let size_avail = if this.ptr_is_null(size.ptr())? { diff --git a/src/tools/miri/src/shims/windows/foreign_items.rs b/src/tools/miri/src/shims/windows/foreign_items.rs index 22634c509bed0..a4f40f9447e79 100644 --- a/src/tools/miri/src/shims/windows/foreign_items.rs +++ b/src/tools/miri/src/shims/windows/foreign_items.rs @@ -22,7 +22,7 @@ pub fn is_dyn_sym(name: &str) -> bool { #[cfg(windows)] fn win_absolute<'tcx>(path: &Path) -> InterpResult<'tcx, io::Result> { // We are on Windows so we can simply let the host do this. - Ok(path::absolute(path)) + interp_ok(path::absolute(path)) } #[cfg(unix)] @@ -33,7 +33,7 @@ fn win_absolute<'tcx>(path: &Path) -> InterpResult<'tcx, io::Result> { // If it starts with `//` (these were backslashes but are already converted) // then this is a magic special path, we just leave it unchanged. if bytes.get(0).copied() == Some(b'/') && bytes.get(1).copied() == Some(b'/') { - return Ok(Ok(path.into())); + return interp_ok(Ok(path.into())); }; // Special treatment for Windows' magic filenames: they are treated as being relative to `\\.\`. let magic_filenames = &[ @@ -43,7 +43,7 @@ fn win_absolute<'tcx>(path: &Path) -> InterpResult<'tcx, io::Result> { if magic_filenames.iter().any(|m| m.as_bytes() == bytes) { let mut result: Vec = br"//./".into(); result.extend(bytes); - return Ok(Ok(bytes_to_os_str(&result)?.into())); + return interp_ok(Ok(bytes_to_os_str(&result)?.into())); } // Otherwise we try to do something kind of close to what Windows does, but this is probably not // right in all cases. We iterate over the components between `/`, and remove trailing `.`, @@ -71,7 +71,7 @@ fn win_absolute<'tcx>(path: &Path) -> InterpResult<'tcx, io::Result> { } } // Let the host `absolute` function do working-dir handling - Ok(path::absolute(bytes_to_os_str(&result)?)) + interp_ok(path::absolute(bytes_to_os_str(&result)?)) } impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} @@ -769,12 +769,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // This function looks and behaves excatly like miri_start_unwind. let [payload] = this.check_shim(abi, Abi::C { unwind: true }, link_name, args)?; this.handle_miri_start_unwind(payload)?; - return Ok(EmulateItemResult::NeedsUnwind); + return interp_ok(EmulateItemResult::NeedsUnwind); } - _ => return Ok(EmulateItemResult::NotSupported), + _ => return interp_ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsReturn) + interp_ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/windows/handle.rs b/src/tools/miri/src/shims/windows/handle.rs index 0a8e0e8fdfb75..69d78f58bed25 100644 --- a/src/tools/miri/src/shims/windows/handle.rs +++ b/src/tools/miri/src/shims/windows/handle.rs @@ -139,10 +139,10 @@ impl Handle { signed_handle as u32 } else { // if a handle doesn't fit in an i32, it isn't valid. - return Ok(None); + return interp_ok(None); }; - Ok(Self::from_packed(handle)) + interp_ok(Self::from_packed(handle)) } } @@ -167,6 +167,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { _ => this.invalid_handle("CloseHandle")?, } - Ok(()) + interp_ok(()) } } diff --git a/src/tools/miri/src/shims/windows/sync.rs b/src/tools/miri/src/shims/windows/sync.rs index 5786b17e50c92..6755c23039eeb 100644 --- a/src/tools/miri/src/shims/windows/sync.rs +++ b/src/tools/miri/src/shims/windows/sync.rs @@ -24,7 +24,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx, bool> { let this = self.eval_context_mut(); - Ok(match this.init_once_status(id) { + interp_ok(match this.init_once_status(id) { InitOnceStatus::Uninitialized => { this.init_once_begin(id); this.write_scalar(this.eval_windows("c", "TRUE"), pending_place)?; @@ -70,7 +70,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if this.init_once_try_begin(id, &pending_place, dest)? { // Done! - return Ok(()); + return interp_ok(()); } // We have to block, and then try again when we are woken up. @@ -86,11 +86,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { @unblock = |this| { let ret = this.init_once_try_begin(id, &pending_place, &dest)?; assert!(ret, "we were woken up but init_once_try_begin still failed"); - Ok(()) + interp_ok(()) } ), ); - Ok(()) + interp_ok(()) } fn InitOnceComplete( @@ -130,7 +130,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.init_once_fail(id)?; } - Ok(this.eval_windows("c", "TRUE")) + interp_ok(this.eval_windows("c", "TRUE")) } fn WaitOnAddress( @@ -154,7 +154,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let invalid_param = this.eval_windows("c", "ERROR_INVALID_PARAMETER"); this.set_last_error(invalid_param)?; this.write_scalar(Scalar::from_i32(0), dest)?; - return Ok(()); + return interp_ok(()); }; let size = Size::from_bytes(size); @@ -188,7 +188,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(Scalar::from_i32(1), dest)?; - Ok(()) + interp_ok(()) } fn WakeByAddressSingle(&mut self, ptr_op: &OpTy<'tcx>) -> InterpResult<'tcx> { @@ -202,7 +202,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let addr = ptr.addr().bytes(); this.futex_wake(addr, u32::MAX)?; - Ok(()) + interp_ok(()) } fn WakeByAddressAll(&mut self, ptr_op: &OpTy<'tcx>) -> InterpResult<'tcx> { let this = self.eval_context_mut(); @@ -215,6 +215,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let addr = ptr.addr().bytes(); while this.futex_wake(addr, u32::MAX)? {} - Ok(()) + interp_ok(()) } } diff --git a/src/tools/miri/src/shims/windows/thread.rs b/src/tools/miri/src/shims/windows/thread.rs index a4c177311d46d..a920abea3a8a0 100644 --- a/src/tools/miri/src/shims/windows/thread.rs +++ b/src/tools/miri/src/shims/windows/thread.rs @@ -79,6 +79,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.join_thread(thread)?; - Ok(0) + interp_ok(0) } } diff --git a/src/tools/miri/src/shims/x86/aesni.rs b/src/tools/miri/src/shims/x86/aesni.rs index dbbae0731e0b7..3a8138654fd98 100644 --- a/src/tools/miri/src/shims/x86/aesni.rs +++ b/src/tools/miri/src/shims/x86/aesni.rs @@ -123,9 +123,9 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } // TODO: Implement the `llvm.x86.aesni.aeskeygenassist` when possible // with an external crate. - _ => return Ok(EmulateItemResult::NotSupported), + _ => return interp_ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsReturn) + interp_ok(EmulateItemResult::NeedsReturn) } } @@ -162,5 +162,5 @@ fn aes_round<'tcx>( this.write_scalar(Scalar::from_u128(res), &dest)?; } - Ok(()) + interp_ok(()) } diff --git a/src/tools/miri/src/shims/x86/avx.rs b/src/tools/miri/src/shims/x86/avx.rs index 169f8f8111adb..080e7c8ce0bcf 100644 --- a/src/tools/miri/src/shims/x86/avx.rs +++ b/src/tools/miri/src/shims/x86/avx.rs @@ -348,8 +348,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // The only thing that needs to be ensured is the correct calling convention. let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; } - _ => return Ok(EmulateItemResult::NotSupported), + _ => return interp_ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsReturn) + interp_ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/x86/avx2.rs b/src/tools/miri/src/shims/x86/avx2.rs index 1dda1a10a0503..331b693a3e45b 100644 --- a/src/tools/miri/src/shims/x86/avx2.rs +++ b/src/tools/miri/src/shims/x86/avx2.rs @@ -435,8 +435,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { shift_simd_by_simd(this, left, right, which, dest)?; } - _ => return Ok(EmulateItemResult::NotSupported), + _ => return interp_ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsReturn) + interp_ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/x86/bmi.rs b/src/tools/miri/src/shims/x86/bmi.rs index e70757f4397ee..59e76abf179c2 100644 --- a/src/tools/miri/src/shims/x86/bmi.rs +++ b/src/tools/miri/src/shims/x86/bmi.rs @@ -30,7 +30,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.expect_target_feature_for_intrinsic(link_name, target_feature)?; if is_64_bit && this.tcx.sess.target.arch != "x86_64" { - return Ok(EmulateItemResult::NotSupported); + return interp_ok(EmulateItemResult::NotSupported); } let [left, right] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; @@ -93,7 +93,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } result } - _ => return Ok(EmulateItemResult::NotSupported), + _ => return interp_ok(EmulateItemResult::NotSupported), }; let result = if is_64_bit { @@ -103,6 +103,6 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }; this.write_scalar(result, dest)?; - Ok(EmulateItemResult::NeedsReturn) + interp_ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/x86/mod.rs b/src/tools/miri/src/shims/x86/mod.rs index 8132de7d64697..19c678cb7faca 100644 --- a/src/tools/miri/src/shims/x86/mod.rs +++ b/src/tools/miri/src/shims/x86/mod.rs @@ -43,7 +43,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // https://www.intel.com/content/www/us/en/docs/cpp-compiler/developer-guide-reference/2021-8/subborrow-u32-subborrow-u64.html "addcarry.32" | "addcarry.64" | "subborrow.32" | "subborrow.64" => { if unprefixed_name.ends_with("64") && this.tcx.sess.target.arch != "x86_64" { - return Ok(EmulateItemResult::NotSupported); + return interp_ok(EmulateItemResult::NotSupported); } let [cb_in, a, b] = this.check_shim(abi, Abi::Unadjusted, link_name, args)?; @@ -67,7 +67,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let is_u64 = unprefixed_name.ends_with("64"); if is_u64 && this.tcx.sess.target.arch != "x86_64" { - return Ok(EmulateItemResult::NotSupported); + return interp_ok(EmulateItemResult::NotSupported); } let [c_in, a, b, out] = this.check_shim(abi, Abi::Unadjusted, link_name, args)?; @@ -157,9 +157,9 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ); } - _ => return Ok(EmulateItemResult::NotSupported), + _ => return interp_ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsReturn) + interp_ok(EmulateItemResult::NeedsReturn) } } @@ -244,7 +244,7 @@ impl FloatBinOp { this.expect_target_feature_for_intrinsic(intrinsic, "avx")?; unord = !unord; } - Ok(Self::Cmp { gt, lt, eq, unord }) + interp_ok(Self::Cmp { gt, lt, eq, unord }) } } @@ -266,7 +266,7 @@ fn bin_op_float<'tcx, F: rustc_apfloat::Float>( Some(std::cmp::Ordering::Equal) => eq, Some(std::cmp::Ordering::Greater) => gt, }; - Ok(bool_to_simd_element(res, Size::from_bits(F::BITS))) + interp_ok(bool_to_simd_element(res, Size::from_bits(F::BITS))) } FloatBinOp::Min => { let left_scalar = left.to_scalar(); @@ -280,9 +280,9 @@ fn bin_op_float<'tcx, F: rustc_apfloat::Float>( || right.is_nan() || left >= right { - Ok(right_scalar) + interp_ok(right_scalar) } else { - Ok(left_scalar) + interp_ok(left_scalar) } } FloatBinOp::Max => { @@ -297,9 +297,9 @@ fn bin_op_float<'tcx, F: rustc_apfloat::Float>( || right.is_nan() || left <= right { - Ok(right_scalar) + interp_ok(right_scalar) } else { - Ok(left_scalar) + interp_ok(left_scalar) } } } @@ -332,7 +332,7 @@ fn bin_op_simd_float_first<'tcx, F: rustc_apfloat::Float>( this.copy_op(&this.project_index(&left, i)?, &this.project_index(&dest, i)?)?; } - Ok(()) + interp_ok(()) } /// Performs `which` operation on each component of `left` and @@ -360,7 +360,7 @@ fn bin_op_simd_float_all<'tcx, F: rustc_apfloat::Float>( this.write_scalar(res, &dest)?; } - Ok(()) + interp_ok(()) } #[derive(Copy, Clone)] @@ -391,7 +391,7 @@ fn unary_op_f32<'tcx>( // Apply a relative error with a magnitude on the order of 2^-12 to simulate the // inaccuracy of RCP. let res = apply_random_float_error(this, div, -12); - Ok(Scalar::from_f32(res)) + interp_ok(Scalar::from_f32(res)) } FloatUnaryOp::Rsqrt => { let op = op.to_scalar().to_u32()?; @@ -401,7 +401,7 @@ fn unary_op_f32<'tcx>( // Apply a relative error with a magnitude on the order of 2^-12 to simulate the // inaccuracy of RSQRT. let res = apply_random_float_error(this, rsqrt, -12); - Ok(Scalar::from_f32(res)) + interp_ok(Scalar::from_f32(res)) } } } @@ -442,7 +442,7 @@ fn unary_op_ss<'tcx>( this.copy_op(&this.project_index(&op, i)?, &this.project_index(&dest, i)?)?; } - Ok(()) + interp_ok(()) } /// Performs `which` operation on each component of `op`, storing the @@ -466,7 +466,7 @@ fn unary_op_ps<'tcx>( this.write_scalar(res, &dest)?; } - Ok(()) + interp_ok(()) } enum ShiftOp { @@ -532,7 +532,7 @@ fn shift_simd_by_scalar<'tcx>( this.write_scalar(res, &dest)?; } - Ok(()) + interp_ok(()) } /// Shifts each element of `left` by the corresponding element of `right`. @@ -587,7 +587,7 @@ fn shift_simd_by_simd<'tcx>( this.write_scalar(res, &dest)?; } - Ok(()) + interp_ok(()) } /// Takes a 128-bit vector, transmutes it to `[u64; 2]` and extracts @@ -633,7 +633,7 @@ fn round_first<'tcx, F: rustc_apfloat::Float>( this.copy_op(&this.project_index(&left, i)?, &this.project_index(&dest, i)?)?; } - Ok(()) + interp_ok(()) } // Rounds all elements of `op` according to `rounding`. @@ -659,7 +659,7 @@ fn round_all<'tcx, F: rustc_apfloat::Float>( )?; } - Ok(()) + interp_ok(()) } /// Gets equivalent `rustc_apfloat::Round` from rounding mode immediate of @@ -671,14 +671,14 @@ fn rounding_from_imm<'tcx>(rounding: i32) -> InterpResult<'tcx, rustc_apfloat::R match rounding & !0b1000 { // When the third bit is 0, the rounding mode is determined by the // first two bits. - 0b000 => Ok(rustc_apfloat::Round::NearestTiesToEven), - 0b001 => Ok(rustc_apfloat::Round::TowardNegative), - 0b010 => Ok(rustc_apfloat::Round::TowardPositive), - 0b011 => Ok(rustc_apfloat::Round::TowardZero), + 0b000 => interp_ok(rustc_apfloat::Round::NearestTiesToEven), + 0b001 => interp_ok(rustc_apfloat::Round::TowardNegative), + 0b010 => interp_ok(rustc_apfloat::Round::TowardPositive), + 0b011 => interp_ok(rustc_apfloat::Round::TowardZero), // When the third bit is 1, the rounding mode is determined by the // SSE status register. Since we do not support modifying it from // Miri (or Rust), we assume it to be at its default mode (round-to-nearest). - 0b100..=0b111 => Ok(rustc_apfloat::Round::NearestTiesToEven), + 0b100..=0b111 => interp_ok(rustc_apfloat::Round::NearestTiesToEven), rounding => panic!("invalid rounding mode 0x{rounding:02x}"), } } @@ -717,7 +717,7 @@ fn convert_float_to_int<'tcx>( this.write_scalar(Scalar::from_int(0, dest.layout.size), &dest)?; } - Ok(()) + interp_ok(()) } /// Calculates absolute value of integers in `op` and stores the result in `dest`. @@ -747,7 +747,7 @@ fn int_abs<'tcx>( this.write_immediate(*res, &dest)?; } - Ok(()) + interp_ok(()) } /// Splits `op` (which must be a SIMD vector) into 128-bit chunks. @@ -778,7 +778,7 @@ fn split_simd_to_128bit_chunks<'tcx, P: Projectable<'tcx, Provenance>>( .unwrap(); let chunked_op = op.transmute(chunked_layout, this)?; - Ok((num_chunks, items_per_chunk, chunked_op)) + interp_ok((num_chunks, items_per_chunk, chunked_op)) } /// Horizontally performs `which` operation on adjacent values of @@ -830,7 +830,7 @@ fn horizontal_bin_op<'tcx>( } } - Ok(()) + interp_ok(()) } /// Conditionally multiplies the packed floating-point elements in @@ -892,7 +892,7 @@ fn conditional_dot_product<'tcx>( } } - Ok(()) + interp_ok(()) } /// Calculates two booleans. @@ -923,7 +923,7 @@ fn test_bits_masked<'tcx>( masked_set &= (op & mask) == mask; } - Ok((all_zero, masked_set)) + interp_ok((all_zero, masked_set)) } /// Calculates two booleans. @@ -956,7 +956,7 @@ fn test_high_bits_masked<'tcx>( negated &= (!op & mask) >> high_bit_offset == 0; } - Ok((direct, negated)) + interp_ok((direct, negated)) } /// Conditionally loads from `ptr` according the high bit of each @@ -989,7 +989,7 @@ fn mask_load<'tcx>( } } - Ok(()) + interp_ok(()) } /// Conditionally stores into `ptr` according the high bit of each @@ -1023,7 +1023,7 @@ fn mask_store<'tcx>( } } - Ok(()) + interp_ok(()) } /// Compute the sum of absolute differences of quadruplets of unsigned @@ -1082,7 +1082,7 @@ fn mpsadbw<'tcx>( } } - Ok(()) + interp_ok(()) } /// Multiplies packed 16-bit signed integer values, truncates the 32-bit @@ -1120,7 +1120,7 @@ fn pmulhrsw<'tcx>( this.write_scalar(Scalar::from_i16(res), &dest)?; } - Ok(()) + interp_ok(()) } /// Perform a carry-less multiplication of two 64-bit integers, selected from `left` and `right` according to `imm8`, @@ -1182,7 +1182,7 @@ fn pclmulqdq<'tcx>( let dest_high = this.project_index(&dest, 1)?; this.write_scalar(Scalar::from_u64(result_high), &dest_high)?; - Ok(()) + interp_ok(()) } /// Packs two N-bit integer vectors to a single N/2-bit integers. @@ -1227,7 +1227,7 @@ fn pack_generic<'tcx>( } } - Ok(()) + interp_ok(()) } /// Converts two 16-bit integer vectors to a single 8-bit integer @@ -1245,7 +1245,7 @@ fn packsswb<'tcx>( pack_generic(this, left, right, dest, |op| { let op = op.to_i16()?; let res = i8::try_from(op).unwrap_or(if op < 0 { i8::MIN } else { i8::MAX }); - Ok(Scalar::from_i8(res)) + interp_ok(Scalar::from_i8(res)) }) } @@ -1264,7 +1264,7 @@ fn packuswb<'tcx>( pack_generic(this, left, right, dest, |op| { let op = op.to_i16()?; let res = u8::try_from(op).unwrap_or(if op < 0 { 0 } else { u8::MAX }); - Ok(Scalar::from_u8(res)) + interp_ok(Scalar::from_u8(res)) }) } @@ -1283,7 +1283,7 @@ fn packssdw<'tcx>( pack_generic(this, left, right, dest, |op| { let op = op.to_i32()?; let res = i16::try_from(op).unwrap_or(if op < 0 { i16::MIN } else { i16::MAX }); - Ok(Scalar::from_i16(res)) + interp_ok(Scalar::from_i16(res)) }) } @@ -1302,7 +1302,7 @@ fn packusdw<'tcx>( pack_generic(this, left, right, dest, |op| { let op = op.to_i32()?; let res = u16::try_from(op).unwrap_or(if op < 0 { 0 } else { u16::MAX }); - Ok(Scalar::from_u16(res)) + interp_ok(Scalar::from_u16(res)) }) } @@ -1334,7 +1334,7 @@ fn psign<'tcx>( this.write_immediate(*res, &dest)?; } - Ok(()) + interp_ok(()) } /// Calcultates either `a + b + cb_in` or `a - b - cb_in` depending on the value @@ -1358,5 +1358,5 @@ fn carrying_add<'tcx>( this.binary_op(op, &sum, &ImmTy::from_uint(cb_in, a.layout))?.to_pair(this); let cb_out = overflow1.to_scalar().to_bool()? | overflow2.to_scalar().to_bool()?; - Ok((sum, Scalar::from_u8(cb_out.into()))) + interp_ok((sum, Scalar::from_u8(cb_out.into()))) } diff --git a/src/tools/miri/src/shims/x86/sha.rs b/src/tools/miri/src/shims/x86/sha.rs index d9cd34377e683..8553771b694e6 100644 --- a/src/tools/miri/src/shims/x86/sha.rs +++ b/src/tools/miri/src/shims/x86/sha.rs @@ -31,7 +31,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let projected = &this.project_index(reg, i.try_into().unwrap())?; *dst = this.read_scalar(projected)?.to_u32()? } - Ok(res) + interp_ok(res) } fn write<'c>( @@ -45,7 +45,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let projected = &this.project_index(dest, i.try_into().unwrap())?; this.write_scalar(Scalar::from_u32(part), projected)?; } - Ok(()) + interp_ok(()) } match unprefixed_name { @@ -106,9 +106,9 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let result = sha256msg2(a, b); write(this, &dest, result)?; } - _ => return Ok(EmulateItemResult::NotSupported), + _ => return interp_ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsReturn) + interp_ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/x86/sse.rs b/src/tools/miri/src/shims/x86/sse.rs index 254d25bb5293a..013900e95d362 100644 --- a/src/tools/miri/src/shims/x86/sse.rs +++ b/src/tools/miri/src/shims/x86/sse.rs @@ -201,8 +201,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.copy_op(&this.project_index(&left, i)?, &this.project_index(&dest, i)?)?; } } - _ => return Ok(EmulateItemResult::NotSupported), + _ => return interp_ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsReturn) + interp_ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/x86/sse2.rs b/src/tools/miri/src/shims/x86/sse2.rs index 7c23e37266430..de72e63f8aff8 100644 --- a/src/tools/miri/src/shims/x86/sse2.rs +++ b/src/tools/miri/src/shims/x86/sse2.rs @@ -344,8 +344,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.copy_op(&this.project_index(&left, i)?, &this.project_index(&dest, i)?)?; } } - _ => return Ok(EmulateItemResult::NotSupported), + _ => return interp_ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsReturn) + interp_ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/x86/sse3.rs b/src/tools/miri/src/shims/x86/sse3.rs index ef5a55d6eb28e..9aa4dd5995059 100644 --- a/src/tools/miri/src/shims/x86/sse3.rs +++ b/src/tools/miri/src/shims/x86/sse3.rs @@ -47,8 +47,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.mem_copy(src_ptr, dest.ptr(), dest.layout.size, /*nonoverlapping*/ true)?; } - _ => return Ok(EmulateItemResult::NotSupported), + _ => return interp_ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsReturn) + interp_ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/x86/sse41.rs b/src/tools/miri/src/shims/x86/sse41.rs index 8fcdc491c0ca3..7bf8ddea2fec8 100644 --- a/src/tools/miri/src/shims/x86/sse41.rs +++ b/src/tools/miri/src/shims/x86/sse41.rs @@ -172,8 +172,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(Scalar::from_i32(res.into()), dest)?; } - _ => return Ok(EmulateItemResult::NotSupported), + _ => return interp_ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsReturn) + interp_ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/x86/sse42.rs b/src/tools/miri/src/shims/x86/sse42.rs index 0b6e0d9e0d7e7..f31cd40634972 100644 --- a/src/tools/miri/src/shims/x86/sse42.rs +++ b/src/tools/miri/src/shims/x86/sse42.rs @@ -115,7 +115,7 @@ fn compare_strings<'tcx>( (false, true) => i32::from(ch.to_i8()?), (false, false) => i32::from(ch.to_u8()?), }; - Ok(result) + interp_ok(result) }; for i in 0..len2 { @@ -183,7 +183,7 @@ fn compare_strings<'tcx>( _ => (), } - Ok(result) + interp_ok(result) } /// Obtain the arguments of the intrinsic based on its name. @@ -235,7 +235,7 @@ fn deconstruct_args<'tcx>( let str1 = str1.transmute(array_layout, this)?; let str2 = str2.transmute(array_layout, this)?; - Ok((str1, str2, Some((len1, len2)), imm)) + interp_ok((str1, str2, Some((len1, len2)), imm)) } else { let [str1, str2, imm] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let imm = this.read_scalar(imm)?.to_u8()?; @@ -244,7 +244,7 @@ fn deconstruct_args<'tcx>( let str1 = str1.transmute(array_layout, this)?; let str2 = str2.transmute(array_layout, this)?; - Ok((str1, str2, None, imm)) + interp_ok((str1, str2, None, imm)) } } @@ -266,7 +266,7 @@ fn implicit_len<'tcx>( break; } } - Ok(result) + interp_ok(result) } #[inline] @@ -433,7 +433,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }; if bit_size == 64 && this.tcx.sess.target.arch != "x86_64" { - return Ok(EmulateItemResult::NotSupported); + return interp_ok(EmulateItemResult::NotSupported); } let [left, right] = @@ -493,8 +493,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(result, dest)?; } - _ => return Ok(EmulateItemResult::NotSupported), + _ => return interp_ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsReturn) + interp_ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/x86/ssse3.rs b/src/tools/miri/src/shims/x86/ssse3.rs index 096b0fb9e4b17..76a2451dea415 100644 --- a/src/tools/miri/src/shims/x86/ssse3.rs +++ b/src/tools/miri/src/shims/x86/ssse3.rs @@ -133,8 +133,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { psign(this, left, right, dest)?; } - _ => return Ok(EmulateItemResult::NotSupported), + _ => return interp_ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsReturn) + interp_ok(EmulateItemResult::NeedsReturn) } } From 4891dd4627be38fa6133fcf3cfb0c1a65867bf87 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 29 Sep 2024 11:53:23 +0200 Subject: [PATCH 071/120] make InterpResult a dedicated type to avoid accidentally discarding the error --- clippy_utils/src/consts.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index bf47cf6d372d2..53a1170d6a618 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -870,10 +870,10 @@ pub fn mir_to_const<'tcx>(tcx: TyCtxt<'tcx>, result: mir::Const<'tcx>) -> Option let range = alloc_range(offset + size * idx, size); let val = alloc.read_scalar(&tcx, range, /* read_provenance */ false).ok()?; res.push(match flt { - FloatTy::F16 => Constant::F16(f16::from_bits(val.to_u16().ok()?)), - FloatTy::F32 => Constant::F32(f32::from_bits(val.to_u32().ok()?)), - FloatTy::F64 => Constant::F64(f64::from_bits(val.to_u64().ok()?)), - FloatTy::F128 => Constant::F128(f128::from_bits(val.to_u128().ok()?)), + FloatTy::F16 => Constant::F16(f16::from_bits(val.to_u16().discard_err()?)), + FloatTy::F32 => Constant::F32(f32::from_bits(val.to_u32().discard_err()?)), + FloatTy::F64 => Constant::F64(f64::from_bits(val.to_u64().discard_err()?)), + FloatTy::F128 => Constant::F128(f128::from_bits(val.to_u128().discard_err()?)), }); } Some(Constant::Vec(res)) @@ -903,7 +903,7 @@ fn mir_is_empty<'tcx>(tcx: TyCtxt<'tcx>, result: mir::Const<'tcx>) -> Option Date: Wed, 2 Oct 2024 01:47:18 +0530 Subject: [PATCH 072/120] Update helper docs display disable option Updated helper docs via configure.py to make it clearer that users can control options with enable and disable --- src/bootstrap/configure.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py index 49d564642bd65..a555a26367d51 100755 --- a/src/bootstrap/configure.py +++ b/src/bootstrap/configure.py @@ -193,7 +193,7 @@ def is_value_list(key): if option.value: print('\t{:30} {}'.format('--{}=VAL'.format(option.name), option.desc)) else: - print('\t{:30} {}'.format('--enable-{}'.format(option.name), option.desc)) + print('\t{:30} {}'.format('--enable-{} OR --disable-{}'.format(option.name, option.name), option.desc)) print('') print('This configure script is a thin configuration shim over the true') print('configuration system, `config.toml`. You can explore the comments') From c88cb08afc100a400cf8f0095bac5b83175b5791 Mon Sep 17 00:00:00 2001 From: Rob Shearman Date: Thu, 18 Jul 2024 13:38:05 +0100 Subject: [PATCH 073/120] Fix `mut_mutex_lock` when reference not ultimately mutable When there is are multiple references where one of the references isn't mutable then this results in a false-positive for `mut_mutex_lock` as it only checks the mutability of the first reference level. Fix this by using `peel_mid_ty_refs_is_mutable` which correctly determines whether the reference is ultimately mutable and thus whether `Mutex::get_lock()` can actually be used. Fixes #9854 --- clippy_lints/src/methods/mut_mutex_lock.rs | 6 +++--- tests/ui/mut_mutex_lock.fixed | 12 ++++++++++++ tests/ui/mut_mutex_lock.rs | 12 ++++++++++++ tests/ui/mut_mutex_lock.stderr | 8 +++++++- 4 files changed, 34 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/methods/mut_mutex_lock.rs b/clippy_lints/src/methods/mut_mutex_lock.rs index 83e8370f939bc..320523aceb675 100644 --- a/clippy_lints/src/methods/mut_mutex_lock.rs +++ b/clippy_lints/src/methods/mut_mutex_lock.rs @@ -1,17 +1,17 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::expr_custom_deref_adjustment; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable}; use rustc_errors::Applicability; use rustc_hir::{Expr, Mutability}; use rustc_lint::LateContext; -use rustc_middle::ty; use rustc_span::{Span, sym}; use super::MUT_MUTEX_LOCK; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'tcx>, recv: &'tcx Expr<'tcx>, name_span: Span) { if matches!(expr_custom_deref_adjustment(cx, recv), None | Some(Mutability::Mut)) - && let ty::Ref(_, _, Mutability::Mut) = cx.typeck_results().expr_ty(recv).kind() + && let (_, ref_depth, Mutability::Mut) = peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(recv)) + && ref_depth >= 1 && let Some(method_id) = cx.typeck_results().type_dependent_def_id(ex.hir_id) && let Some(impl_id) = cx.tcx.impl_of_method(method_id) && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::Mutex) diff --git a/tests/ui/mut_mutex_lock.fixed b/tests/ui/mut_mutex_lock.fixed index bbedbb2bed23f..29c5a27cb6247 100644 --- a/tests/ui/mut_mutex_lock.fixed +++ b/tests/ui/mut_mutex_lock.fixed @@ -9,6 +9,11 @@ fn mut_mutex_lock() { let mut value = value_mutex.get_mut().unwrap(); *value += 1; + + let mut value_mutex = Mutex::new(42_u8); + let mut_ref_mut_ref_mutex = &mut &mut value_mutex; + let mut value = mut_ref_mut_ref_mutex.get_mut().unwrap(); + *value += 1; } fn no_owned_mutex_lock() { @@ -24,4 +29,11 @@ fn issue9415() { *guard += 1; } +fn mut_ref_ref_mutex_lock() { + let mutex = Mutex::new(42_u8); + let mut_ref_ref_mutex = &mut &mutex; + let mut guard = mut_ref_ref_mutex.lock().unwrap(); + *guard += 1; +} + fn main() {} diff --git a/tests/ui/mut_mutex_lock.rs b/tests/ui/mut_mutex_lock.rs index 74116100e82a7..fcdb3ff97dbc5 100644 --- a/tests/ui/mut_mutex_lock.rs +++ b/tests/ui/mut_mutex_lock.rs @@ -9,6 +9,11 @@ fn mut_mutex_lock() { let mut value = value_mutex.lock().unwrap(); *value += 1; + + let mut value_mutex = Mutex::new(42_u8); + let mut_ref_mut_ref_mutex = &mut &mut value_mutex; + let mut value = mut_ref_mut_ref_mutex.lock().unwrap(); + *value += 1; } fn no_owned_mutex_lock() { @@ -24,4 +29,11 @@ fn issue9415() { *guard += 1; } +fn mut_ref_ref_mutex_lock() { + let mutex = Mutex::new(42_u8); + let mut_ref_ref_mutex = &mut &mutex; + let mut guard = mut_ref_ref_mutex.lock().unwrap(); + *guard += 1; +} + fn main() {} diff --git a/tests/ui/mut_mutex_lock.stderr b/tests/ui/mut_mutex_lock.stderr index a3d4905c04cb7..92601c4c61215 100644 --- a/tests/ui/mut_mutex_lock.stderr +++ b/tests/ui/mut_mutex_lock.stderr @@ -7,5 +7,11 @@ LL | let mut value = value_mutex.lock().unwrap(); = note: `-D clippy::mut-mutex-lock` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::mut_mutex_lock)]` -error: aborting due to 1 previous error +error: calling `&mut Mutex::lock` unnecessarily locks an exclusive (mutable) reference + --> tests/ui/mut_mutex_lock.rs:15:43 + | +LL | let mut value = mut_ref_mut_ref_mutex.lock().unwrap(); + | ^^^^ help: change this to: `get_mut` + +error: aborting due to 2 previous errors From 5b1a2b8712887fff7e0295d4e87aac24ea73797d Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Tue, 1 Oct 2024 20:52:17 +0000 Subject: [PATCH 074/120] TransmuteFrom: Gracefully handle unnormalized types and normalization errors Fixes #130413 --- .../traits/fulfillment_errors.rs | 8 +++++ .../src/error_reporting/traits/mod.rs | 1 + compiler/rustc_transmute/src/layout/tree.rs | 5 +-- tests/ui/transmutability/assoc-bound.rs | 25 +++++++++++++++ tests/ui/transmutability/assoc-bound.stderr | 31 +++++++++++++++++++ 5 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 tests/ui/transmutability/assoc-bound.rs create mode 100644 tests/ui/transmutability/assoc-bound.stderr diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 34a0f182ab45f..f494d9637f324 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -245,6 +245,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { span, "silent safe transmute error" ); } + GetSafeTransmuteErrorAndReason::Default => { + (err_msg, None) + } GetSafeTransmuteErrorAndReason::Error { err_msg, safe_transmute_explanation, @@ -2221,6 +2224,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ) -> GetSafeTransmuteErrorAndReason { use rustc_transmute::Answer; + if obligation.predicate.has_non_region_param() || obligation.has_non_region_infer() { + return GetSafeTransmuteErrorAndReason::Default; + } + // Erase regions because layout code doesn't particularly care about regions. let trait_ref = self.tcx.erase_regions(self.tcx.instantiate_bound_regions_with_erased(trait_ref)); @@ -2243,6 +2250,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let dst = trait_ref.args.type_at(0); let src = trait_ref.args.type_at(1); + let err_msg = format!("`{src}` cannot be safely transmuted into `{dst}`"); match rustc_transmute::TransmuteTypeEnv::new(self.infcx).is_transmutable( diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs index 109bae10b54cc..becc1acfb6641 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs @@ -43,6 +43,7 @@ pub struct ImplCandidate<'tcx> { enum GetSafeTransmuteErrorAndReason { Silent, + Default, Error { err_msg: String, safe_transmute_explanation: Option }, } diff --git a/compiler/rustc_transmute/src/layout/tree.rs b/compiler/rustc_transmute/src/layout/tree.rs index 6d5859a5a6563..17eddbfcd7f5e 100644 --- a/compiler/rustc_transmute/src/layout/tree.rs +++ b/compiler/rustc_transmute/src/layout/tree.rs @@ -195,10 +195,11 @@ pub(crate) mod rustc { impl<'tcx> From<&LayoutError<'tcx>> for Err { fn from(err: &LayoutError<'tcx>) -> Self { match err { - LayoutError::Unknown(..) | LayoutError::ReferencesError(..) => Self::UnknownLayout, + LayoutError::Unknown(..) + | LayoutError::ReferencesError(..) + | LayoutError::NormalizationFailure(..) => Self::UnknownLayout, LayoutError::SizeOverflow(..) => Self::SizeOverflow, LayoutError::Cycle(err) => Self::TypeError(*err), - err => unimplemented!("{:?}", err), } } } diff --git a/tests/ui/transmutability/assoc-bound.rs b/tests/ui/transmutability/assoc-bound.rs new file mode 100644 index 0000000000000..e8a20b45cde34 --- /dev/null +++ b/tests/ui/transmutability/assoc-bound.rs @@ -0,0 +1,25 @@ +#![crate_type = "lib"] +#![feature(transmutability)] + +trait A { + type AssocA; +} + +trait B { + type AssocB: std::mem::TransmuteFrom<()>; +} + +impl B for (T, u8) +where + T: A, +{ + type AssocB = T::AssocA; //~ERROR: the trait bound `::AssocA: TransmuteFrom<(), Assume { alignment: false, lifetimes: false, safety: false, validity: false }>` is not satisfied [E0277] +} + + +impl B for (T, u16) +where + for<'a> &'a i32: A, +{ + type AssocB = <&'static i32 as A>::AssocA; //~ERROR: `()` cannot be safely transmuted into `<&i32 as A>::AssocA` +} diff --git a/tests/ui/transmutability/assoc-bound.stderr b/tests/ui/transmutability/assoc-bound.stderr new file mode 100644 index 0000000000000..08d9089439645 --- /dev/null +++ b/tests/ui/transmutability/assoc-bound.stderr @@ -0,0 +1,31 @@ +error[E0277]: the trait bound `::AssocA: TransmuteFrom<(), Assume { alignment: false, lifetimes: false, safety: false, validity: false }>` is not satisfied + --> $DIR/assoc-bound.rs:16:19 + | +LL | type AssocB = T::AssocA; + | ^^^^^^^^^ the trait `TransmuteFrom<(), Assume { alignment: false, lifetimes: false, safety: false, validity: false }>` is not implemented for `::AssocA` + | +note: required by a bound in `B::AssocB` + --> $DIR/assoc-bound.rs:9:18 + | +LL | type AssocB: std::mem::TransmuteFrom<()>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `B::AssocB` +help: consider further restricting the associated type + | +LL | T: A, ::AssocA: TransmuteFrom<(), Assume { alignment: false, lifetimes: false, safety: false, validity: false }> + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error[E0277]: `()` cannot be safely transmuted into `<&i32 as A>::AssocA` + --> $DIR/assoc-bound.rs:24:19 + | +LL | type AssocB = <&'static i32 as A>::AssocA; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `<&i32 as A>::AssocA` has an unknown layout + | +note: required by a bound in `B::AssocB` + --> $DIR/assoc-bound.rs:9:18 + | +LL | type AssocB: std::mem::TransmuteFrom<()>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `B::AssocB` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. From 38ea690363e24acf4970b2a7e942af6c05893691 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Tue, 1 Oct 2024 23:16:35 +0000 Subject: [PATCH 075/120] fix extension for `-Zdump-mir-dataflow` graphviz files --- compiler/rustc_mir_dataflow/src/framework/engine.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_mir_dataflow/src/framework/engine.rs b/compiler/rustc_mir_dataflow/src/framework/engine.rs index da01a9740943d..faf5c610a0c47 100644 --- a/compiler/rustc_mir_dataflow/src/framework/engine.rs +++ b/compiler/rustc_mir_dataflow/src/framework/engine.rs @@ -285,7 +285,7 @@ where } None if dump_enabled(tcx, A::NAME, def_id) => { - create_dump_file(tcx, ".dot", false, A::NAME, &pass_name.unwrap_or("-----"), body)? + create_dump_file(tcx, "dot", false, A::NAME, &pass_name.unwrap_or("-----"), body)? } _ => return (Ok(()), results), From 98d6fdb24206f632e0f151719bbdcc73ff67ff2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Tue, 1 Oct 2024 23:55:55 +0000 Subject: [PATCH 076/120] make `Borrows` dataflow dumps about its loan domain --- compiler/rustc_borrowck/src/dataflow.rs | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/compiler/rustc_borrowck/src/dataflow.rs b/compiler/rustc_borrowck/src/dataflow.rs index 4af5a10f5812d..5ea48d3fc7e94 100644 --- a/compiler/rustc_borrowck/src/dataflow.rs +++ b/compiler/rustc_borrowck/src/dataflow.rs @@ -1,5 +1,3 @@ -use std::fmt; - use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::graph; use rustc_index::bit_set::BitSet; @@ -425,10 +423,6 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> { Borrows { tcx, body, borrow_set, borrows_out_of_scope_at_location } } - pub fn location(&self, idx: BorrowIndex) -> &Location { - &self.borrow_set[idx].reserve_location - } - /// Add all borrows to the kill set, if those borrows are out of scope at `location`. /// That means they went out of a nonlexical scope fn kill_loans_out_of_scope_at_location( @@ -615,8 +609,4 @@ impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> { } } -impl DebugWithContext> for BorrowIndex { - fn fmt_with(&self, ctxt: &Borrows<'_, '_>, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?}", ctxt.location(*self)) - } -} +impl DebugWithContext for BorrowIndex {} From bb5a8276be2d3dbc97d0f52e90db15455d542edf Mon Sep 17 00:00:00 2001 From: Jacob Kiesel Date: Sat, 22 Jun 2024 01:27:59 -0600 Subject: [PATCH 077/120] add unstable support for outputting file checksums for use in cargo --- Cargo.lock | 26 ++++ .../src/debuginfo/metadata.rs | 1 + compiler/rustc_interface/src/interface.rs | 3 +- compiler/rustc_interface/src/lib.rs | 1 + compiler/rustc_interface/src/passes.rs | 105 +++++++++++-- compiler/rustc_interface/src/tests.rs | 2 + compiler/rustc_metadata/src/rmeta/decoder.rs | 2 + .../src/ich/impls_syntax.rs | 2 + compiler/rustc_session/src/config.rs | 4 + compiler/rustc_session/src/options.rs | 22 ++- compiler/rustc_span/Cargo.toml | 1 + compiler/rustc_span/src/lib.rs | 146 +++++++++++++++++- compiler/rustc_span/src/source_map.rs | 16 +- compiler/rustc_span/src/source_map/tests.rs | 2 + compiler/rustc_span/src/tests.rs | 10 +- src/tools/tidy/src/deps.rs | 6 + 16 files changed, 321 insertions(+), 28 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6c1071ccbc226..6348046751a8b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -199,6 +199,12 @@ dependencies = [ "object 0.36.4", ] +[[package]] +name = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + [[package]] name = "arrayvec" version = "0.7.6" @@ -262,6 +268,19 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +[[package]] +name = "blake3" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d08263faac5cde2a4d52b513dadb80846023aade56fcd8fc99ba73ba8050e92" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -719,6 +738,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "constant_time_eq" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -4374,6 +4399,7 @@ dependencies = [ name = "rustc_span" version = "0.0.0" dependencies = [ + "blake3", "derive-where", "indexmap", "itoa", diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 964b83c0fa07a..b7a6f80956dd3 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -630,6 +630,7 @@ pub(crate) fn file_metadata<'ll>(cx: &CodegenCx<'ll, '_>, source_file: &SourceFi rustc_span::SourceFileHashAlgorithm::Md5 => llvm::ChecksumKind::MD5, rustc_span::SourceFileHashAlgorithm::Sha1 => llvm::ChecksumKind::SHA1, rustc_span::SourceFileHashAlgorithm::Sha256 => llvm::ChecksumKind::SHA256, + rustc_span::SourceFileHashAlgorithm::Blake3 => llvm::ChecksumKind::None, }; let hash_value = hex_encode(source_file.src_hash.hash_bytes()); diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index bd38b3c109a41..3920d3077d361 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -389,12 +389,13 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se let file_loader = config.file_loader.unwrap_or_else(|| Box::new(RealFileLoader)); let path_mapping = config.opts.file_path_mapping(); let hash_kind = config.opts.unstable_opts.src_hash_algorithm(&target); + let checksum_hash_kind = config.opts.unstable_opts.checksum_hash_algorithm(); util::run_in_thread_pool_with_globals( &early_dcx, config.opts.edition, config.opts.unstable_opts.threads, - SourceMapInputs { file_loader, path_mapping, hash_kind }, + SourceMapInputs { file_loader, path_mapping, hash_kind, checksum_hash_kind }, |current_gcx| { // The previous `early_dcx` can't be reused here because it doesn't // impl `Send`. Creating a new one is fine. diff --git a/compiler/rustc_interface/src/lib.rs b/compiler/rustc_interface/src/lib.rs index b81a740270163..1c4dda2a4367e 100644 --- a/compiler/rustc_interface/src/lib.rs +++ b/compiler/rustc_interface/src/lib.rs @@ -1,6 +1,7 @@ // tidy-alphabetical-start #![feature(decl_macro)] #![feature(file_buffered)] +#![feature(iter_intersperse)] #![feature(let_chains)] #![feature(try_blocks)] #![warn(unreachable_pub)] diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 84b320975bbab..b9b65755db5d2 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -32,8 +32,8 @@ use rustc_session::cstore::Untracked; use rustc_session::output::{collect_crate_types, filename_for_input, find_crate_name}; use rustc_session::search_paths::PathKind; use rustc_session::{Limit, Session}; -use rustc_span::FileName; use rustc_span::symbol::{Symbol, sym}; +use rustc_span::{FileName, SourceFileHash, SourceFileHashAlgorithm}; use rustc_target::spec::PanicStrategy; use rustc_trait_selection::traits; use tracing::{info, instrument}; @@ -417,15 +417,23 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P let result: io::Result<()> = try { // Build a list of files used to compile the output and // write Makefile-compatible dependency rules - let mut files: Vec = sess + let mut files: Vec<(String, u64, Option)> = sess .source_map() .files() .iter() .filter(|fmap| fmap.is_real_file()) .filter(|fmap| !fmap.is_imported()) - .map(|fmap| escape_dep_filename(&fmap.name.prefer_local().to_string())) + .map(|fmap| { + ( + escape_dep_filename(&fmap.name.prefer_local().to_string()), + fmap.source_len.0 as u64, + fmap.checksum_hash, + ) + }) .collect(); + let checksum_hash_algo = sess.opts.unstable_opts.checksum_hash_algorithm; + // Account for explicitly marked-to-track files // (e.g. accessed in proc macros). let file_depinfo = sess.psess.file_depinfo.borrow(); @@ -437,22 +445,58 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P // The entries will be used to declare dependencies between files in a // Makefile-like output, so the iteration order does not matter. + fn hash_iter_files>( + it: impl Iterator, + checksum_hash_algo: Option, + ) -> impl Iterator)> { + it.map(move |path| { + match checksum_hash_algo.and_then(|algo| { + fs::File::open(path.as_ref()) + .and_then(|mut file| { + SourceFileHash::new(algo, &mut file).map(|h| (file, h)) + }) + .and_then(|(file, h)| file.metadata().map(|m| (m.len(), h))) + .map_err(|e| { + tracing::error!( + "failed to compute checksum, omitting it from dep-info {} {e}", + path.as_ref().display() + ) + }) + .ok() + }) { + Some((file_len, checksum)) => (path, file_len, Some(checksum)), + None => (path, 0, None), + } + }) + } + #[allow(rustc::potential_query_instability)] - let extra_tracked_files = - file_depinfo.iter().map(|path_sym| normalize_path(PathBuf::from(path_sym.as_str()))); + let extra_tracked_files = hash_iter_files( + file_depinfo.iter().map(|path_sym| normalize_path(PathBuf::from(path_sym.as_str()))), + checksum_hash_algo, + ); files.extend(extra_tracked_files); // We also need to track used PGO profile files if let Some(ref profile_instr) = sess.opts.cg.profile_use { - files.push(normalize_path(profile_instr.as_path().to_path_buf())); + files.extend(hash_iter_files( + iter::once(normalize_path(profile_instr.as_path().to_path_buf())), + checksum_hash_algo, + )); } if let Some(ref profile_sample) = sess.opts.unstable_opts.profile_sample_use { - files.push(normalize_path(profile_sample.as_path().to_path_buf())); + files.extend(hash_iter_files( + iter::once(normalize_path(profile_sample.as_path().to_path_buf())), + checksum_hash_algo, + )); } // Debugger visualizer files for debugger_visualizer in tcx.debugger_visualizers(LOCAL_CRATE) { - files.push(normalize_path(debugger_visualizer.path.clone().unwrap())); + files.extend(hash_iter_files( + iter::once(normalize_path(debugger_visualizer.path.clone().unwrap())), + checksum_hash_algo, + )); } if sess.binary_dep_depinfo() { @@ -460,33 +504,54 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P if backend.contains('.') { // If the backend name contain a `.`, it is the path to an external dynamic // library. If not, it is not a path. - files.push(backend.to_string()); + files.extend(hash_iter_files( + iter::once(backend.to_string()), + checksum_hash_algo, + )); } } for &cnum in tcx.crates(()) { let source = tcx.used_crate_source(cnum); if let Some((path, _)) = &source.dylib { - files.push(escape_dep_filename(&path.display().to_string())); + files.extend(hash_iter_files( + iter::once(escape_dep_filename(&path.display().to_string())), + checksum_hash_algo, + )); } if let Some((path, _)) = &source.rlib { - files.push(escape_dep_filename(&path.display().to_string())); + files.extend(hash_iter_files( + iter::once(escape_dep_filename(&path.display().to_string())), + checksum_hash_algo, + )); } if let Some((path, _)) = &source.rmeta { - files.push(escape_dep_filename(&path.display().to_string())); + files.extend(hash_iter_files( + iter::once(escape_dep_filename(&path.display().to_string())), + checksum_hash_algo, + )); } } } let write_deps_to_file = |file: &mut dyn Write| -> io::Result<()> { for path in out_filenames { - writeln!(file, "{}: {}\n", path.display(), files.join(" "))?; + writeln!( + file, + "{}: {}\n", + path.display(), + files + .iter() + .map(|(path, _file_len, _checksum_hash_algo)| path.as_str()) + .intersperse(" ") + .collect::() + )?; } // Emit a fake target for each input file to the compilation. This // prevents `make` from spitting out an error if a file is later // deleted. For more info see #28735 - for path in files { + for (path, _file_len, _checksum_hash_algo) in &files { writeln!(file, "{path}:")?; } @@ -510,6 +575,18 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P } } + // If caller requested this information, add special comments about source file checksums. + // These are not necessarily the same checksums as was used in the debug files. + if sess.opts.unstable_opts.checksum_hash_algorithm().is_some() { + for (path, file_len, checksum_hash) in + files.iter().filter_map(|(path, file_len, hash_algo)| { + hash_algo.map(|hash_algo| (path, file_len, hash_algo)) + }) + { + writeln!(file, "# checksum:{checksum_hash} file_len:{file_len} {path}")?; + } + } + Ok(()) }; diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 895897eca6b8b..536ce154cd071 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -44,10 +44,12 @@ where let sysroot = filesearch::materialize_sysroot(sessopts.maybe_sysroot.clone()); let target = rustc_session::config::build_target_config(&early_dcx, &sessopts, &sysroot); let hash_kind = sessopts.unstable_opts.src_hash_algorithm(&target); + let checksum_hash_kind = sessopts.unstable_opts.checksum_hash_algorithm(); let sm_inputs = Some(SourceMapInputs { file_loader: Box::new(RealFileLoader) as _, path_mapping: sessopts.file_path_mapping(), hash_kind, + checksum_hash_kind, }); rustc_span::create_session_globals_then(DEFAULT_EDITION, sm_inputs, || { diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 2157324d5cc3c..f02fd2ab6fe3d 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -1702,6 +1702,7 @@ impl<'a> CrateMetadataRef<'a> { let rustc_span::SourceFile { mut name, src_hash, + checksum_hash, start_pos: original_start_pos, source_len, lines, @@ -1752,6 +1753,7 @@ impl<'a> CrateMetadataRef<'a> { let local_version = sess.source_map().new_imported_source_file( name, src_hash, + checksum_hash, stable_id, source_len.to_u32(), self.cnum, diff --git a/compiler/rustc_query_system/src/ich/impls_syntax.rs b/compiler/rustc_query_system/src/ich/impls_syntax.rs index 8d7a6e4fa9b0c..5e450979273ab 100644 --- a/compiler/rustc_query_system/src/ich/impls_syntax.rs +++ b/compiler/rustc_query_system/src/ich/impls_syntax.rs @@ -68,6 +68,8 @@ impl<'a> HashStable> for SourceFile { // Do not hash the source as it is not encoded src: _, ref src_hash, + // Already includes src_hash, this is redundant + checksum_hash: _, external_src: _, start_pos: _, source_len: _, diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 0d293415aa9c7..d2c03e588ff36 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1242,6 +1242,10 @@ impl UnstableOptions { } }) } + + pub fn checksum_hash_algorithm(&self) -> Option { + self.checksum_hash_algorithm + } } // The type of entry function, so users can have their own entry functions diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 1de09b8be4d63..a1d85dfad32af 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -418,7 +418,8 @@ mod desc { "one of: `legacy`, `v0` (RFC 2603), or `hashed`"; pub(crate) const parse_opt_symbol_visibility: &str = "one of: `hidden`, `protected`, or `interposable`"; - pub(crate) const parse_src_file_hash: &str = "either `md5` or `sha1`"; + pub(crate) const parse_cargo_src_file_hash: &str = "one of `md5`, `sha1`, or `sha256`"; + pub(crate) const parse_src_file_hash: &str = "one of `md5`, `sha1`, or `sha256`"; pub(crate) const parse_relocation_model: &str = "one of supported relocation models (`rustc --print relocation-models`)"; pub(crate) const parse_code_model: &str = @@ -1288,6 +1289,23 @@ mod parse { true } + pub(crate) fn parse_cargo_src_file_hash( + slot: &mut Option, + v: Option<&str>, + ) -> bool { + match v.and_then(|s| SourceFileHashAlgorithm::from_str(s).ok()) { + Some(hash_kind) => { + if hash_kind.supported_in_cargo() { + *slot = Some(hash_kind); + } else { + return false; + } + } + _ => return false, + } + true + } + pub(crate) fn parse_target_feature(slot: &mut String, v: Option<&str>) -> bool { match v { Some(s) => { @@ -1688,6 +1706,8 @@ options! { "instrument control-flow architecture protection"), check_cfg_all_expected: bool = (false, parse_bool, [UNTRACKED], "show all expected values in check-cfg diagnostics (default: no)"), + checksum_hash_algorithm: Option = (None, parse_cargo_src_file_hash, [TRACKED], + "hash algorithm of source files used to check freshness in cargo (`sha256`)"), codegen_backend: Option = (None, parse_opt_string, [TRACKED], "the backend to use"), combine_cgu: bool = (false, parse_bool, [TRACKED], diff --git a/compiler/rustc_span/Cargo.toml b/compiler/rustc_span/Cargo.toml index 3fdfe77ead972..c52d1fcc07fe2 100644 --- a/compiler/rustc_span/Cargo.toml +++ b/compiler/rustc_span/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start +blake3 = "1.5.2" derive-where = "1.2.7" indexmap = { version = "2.0.0" } itoa = "1.0" diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 9dbdab84a81e1..5ab1caaa220a7 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -75,7 +75,9 @@ pub mod profiling; use std::borrow::Cow; use std::cmp::{self, Ordering}; +use std::fmt::Display; use std::hash::Hash; +use std::io::{self, Read}; use std::ops::{Add, Range, Sub}; use std::path::{Path, PathBuf}; use std::str::FromStr; @@ -1395,6 +1397,27 @@ pub enum SourceFileHashAlgorithm { Md5, Sha1, Sha256, + Blake3, +} + +impl SourceFileHashAlgorithm { + pub fn supported_in_cargo(&self) -> bool { + match self { + Self::Md5 | Self::Sha1 => false, + Self::Sha256 | Self::Blake3 => true, + } + } +} + +impl Display for SourceFileHashAlgorithm { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(match self { + Self::Md5 => "md5", + Self::Sha1 => "sha1", + Self::Sha256 => "sha256", + Self::Blake3 => "blake3", + }) + } } impl FromStr for SourceFileHashAlgorithm { @@ -1405,12 +1428,13 @@ impl FromStr for SourceFileHashAlgorithm { "md5" => Ok(SourceFileHashAlgorithm::Md5), "sha1" => Ok(SourceFileHashAlgorithm::Sha1), "sha256" => Ok(SourceFileHashAlgorithm::Sha256), + "blake3" => Ok(SourceFileHashAlgorithm::Blake3), _ => Err(()), } } } -/// The hash of the on-disk source file used for debug info. +/// The hash of the on-disk source file used for debug info and cargo freshness checks. #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] #[derive(HashStable_Generic, Encodable, Decodable)] pub struct SourceFileHash { @@ -1418,12 +1442,22 @@ pub struct SourceFileHash { value: [u8; 32], } +impl Display for SourceFileHash { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}=", self.kind)?; + for byte in self.value[0..self.hash_len()].into_iter() { + write!(f, "{byte:02x}")?; + } + Ok(()) + } +} + impl SourceFileHash { - pub fn new(kind: SourceFileHashAlgorithm, src: &str) -> SourceFileHash { + pub fn new_in_memory(kind: SourceFileHashAlgorithm, src: impl AsRef<[u8]>) -> SourceFileHash { let mut hash = SourceFileHash { kind, value: Default::default() }; let len = hash.hash_len(); let value = &mut hash.value[..len]; - let data = src.as_bytes(); + let data = src.as_ref(); match kind { SourceFileHashAlgorithm::Md5 => { value.copy_from_slice(&Md5::digest(data)); @@ -1434,13 +1468,94 @@ impl SourceFileHash { SourceFileHashAlgorithm::Sha256 => { value.copy_from_slice(&Sha256::digest(data)); } - } + SourceFileHashAlgorithm::Blake3 => value.copy_from_slice(blake3::hash(data).as_bytes()), + }; hash } + pub fn new(kind: SourceFileHashAlgorithm, src: impl Read) -> Result { + let mut hash = SourceFileHash { kind, value: Default::default() }; + let len = hash.hash_len(); + let value = &mut hash.value[..len]; + // Buffer size is the recommended amount to fully leverage SIMD instructions on AVX-512 as per + // blake3 documentation. + let mut buf = vec![0; 16 * 1024]; + + fn digest( + mut hasher: T, + mut update: impl FnMut(&mut T, &[u8]), + finish: impl FnOnce(T, &mut [u8]), + mut src: impl Read, + buf: &mut [u8], + value: &mut [u8], + ) -> Result<(), io::Error> { + loop { + let bytes_read = src.read(buf)?; + if bytes_read == 0 { + break; + } + update(&mut hasher, &buf[0..bytes_read]); + } + finish(hasher, value); + Ok(()) + } + + match kind { + SourceFileHashAlgorithm::Sha256 => { + digest( + Sha256::new(), + |h, b| { + h.update(b); + }, + |h, out| out.copy_from_slice(&h.finalize()), + src, + &mut buf, + value, + )?; + } + SourceFileHashAlgorithm::Sha1 => { + digest( + Sha1::new(), + |h, b| { + h.update(b); + }, + |h, out| out.copy_from_slice(&h.finalize()), + src, + &mut buf, + value, + )?; + } + SourceFileHashAlgorithm::Md5 => { + digest( + Md5::new(), + |h, b| { + h.update(b); + }, + |h, out| out.copy_from_slice(&h.finalize()), + src, + &mut buf, + value, + )?; + } + SourceFileHashAlgorithm::Blake3 => { + digest( + blake3::Hasher::new(), + |h, b| { + h.update(b); + }, + |h, out| out.copy_from_slice(h.finalize().as_bytes()), + src, + &mut buf, + value, + )?; + } + } + Ok(hash) + } + /// Check if the stored hash matches the hash of the string. pub fn matches(&self, src: &str) -> bool { - Self::new(self.kind, src) == *self + Self::new_in_memory(self.kind, src.as_bytes()) == *self } /// The bytes of the hash. @@ -1453,7 +1568,7 @@ impl SourceFileHash { match self.kind { SourceFileHashAlgorithm::Md5 => 16, SourceFileHashAlgorithm::Sha1 => 20, - SourceFileHashAlgorithm::Sha256 => 32, + SourceFileHashAlgorithm::Sha256 | SourceFileHashAlgorithm::Blake3 => 32, } } } @@ -1509,6 +1624,10 @@ pub struct SourceFile { pub src: Option>, /// The source code's hash. pub src_hash: SourceFileHash, + /// Used to enable cargo to use checksums to check if a crate is fresh rather + /// than mtimes. This might be the same as `src_hash`, and if the requested algorithm + /// is identical we won't compute it twice. + pub checksum_hash: Option, /// The external source code (used for external crates, which will have a `None` /// value as `self.src`. pub external_src: FreezeLock, @@ -1536,6 +1655,7 @@ impl Clone for SourceFile { name: self.name.clone(), src: self.src.clone(), src_hash: self.src_hash, + checksum_hash: self.checksum_hash, external_src: self.external_src.clone(), start_pos: self.start_pos, source_len: self.source_len, @@ -1552,6 +1672,7 @@ impl Encodable for SourceFile { fn encode(&self, s: &mut S) { self.name.encode(s); self.src_hash.encode(s); + self.checksum_hash.encode(s); // Do not encode `start_pos` as it's global state for this session. self.source_len.encode(s); @@ -1625,6 +1746,7 @@ impl Decodable for SourceFile { fn decode(d: &mut D) -> SourceFile { let name: FileName = Decodable::decode(d); let src_hash: SourceFileHash = Decodable::decode(d); + let checksum_hash: Option = Decodable::decode(d); let source_len: RelativeBytePos = Decodable::decode(d); let lines = { let num_lines: u32 = Decodable::decode(d); @@ -1650,6 +1772,7 @@ impl Decodable for SourceFile { source_len, src: None, src_hash, + checksum_hash, // Unused - the metadata decoder will construct // a new SourceFile, filling in `external_src` properly external_src: FreezeLock::frozen(ExternalSource::Unneeded), @@ -1733,9 +1856,17 @@ impl SourceFile { name: FileName, mut src: String, hash_kind: SourceFileHashAlgorithm, + checksum_hash_kind: Option, ) -> Result { // Compute the file hash before any normalization. - let src_hash = SourceFileHash::new(hash_kind, &src); + let src_hash = SourceFileHash::new_in_memory(hash_kind, src.as_bytes()); + let checksum_hash = checksum_hash_kind.map(|checksum_hash_kind| { + if checksum_hash_kind == hash_kind { + src_hash + } else { + SourceFileHash::new_in_memory(checksum_hash_kind, src.as_bytes()) + } + }); let normalized_pos = normalize_src(&mut src); let stable_id = StableSourceFileId::from_filename_in_current_crate(&name); @@ -1748,6 +1879,7 @@ impl SourceFile { name, src: Some(Lrc::new(src)), src_hash, + checksum_hash, external_src: FreezeLock::frozen(ExternalSource::Unneeded), start_pos: BytePos::from_u32(0), source_len: RelativeBytePos::from_u32(source_len), diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs index 98447147d3e13..8a02330593711 100644 --- a/compiler/rustc_span/src/source_map.rs +++ b/compiler/rustc_span/src/source_map.rs @@ -175,6 +175,7 @@ pub struct SourceMapInputs { pub file_loader: Box, pub path_mapping: FilePathMapping, pub hash_kind: SourceFileHashAlgorithm, + pub checksum_hash_kind: Option, } pub struct SourceMap { @@ -187,6 +188,12 @@ pub struct SourceMap { /// The algorithm used for hashing the contents of each source file. hash_kind: SourceFileHashAlgorithm, + + /// Similar to `hash_kind`, however this algorithm is used for checksums to determine if a crate is fresh. + /// `cargo` is the primary user of these. + /// + /// If this is equal to `hash_kind` then the checksum won't be computed twice. + checksum_hash_kind: Option, } impl SourceMap { @@ -195,17 +202,19 @@ impl SourceMap { file_loader: Box::new(RealFileLoader), path_mapping, hash_kind: SourceFileHashAlgorithm::Md5, + checksum_hash_kind: None, }) } pub fn with_inputs( - SourceMapInputs { file_loader, path_mapping, hash_kind }: SourceMapInputs, + SourceMapInputs { file_loader, path_mapping, hash_kind, checksum_hash_kind }: SourceMapInputs, ) -> SourceMap { SourceMap { files: Default::default(), file_loader: IntoDynSyncSend(file_loader), path_mapping, hash_kind, + checksum_hash_kind, } } @@ -307,7 +316,8 @@ impl SourceMap { match self.source_file_by_stable_id(stable_id) { Some(lrc_sf) => Ok(lrc_sf), None => { - let source_file = SourceFile::new(filename, src, self.hash_kind)?; + let source_file = + SourceFile::new(filename, src, self.hash_kind, self.checksum_hash_kind)?; // Let's make sure the file_id we generated above actually matches // the ID we generate for the SourceFile we just created. @@ -326,6 +336,7 @@ impl SourceMap { &self, filename: FileName, src_hash: SourceFileHash, + checksum_hash: Option, stable_id: StableSourceFileId, source_len: u32, cnum: CrateNum, @@ -340,6 +351,7 @@ impl SourceMap { name: filename, src: None, src_hash, + checksum_hash, external_src: FreezeLock::new(ExternalSource::Foreign { kind: ExternalSourceKind::AbsentOk, metadata_index, diff --git a/compiler/rustc_span/src/source_map/tests.rs b/compiler/rustc_span/src/source_map/tests.rs index 0c818b94b85a3..360baec273d9a 100644 --- a/compiler/rustc_span/src/source_map/tests.rs +++ b/compiler/rustc_span/src/source_map/tests.rs @@ -229,6 +229,7 @@ fn t10() { let SourceFile { name, src_hash, + checksum_hash, source_len, lines, multibyte_chars, @@ -240,6 +241,7 @@ fn t10() { let imported_src_file = sm.new_imported_source_file( name, src_hash, + checksum_hash, stable_id, source_len.to_u32(), CrateNum::ZERO, diff --git a/compiler/rustc_span/src/tests.rs b/compiler/rustc_span/src/tests.rs index 48fa786fb1c80..ed1db34463429 100644 --- a/compiler/rustc_span/src/tests.rs +++ b/compiler/rustc_span/src/tests.rs @@ -3,9 +3,13 @@ use super::*; #[test] fn test_lookup_line() { let source = "abcdefghijklm\nabcdefghij\n...".to_owned(); - let mut sf = - SourceFile::new(FileName::Anon(Hash64::ZERO), source, SourceFileHashAlgorithm::Sha256) - .unwrap(); + let mut sf = SourceFile::new( + FileName::Anon(Hash64::ZERO), + source, + SourceFileHashAlgorithm::Sha256, + Some(SourceFileHashAlgorithm::Sha256), + ) + .unwrap(); sf.start_pos = BytePos(3); assert_eq!(sf.lines(), &[RelativeBytePos(0), RelativeBytePos(14), RelativeBytePos(25)]); diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 49d5b71ff2ddf..1ffad06457f9b 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -88,7 +88,10 @@ pub(crate) const WORKSPACES: &[(&str, ExceptionList, Option<(&[&str], &[&str])>, const EXCEPTIONS: ExceptionList = &[ // tidy-alphabetical-start ("ar_archive_writer", "Apache-2.0 WITH LLVM-exception"), // rustc + ("arrayref", "BSD-2-Clause"), // rustc + ("blake3", "CC0-1.0 OR Apache-2.0 OR Apache-2.0 WITH LLVM-exception"), // rustc ("colored", "MPL-2.0"), // rustfmt + ("constant_time_eq", "CC0-1.0 OR MIT-0 OR Apache-2.0"), // rustc ("dissimilar", "Apache-2.0"), // rustdoc, rustc_lexer (few tests) via expect-test, (dev deps) ("fluent-langneg", "Apache-2.0"), // rustc (fluent translations) ("instant", "BSD-3-Clause"), // rustc_driver/tracing-subscriber/parking_lot @@ -249,14 +252,17 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "annotate-snippets", "anstyle", "ar_archive_writer", + "arrayref", "arrayvec", "autocfg", "bitflags", + "blake3", "block-buffer", "byteorder", // via ruzstd in object in thorin-dwp "cc", "cfg-if", "cfg_aliases", + "constant_time_eq", "cpufeatures", "crc32fast", "crossbeam-channel", From 6ff7a3e2aaa383062aef31f239a0b3939e544a86 Mon Sep 17 00:00:00 2001 From: Jacob Kiesel Date: Sat, 27 Jul 2024 12:27:49 -0600 Subject: [PATCH 078/120] Fix options help text --- compiler/rustc_session/src/options.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index a1d85dfad32af..c32cf3d015f0c 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1707,7 +1707,7 @@ options! { check_cfg_all_expected: bool = (false, parse_bool, [UNTRACKED], "show all expected values in check-cfg diagnostics (default: no)"), checksum_hash_algorithm: Option = (None, parse_cargo_src_file_hash, [TRACKED], - "hash algorithm of source files used to check freshness in cargo (`sha256`)"), + "hash algorithm of source files used to check freshness in cargo (`blake3` or `sha256`)"), codegen_backend: Option = (None, parse_opt_string, [TRACKED], "the backend to use"), combine_cgu: bool = (false, parse_bool, [TRACKED], From 081661b78d44bd41f8a1fd46b9ed11823cd7811e Mon Sep 17 00:00:00 2001 From: Jacob Kiesel Date: Sat, 28 Sep 2024 17:38:28 -0600 Subject: [PATCH 079/120] disregard what we believe is supported in cargo for hash type --- compiler/rustc_session/src/options.rs | 6 +----- compiler/rustc_span/src/lib.rs | 9 --------- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index c32cf3d015f0c..b78d370bce0a8 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1295,11 +1295,7 @@ mod parse { ) -> bool { match v.and_then(|s| SourceFileHashAlgorithm::from_str(s).ok()) { Some(hash_kind) => { - if hash_kind.supported_in_cargo() { - *slot = Some(hash_kind); - } else { - return false; - } + *slot = Some(hash_kind); } _ => return false, } diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 5ab1caaa220a7..b55465ddef7f1 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -1400,15 +1400,6 @@ pub enum SourceFileHashAlgorithm { Blake3, } -impl SourceFileHashAlgorithm { - pub fn supported_in_cargo(&self) -> bool { - match self { - Self::Md5 | Self::Sha1 => false, - Self::Sha256 | Self::Blake3 => true, - } - } -} - impl Display for SourceFileHashAlgorithm { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(match self { From dba814a9221e815a589a3ba48d2ef5287cd951ab Mon Sep 17 00:00:00 2001 From: Jacob Kiesel Date: Sat, 28 Sep 2024 18:09:44 -0600 Subject: [PATCH 080/120] Pile all the checksum info into a comment that goes in the same order as the file list for the makefile --- compiler/rustc_interface/src/passes.rs | 33 ++++++++++++++++---------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index b9b65755db5d2..cd5624e38bd23 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -536,7 +536,7 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P let write_deps_to_file = |file: &mut dyn Write| -> io::Result<()> { for path in out_filenames { - writeln!( + write!( file, "{}: {}\n", path.display(), @@ -546,6 +546,25 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P .intersperse(" ") .collect::() )?; + + // If caller requested this information, add special comments about source file checksums. + // These are not necessarily the same checksums as was used in the debug files. + if sess.opts.unstable_opts.checksum_hash_algorithm().is_some() { + assert!( + files.iter().all(|(_path, _file_len, hash_algo)| hash_algo.is_some()), + "all files must have a checksum hash computed to output checksum hashes" + ); + write!(file, " # ")?; + files + .iter() + .filter_map(|(_path, file_len, hash_algo)| { + hash_algo.map(|hash_algo| (path, file_len, hash_algo)) + }) + .try_for_each(|(_path, file_len, checksum_hash)| { + write!(file, "checksum:{checksum_hash} file_len:{file_len}, ") + })?; + } + writeln!(file)?; } // Emit a fake target for each input file to the compilation. This @@ -575,18 +594,6 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P } } - // If caller requested this information, add special comments about source file checksums. - // These are not necessarily the same checksums as was used in the debug files. - if sess.opts.unstable_opts.checksum_hash_algorithm().is_some() { - for (path, file_len, checksum_hash) in - files.iter().filter_map(|(path, file_len, hash_algo)| { - hash_algo.map(|hash_algo| (path, file_len, hash_algo)) - }) - { - writeln!(file, "# checksum:{checksum_hash} file_len:{file_len} {path}")?; - } - } - Ok(()) }; From e3089c787ce11ea45e0bb59a7c43c2803b326290 Mon Sep 17 00:00:00 2001 From: Jacob Kiesel Date: Sat, 28 Sep 2024 18:26:18 -0600 Subject: [PATCH 081/120] improve shell help text --- compiler/rustc_session/src/options.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index b78d370bce0a8..d63276db4938b 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -418,7 +418,8 @@ mod desc { "one of: `legacy`, `v0` (RFC 2603), or `hashed`"; pub(crate) const parse_opt_symbol_visibility: &str = "one of: `hidden`, `protected`, or `interposable`"; - pub(crate) const parse_cargo_src_file_hash: &str = "one of `md5`, `sha1`, or `sha256`"; + pub(crate) const parse_cargo_src_file_hash: &str = + "one of `blake3`, `md5`, `sha1`, or `sha256`"; pub(crate) const parse_src_file_hash: &str = "one of `md5`, `sha1`, or `sha256`"; pub(crate) const parse_relocation_model: &str = "one of supported relocation models (`rustc --print relocation-models`)"; From 6708d56fd2d5f937bc4b137757f18cd7c8dbd06b Mon Sep 17 00:00:00 2001 From: Jacob Kiesel Date: Sat, 28 Sep 2024 23:13:18 -0600 Subject: [PATCH 082/120] Fix bug in depinfo output --- compiler/rustc_interface/src/passes.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index cd5624e38bd23..5a8ea410a63f0 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -538,7 +538,7 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P for path in out_filenames { write!( file, - "{}: {}\n", + "{}: {}", path.display(), files .iter() @@ -565,6 +565,7 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P })?; } writeln!(file)?; + writeln!(file)?; } // Emit a fake target for each input file to the compilation. This From 6fd9ef606f61f3162590ce66e1aa79319b44f2e9 Mon Sep 17 00:00:00 2001 From: Jacob Kiesel Date: Sun, 29 Sep 2024 00:06:40 -0600 Subject: [PATCH 083/120] no need to comma delimit this, it's already space delimited --- compiler/rustc_interface/src/passes.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 5a8ea410a63f0..7429893b51fc6 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -554,14 +554,14 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P files.iter().all(|(_path, _file_len, hash_algo)| hash_algo.is_some()), "all files must have a checksum hash computed to output checksum hashes" ); - write!(file, " # ")?; + write!(file, " #")?; files .iter() .filter_map(|(_path, file_len, hash_algo)| { hash_algo.map(|hash_algo| (path, file_len, hash_algo)) }) .try_for_each(|(_path, file_len, checksum_hash)| { - write!(file, "checksum:{checksum_hash} file_len:{file_len}, ") + write!(file, " checksum:{checksum_hash} file_len:{file_len}") })?; } writeln!(file)?; From 0069649c3d23282942e44ee9d5d60546ae920070 Mon Sep 17 00:00:00 2001 From: Jacob Kiesel Date: Sun, 29 Sep 2024 01:22:30 -0600 Subject: [PATCH 084/120] Add basic integration test for checksum-hash-algorithm feature --- tests/run-make/checksum-freshness/expected.d | 3 +++ tests/run-make/checksum-freshness/lib.rs | 5 +++++ tests/run-make/checksum-freshness/rmake.rs | 9 +++++++++ 3 files changed, 17 insertions(+) create mode 100644 tests/run-make/checksum-freshness/expected.d create mode 100644 tests/run-make/checksum-freshness/lib.rs create mode 100644 tests/run-make/checksum-freshness/rmake.rs diff --git a/tests/run-make/checksum-freshness/expected.d b/tests/run-make/checksum-freshness/expected.d new file mode 100644 index 0000000000000..c79bc8ff7dec7 --- /dev/null +++ b/tests/run-make/checksum-freshness/expected.d @@ -0,0 +1,3 @@ +lib.d: lib.rs # checksum:blake3=5fd6328c93a3360bc580567aec10731ab3e5efc4a0f957b1bba0db0d27af3767 file_len:110 + +lib.rs: diff --git a/tests/run-make/checksum-freshness/lib.rs b/tests/run-make/checksum-freshness/lib.rs new file mode 100644 index 0000000000000..c2895dc2613c3 --- /dev/null +++ b/tests/run-make/checksum-freshness/lib.rs @@ -0,0 +1,5 @@ +// A basic library to be used in tests with no real purpose. + +pub fn sum(a: i32, b: i32) -> i32 { + a + b +} diff --git a/tests/run-make/checksum-freshness/rmake.rs b/tests/run-make/checksum-freshness/rmake.rs new file mode 100644 index 0000000000000..071db6b145b51 --- /dev/null +++ b/tests/run-make/checksum-freshness/rmake.rs @@ -0,0 +1,9 @@ +use run_make_support::{rfs, rustc}; + +fn main() { + rustc().input("lib.rs").arg("-Zchecksum-hash-algorithm=blake3").emit("dep-info").run(); + let make_file_contents = rfs::read_to_string("lib.d"); + let expected_contents = rfs::read_to_string("expected.d"); + assert_eq!(make_file_contents, expected_contents); + assert!(!expected_contents.is_empty()); +} From 4d9fa6fa775fc70cdaf1996ebe2c5b034d743819 Mon Sep 17 00:00:00 2001 From: Jacob Kiesel Date: Sun, 29 Sep 2024 01:27:47 -0600 Subject: [PATCH 085/120] add another file to the integration test --- tests/run-make/checksum-freshness/expected.d | 3 ++- tests/run-make/checksum-freshness/foo.rs | 5 +++++ tests/run-make/checksum-freshness/lib.rs | 2 ++ 3 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 tests/run-make/checksum-freshness/foo.rs diff --git a/tests/run-make/checksum-freshness/expected.d b/tests/run-make/checksum-freshness/expected.d index c79bc8ff7dec7..4b10e293229ce 100644 --- a/tests/run-make/checksum-freshness/expected.d +++ b/tests/run-make/checksum-freshness/expected.d @@ -1,3 +1,4 @@ -lib.d: lib.rs # checksum:blake3=5fd6328c93a3360bc580567aec10731ab3e5efc4a0f957b1bba0db0d27af3767 file_len:110 +lib.d: lib.rs foo.rs # checksum:blake3=94af75ee4ed805434484c3de51c9025278e5c3ada2315e2592052e102168a503 file_len:120 checksum:blake3=2720e17bfda4f3b2a5c96bb61b7e76ed8ebe3359b34128c0e5d8032c090a4f1a file_len:119 lib.rs: +foo.rs: diff --git a/tests/run-make/checksum-freshness/foo.rs b/tests/run-make/checksum-freshness/foo.rs new file mode 100644 index 0000000000000..d3ef768f18759 --- /dev/null +++ b/tests/run-make/checksum-freshness/foo.rs @@ -0,0 +1,5 @@ +// This is another file, just to prove we can handle two of them + +pub fn subtract(a: i32, b: i32) -> i32 { + a - b +} diff --git a/tests/run-make/checksum-freshness/lib.rs b/tests/run-make/checksum-freshness/lib.rs index c2895dc2613c3..7bc6757959b1d 100644 --- a/tests/run-make/checksum-freshness/lib.rs +++ b/tests/run-make/checksum-freshness/lib.rs @@ -1,5 +1,7 @@ // A basic library to be used in tests with no real purpose. +mod foo; + pub fn sum(a: i32, b: i32) -> i32 { a + b } From 15efbc6e8d78b257f594ddfb64d215c9ac3bee6d Mon Sep 17 00:00:00 2001 From: Jacob Kiesel Date: Tue, 1 Oct 2024 21:26:03 -0600 Subject: [PATCH 086/120] Write two newlines intentionally --- compiler/rustc_interface/src/passes.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 7429893b51fc6..a8afb8c2900ee 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -564,8 +564,7 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P write!(file, " checksum:{checksum_hash} file_len:{file_len}") })?; } - writeln!(file)?; - writeln!(file)?; + write!(file, "\n\n")?; } // Emit a fake target for each input file to the compilation. This From b48c5f19e07c46d43d03328c86d3f7af6f9c8187 Mon Sep 17 00:00:00 2001 From: Jacob Kiesel Date: Tue, 1 Oct 2024 23:05:24 -0600 Subject: [PATCH 087/120] Restore prior behavior with less duplicate info in dep file --- compiler/rustc_interface/src/passes.rs | 35 ++++++++------------ tests/run-make/checksum-freshness/expected.d | 4 ++- 2 files changed, 17 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index a8afb8c2900ee..b9b65755db5d2 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -536,9 +536,9 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P let write_deps_to_file = |file: &mut dyn Write| -> io::Result<()> { for path in out_filenames { - write!( + writeln!( file, - "{}: {}", + "{}: {}\n", path.display(), files .iter() @@ -546,25 +546,6 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P .intersperse(" ") .collect::() )?; - - // If caller requested this information, add special comments about source file checksums. - // These are not necessarily the same checksums as was used in the debug files. - if sess.opts.unstable_opts.checksum_hash_algorithm().is_some() { - assert!( - files.iter().all(|(_path, _file_len, hash_algo)| hash_algo.is_some()), - "all files must have a checksum hash computed to output checksum hashes" - ); - write!(file, " #")?; - files - .iter() - .filter_map(|(_path, file_len, hash_algo)| { - hash_algo.map(|hash_algo| (path, file_len, hash_algo)) - }) - .try_for_each(|(_path, file_len, checksum_hash)| { - write!(file, " checksum:{checksum_hash} file_len:{file_len}") - })?; - } - write!(file, "\n\n")?; } // Emit a fake target for each input file to the compilation. This @@ -594,6 +575,18 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P } } + // If caller requested this information, add special comments about source file checksums. + // These are not necessarily the same checksums as was used in the debug files. + if sess.opts.unstable_opts.checksum_hash_algorithm().is_some() { + for (path, file_len, checksum_hash) in + files.iter().filter_map(|(path, file_len, hash_algo)| { + hash_algo.map(|hash_algo| (path, file_len, hash_algo)) + }) + { + writeln!(file, "# checksum:{checksum_hash} file_len:{file_len} {path}")?; + } + } + Ok(()) }; diff --git a/tests/run-make/checksum-freshness/expected.d b/tests/run-make/checksum-freshness/expected.d index 4b10e293229ce..51467af53a20e 100644 --- a/tests/run-make/checksum-freshness/expected.d +++ b/tests/run-make/checksum-freshness/expected.d @@ -1,4 +1,6 @@ -lib.d: lib.rs foo.rs # checksum:blake3=94af75ee4ed805434484c3de51c9025278e5c3ada2315e2592052e102168a503 file_len:120 checksum:blake3=2720e17bfda4f3b2a5c96bb61b7e76ed8ebe3359b34128c0e5d8032c090a4f1a file_len:119 +lib.d: lib.rs foo.rs lib.rs: foo.rs: +# checksum:blake3=94af75ee4ed805434484c3de51c9025278e5c3ada2315e2592052e102168a503 file_len:120 lib.rs +# checksum:blake3=2720e17bfda4f3b2a5c96bb61b7e76ed8ebe3359b34128c0e5d8032c090a4f1a file_len:119 foo.rs From 807e812077ff9d70840f14822ae6e0e9753bd1fb Mon Sep 17 00:00:00 2001 From: ismailarilik Date: Wed, 2 Oct 2024 08:25:55 +0300 Subject: [PATCH 088/120] Handle `rustc-hir-analysis` cases of `rustc::potential_query_instability` lint --- .../rustc_hir_analysis/src/check/wfcheck.rs | 20 ++++++++--------- .../src/coherence/inherent_impls_overlap.rs | 4 ++-- .../src/hir_ty_lowering/mod.rs | 6 ++--- compiler/rustc_hir_analysis/src/lib.rs | 1 - compiler/rustc_middle/src/ty/mod.rs | 2 +- compiler/rustc_middle/src/ty/visit.rs | 10 ++++----- compiler/rustc_resolve/src/lib.rs | 2 +- .../self-outlives-lint.stderr | 22 +++++++++---------- 8 files changed, 33 insertions(+), 34 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 70b0b3f5788ed..02d23b95d46e6 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -2,7 +2,7 @@ use std::cell::LazyCell; use std::ops::{ControlFlow, Deref}; use hir::intravisit::{self, Visitor}; -use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::codes::*; use rustc_errors::{Applicability, ErrorGuaranteed, pluralize, struct_span_code_err}; use rustc_hir::ItemKind; @@ -404,7 +404,7 @@ fn check_trait_item<'tcx>( /// ``` fn check_gat_where_clauses(tcx: TyCtxt<'_>, trait_def_id: LocalDefId) { // Associates every GAT's def_id to a list of possibly missing bounds detected by this lint. - let mut required_bounds_by_item = FxHashMap::default(); + let mut required_bounds_by_item = FxIndexMap::default(); let associated_items = tcx.associated_items(trait_def_id); // Loop over all GATs together, because if this lint suggests adding a where-clause bound @@ -430,7 +430,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, trait_def_id: LocalDefId) { // Gather the bounds with which all other items inside of this trait constrain the GAT. // This is calculated by taking the intersection of the bounds that each item // constrains the GAT with individually. - let mut new_required_bounds: Option>> = None; + let mut new_required_bounds: Option>> = None; for item in associated_items.in_definition_order() { let item_def_id = item.def_id.expect_local(); // Skip our own GAT, since it does not constrain itself at all. @@ -589,7 +589,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, trait_def_id: LocalDefId) { fn augment_param_env<'tcx>( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, - new_predicates: Option<&FxHashSet>>, + new_predicates: Option<&FxIndexSet>>, ) -> ty::ParamEnv<'tcx> { let Some(new_predicates) = new_predicates else { return param_env; @@ -625,9 +625,9 @@ fn gather_gat_bounds<'tcx, T: TypeFoldable>>( wf_tys: &FxIndexSet>, gat_def_id: LocalDefId, gat_generics: &'tcx ty::Generics, -) -> Option>> { +) -> Option>> { // The bounds we that we would require from `to_check` - let mut bounds = FxHashSet::default(); + let mut bounds = FxIndexSet::default(); let (regions, types) = GATArgsCollector::visit(gat_def_id.to_def_id(), to_check); @@ -789,18 +789,18 @@ fn test_region_obligations<'tcx>( struct GATArgsCollector<'tcx> { gat: DefId, // Which region appears and which parameter index its instantiated with - regions: FxHashSet<(ty::Region<'tcx>, usize)>, + regions: FxIndexSet<(ty::Region<'tcx>, usize)>, // Which params appears and which parameter index its instantiated with - types: FxHashSet<(Ty<'tcx>, usize)>, + types: FxIndexSet<(Ty<'tcx>, usize)>, } impl<'tcx> GATArgsCollector<'tcx> { fn visit>>( gat: DefId, t: T, - ) -> (FxHashSet<(ty::Region<'tcx>, usize)>, FxHashSet<(Ty<'tcx>, usize)>) { + ) -> (FxIndexSet<(ty::Region<'tcx>, usize)>, FxIndexSet<(Ty<'tcx>, usize)>) { let mut visitor = - GATArgsCollector { gat, regions: FxHashSet::default(), types: FxHashSet::default() }; + GATArgsCollector { gat, regions: FxIndexSet::default(), types: FxIndexSet::default() }; t.visit_with(&mut visitor); (visitor.regions, visitor.types) } diff --git a/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs b/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs index b8066b4b47d90..d1c888a185ed9 100644 --- a/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs +++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs @@ -1,4 +1,4 @@ -use rustc_data_structures::fx::{FxHashSet, FxIndexMap, IndexEntry}; +use rustc_data_structures::fx::{FxIndexMap, FxIndexSet, IndexEntry}; use rustc_errors::codes::*; use rustc_errors::struct_span_code_err; use rustc_hir as hir; @@ -215,7 +215,7 @@ impl<'tcx> InherentOverlapChecker<'tcx> { struct ConnectedRegion { idents: SmallVec<[Symbol; 8]>, - impl_blocks: FxHashSet, + impl_blocks: FxIndexSet, } let mut connected_regions: IndexVec = Default::default(); // Reverse map from the Symbol to the connected region id. diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 2186952720f33..6dd3a06ef3773 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -23,7 +23,7 @@ mod lint; use std::slice; use rustc_ast::TraitObjectSyntax; -use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::codes::*; use rustc_errors::{ Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, FatalError, struct_span_code_err, @@ -2394,8 +2394,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { #[instrument(level = "trace", skip(self, generate_err))] fn validate_late_bound_regions<'cx>( &'cx self, - constrained_regions: FxHashSet, - referenced_regions: FxHashSet, + constrained_regions: FxIndexSet, + referenced_regions: FxIndexSet, generate_err: impl Fn(&str) -> Diag<'cx>, ) { for br in referenced_regions.difference(&constrained_regions) { diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index 92d85d48a42c0..7d40a7746b97c 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -58,7 +58,6 @@ This API is completely unstable and subject to change. // tidy-alphabetical-start #![allow(internal_features)] #![allow(rustc::diagnostic_outside_of_impl)] -#![allow(rustc::potential_query_instability)] #![allow(rustc::untranslatable_diagnostic)] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 2b1212a5eb567..c4a28845085d9 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -186,7 +186,7 @@ pub struct ResolverGlobalCtxt { pub proc_macros: Vec, /// Mapping from ident span to path span for paths that don't exist as written, but that /// exist under `std`. For example, wrote `str::from_utf8` instead of `std::str::from_utf8`. - pub confused_type_with_std_module: FxHashMap, + pub confused_type_with_std_module: FxIndexMap, pub doc_link_resolutions: FxHashMap, pub doc_link_traits_in_scope: FxHashMap>, pub all_macro_rules: FxHashMap>, diff --git a/compiler/rustc_middle/src/ty/visit.rs b/compiler/rustc_middle/src/ty/visit.rs index 78d83004c14e1..4efaccefcf74b 100644 --- a/compiler/rustc_middle/src/ty/visit.rs +++ b/compiler/rustc_middle/src/ty/visit.rs @@ -1,6 +1,6 @@ use std::ops::ControlFlow; -use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::fx::FxIndexSet; use rustc_type_ir::fold::TypeFoldable; pub use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor}; @@ -110,7 +110,7 @@ impl<'tcx> TyCtxt<'tcx> { pub fn collect_constrained_late_bound_regions( self, value: Binder<'tcx, T>, - ) -> FxHashSet + ) -> FxIndexSet where T: TypeFoldable>, { @@ -121,7 +121,7 @@ impl<'tcx> TyCtxt<'tcx> { pub fn collect_referenced_late_bound_regions( self, value: Binder<'tcx, T>, - ) -> FxHashSet + ) -> FxIndexSet where T: TypeFoldable>, { @@ -132,7 +132,7 @@ impl<'tcx> TyCtxt<'tcx> { self, value: Binder<'tcx, T>, just_constrained: bool, - ) -> FxHashSet + ) -> FxIndexSet where T: TypeFoldable>, { @@ -148,7 +148,7 @@ impl<'tcx> TyCtxt<'tcx> { /// into a hash set. struct LateBoundRegionsCollector { current_index: ty::DebruijnIndex, - regions: FxHashSet, + regions: FxIndexSet, /// `true` if we only want regions that are known to be /// "constrained" when you equate this type with another type. In diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index b84408cd0cb7a..0c1a0038f9c79 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -1188,7 +1188,7 @@ pub struct Resolver<'ra, 'tcx> { /// A list of proc macro LocalDefIds, written out in the order in which /// they are declared in the static array generated by proc_macro_harness. proc_macros: Vec, - confused_type_with_std_module: FxHashMap, + confused_type_with_std_module: FxIndexMap, /// Whether lifetime elision was successful. lifetime_elision_allowed: FxHashSet, diff --git a/tests/ui/generic-associated-types/self-outlives-lint.stderr b/tests/ui/generic-associated-types/self-outlives-lint.stderr index 9e9b2e18abeb8..58172bf06b512 100644 --- a/tests/ui/generic-associated-types/self-outlives-lint.stderr +++ b/tests/ui/generic-associated-types/self-outlives-lint.stderr @@ -108,17 +108,6 @@ LL | type Bar<'b>; = note: this bound is currently required to ensure that impls have maximum flexibility = note: we are soliciting feedback, see issue #87479 for more information -error: missing required bound on `Iterator` - --> $DIR/self-outlives-lint.rs:142:5 - | -LL | type Iterator<'a>: Iterator>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: add the required where clause: `where Self: 'a` - | - = note: this bound is currently required to ensure that impls have maximum flexibility - = note: we are soliciting feedback, see issue #87479 for more information - error: missing required bound on `Item` --> $DIR/self-outlives-lint.rs:140:5 | @@ -130,6 +119,17 @@ LL | type Item<'a>; = note: this bound is currently required to ensure that impls have maximum flexibility = note: we are soliciting feedback, see issue #87479 for more information +error: missing required bound on `Iterator` + --> $DIR/self-outlives-lint.rs:142:5 + | +LL | type Iterator<'a>: Iterator>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: add the required where clause: `where Self: 'a` + | + = note: this bound is currently required to ensure that impls have maximum flexibility + = note: we are soliciting feedback, see issue #87479 for more information + error: missing required bound on `Item` --> $DIR/self-outlives-lint.rs:148:5 | From 9fa120593e2b6325812aa558fa8878d5aa6635b2 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 2 Oct 2024 08:00:17 +0200 Subject: [PATCH 089/120] mpmc doctest: make sure main thread waits for child threads --- library/std/src/sync/mpmc/mod.rs | 46 +++++++++++++++++--------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/library/std/src/sync/mpmc/mod.rs b/library/std/src/sync/mpmc/mod.rs index 77a67f4fd386d..44e146a89bafb 100644 --- a/library/std/src/sync/mpmc/mod.rs +++ b/library/std/src/sync/mpmc/mod.rs @@ -66,29 +66,31 @@ //! use std::thread; //! use std::sync::mpmc::channel; //! -//! // Create a shared channel that can be sent along from many threads -//! // where tx is the sending half (tx for transmission), and rx is the receiving -//! // half (rx for receiving). -//! let (tx, rx) = channel(); -//! for i in 0..10 { -//! let tx = tx.clone(); -//! thread::spawn(move || { -//! tx.send(i).unwrap(); -//! }); -//! } +//! thread::scope(|s| { +//! // Create a shared channel that can be sent along from many threads +//! // where tx is the sending half (tx for transmission), and rx is the receiving +//! // half (rx for receiving). +//! let (tx, rx) = channel(); +//! for i in 0..10 { +//! let tx = tx.clone(); +//! s.spawn(move || { +//! tx.send(i).unwrap(); +//! }); +//! } //! -//! for _ in 0..5 { -//! let rx1 = rx.clone(); -//! let rx2 = rx.clone(); -//! thread::spawn(move || { -//! let j = rx1.recv().unwrap(); -//! assert!(0 <= j && j < 10); -//! }); -//! thread::spawn(move || { -//! let j = rx2.recv().unwrap(); -//! assert!(0 <= j && j < 10); -//! }); -//! } +//! for _ in 0..5 { +//! let rx1 = rx.clone(); +//! let rx2 = rx.clone(); +//! s.spawn(move || { +//! let j = rx1.recv().unwrap(); +//! assert!(0 <= j && j < 10); +//! }); +//! s.spawn(move || { +//! let j = rx2.recv().unwrap(); +//! assert!(0 <= j && j < 10); +//! }); +//! } +//! }) //! ``` //! //! Propagating panics: From 58c5ac43aea5c94d61e3a7a4e8646e714230574d Mon Sep 17 00:00:00 2001 From: Jacob Kiesel Date: Wed, 2 Oct 2024 00:49:46 -0600 Subject: [PATCH 090/120] restore prior more readable suggestion --- compiler/rustc_interface/src/passes.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index b9b65755db5d2..204ae437a3e9a 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -578,13 +578,14 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P // If caller requested this information, add special comments about source file checksums. // These are not necessarily the same checksums as was used in the debug files. if sess.opts.unstable_opts.checksum_hash_algorithm().is_some() { - for (path, file_len, checksum_hash) in - files.iter().filter_map(|(path, file_len, hash_algo)| { + files + .iter() + .filter_map(|(path, file_len, hash_algo)| { hash_algo.map(|hash_algo| (path, file_len, hash_algo)) }) - { - writeln!(file, "# checksum:{checksum_hash} file_len:{file_len} {path}")?; - } + .try_for_each(|(path, file_len, checksum_hash)| { + writeln!(file, "# checksum:{checksum_hash} file_len:{file_len} {path}") + })?; } Ok(()) From 8760a401bd55205a188fdc174785c6f32f4027fa Mon Sep 17 00:00:00 2001 From: Urgau Date: Wed, 2 Oct 2024 09:44:51 +0200 Subject: [PATCH 091/120] Update hashbrown to 0.15 and adjust some methods as well as removing some from std as they no longer exists in Hashbrown it-self. --- library/Cargo.lock | 4 +- library/std/Cargo.toml | 2 +- library/std/src/collections/hash/map.rs | 137 ++++++++++-------------- library/std/src/collections/hash/set.rs | 32 ------ 4 files changed, 58 insertions(+), 117 deletions(-) diff --git a/library/Cargo.lock b/library/Cargo.lock index 877ae0cc1cc93..209e30b304027 100644 --- a/library/Cargo.lock +++ b/library/Cargo.lock @@ -135,9 +135,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.5" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" dependencies = [ "allocator-api2", "compiler_builtins", diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 7d860b49e8739..63c65b8ef3994 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -20,7 +20,7 @@ core = { path = "../core", public = true } compiler_builtins = { version = "0.1.130" } profiler_builtins = { path = "../profiler_builtins", optional = true } unwind = { path = "../unwind" } -hashbrown = { version = "0.14", default-features = false, features = [ +hashbrown = { version = "0.15", default-features = false, features = [ 'rustc-dep-of-std', ] } # FIXME(#127890): `object` depends on `memchr`, but `memchr` > v2.5 causes diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs index 1a18721b15e92..6f2b4100620ee 100644 --- a/library/std/src/collections/hash/map.rs +++ b/library/std/src/collections/hash/map.rs @@ -909,8 +909,11 @@ where /// Attempts to get mutable references to `N` values in the map at once. /// /// Returns an array of length `N` with the results of each query. For soundness, at most one - /// mutable reference will be returned to any value. `None` will be returned if any of the - /// keys are duplicates or missing. + /// mutable reference will be returned to any value. `None` will be used if the key is missing. + /// + /// # Panics + /// + /// Panics if any keys are overlapping. /// /// # Examples /// @@ -924,16 +927,23 @@ where /// libraries.insert("Herzogin-Anna-Amalia-Bibliothek".to_string(), 1691); /// libraries.insert("Library of Congress".to_string(), 1800); /// + /// // Get Athenæum and Bodleian Library + /// let [Some(a), Some(b)] = libraries.get_many_mut([ + /// "Athenæum", + /// "Bodleian Library", + /// ]) else { panic!() }; + /// + /// // Assert values of Athenæum and Library of Congress /// let got = libraries.get_many_mut([ /// "Athenæum", /// "Library of Congress", /// ]); /// assert_eq!( /// got, - /// Some([ - /// &mut 1807, - /// &mut 1800, - /// ]), + /// [ + /// Some(&mut 1807), + /// Some(&mut 1800), + /// ], /// ); /// /// // Missing keys result in None @@ -941,18 +951,31 @@ where /// "Athenæum", /// "New York Public Library", /// ]); - /// assert_eq!(got, None); + /// assert_eq!( + /// got, + /// [ + /// Some(&mut 1807), + /// None + /// ] + /// ); + /// ``` + /// + /// ```should_panic + /// #![feature(map_many_mut)] + /// use std::collections::HashMap; /// - /// // Duplicate keys result in None + /// let mut libraries = HashMap::new(); + /// libraries.insert("Athenæum".to_string(), 1807); + /// + /// // Duplicate keys panic! /// let got = libraries.get_many_mut([ /// "Athenæum", /// "Athenæum", /// ]); - /// assert_eq!(got, None); /// ``` #[inline] #[unstable(feature = "map_many_mut", issue = "97601")] - pub fn get_many_mut(&mut self, ks: [&Q; N]) -> Option<[&'_ mut V; N]> + pub fn get_many_mut(&mut self, ks: [&Q; N]) -> [Option<&'_ mut V>; N] where K: Borrow, Q: Hash + Eq, @@ -963,10 +986,10 @@ where /// Attempts to get mutable references to `N` values in the map at once, without validating that /// the values are unique. /// - /// Returns an array of length `N` with the results of each query. `None` will be returned if - /// any of the keys are missing. + /// Returns an array of length `N` with the results of each query. `None` will be used if + /// the key is missing. /// - /// For a safe alternative see [`get_many_mut`](Self::get_many_mut). + /// For a safe alternative see [`get_many_mut`](`HashMap::get_many_mut`). /// /// # Safety /// @@ -987,31 +1010,39 @@ where /// libraries.insert("Herzogin-Anna-Amalia-Bibliothek".to_string(), 1691); /// libraries.insert("Library of Congress".to_string(), 1800); /// - /// let got = libraries.get_many_mut([ + /// // SAFETY: The keys do not overlap. + /// let [Some(a), Some(b)] = (unsafe { libraries.get_many_unchecked_mut([ + /// "Athenæum", + /// "Bodleian Library", + /// ]) }) else { panic!() }; + /// + /// // SAFETY: The keys do not overlap. + /// let got = unsafe { libraries.get_many_unchecked_mut([ /// "Athenæum", /// "Library of Congress", - /// ]); + /// ]) }; /// assert_eq!( /// got, - /// Some([ - /// &mut 1807, - /// &mut 1800, - /// ]), + /// [ + /// Some(&mut 1807), + /// Some(&mut 1800), + /// ], /// ); /// - /// // Missing keys result in None - /// let got = libraries.get_many_mut([ + /// // SAFETY: The keys do not overlap. + /// let got = unsafe { libraries.get_many_unchecked_mut([ /// "Athenæum", /// "New York Public Library", - /// ]); - /// assert_eq!(got, None); + /// ]) }; + /// // Missing keys result in None + /// assert_eq!(got, [Some(&mut 1807), None]); /// ``` #[inline] #[unstable(feature = "map_many_mut", issue = "97601")] pub unsafe fn get_many_unchecked_mut( &mut self, ks: [&Q; N], - ) -> Option<[&'_ mut V; N]> + ) -> [Option<&'_ mut V>; N] where K: Borrow, Q: Hash + Eq, @@ -2978,64 +3009,6 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> { pub fn remove(self) -> V { self.base.remove() } - - /// Replaces the entry, returning the old key and value. The new key in the hash map will be - /// the key used to create this entry. - /// - /// # Examples - /// - /// ``` - /// #![feature(map_entry_replace)] - /// use std::collections::hash_map::{Entry, HashMap}; - /// use std::rc::Rc; - /// - /// let mut map: HashMap, u32> = HashMap::new(); - /// map.insert(Rc::new("Stringthing".to_string()), 15); - /// - /// let my_key = Rc::new("Stringthing".to_string()); - /// - /// if let Entry::Occupied(entry) = map.entry(my_key) { - /// // Also replace the key with a handle to our other key. - /// let (old_key, old_value): (Rc, u32) = entry.replace_entry(16); - /// } - /// - /// ``` - #[inline] - #[unstable(feature = "map_entry_replace", issue = "44286")] - pub fn replace_entry(self, value: V) -> (K, V) { - self.base.replace_entry(value) - } - - /// Replaces the key in the hash map with the key used to create this entry. - /// - /// # Examples - /// - /// ``` - /// #![feature(map_entry_replace)] - /// use std::collections::hash_map::{Entry, HashMap}; - /// use std::rc::Rc; - /// - /// let mut map: HashMap, u32> = HashMap::new(); - /// let known_strings: Vec> = Vec::new(); - /// - /// // Initialise known strings, run program, etc. - /// - /// reclaim_memory(&mut map, &known_strings); - /// - /// fn reclaim_memory(map: &mut HashMap, u32>, known_strings: &[Rc] ) { - /// for s in known_strings { - /// if let Entry::Occupied(entry) = map.entry(Rc::clone(s)) { - /// // Replaces the entry's key with our version of it in `known_strings`. - /// entry.replace_key(); - /// } - /// } - /// } - /// ``` - #[inline] - #[unstable(feature = "map_entry_replace", issue = "44286")] - pub fn replace_key(self) -> K { - self.base.replace_key() - } } impl<'a, K: 'a, V: 'a> VacantEntry<'a, K, V> { diff --git a/library/std/src/collections/hash/set.rs b/library/std/src/collections/hash/set.rs index 4a113ddea3a6b..e69fb0878e7d0 100644 --- a/library/std/src/collections/hash/set.rs +++ b/library/std/src/collections/hash/set.rs @@ -724,38 +724,6 @@ where self.base.get_or_insert(value) } - /// Inserts an owned copy of the given `value` into the set if it is not - /// present, then returns a reference to the value in the set. - /// - /// # Examples - /// - /// ``` - /// #![feature(hash_set_entry)] - /// - /// use std::collections::HashSet; - /// - /// let mut set: HashSet = ["cat", "dog", "horse"] - /// .iter().map(|&pet| pet.to_owned()).collect(); - /// - /// assert_eq!(set.len(), 3); - /// for &pet in &["cat", "dog", "fish"] { - /// let value = set.get_or_insert_owned(pet); - /// assert_eq!(value, pet); - /// } - /// assert_eq!(set.len(), 4); // a new "fish" was inserted - /// ``` - #[inline] - #[unstable(feature = "hash_set_entry", issue = "60896")] - pub fn get_or_insert_owned(&mut self, value: &Q) -> &T - where - T: Borrow, - Q: Hash + Eq + ToOwned, - { - // Although the raw entry gives us `&mut T`, we only return `&T` to be consistent with - // `get`. Key mutation is "raw" because you're not supposed to affect `Eq` or `Hash`. - self.base.get_or_insert_owned(value) - } - /// Inserts a value computed from `f` into the set if the given `value` is /// not present, then returns a reference to the value in the set. /// From 37e1c955c511cc7ee8e53d848006ae8077555009 Mon Sep 17 00:00:00 2001 From: Urgau Date: Wed, 2 Oct 2024 09:45:27 +0200 Subject: [PATCH 092/120] Adjust check-cfg get_many_mut usage following API change --- compiler/rustc_session/src/config/cfg.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_session/src/config/cfg.rs b/compiler/rustc_session/src/config/cfg.rs index 44b96d92b1995..ccc01728958b8 100644 --- a/compiler/rustc_session/src/config/cfg.rs +++ b/compiler/rustc_session/src/config/cfg.rs @@ -402,18 +402,18 @@ impl CheckCfg { // Get all values map at once otherwise it would be costly. // (8 values * 220 targets ~= 1760 times, at the time of writing this comment). let [ - values_target_abi, - values_target_arch, - values_target_endian, - values_target_env, - values_target_family, - values_target_os, - values_target_pointer_width, - values_target_vendor, - ] = self - .expecteds - .get_many_mut(VALUES) - .expect("unable to get all the check-cfg values buckets"); + Some(values_target_abi), + Some(values_target_arch), + Some(values_target_endian), + Some(values_target_env), + Some(values_target_family), + Some(values_target_os), + Some(values_target_pointer_width), + Some(values_target_vendor), + ] = self.expecteds.get_many_mut(VALUES) + else { + panic!("unable to get all the check-cfg values buckets"); + }; for target in TARGETS .iter() From a923fc011bf14e94071594fd95a36c970ed8623d Mon Sep 17 00:00:00 2001 From: Urgau Date: Wed, 2 Oct 2024 11:15:48 +0200 Subject: [PATCH 093/120] Fix rustdoc-js-std path-ordering test due to API removal --- tests/rustdoc-js-std/path-ordering.js | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/rustdoc-js-std/path-ordering.js b/tests/rustdoc-js-std/path-ordering.js index e6b7bfab1e5f5..4bfc6256052ca 100644 --- a/tests/rustdoc-js-std/path-ordering.js +++ b/tests/rustdoc-js-std/path-ordering.js @@ -6,7 +6,6 @@ const EXPECTED = [ { 'path': 'std::collections::hash_set::HashSet', 'name': 'insert' }, { 'path': 'std::collections::hash_set::HashSet', 'name': 'get_or_insert' }, { 'path': 'std::collections::hash_set::HashSet', 'name': 'get_or_insert_with' }, - { 'path': 'std::collections::hash_set::HashSet', 'name': 'get_or_insert_owned' }, ], }, { From e9b2d09ad7905c51e7795730147f8d2303fb4bbd Mon Sep 17 00:00:00 2001 From: bohan Date: Wed, 2 Oct 2024 17:36:31 +0800 Subject: [PATCH 094/120] only query `params_in_repr` if def kind is adt --- compiler/rustc_middle/src/values.rs | 2 +- tests/crashes/128327.rs | 5 ----- tests/ui/infinite/auxiliary/alias.rs | 3 +++ tests/ui/infinite/infinite-assoc.rs | 16 ++++++++++++++++ tests/ui/infinite/infinite-assoc.stderr | 25 +++++++++++++++++++++++++ 5 files changed, 45 insertions(+), 6 deletions(-) delete mode 100644 tests/crashes/128327.rs create mode 100644 tests/ui/infinite/infinite-assoc.rs create mode 100644 tests/ui/infinite/infinite-assoc.stderr diff --git a/compiler/rustc_middle/src/values.rs b/compiler/rustc_middle/src/values.rs index c98d88e22d409..48ca38344cf2e 100644 --- a/compiler/rustc_middle/src/values.rs +++ b/compiler/rustc_middle/src/values.rs @@ -358,7 +358,7 @@ fn find_item_ty_spans( match ty.kind { hir::TyKind::Path(hir::QPath::Resolved(_, path)) => { if let Res::Def(kind, def_id) = path.res - && !matches!(kind, DefKind::TyAlias) + && matches!(kind, DefKind::Enum | DefKind::Struct | DefKind::Union) { let check_params = def_id.as_local().map_or(true, |def_id| { if def_id == needle { diff --git a/tests/crashes/128327.rs b/tests/crashes/128327.rs deleted file mode 100644 index a63f758c317cf..0000000000000 --- a/tests/crashes/128327.rs +++ /dev/null @@ -1,5 +0,0 @@ -//@ known-bug: rust-lang/rust#128327 - -use std::ops::Deref; -struct Apple((Apple, <&'static [f64] as Deref>::Target(Banana ? Citron))); -fn main(){} diff --git a/tests/ui/infinite/auxiliary/alias.rs b/tests/ui/infinite/auxiliary/alias.rs index 59add7eb18bdb..5ae124e8aba60 100644 --- a/tests/ui/infinite/auxiliary/alias.rs +++ b/tests/ui/infinite/auxiliary/alias.rs @@ -1,2 +1,5 @@ pub struct W(T); pub type Wrapper = W; +pub trait Trait { + type T; +} diff --git a/tests/ui/infinite/infinite-assoc.rs b/tests/ui/infinite/infinite-assoc.rs new file mode 100644 index 0000000000000..d128a7e0d2d59 --- /dev/null +++ b/tests/ui/infinite/infinite-assoc.rs @@ -0,0 +1,16 @@ +//@ aux-build: alias.rs + +// issue#128327 + +extern crate alias; + +use alias::Trait; +struct S; +impl Trait for S { + type T = (); +} +struct A((A, ::T)); +//~^ ERROR: invalid `?` in type +//~| ERROR: recursive type `A` has infinite size + +fn main() {} diff --git a/tests/ui/infinite/infinite-assoc.stderr b/tests/ui/infinite/infinite-assoc.stderr new file mode 100644 index 0000000000000..e6b91f1324138 --- /dev/null +++ b/tests/ui/infinite/infinite-assoc.stderr @@ -0,0 +1,25 @@ +error: invalid `?` in type + --> $DIR/infinite-assoc.rs:12:39 + | +LL | struct A((A, ::T)); + | ^ `?` is only allowed on expressions, not types + | +help: if you meant to express that the type might not contain a value, use the `Option` wrapper type + | +LL | struct A((A, ::T>)); + | +++++++ ~ + +error[E0072]: recursive type `A` has infinite size + --> $DIR/infinite-assoc.rs:12:1 + | +LL | struct A((A, ::T)); + | ^^^^^^^^ - recursive without indirection + | +help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle + | +LL | struct A((Box, ::T)); + | ++++ + + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0072`. From aa3251dd3b3d4550bdfd740ba8b4855a6c83d3be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?= <39484203+jieyouxu@users.noreply.github.com> Date: Wed, 2 Oct 2024 18:51:14 +0800 Subject: [PATCH 095/120] Replace zero-width whitespace with a visible `\` People tried to copy the `r?` which had an invisible zero-width whitespace, causing rustbot to not recognize the instruction, and which makes it difficult to figure out why it didn't work. --- .github/pull_request_template.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index fd54a153a1609..ecf8f993f90b0 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -7,6 +7,6 @@ tracking issue or there are none, feel free to ignore this. This PR will get automatically assigned to a reviewer. In case you would like a specific user to review your work, you can assign it to them by using - r​? + r\? (with the `\` removed) --> From 7f6150b5777a25ef8975a11266b0205d6ba8ab71 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Wed, 2 Oct 2024 19:42:06 +0800 Subject: [PATCH 096/120] Improve const traits diagnostics for new desugaring --- compiler/rustc_middle/src/macros.rs | 2 +- compiler/rustc_middle/src/ty/print/pretty.rs | 46 +++++++--- .../traits/fulfillment_errors.rs | 86 +++++++++++++++---- compiler/rustc_type_ir/src/predicate.rs | 7 ++ .../assoc-type-const-bound-usage-0.stderr | 16 +++- .../assoc-type-const-bound-usage-1.stderr | 16 +++- .../call-const-trait-method-fail.rs | 6 +- .../call-const-trait-method-fail.stderr | 29 ++----- .../call-generic-method-nonconst.rs | 7 +- .../call-generic-method-nonconst.stderr | 29 ++----- .../const-default-method-bodies.rs | 6 +- .../const-default-method-bodies.stderr | 29 ++----- .../const-fns-are-early-bound.stderr | 4 +- .../cross-crate.gatednc.stderr | 8 +- ...ault-method-body-is-const-same-trait-ck.rs | 6 +- ...-method-body-is-const-same-trait-ck.stderr | 29 ++----- .../super-traits-fail-2.rs | 3 +- .../super-traits-fail-2.yn.stderr | 10 +-- .../super-traits-fail-2.yy.stderr | 10 +-- .../super-traits-fail-3.rs | 2 +- .../super-traits-fail-3.yn.stderr | 10 +-- .../trait-where-clause-const.stderr | 16 +--- .../unsatisfied-const-trait-bound.rs | 1 - .../unsatisfied-const-trait-bound.stderr | 14 +-- 24 files changed, 224 insertions(+), 168 deletions(-) diff --git a/compiler/rustc_middle/src/macros.rs b/compiler/rustc_middle/src/macros.rs index d385be007d33f..39816c17b985f 100644 --- a/compiler/rustc_middle/src/macros.rs +++ b/compiler/rustc_middle/src/macros.rs @@ -67,7 +67,7 @@ macro_rules! TrivialLiftImpls { }; } -/// Used for types that are `Copy` and which **do not care arena +/// Used for types that are `Copy` and which **do not care about arena /// allocated data** (i.e., don't need to be folded). #[macro_export] macro_rules! TrivialTypeTraversalImpls { diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 8fd4c30457f88..7ada5fd93ba7d 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1951,19 +1951,18 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { fn pretty_print_bound_constness( &mut self, - trait_ref: ty::TraitRef<'tcx>, + constness: ty::BoundConstness, ) -> Result<(), PrintError> { define_scoped_cx!(self); - let Some(idx) = self.tcx().generics_of(trait_ref.def_id).host_effect_index else { - return Ok(()); - }; - let arg = trait_ref.args.const_at(idx); - - if arg == self.tcx().consts.false_ { - p!("const "); - } else if arg != self.tcx().consts.true_ && !arg.has_infer() { - p!("~const "); + match constness { + ty::BoundConstness::NotConst => {} + ty::BoundConstness::Const => { + p!("const "); + } + ty::BoundConstness::ConstIfConst => { + p!("~const "); + } } Ok(()) } @@ -2948,6 +2947,15 @@ impl<'tcx> ty::TraitPredicate<'tcx> { } } +#[derive(Copy, Clone, TypeFoldable, TypeVisitable, Lift)] +pub struct TraitPredPrintWithBoundConstness<'tcx>(ty::TraitPredicate<'tcx>, ty::BoundConstness); + +impl<'tcx> fmt::Debug for TraitPredPrintWithBoundConstness<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + #[extension(pub trait PrintPolyTraitPredicateExt<'tcx>)] impl<'tcx> ty::PolyTraitPredicate<'tcx> { fn print_modifiers_and_trait_path( @@ -2955,6 +2963,13 @@ impl<'tcx> ty::PolyTraitPredicate<'tcx> { ) -> ty::Binder<'tcx, TraitPredPrintModifiersAndPath<'tcx>> { self.map_bound(TraitPredPrintModifiersAndPath) } + + fn print_with_bound_constness( + self, + constness: ty::BoundConstness, + ) -> ty::Binder<'tcx, TraitPredPrintWithBoundConstness<'tcx>> { + self.map_bound(|trait_pred| TraitPredPrintWithBoundConstness(trait_pred, constness)) + } } #[derive(Debug, Copy, Clone, Lift)] @@ -3052,7 +3067,6 @@ define_print! { ty::TraitPredicate<'tcx> { p!(print(self.trait_ref.self_ty()), ": "); - p!(pretty_print_bound_constness(self.trait_ref)); if let ty::PredicatePolarity::Negative = self.polarity { p!("!"); } @@ -3184,13 +3198,21 @@ define_print_and_forward_display! { } TraitPredPrintModifiersAndPath<'tcx> { - p!(pretty_print_bound_constness(self.0.trait_ref)); if let ty::PredicatePolarity::Negative = self.0.polarity { p!("!") } p!(print(self.0.trait_ref.print_trait_sugared())); } + TraitPredPrintWithBoundConstness<'tcx> { + p!(print(self.0.trait_ref.self_ty()), ": "); + p!(pretty_print_bound_constness(self.1)); + if let ty::PredicatePolarity::Negative = self.0.polarity { + p!("!"); + } + p!(print(self.0.trait_ref.print_trait_sugared())) + } + PrintClosureAsImpl<'tcx> { p!(pretty_closure_as_impl(self.closure)) } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 34a0f182ab45f..5af117a3f48c4 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -19,8 +19,8 @@ use rustc_middle::ty::abstract_const::NotConstEvaluatable; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::print::{ - FmtPrinter, Print, PrintTraitPredicateExt as _, PrintTraitRefExt as _, - with_forced_trimmed_paths, + FmtPrinter, Print, PrintPolyTraitPredicateExt, PrintTraitPredicateExt as _, + PrintTraitRefExt as _, with_forced_trimmed_paths, }; use rustc_middle::ty::{ self, ToPolyTraitRef, TraitRef, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, Upcast, @@ -154,6 +154,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } else { (leaf_trait_predicate, &obligation) }; + + let (main_trait_predicate, leaf_trait_predicate, predicate_constness) = self.get_effects_trait_pred_override(main_trait_predicate, leaf_trait_predicate, span); + let main_trait_ref = main_trait_predicate.to_poly_trait_ref(); let leaf_trait_ref = leaf_trait_predicate.to_poly_trait_ref(); @@ -164,9 +167,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { return guar; } - // FIXME(effects) - let predicate_is_const = false; - if let Err(guar) = leaf_trait_predicate.error_reported() { return guar; @@ -227,7 +227,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let err_msg = self.get_standard_error_message( main_trait_predicate, message, - predicate_is_const, + predicate_constness, append_const_msg, post_message, ); @@ -286,7 +286,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } if tcx.is_lang_item(leaf_trait_ref.def_id(), LangItem::Drop) - && predicate_is_const + && matches!(predicate_constness, ty::BoundConstness::ConstIfConst | ty::BoundConstness::Const) { err.note("`~const Drop` was renamed to `~const Destruct`"); err.note("See for more details"); @@ -2187,29 +2187,34 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { &self, trait_predicate: ty::PolyTraitPredicate<'tcx>, message: Option, - predicate_is_const: bool, + predicate_constness: ty::BoundConstness, append_const_msg: Option, post_message: String, ) -> String { message .and_then(|cannot_do_this| { - match (predicate_is_const, append_const_msg) { + match (predicate_constness, append_const_msg) { // do nothing if predicate is not const - (false, _) => Some(cannot_do_this), + (ty::BoundConstness::NotConst, _) => Some(cannot_do_this), // suggested using default post message - (true, Some(AppendConstMessage::Default)) => { - Some(format!("{cannot_do_this} in const contexts")) - } + ( + ty::BoundConstness::Const | ty::BoundConstness::ConstIfConst, + Some(AppendConstMessage::Default), + ) => Some(format!("{cannot_do_this} in const contexts")), // overridden post message - (true, Some(AppendConstMessage::Custom(custom_msg, _))) => { - Some(format!("{cannot_do_this}{custom_msg}")) - } + ( + ty::BoundConstness::Const | ty::BoundConstness::ConstIfConst, + Some(AppendConstMessage::Custom(custom_msg, _)), + ) => Some(format!("{cannot_do_this}{custom_msg}")), // fallback to generic message - (true, None) => None, + (ty::BoundConstness::Const | ty::BoundConstness::ConstIfConst, None) => None, } }) .unwrap_or_else(|| { - format!("the trait bound `{trait_predicate}` is not satisfied{post_message}") + format!( + "the trait bound `{}` is not satisfied{post_message}", + trait_predicate.print_with_bound_constness(predicate_constness) + ) }) } @@ -2333,6 +2338,51 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } + /// For effects predicates such as `::Effects: Compat`, pretend that the + /// predicate that failed was `u32: Add`. Return the constness of such predicate to later + /// print as `u32: ~const Add`. + fn get_effects_trait_pred_override( + &self, + p: ty::PolyTraitPredicate<'tcx>, + leaf: ty::PolyTraitPredicate<'tcx>, + span: Span, + ) -> (ty::PolyTraitPredicate<'tcx>, ty::PolyTraitPredicate<'tcx>, ty::BoundConstness) { + let trait_ref = p.to_poly_trait_ref(); + if !self.tcx.is_lang_item(trait_ref.def_id(), LangItem::EffectsCompat) { + return (p, leaf, ty::BoundConstness::NotConst); + } + + let Some(ty::Alias(ty::AliasTyKind::Projection, projection)) = + trait_ref.self_ty().no_bound_vars().map(Ty::kind) + else { + return (p, leaf, ty::BoundConstness::NotConst); + }; + + let constness = trait_ref.skip_binder().args.const_at(1); + + let constness = if constness == self.tcx.consts.true_ || constness.is_ct_infer() { + ty::BoundConstness::NotConst + } else if constness == self.tcx.consts.false_ { + ty::BoundConstness::Const + } else if matches!(constness.kind(), ty::ConstKind::Param(_)) { + ty::BoundConstness::ConstIfConst + } else { + self.dcx().span_bug(span, format!("Unknown constness argument: {constness:?}")); + }; + + let new_pred = p.map_bound(|mut trait_pred| { + trait_pred.trait_ref = projection.trait_ref(self.tcx); + trait_pred + }); + + let new_leaf = leaf.map_bound(|mut trait_pred| { + trait_pred.trait_ref = projection.trait_ref(self.tcx); + trait_pred + }); + + (new_pred, new_leaf, constness) + } + fn add_tuple_trait_message( &self, obligation_cause_code: &ObligationCauseCode<'tcx>, diff --git a/compiler/rustc_type_ir/src/predicate.rs b/compiler/rustc_type_ir/src/predicate.rs index 76065c10d190e..8146181df6c17 100644 --- a/compiler/rustc_type_ir/src/predicate.rs +++ b/compiler/rustc_type_ir/src/predicate.rs @@ -755,3 +755,10 @@ impl fmt::Display for BoundConstness { } } } + +impl Lift for BoundConstness { + type Lifted = BoundConstness; + fn lift_to_interner(self, _: I) -> Option { + Some(self) + } +} diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage-0.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage-0.stderr index fb491453b379b..8288c660ce7c8 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage-0.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage-0.stderr @@ -3,11 +3,11 @@ error: using `#![feature(effects)]` without enabling next trait solver globally = note: the next trait solver must be enabled globally for the effects feature to work correctly = help: use `-Znext-solver` to enable -error[E0277]: the trait bound `Trait::{synthetic#0}: Compat` is not satisfied +error[E0277]: the trait bound `::Assoc: Trait` is not satisfied --> $DIR/assoc-type-const-bound-usage-0.rs:13:5 | LL | T::Assoc::func() - | ^^^^^^^^ the trait `Compat` is not implemented for `Trait::{synthetic#0}` + | ^^^^^^^^ the trait `Trait` is not implemented for `::Assoc` | note: required by a bound in `Trait::func` --> $DIR/assoc-type-const-bound-usage-0.rs:6:1 @@ -17,12 +17,16 @@ LL | #[const_trait] ... LL | fn func() -> i32; | ---- required by a bound in this associated function +help: consider further restricting the associated type + | +LL | const fn unqualified() -> i32 where ::Assoc: Trait { + | ++++++++++++++++++++++++++++++++ -error[E0277]: the trait bound `Trait::{synthetic#0}: Compat` is not satisfied +error[E0277]: the trait bound `::Assoc: Trait` is not satisfied --> $DIR/assoc-type-const-bound-usage-0.rs:17:5 | LL | ::Assoc::func() - | ^^^^^^^^^^^^^^^^^^^ the trait `Compat` is not implemented for `Trait::{synthetic#0}` + | ^^^^^^^^^^^^^^^^^^^ the trait `Trait` is not implemented for `::Assoc` | note: required by a bound in `Trait::func` --> $DIR/assoc-type-const-bound-usage-0.rs:6:1 @@ -32,6 +36,10 @@ LL | #[const_trait] ... LL | fn func() -> i32; | ---- required by a bound in this associated function +help: consider further restricting the associated type + | +LL | const fn qualified() -> i32 where ::Assoc: Trait { + | ++++++++++++++++++++++++++++++++ error: aborting due to 3 previous errors diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage-1.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage-1.stderr index 392b310a4c99e..0792d090321d3 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage-1.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage-1.stderr @@ -3,11 +3,11 @@ error: using `#![feature(effects)]` without enabling next trait solver globally = note: the next trait solver must be enabled globally for the effects feature to work correctly = help: use `-Znext-solver` to enable -error[E0277]: the trait bound `Trait::{synthetic#0}: Compat` is not satisfied +error[E0277]: the trait bound `::Assoc: Trait` is not satisfied --> $DIR/assoc-type-const-bound-usage-1.rs:15:44 | LL | fn unqualified() -> Type<{ T::Assoc::func() }> { - | ^^^^^^^^ the trait `Compat` is not implemented for `Trait::{synthetic#0}` + | ^^^^^^^^ the trait `Trait` is not implemented for `::Assoc` | note: required by a bound in `Trait::func` --> $DIR/assoc-type-const-bound-usage-1.rs:7:1 @@ -17,12 +17,16 @@ LL | #[const_trait] ... LL | fn func() -> i32; | ---- required by a bound in this associated function +help: consider further restricting the associated type + | +LL | fn unqualified() -> Type<{ T::Assoc::func() }> where ::Assoc: Trait { + | ++++++++++++++++++++++++++++++++ -error[E0277]: the trait bound `Trait::{synthetic#0}: Compat` is not satisfied +error[E0277]: the trait bound `::Assoc: Trait` is not satisfied --> $DIR/assoc-type-const-bound-usage-1.rs:19:42 | LL | fn qualified() -> Type<{ ::Assoc::func() }> { - | ^^^^^^^^^^^^^^^^^^^ the trait `Compat` is not implemented for `Trait::{synthetic#0}` + | ^^^^^^^^^^^^^^^^^^^ the trait `Trait` is not implemented for `::Assoc` | note: required by a bound in `Trait::func` --> $DIR/assoc-type-const-bound-usage-1.rs:7:1 @@ -32,6 +36,10 @@ LL | #[const_trait] ... LL | fn func() -> i32; | ---- required by a bound in this associated function +help: consider further restricting the associated type + | +LL | fn qualified() -> Type<{ ::Assoc::func() }> where ::Assoc: Trait { + | ++++++++++++++++++++++++++++++++ error: aborting due to 3 previous errors diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/call-const-trait-method-fail.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/call-const-trait-method-fail.rs index bb9e9045f8fb5..878f9a713a0b4 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/call-const-trait-method-fail.rs +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/call-const-trait-method-fail.rs @@ -1,4 +1,6 @@ -#![feature(const_trait_impl, effects)] //~ WARN the feature `effects` is incomplete +//@ compile-flags: -Znext-solver +#![allow(incomplete_features)] +#![feature(const_trait_impl, effects)] #[const_trait] pub trait Plus { @@ -23,7 +25,7 @@ pub const fn add_i32(a: i32, b: i32) -> i32 { pub const fn add_u32(a: u32, b: u32) -> u32 { a.plus(b) - //~^ ERROR the trait bound + //~^ ERROR the trait bound `u32: ~const Plus` } fn main() {} diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/call-const-trait-method-fail.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/call-const-trait-method-fail.stderr index 73ea1422bf9d7..5d2333d94fe89 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/call-const-trait-method-fail.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/call-const-trait-method-fail.stderr @@ -1,33 +1,22 @@ -warning: the feature `effects` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/call-const-trait-method-fail.rs:1:30 - | -LL | #![feature(const_trait_impl, effects)] - | ^^^^^^^ - | - = note: see issue #102090 for more information - = note: `#[warn(incomplete_features)]` on by default - -error: using `#![feature(effects)]` without enabling next trait solver globally - | - = note: the next trait solver must be enabled globally for the effects feature to work correctly - = help: use `-Znext-solver` to enable - -error[E0277]: the trait bound `Runtime: ~const Compat` is not satisfied - --> $DIR/call-const-trait-method-fail.rs:25:5 +error[E0277]: the trait bound `u32: ~const Plus` is not satisfied + --> $DIR/call-const-trait-method-fail.rs:27:5 | LL | a.plus(b) - | ^ the trait `~const Compat` is not implemented for `Runtime` + | ^ the trait `Plus` is not implemented for `u32` | - = help: the trait `Compat` is implemented for `Runtime` note: required by a bound in `Plus::plus` - --> $DIR/call-const-trait-method-fail.rs:3:1 + --> $DIR/call-const-trait-method-fail.rs:5:1 | LL | #[const_trait] | ^^^^^^^^^^^^^^ required by this bound in `Plus::plus` LL | pub trait Plus { LL | fn plus(self, rhs: Self) -> Self; | ---- required by a bound in this associated function +help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement + | +LL | pub const fn add_u32(a: u32, b: u32) -> u32 where u32: Plus { + | +++++++++++++++ -error: aborting due to 2 previous errors; 1 warning emitted +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-nonconst.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-nonconst.rs index 74e33ca72fffc..f9e79d417521d 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-nonconst.rs +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-nonconst.rs @@ -1,4 +1,6 @@ -#![feature(const_trait_impl, effects)] //~ WARN the feature `effects` is incomplete +//@ compile-flags: -Znext-solver +#![allow(incomplete_features)] +#![feature(const_trait_impl, effects)] struct S; @@ -21,7 +23,6 @@ const fn equals_self(t: &T) -> bool { // it not using the impl. pub const EQ: bool = equals_self(&S); -//~^ ERROR: the trait bound `Runtime: const Compat` is not satisfied -// FIXME(effects) diagnostic +//~^ ERROR: the trait bound `S: const Foo` is not satisfied fn main() {} diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-nonconst.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-nonconst.stderr index b2a98041c1cdf..68c9fc400104e 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-nonconst.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-nonconst.stderr @@ -1,32 +1,21 @@ -warning: the feature `effects` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/call-generic-method-nonconst.rs:1:30 - | -LL | #![feature(const_trait_impl, effects)] - | ^^^^^^^ - | - = note: see issue #102090 for more information - = note: `#[warn(incomplete_features)]` on by default - -error: using `#![feature(effects)]` without enabling next trait solver globally - | - = note: the next trait solver must be enabled globally for the effects feature to work correctly - = help: use `-Znext-solver` to enable - -error[E0277]: the trait bound `Runtime: const Compat` is not satisfied - --> $DIR/call-generic-method-nonconst.rs:23:34 +error[E0277]: the trait bound `S: const Foo` is not satisfied + --> $DIR/call-generic-method-nonconst.rs:25:34 | LL | pub const EQ: bool = equals_self(&S); - | ----------- ^^ the trait `const Compat` is not implemented for `Runtime` + | ----------- ^^ the trait `Foo` is not implemented for `S` | | | required by a bound introduced by this call | - = help: the trait `Compat` is implemented for `Runtime` note: required by a bound in `equals_self` - --> $DIR/call-generic-method-nonconst.rs:16:25 + --> $DIR/call-generic-method-nonconst.rs:18:25 | LL | const fn equals_self(t: &T) -> bool { | ^^^^^^^^^^ required by this bound in `equals_self` +help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement + | +LL | pub const EQ: bool where S: Foo = equals_self(&S); + | ++++++++++++ -error: aborting due to 2 previous errors; 1 warning emitted +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-default-method-bodies.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-default-method-bodies.rs index 2fd58b05178c2..a0333153f857a 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-default-method-bodies.rs +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-default-method-bodies.rs @@ -1,4 +1,6 @@ -#![feature(const_trait_impl, effects)] //~ WARN the feature `effects` is incomplete +//@ compile-flags: -Znext-solver +#![allow(incomplete_features)] +#![feature(const_trait_impl, effects)] #[const_trait] trait ConstDefaultFn: Sized { @@ -22,7 +24,7 @@ impl const ConstDefaultFn for ConstImpl { const fn test() { NonConstImpl.a(); - //~^ ERROR the trait bound + //~^ ERROR the trait bound `NonConstImpl: ~const ConstDefaultFn` is not satisfied ConstImpl.a(); } diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-default-method-bodies.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-default-method-bodies.stderr index 02f9dffba3239..0809d9c1e1d32 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-default-method-bodies.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-default-method-bodies.stderr @@ -1,33 +1,22 @@ -warning: the feature `effects` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/const-default-method-bodies.rs:1:30 - | -LL | #![feature(const_trait_impl, effects)] - | ^^^^^^^ - | - = note: see issue #102090 for more information - = note: `#[warn(incomplete_features)]` on by default - -error: using `#![feature(effects)]` without enabling next trait solver globally - | - = note: the next trait solver must be enabled globally for the effects feature to work correctly - = help: use `-Znext-solver` to enable - -error[E0277]: the trait bound `Runtime: ~const Compat` is not satisfied - --> $DIR/const-default-method-bodies.rs:24:18 +error[E0277]: the trait bound `NonConstImpl: ~const ConstDefaultFn` is not satisfied + --> $DIR/const-default-method-bodies.rs:26:18 | LL | NonConstImpl.a(); - | ^ the trait `~const Compat` is not implemented for `Runtime` + | ^ the trait `ConstDefaultFn` is not implemented for `NonConstImpl` | - = help: the trait `Compat` is implemented for `Runtime` note: required by a bound in `ConstDefaultFn::a` - --> $DIR/const-default-method-bodies.rs:3:1 + --> $DIR/const-default-method-bodies.rs:5:1 | LL | #[const_trait] | ^^^^^^^^^^^^^^ required by this bound in `ConstDefaultFn::a` ... LL | fn a(self) { | - required by a bound in this associated function +help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement + | +LL | const fn test() where NonConstImpl: ConstDefaultFn { + | ++++++++++++++++++++++++++++++++++ -error: aborting due to 2 previous errors; 1 warning emitted +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-fns-are-early-bound.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-fns-are-early-bound.stderr index 7aa3aa8c6bbb3..9eda9d98ec552 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-fns-are-early-bound.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-fns-are-early-bound.stderr @@ -1,8 +1,8 @@ -error[E0277]: the trait bound `FnOnce<()>::{synthetic#0}: const Compat` is not satisfied +error[E0277]: the trait bound `fn() {foo}: const FnOnce()` is not satisfied --> $DIR/const-fns-are-early-bound.rs:31:17 | LL | is_const_fn(foo); - | ----------- ^^^ the trait `const Compat` is not implemented for `FnOnce<()>::{synthetic#0}` + | ----------- ^^^ the trait `FnOnce()` is not implemented for fn item `fn() {foo}` | | | required by a bound introduced by this call | diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/cross-crate.gatednc.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/cross-crate.gatednc.stderr index b7209827c22c0..a34bae843c816 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/cross-crate.gatednc.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/cross-crate.gatednc.stderr @@ -1,8 +1,8 @@ -error[E0277]: the trait bound `cross_crate::MyTrait::{synthetic#0}: ~const Compat` is not satisfied +error[E0277]: the trait bound `cross_crate::NonConst: ~const cross_crate::MyTrait` is not satisfied --> $DIR/cross-crate.rs:19:14 | LL | NonConst.func(); - | ^^^^ the trait `~const Compat` is not implemented for `cross_crate::MyTrait::{synthetic#0}` + | ^^^^ the trait `cross_crate::MyTrait` is not implemented for `cross_crate::NonConst` | note: required by a bound in `func` --> $DIR/auxiliary/cross-crate.rs:5:1 @@ -12,6 +12,10 @@ LL | #[const_trait] ... LL | fn func(self); | ---- required by a bound in this associated function +help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement + | +LL | const fn const_context() where cross_crate::NonConst: cross_crate::MyTrait { + | +++++++++++++++++++++++++++++++++++++++++++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/default-method-body-is-const-same-trait-ck.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/default-method-body-is-const-same-trait-ck.rs index 64f23824b397d..0c2d93775a4d7 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/default-method-body-is-const-same-trait-ck.rs +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/default-method-body-is-const-same-trait-ck.rs @@ -1,4 +1,6 @@ -#![feature(const_trait_impl, effects)] //~ WARN the feature `effects` is incomplete +//@ compile-flags: -Znext-solver +#![allow(incomplete_features)] +#![feature(const_trait_impl, effects)] #[const_trait] pub trait Tr { @@ -6,7 +8,7 @@ pub trait Tr { fn b(&self) { ().a() - //~^ ERROR the trait bound + //~^ ERROR the trait bound `(): ~const Tr` is not satisfied } } diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/default-method-body-is-const-same-trait-ck.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/default-method-body-is-const-same-trait-ck.stderr index 1b5aa9c91918c..d0f22c0b9b62b 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/default-method-body-is-const-same-trait-ck.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/default-method-body-is-const-same-trait-ck.stderr @@ -1,33 +1,22 @@ -warning: the feature `effects` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/default-method-body-is-const-same-trait-ck.rs:1:30 - | -LL | #![feature(const_trait_impl, effects)] - | ^^^^^^^ - | - = note: see issue #102090 for more information - = note: `#[warn(incomplete_features)]` on by default - -error: using `#![feature(effects)]` without enabling next trait solver globally - | - = note: the next trait solver must be enabled globally for the effects feature to work correctly - = help: use `-Znext-solver` to enable - -error[E0277]: the trait bound `Runtime: ~const Compat` is not satisfied - --> $DIR/default-method-body-is-const-same-trait-ck.rs:8:12 +error[E0277]: the trait bound `(): ~const Tr` is not satisfied + --> $DIR/default-method-body-is-const-same-trait-ck.rs:10:12 | LL | ().a() - | ^ the trait `~const Compat` is not implemented for `Runtime` + | ^ the trait `Tr` is not implemented for `()` | - = help: the trait `Compat` is implemented for `Runtime` note: required by a bound in `Tr::a` - --> $DIR/default-method-body-is-const-same-trait-ck.rs:3:1 + --> $DIR/default-method-body-is-const-same-trait-ck.rs:5:1 | LL | #[const_trait] | ^^^^^^^^^^^^^^ required by this bound in `Tr::a` LL | pub trait Tr { LL | fn a(&self) {} | - required by a bound in this associated function +help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement + | +LL | pub trait Tr where (): Tr { + | ++++++++++++ -error: aborting due to 2 previous errors; 1 warning emitted +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.rs index 0d659744e700e..93a6f385e47db 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.rs +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.rs @@ -17,8 +17,7 @@ trait Bar: ~const Foo {} const fn foo(x: &T) { x.a(); - //[yy,yn]~^ ERROR the trait bound - // FIXME(effects) diagnostic + //[yy,yn]~^ ERROR the trait bound `T: ~const Foo` } fn main() {} diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.yn.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.yn.stderr index d4064e01ef10c..873c57ec71ffe 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.yn.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.yn.stderr @@ -10,11 +10,11 @@ note: this trait is not a `#[const_trait]`, so it cannot have `~const` trait bou LL | trait Bar: ~const Foo {} | ^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0277]: the trait bound `Foo::{synthetic#0}: ~const Compat` is not satisfied +error[E0277]: the trait bound `T: ~const Foo` is not satisfied --> $DIR/super-traits-fail-2.rs:19:7 | LL | x.a(); - | ^ the trait `~const Compat` is not implemented for `Foo::{synthetic#0}` + | ^ the trait `Foo` is not implemented for `T` | note: required by a bound in `Foo::a` --> $DIR/super-traits-fail-2.rs:6:25 @@ -24,10 +24,10 @@ LL | #[cfg_attr(any(yy, yn), const_trait)] LL | trait Foo { LL | fn a(&self); | - required by a bound in this associated function -help: consider further restricting the associated type +help: consider further restricting this bound | -LL | const fn foo(x: &T) where Foo::{synthetic#0}: ~const Compat { - | +++++++++++++++++++++++++++++++++++++++ +LL | const fn foo(x: &T) { + | +++++ error: aborting due to 2 previous errors diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.yy.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.yy.stderr index 9f9f96c6b486b..bea3aea2f3afb 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.yy.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.yy.stderr @@ -1,8 +1,8 @@ -error[E0277]: the trait bound `Foo::{synthetic#0}: ~const Compat` is not satisfied +error[E0277]: the trait bound `T: ~const Foo` is not satisfied --> $DIR/super-traits-fail-2.rs:19:7 | LL | x.a(); - | ^ the trait `~const Compat` is not implemented for `Foo::{synthetic#0}` + | ^ the trait `Foo` is not implemented for `T` | note: required by a bound in `Foo::a` --> $DIR/super-traits-fail-2.rs:6:25 @@ -12,10 +12,10 @@ LL | #[cfg_attr(any(yy, yn), const_trait)] LL | trait Foo { LL | fn a(&self); | - required by a bound in this associated function -help: consider further restricting the associated type +help: consider further restricting this bound | -LL | const fn foo(x: &T) where Foo::{synthetic#0}: ~const Compat { - | +++++++++++++++++++++++++++++++++++++++ +LL | const fn foo(x: &T) { + | +++++ error: aborting due to 1 previous error diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-3.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-3.rs index c7e224dcce02c..b5643b1170000 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-3.rs +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-3.rs @@ -20,7 +20,7 @@ trait Bar: ~const Foo {} const fn foo(x: &T) { //[yn,nn]~^ ERROR: `~const` can only be applied to `#[const_trait]` x.a(); - //[yn]~^ ERROR: the trait bound + //[yn]~^ ERROR: the trait bound `T: ~const Foo` is not satisfied } fn main() {} diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-3.yn.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-3.yn.stderr index 0b48633a10e20..bbc95948a59db 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-3.yn.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-3.yn.stderr @@ -16,11 +16,11 @@ error: `~const` can only be applied to `#[const_trait]` traits LL | const fn foo(x: &T) { | ^^^ -error[E0277]: the trait bound `Foo::{synthetic#0}: ~const Compat` is not satisfied +error[E0277]: the trait bound `T: ~const Foo` is not satisfied --> $DIR/super-traits-fail-3.rs:22:7 | LL | x.a(); - | ^ the trait `~const Compat` is not implemented for `Foo::{synthetic#0}` + | ^ the trait `Foo` is not implemented for `T` | note: required by a bound in `Foo::a` --> $DIR/super-traits-fail-3.rs:8:25 @@ -30,10 +30,10 @@ LL | #[cfg_attr(any(yy, yn), const_trait)] LL | trait Foo { LL | fn a(&self); | - required by a bound in this associated function -help: consider further restricting the associated type +help: consider further restricting this bound | -LL | const fn foo(x: &T) where Foo::{synthetic#0}: ~const Compat { - | +++++++++++++++++++++++++++++++++++++++ +LL | const fn foo(x: &T) { + | +++++ error: aborting due to 3 previous errors diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/trait-where-clause-const.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/trait-where-clause-const.stderr index 979f1e798e0a7..eaa981ec74444 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/trait-where-clause-const.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/trait-where-clause-const.stderr @@ -1,8 +1,8 @@ -error[E0277]: the trait bound `Foo::{synthetic#0}: Compat` is not satisfied +error[E0277]: the trait bound `T: Foo` is not satisfied --> $DIR/trait-where-clause-const.rs:22:5 | LL | T::b(); - | ^ the trait `Compat` is not implemented for `Foo::{synthetic#0}` + | ^ the trait `Foo` is not implemented for `T` | note: required by a bound in `Foo::b` --> $DIR/trait-where-clause-const.rs:13:1 @@ -12,10 +12,6 @@ LL | #[const_trait] ... LL | fn b() where Self: ~const Bar; | - required by a bound in this associated function -help: consider further restricting the associated type - | -LL | const fn test1() where Foo::{synthetic#0}: Compat { - | ++++++++++++++++++++++++++++++++ error[E0308]: mismatched types --> $DIR/trait-where-clause-const.rs:22:5 @@ -26,11 +22,11 @@ LL | T::b(); = note: expected constant `host` found constant `true` -error[E0277]: the trait bound `Foo::{synthetic#0}: Compat` is not satisfied +error[E0277]: the trait bound `T: Foo` is not satisfied --> $DIR/trait-where-clause-const.rs:25:5 | LL | T::c::(); - | ^ the trait `Compat` is not implemented for `Foo::{synthetic#0}` + | ^ the trait `Foo` is not implemented for `T` | note: required by a bound in `Foo::c` --> $DIR/trait-where-clause-const.rs:13:1 @@ -40,10 +36,6 @@ LL | #[const_trait] ... LL | fn c(); | - required by a bound in this associated function -help: consider further restricting the associated type - | -LL | const fn test1() where Foo::{synthetic#0}: Compat { - | ++++++++++++++++++++++++++++++++ error[E0308]: mismatched types --> $DIR/trait-where-clause-const.rs:25:5 diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/unsatisfied-const-trait-bound.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/unsatisfied-const-trait-bound.rs index 5fffe54fc1a99..d336719f52edc 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/unsatisfied-const-trait-bound.rs +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/unsatisfied-const-trait-bound.rs @@ -19,7 +19,6 @@ impl Trait for Ty { } fn main() { - // FIXME(effects): improve diagnostics on this require::(); } diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/unsatisfied-const-trait-bound.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/unsatisfied-const-trait-bound.stderr index 0806ffa4b5df6..848aa68689b48 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/unsatisfied-const-trait-bound.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/unsatisfied-const-trait-bound.stderr @@ -7,7 +7,7 @@ LL | #![feature(const_trait_impl, effects, generic_const_exprs)] = help: remove one of these features error[E0308]: mismatched types - --> $DIR/unsatisfied-const-trait-bound.rs:30:37 + --> $DIR/unsatisfied-const-trait-bound.rs:29:37 | LL | fn accept0(_: Container<{ T::make() }>) {} | ^^^^^^^^^ expected `false`, found `true` @@ -16,7 +16,7 @@ LL | fn accept0(_: Container<{ T::make() }>) {} found constant `true` error[E0308]: mismatched types - --> $DIR/unsatisfied-const-trait-bound.rs:34:50 + --> $DIR/unsatisfied-const-trait-bound.rs:33:50 | LL | const fn accept1(_: Container<{ T::make() }>) {} | ^^^^^^^^^ expected `false`, found `host` @@ -24,17 +24,21 @@ LL | const fn accept1(_: Container<{ T::make() }>) {} = note: expected constant `false` found constant `host` -error[E0277]: the trait bound `Trait::{synthetic#0}: const Compat` is not satisfied - --> $DIR/unsatisfied-const-trait-bound.rs:23:15 +error[E0277]: the trait bound `Ty: const Trait` is not satisfied + --> $DIR/unsatisfied-const-trait-bound.rs:22:15 | LL | require::(); - | ^^ the trait `const Compat` is not implemented for `Trait::{synthetic#0}` + | ^^ the trait `Trait` is not implemented for `Ty` | note: required by a bound in `require` --> $DIR/unsatisfied-const-trait-bound.rs:8:15 | LL | fn require() {} | ^^^^^^^^^^^ required by this bound in `require` +help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement + | +LL | fn main() where Ty: Trait { + | +++++++++++++++ error: aborting due to 4 previous errors From 1a04a317c447210306396e5ad4a100f423b1dfa6 Mon Sep 17 00:00:00 2001 From: lcnr Date: Wed, 2 Oct 2024 14:39:43 +0200 Subject: [PATCH 097/120] review --- .../src/infer/relate/type_relating.rs | 8 +++++--- compiler/rustc_middle/src/ty/fold.rs | 17 +++++++++-------- .../src/canonicalizer.rs | 12 +++++++----- compiler/rustc_next_trait_solver/src/resolve.rs | 2 ++ .../src/solve/eval_ctxt/mod.rs | 15 ++++++++------- 5 files changed, 31 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_infer/src/infer/relate/type_relating.rs b/compiler/rustc_infer/src/infer/relate/type_relating.rs index c8ced4a2cda85..7acfea643dd1e 100644 --- a/compiler/rustc_infer/src/infer/relate/type_relating.rs +++ b/compiler/rustc_infer/src/infer/relate/type_relating.rs @@ -14,14 +14,16 @@ use crate::infer::{DefineOpaqueTypes, InferCtxt, SubregionOrigin}; /// Enforce that `a` is equal to or a subtype of `b`. pub struct TypeRelating<'combine, 'a, 'tcx> { - // Partially mutable. + // Immutable except for the `InferCtxt` and the + // resulting nested `goals`. fields: &'combine mut CombineFields<'a, 'tcx>, - // Immutable fields. + // Immutable field. structurally_relate_aliases: StructurallyRelateAliases, + // Mutable field. ambient_variance: ty::Variance, - /// The cache has only tracks the `ambient_variance` as its the + /// The cache only tracks the `ambient_variance` as it's the /// only field which is mutable and which meaningfully changes /// the result when relating types. /// diff --git a/compiler/rustc_middle/src/ty/fold.rs b/compiler/rustc_middle/src/ty/fold.rs index e152d3f5fbe9f..b5a77b3c9423e 100644 --- a/compiler/rustc_middle/src/ty/fold.rs +++ b/compiler/rustc_middle/src/ty/fold.rs @@ -210,16 +210,17 @@ where debug_assert!(!ty.has_vars_bound_above(ty::INNERMOST)); ty::fold::shift_vars(self.tcx, ty, self.current_index.as_u32()) } - _ if t.has_vars_bound_at_or_above(self.current_index) => { - if let Some(&ty) = self.cache.get(&(self.current_index, t)) { - return ty; + _ => { + if !t.has_vars_bound_at_or_above(self.current_index) { + t + } else if let Some(&t) = self.cache.get(&(self.current_index, t)) { + t + } else { + let res = t.super_fold_with(self); + assert!(self.cache.insert((self.current_index, t), res)); + res } - - let res = t.super_fold_with(self); - assert!(self.cache.insert((self.current_index, t), res)); - res } - _ => t, } } diff --git a/compiler/rustc_next_trait_solver/src/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonicalizer.rs index 5c3c35b7b3e46..0bf9d7b924952 100644 --- a/compiler/rustc_next_trait_solver/src/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonicalizer.rs @@ -42,17 +42,19 @@ pub enum CanonicalizeMode { pub struct Canonicalizer<'a, D: SolverDelegate, I: Interner> { delegate: &'a D, + + // Immutable field. canonicalize_mode: CanonicalizeMode, + // Mutable fields. variables: &'a mut Vec, - variable_lookup_table: HashMap, - primitive_var_infos: Vec>, + variable_lookup_table: HashMap, binder_index: ty::DebruijnIndex, - /// We only use the debruijn index during lookup as all other fields - /// should not be impacted by whether a type is folded once or multiple - /// times. + /// We only use the debruijn index during lookup. We don't need to + /// track the `variables` as each generic arg only results in a single + /// bound variable regardless of how many times it is encountered. cache: HashMap<(ty::DebruijnIndex, I::Ty), I::Ty>, } diff --git a/compiler/rustc_next_trait_solver/src/resolve.rs b/compiler/rustc_next_trait_solver/src/resolve.rs index a37066cec669f..f2654f7534e1d 100644 --- a/compiler/rustc_next_trait_solver/src/resolve.rs +++ b/compiler/rustc_next_trait_solver/src/resolve.rs @@ -16,6 +16,8 @@ where I: Interner, { delegate: &'a D, + /// We're able to use a cache here as the folder does not have any + /// mutable state. cache: DelayedMap, } diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 12b4b3cb3a9d8..baa00465c0cce 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -1057,16 +1057,17 @@ where ); infer_ty } - _ if ty.has_aliases() => { - if let Some(&entry) = self.cache.get(&ty) { + _ => { + if !ty.has_aliases() { + ty + } else if let Some(&entry) = self.cache.get(&ty) { return entry; + } else { + let res = ty.super_fold_with(self); + assert!(self.cache.insert(ty, res).is_none()); + res } - - let res = ty.super_fold_with(self); - assert!(self.cache.insert(ty, res).is_none()); - res } - _ => ty, } } From d868fdce6b9ddef6abcc8de86b3ba8459def36a2 Mon Sep 17 00:00:00 2001 From: joboet Date: Thu, 18 Jul 2024 13:59:48 +0200 Subject: [PATCH 098/120] std: make `thread::current` available in all `thread_local!` destructors --- library/std/src/rt.rs | 34 ++- library/std/src/sys/pal/hermit/mod.rs | 6 +- library/std/src/sys/pal/hermit/thread.rs | 1 + library/std/src/sys/sync/rwlock/queue.rs | 6 +- .../std/src/sys/thread_local/guard/apple.rs | 1 + library/std/src/sys/thread_local/guard/key.rs | 39 ++- .../std/src/sys/thread_local/guard/solid.rs | 5 +- .../std/src/sys/thread_local/guard/windows.rs | 8 +- library/std/src/sys/thread_local/key/xous.rs | 2 + library/std/src/sys/thread_local/mod.rs | 48 ++-- .../std/src/sys/thread_local/native/mod.rs | 31 +++ library/std/src/sys/thread_local/os.rs | 34 ++- library/std/src/sys/thread_local/statik.rs | 33 ++- library/std/src/thread/current.rs | 252 ++++++++++++++++++ library/std/src/thread/local/tests.rs | 33 ++- library/std/src/thread/mod.rs | 128 +++------ 16 files changed, 529 insertions(+), 132 deletions(-) create mode 100644 library/std/src/thread/current.rs diff --git a/library/std/src/rt.rs b/library/std/src/rt.rs index b6f36931ec28a..0a841f07e3be7 100644 --- a/library/std/src/rt.rs +++ b/library/std/src/rt.rs @@ -21,9 +21,10 @@ pub use crate::panicking::{begin_panic, panic_count}; pub use core::panicking::{panic_display, panic_fmt}; #[rustfmt::skip] +use crate::any::Any; use crate::sync::Once; -use crate::sys; use crate::thread::{self, Thread}; +use crate::{mem, panic, sys}; // Prints to the "panic output", depending on the platform this may be: // - the standard error output @@ -66,6 +67,11 @@ macro_rules! rtunwrap { }; } +fn handle_rt_panic(e: Box) { + mem::forget(e); + rtabort!("initialization or cleanup bug"); +} + // One-time runtime initialization. // Runs before `main`. // SAFETY: must be called only once during runtime initialization. @@ -101,6 +107,20 @@ unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { thread::set_current(thread); } +/// Clean up the thread-local runtime state. This *should* be run after all other +/// code managed by the Rust runtime, but will not cause UB if that condition is +/// not fulfilled. Also note that this function is not guaranteed to be run, but +/// skipping it will cause leaks and therefore is to be avoided. +pub(crate) fn thread_cleanup() { + // This function is run in situations where unwinding leads to an abort + // (think `extern "C"` functions). Abort here instead so that we can + // print a nice message. + panic::catch_unwind(|| { + crate::thread::drop_current(); + }) + .unwrap_or_else(handle_rt_panic); +} + // One-time runtime cleanup. // Runs after `main` or at program exit. // NOTE: this is not guaranteed to run, for example when the program aborts. @@ -123,11 +143,6 @@ fn lang_start_internal( argv: *const *const u8, sigpipe: u8, ) -> Result { - use crate::{mem, panic}; - let rt_abort = move |e| { - mem::forget(e); - rtabort!("initialization or cleanup bug"); - }; // Guard against the code called by this function from unwinding outside of the Rust-controlled // code, which is UB. This is a requirement imposed by a combination of how the // `#[lang="start"]` attribute is implemented as well as by the implementation of the panicking @@ -139,16 +154,17 @@ fn lang_start_internal( // prevent std from accidentally introducing a panic to these functions. Another is from // user code from `main` or, more nefariously, as described in e.g. issue #86030. // SAFETY: Only called once during runtime initialization. - panic::catch_unwind(move || unsafe { init(argc, argv, sigpipe) }).map_err(rt_abort)?; + panic::catch_unwind(move || unsafe { init(argc, argv, sigpipe) }) + .unwrap_or_else(handle_rt_panic); let ret_code = panic::catch_unwind(move || panic::catch_unwind(main).unwrap_or(101) as isize) .map_err(move |e| { mem::forget(e); rtabort!("drop of the panic payload panicked"); }); - panic::catch_unwind(cleanup).map_err(rt_abort)?; + panic::catch_unwind(cleanup).unwrap_or_else(handle_rt_panic); // Guard against multiple threads calling `libc::exit` concurrently. // See the documentation for `unique_thread_exit` for more information. - panic::catch_unwind(|| crate::sys::exit_guard::unique_thread_exit()).map_err(rt_abort)?; + panic::catch_unwind(crate::sys::exit_guard::unique_thread_exit).unwrap_or_else(handle_rt_panic); ret_code } diff --git a/library/std/src/sys/pal/hermit/mod.rs b/library/std/src/sys/pal/hermit/mod.rs index f49ef94717499..b62afb40a615f 100644 --- a/library/std/src/sys/pal/hermit/mod.rs +++ b/library/std/src/sys/pal/hermit/mod.rs @@ -92,7 +92,11 @@ pub unsafe extern "C" fn runtime_entry( unsafe { crate::sys::thread_local::destructors::run(); } - unsafe { hermit_abi::exit(result) } + crate::rt::thread_cleanup(); + + unsafe { + hermit_abi::exit(result); + } } #[inline] diff --git a/library/std/src/sys/pal/hermit/thread.rs b/library/std/src/sys/pal/hermit/thread.rs index 4c0c0919f4799..41f2c3e212355 100644 --- a/library/std/src/sys/pal/hermit/thread.rs +++ b/library/std/src/sys/pal/hermit/thread.rs @@ -53,6 +53,7 @@ impl Thread { // run all destructors crate::sys::thread_local::destructors::run(); + crate::rt::thread_cleanup(); } } } diff --git a/library/std/src/sys/sync/rwlock/queue.rs b/library/std/src/sys/sync/rwlock/queue.rs index 0e658328c2e2d..733f51cae8c19 100644 --- a/library/std/src/sys/sync/rwlock/queue.rs +++ b/library/std/src/sys/sync/rwlock/queue.rs @@ -113,7 +113,7 @@ use crate::mem; use crate::ptr::{self, NonNull, null_mut, without_provenance_mut}; use crate::sync::atomic::Ordering::{AcqRel, Acquire, Relaxed, Release}; use crate::sync::atomic::{AtomicBool, AtomicPtr}; -use crate::thread::{self, Thread}; +use crate::thread::{self, Thread, ThreadId}; // Locking uses exponential backoff. `SPIN_COUNT` indicates how many times the // locking operation will be retried. @@ -200,7 +200,9 @@ impl Node { fn prepare(&mut self) { // Fall back to creating an unnamed `Thread` handle to allow locking in // TLS destructors. - self.thread.get_or_init(|| thread::try_current().unwrap_or_else(Thread::new_unnamed)); + self.thread.get_or_init(|| { + thread::try_current().unwrap_or_else(|| Thread::new_unnamed(ThreadId::new())) + }); self.completed = AtomicBool::new(false); } diff --git a/library/std/src/sys/thread_local/guard/apple.rs b/library/std/src/sys/thread_local/guard/apple.rs index 6c27f7ae35cba..fa25b116622fc 100644 --- a/library/std/src/sys/thread_local/guard/apple.rs +++ b/library/std/src/sys/thread_local/guard/apple.rs @@ -26,6 +26,7 @@ pub fn enable() { unsafe extern "C" fn run_dtors(_: *mut u8) { unsafe { destructors::run(); + crate::rt::thread_cleanup(); } } } diff --git a/library/std/src/sys/thread_local/guard/key.rs b/library/std/src/sys/thread_local/guard/key.rs index 77575547c1498..59581e6f281e6 100644 --- a/library/std/src/sys/thread_local/guard/key.rs +++ b/library/std/src/sys/thread_local/guard/key.rs @@ -3,10 +3,12 @@ //! that will run all native TLS destructors in the destructor list. use crate::ptr; -use crate::sys::thread_local::destructors; use crate::sys::thread_local::key::{LazyKey, set}; +#[cfg(target_thread_local)] pub fn enable() { + use crate::sys::thread_local::destructors; + static DTORS: LazyKey = LazyKey::new(Some(run)); // Setting the key value to something other than NULL will result in the @@ -18,6 +20,41 @@ pub fn enable() { unsafe extern "C" fn run(_: *mut u8) { unsafe { destructors::run(); + // On platforms with `__cxa_thread_atexit_impl`, `destructors::run` + // does nothing on newer systems as the TLS destructors are + // registered with the system. But because all of those platforms + // call the destructors of TLS keys after the registered ones, this + // function will still be run last (at the time of writing). + crate::rt::thread_cleanup(); + } + } +} + +/// On platforms with key-based TLS, the system runs the destructors for us. +/// We still have to make sure that [`crate::rt::thread_cleanup`] is called, +/// however. This is done by defering the execution of a TLS destructor to +/// the next round of destruction inside the TLS destructors. +#[cfg(not(target_thread_local))] +pub fn enable() { + const DEFER: *mut u8 = ptr::without_provenance_mut(1); + const RUN: *mut u8 = ptr::without_provenance_mut(2); + + static CLEANUP: LazyKey = LazyKey::new(Some(run)); + + unsafe { set(CLEANUP.force(), DEFER) } + + unsafe extern "C" fn run(state: *mut u8) { + if state == DEFER { + // Make sure that this function is run again in the next round of + // TLS destruction. If there is no futher round, there will be leaks, + // but that's okay, `thread_cleanup` is not guaranteed to be called. + unsafe { set(CLEANUP.force(), RUN) } + } else { + debug_assert_eq!(state, RUN); + // If the state is still RUN in the next round of TLS destruction, + // it means that no other TLS destructors defined by this runtime + // have been run, as they would have set the state to DEFER. + crate::rt::thread_cleanup(); } } } diff --git a/library/std/src/sys/thread_local/guard/solid.rs b/library/std/src/sys/thread_local/guard/solid.rs index 054b2d561c8b4..3bcd46c481dc0 100644 --- a/library/std/src/sys/thread_local/guard/solid.rs +++ b/library/std/src/sys/thread_local/guard/solid.rs @@ -19,6 +19,9 @@ pub fn enable() { } unsafe extern "C" fn tls_dtor(_unused: *mut u8) { - unsafe { destructors::run() }; + unsafe { + destructors::run(); + crate::rt::thread_cleanup(); + } } } diff --git a/library/std/src/sys/thread_local/guard/windows.rs b/library/std/src/sys/thread_local/guard/windows.rs index bf94f7d6e3d13..7ee8e695c753f 100644 --- a/library/std/src/sys/thread_local/guard/windows.rs +++ b/library/std/src/sys/thread_local/guard/windows.rs @@ -80,13 +80,13 @@ pub static CALLBACK: unsafe extern "system" fn(*mut c_void, u32, *mut c_void) = unsafe extern "system" fn tls_callback(_h: *mut c_void, dw_reason: u32, _pv: *mut c_void) { if dw_reason == c::DLL_THREAD_DETACH || dw_reason == c::DLL_PROCESS_DETACH { - #[cfg(target_thread_local)] unsafe { + #[cfg(target_thread_local)] super::super::destructors::run(); - } - #[cfg(not(target_thread_local))] - unsafe { + #[cfg(not(target_thread_local))] super::super::key::run_dtors(); + + crate::rt::thread_cleanup(); } } } diff --git a/library/std/src/sys/thread_local/key/xous.rs b/library/std/src/sys/thread_local/key/xous.rs index 295fb848b304d..2ab4bba7d8e98 100644 --- a/library/std/src/sys/thread_local/key/xous.rs +++ b/library/std/src/sys/thread_local/key/xous.rs @@ -212,4 +212,6 @@ unsafe fn run_dtors() { unsafe { cur = (*cur).next }; } } + + crate::rt::thread_cleanup(); } diff --git a/library/std/src/sys/thread_local/mod.rs b/library/std/src/sys/thread_local/mod.rs index 3d1b91a7ea095..31d3b43906004 100644 --- a/library/std/src/sys/thread_local/mod.rs +++ b/library/std/src/sys/thread_local/mod.rs @@ -31,12 +31,15 @@ cfg_if::cfg_if! { ))] { mod statik; pub use statik::{EagerStorage, LazyStorage, thread_local_inner}; + pub(crate) use statik::{LocalPointer, local_pointer}; } else if #[cfg(target_thread_local)] { mod native; pub use native::{EagerStorage, LazyStorage, thread_local_inner}; + pub(crate) use native::{LocalPointer, local_pointer}; } else { mod os; pub use os::{Storage, thread_local_inner}; + pub(crate) use os::{LocalPointer, local_pointer}; } } @@ -72,36 +75,47 @@ pub(crate) mod destructors { } /// This module provides a way to schedule the execution of the destructor list -/// on systems without a per-variable destructor system. -mod guard { +/// and the [runtime cleanup](crate::rt::thread_cleanup) function. Calling `enable` +/// should ensure that these functions are called at the right times. +pub(crate) mod guard { cfg_if::cfg_if! { if #[cfg(all(target_thread_local, target_vendor = "apple"))] { mod apple; - pub(super) use apple::enable; + pub(crate) use apple::enable; } else if #[cfg(target_os = "windows")] { mod windows; - pub(super) use windows::enable; + pub(crate) use windows::enable; } else if #[cfg(any( - all(target_family = "wasm", target_feature = "atomics"), + target_family = "wasm", + target_os = "uefi", + target_os = "zkvm", ))] { - pub(super) fn enable() { - // FIXME: Right now there is no concept of "thread exit", but - // this is likely going to show up at some point in the form of - // an exported symbol that the wasm runtime is going to be - // expected to call. For now we just leak everything, but if - // such a function starts to exist it will probably need to - // iterate the destructor list with this function: + pub(crate) fn enable() { + // FIXME: Right now there is no concept of "thread exit" on + // wasm, but this is likely going to show up at some point in + // the form of an exported symbol that the wasm runtime is going + // to be expected to call. For now we just leak everything, but + // if such a function starts to exist it will probably need to + // iterate the destructor list with these functions: + #[cfg(all(target_family = "wasm", target_feature = "atomics"))] #[allow(unused)] use super::destructors::run; + #[allow(unused)] + use crate::rt::thread_cleanup; } - } else if #[cfg(target_os = "hermit")] { - pub(super) fn enable() {} + } else if #[cfg(any( + target_os = "hermit", + target_os = "xous", + ))] { + // `std` is the only runtime, so it just calls the destructor functions + // itself when the time comes. + pub(crate) fn enable() {} } else if #[cfg(target_os = "solid_asp3")] { mod solid; - pub(super) use solid::enable; - } else if #[cfg(all(target_thread_local, not(target_family = "wasm")))] { + pub(crate) use solid::enable; + } else { mod key; - pub(super) use key::enable; + pub(crate) use key::enable; } } } diff --git a/library/std/src/sys/thread_local/native/mod.rs b/library/std/src/sys/thread_local/native/mod.rs index 1cc45fe892dee..f498dee0899f9 100644 --- a/library/std/src/sys/thread_local/native/mod.rs +++ b/library/std/src/sys/thread_local/native/mod.rs @@ -29,6 +29,9 @@ //! eliminates the `Destroyed` state for these values, which can allow more niche //! optimizations to occur for the `State` enum. For `Drop` types, `()` is used. +use crate::cell::Cell; +use crate::ptr; + mod eager; mod lazy; @@ -107,3 +110,31 @@ pub macro thread_local_inner { $crate::thread::local_impl::thread_local_inner!(@key $t, $($init)*); }, } + +#[rustc_macro_transparency = "semitransparent"] +pub(crate) macro local_pointer { + () => {}, + ($vis:vis static $name:ident; $($rest:tt)*) => { + #[thread_local] + $vis static $name: $crate::sys::thread_local::LocalPointer = $crate::sys::thread_local::LocalPointer::__new(); + $crate::sys::thread_local::local_pointer! { $($rest)* } + }, +} + +pub(crate) struct LocalPointer { + p: Cell<*mut ()>, +} + +impl LocalPointer { + pub const fn __new() -> LocalPointer { + LocalPointer { p: Cell::new(ptr::null_mut()) } + } + + pub fn get(&self) -> *mut () { + self.p.get() + } + + pub fn set(&self, p: *mut ()) { + self.p.set(p) + } +} diff --git a/library/std/src/sys/thread_local/os.rs b/library/std/src/sys/thread_local/os.rs index f09c396ef0f1e..26ce3322a16e3 100644 --- a/library/std/src/sys/thread_local/os.rs +++ b/library/std/src/sys/thread_local/os.rs @@ -1,8 +1,8 @@ -use super::abort_on_dtor_unwind; +use super::key::{Key, LazyKey, get, set}; +use super::{abort_on_dtor_unwind, guard}; use crate::cell::Cell; use crate::marker::PhantomData; use crate::ptr; -use crate::sys::thread_local::key::{Key, LazyKey, get, set}; #[doc(hidden)] #[allow_internal_unstable(thread_local_internals)] @@ -138,5 +138,35 @@ unsafe extern "C" fn destroy_value(ptr: *mut u8) { drop(ptr); // SAFETY: `key` is the TLS key `ptr` was stored under. unsafe { set(key, ptr::null_mut()) }; + // Make sure that the runtime cleanup will be performed + // after the next round of TLS destruction. + guard::enable(); }); } + +#[rustc_macro_transparency = "semitransparent"] +pub(crate) macro local_pointer { + () => {}, + ($vis:vis static $name:ident; $($rest:tt)*) => { + $vis static $name: $crate::sys::thread_local::LocalPointer = $crate::sys::thread_local::LocalPointer::__new(); + $crate::sys::thread_local::local_pointer! { $($rest)* } + }, +} + +pub(crate) struct LocalPointer { + key: LazyKey, +} + +impl LocalPointer { + pub const fn __new() -> LocalPointer { + LocalPointer { key: LazyKey::new(None) } + } + + pub fn get(&'static self) -> *mut () { + unsafe { get(self.key.force()) as *mut () } + } + + pub fn set(&'static self, p: *mut ()) { + unsafe { set(self.key.force(), p as *mut u8) } + } +} diff --git a/library/std/src/sys/thread_local/statik.rs b/library/std/src/sys/thread_local/statik.rs index a3451ab74e04f..ba94caa669027 100644 --- a/library/std/src/sys/thread_local/statik.rs +++ b/library/std/src/sys/thread_local/statik.rs @@ -1,7 +1,8 @@ //! On some targets like wasm there's no threads, so no need to generate //! thread locals and we can instead just use plain statics! -use crate::cell::UnsafeCell; +use crate::cell::{Cell, UnsafeCell}; +use crate::ptr; #[doc(hidden)] #[allow_internal_unstable(thread_local_internals)] @@ -93,3 +94,33 @@ impl LazyStorage { // SAFETY: the target doesn't have threads. unsafe impl Sync for LazyStorage {} + +#[rustc_macro_transparency = "semitransparent"] +pub(crate) macro local_pointer { + () => {}, + ($vis:vis static $name:ident; $($rest:tt)*) => { + $vis static $name: $crate::sys::thread_local::LocalPointer = $crate::sys::thread_local::LocalPointer::__new(); + $crate::sys::thread_local::local_pointer! { $($rest)* } + }, +} + +pub(crate) struct LocalPointer { + p: Cell<*mut ()>, +} + +impl LocalPointer { + pub const fn __new() -> LocalPointer { + LocalPointer { p: Cell::new(ptr::null_mut()) } + } + + pub fn get(&self) -> *mut () { + self.p.get() + } + + pub fn set(&self, p: *mut ()) { + self.p.set(p) + } +} + +// SAFETY: the target doesn't have threads. +unsafe impl Sync for LocalPointer {} diff --git a/library/std/src/thread/current.rs b/library/std/src/thread/current.rs new file mode 100644 index 0000000000000..b38149a0da740 --- /dev/null +++ b/library/std/src/thread/current.rs @@ -0,0 +1,252 @@ +use super::{Thread, ThreadId}; +use crate::mem::ManuallyDrop; +use crate::ptr; +use crate::sys::thread_local::local_pointer; + +const NONE: *mut () = ptr::null_mut(); +const BUSY: *mut () = ptr::without_provenance_mut(1); +const DESTROYED: *mut () = ptr::without_provenance_mut(2); + +local_pointer! { + static CURRENT; +} + +/// Persistent storage for the thread ID. +/// +/// We store the thread ID so that it never gets destroyed during the lifetime +/// of a thread, either using `#[thread_local]` or multiple `local_pointer!`s. +mod id { + use super::*; + + cfg_if::cfg_if! { + if #[cfg(target_thread_local)] { + use crate::cell::Cell; + + #[thread_local] + static ID: Cell> = Cell::new(None); + + pub(super) const CHEAP: bool = true; + + pub(super) fn get() -> Option { + ID.get() + } + + pub(super) fn set(id: ThreadId) { + ID.set(Some(id)) + } + } else if #[cfg(target_pointer_width = "16")] { + local_pointer! { + static ID0; + static ID16; + static ID32; + static ID48; + } + + pub(super) const CHEAP: bool = false; + + pub(super) fn get() -> Option { + let id0 = ID0.get().addr() as u64; + let id16 = ID16.get().addr() as u64; + let id32 = ID32.get().addr() as u64; + let id48 = ID48.get().addr() as u64; + ThreadId::from_u64((id48 << 48) + (id32 << 32) + (id16 << 16) + id0) + } + + pub(super) fn set(id: ThreadId) { + let val = id.as_u64().get(); + ID0.set(ptr::without_provenance_mut(val as usize)); + ID16.set(ptr::without_provenance_mut((val >> 16) as usize)); + ID32.set(ptr::without_provenance_mut((val >> 32) as usize)); + ID48.set(ptr::without_provenance_mut((val >> 48) as usize)); + } + } else if #[cfg(target_pointer_width = "32")] { + local_pointer! { + static ID0; + static ID32; + } + + pub(super) const CHEAP: bool = false; + + pub(super) fn get() -> Option { + let id0 = ID0.get().addr() as u64; + let id32 = ID32.get().addr() as u64; + ThreadId::from_u64((id32 << 32) + id0) + } + + pub(super) fn set(id: ThreadId) { + let val = id.as_u64().get(); + ID0.set(ptr::without_provenance_mut(val as usize)); + ID32.set(ptr::without_provenance_mut((val >> 32) as usize)); + } + } else { + local_pointer! { + static ID; + } + + pub(super) const CHEAP: bool = true; + + pub(super) fn get() -> Option { + let id = ID.get().addr() as u64; + ThreadId::from_u64(id) + } + + pub(super) fn set(id: ThreadId) { + let val = id.as_u64().get(); + ID.set(ptr::without_provenance_mut(val as usize)); + } + } + } + + #[inline] + pub(super) fn get_or_init() -> ThreadId { + get().unwrap_or_else( + #[cold] + || { + let id = ThreadId::new(); + id::set(id); + id + }, + ) + } +} + +/// Sets the thread handle for the current thread. +/// +/// Aborts if the handle or the ID has been set already. +pub(crate) fn set_current(thread: Thread) { + if CURRENT.get() != NONE || id::get().is_some() { + // Using `panic` here can add ~3kB to the binary size. We have complete + // control over where this is called, so just abort if there is a bug. + rtabort!("thread::set_current should only be called once per thread"); + } + + id::set(thread.id()); + + // Make sure that `crate::rt::thread_cleanup` will be run, which will + // call `drop_current`. + crate::sys::thread_local::guard::enable(); + CURRENT.set(thread.into_raw().cast_mut()); +} + +/// Gets the id of the thread that invokes it. +/// +/// This function will always succeed, will always return the same value for +/// one thread and is guaranteed not to call the global allocator. +#[inline] +pub(crate) fn current_id() -> ThreadId { + // If accessing the persistant thread ID takes multiple TLS accesses, try + // to retrieve it from the current thread handle, which will only take one + // TLS access. + if !id::CHEAP { + let current = CURRENT.get(); + if current > DESTROYED { + unsafe { + let current = ManuallyDrop::new(Thread::from_raw(current)); + return current.id(); + } + } + } + + id::get_or_init() +} + +/// Gets a handle to the thread that invokes it, if the handle has been initialized. +pub(crate) fn try_current() -> Option { + let current = CURRENT.get(); + if current > DESTROYED { + unsafe { + let current = ManuallyDrop::new(Thread::from_raw(current)); + Some((*current).clone()) + } + } else { + None + } +} + +/// Gets a handle to the thread that invokes it. +/// +/// # Examples +/// +/// Getting a handle to the current thread with `thread::current()`: +/// +/// ``` +/// use std::thread; +/// +/// let handler = thread::Builder::new() +/// .name("named thread".into()) +/// .spawn(|| { +/// let handle = thread::current(); +/// assert_eq!(handle.name(), Some("named thread")); +/// }) +/// .unwrap(); +/// +/// handler.join().unwrap(); +/// ``` +#[must_use] +#[stable(feature = "rust1", since = "1.0.0")] +pub fn current() -> Thread { + let current = CURRENT.get(); + if current > DESTROYED { + unsafe { + let current = ManuallyDrop::new(Thread::from_raw(current)); + (*current).clone() + } + } else { + init_current(current) + } +} + +#[cold] +fn init_current(current: *mut ()) -> Thread { + if current == NONE { + CURRENT.set(BUSY); + // If the thread ID was initialized already, use it. + let id = id::get_or_init(); + let thread = Thread::new_unnamed(id); + + // Make sure that `crate::rt::thread_cleanup` will be run, which will + // call `drop_current`. + crate::sys::thread_local::guard::enable(); + CURRENT.set(thread.clone().into_raw().cast_mut()); + thread + } else if current == BUSY { + // BUSY exists solely for this check, but as it is in the slow path, the + // extra TLS write above shouldn't matter. The alternative is nearly always + // a stack overflow. + + // If you came across this message, contact the author of your allocator. + // If you are said author: A surprising amount of functions inside the + // standard library (e.g. `Mutex`, `thread_local!`, `File` when using long + // paths, even `panic!` when using unwinding), need memory allocation, so + // you'll get circular dependencies all over the place when using them. + // I (joboet) highly recommend using only APIs from core in your allocator + // and implementing your own system abstractions. Still, if you feel that + // a particular API should be entirely allocation-free, feel free to open + // an issue on the Rust repository, we'll see what we can do. + rtabort!( + "\n + Attempted to access thread-local data while allocating said data.\n + Do not access functions that allocate in the global allocator!\n + This is a bug in the global allocator.\n + " + ) + } else { + debug_assert_eq!(current, DESTROYED); + panic!( + "use of std::thread::current() is not possible after the thread's + local data has been destroyed" + ) + } +} + +/// This should be run in [`crate::rt::thread_cleanup`] to reset the thread +/// handle. +pub(crate) fn drop_current() { + let current = CURRENT.get(); + if current > DESTROYED { + unsafe { + CURRENT.set(DESTROYED); + drop(Thread::from_raw(current)); + } + } +} diff --git a/library/std/src/thread/local/tests.rs b/library/std/src/thread/local/tests.rs index 6abb9b85a2e29..9d4f52a09218e 100644 --- a/library/std/src/thread/local/tests.rs +++ b/library/std/src/thread/local/tests.rs @@ -1,7 +1,7 @@ use crate::cell::{Cell, UnsafeCell}; use crate::sync::atomic::{AtomicU8, Ordering}; use crate::sync::{Arc, Condvar, Mutex}; -use crate::thread::{self, LocalKey}; +use crate::thread::{self, Builder, LocalKey}; use crate::thread_local; #[derive(Clone, Default)] @@ -343,3 +343,34 @@ fn join_orders_after_tls_destructors() { jh2.join().unwrap(); } } + +// Test that thread::current is still available in TLS destructors. +#[test] +fn thread_current_in_dtor() { + // Go through one round of TLS destruction first. + struct Defer; + impl Drop for Defer { + fn drop(&mut self) { + RETRIEVE.with(|_| {}); + } + } + + struct RetrieveName; + impl Drop for RetrieveName { + fn drop(&mut self) { + *NAME.lock().unwrap() = Some(thread::current().name().unwrap().to_owned()); + } + } + + static NAME: Mutex> = Mutex::new(None); + + thread_local! { + static DEFER: Defer = const { Defer }; + static RETRIEVE: RetrieveName = const { RetrieveName }; + } + + Builder::new().name("test".to_owned()).spawn(|| DEFER.with(|_| {})).unwrap().join().unwrap(); + let name = NAME.lock().unwrap(); + let name = name.as_ref().unwrap(); + assert_eq!(name, "test"); +} diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 22d655833650b..d1d4eabb9bd45 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -141,7 +141,7 @@ //! [`Result`]: crate::result::Result //! [`Ok`]: crate::result::Result::Ok //! [`Err`]: crate::result::Result::Err -//! [`thread::current`]: current +//! [`thread::current`]: current::current //! [`thread::Result`]: Result //! [`unpark`]: Thread::unpark //! [`thread::park_timeout`]: park_timeout @@ -159,7 +159,7 @@ mod tests; use crate::any::Any; -use crate::cell::{Cell, OnceCell, UnsafeCell}; +use crate::cell::UnsafeCell; use crate::ffi::CStr; use crate::marker::PhantomData; use crate::mem::{self, ManuallyDrop, forget}; @@ -179,6 +179,12 @@ mod scoped; #[stable(feature = "scoped_threads", since = "1.63.0")] pub use scoped::{Scope, ScopedJoinHandle, scope}; +mod current; + +#[stable(feature = "rust1", since = "1.0.0")] +pub use current::current; +pub(crate) use current::{current_id, drop_current, set_current, try_current}; + //////////////////////////////////////////////////////////////////////////////// // Thread-local storage //////////////////////////////////////////////////////////////////////////////// @@ -471,7 +477,11 @@ impl Builder { amt }); - let my_thread = name.map_or_else(Thread::new_unnamed, Thread::new); + let id = ThreadId::new(); + let my_thread = match name { + Some(name) => Thread::new(id, name.into()), + None => Thread::new_unnamed(id), + }; let their_thread = my_thread.clone(); let my_packet: Arc> = Arc::new(Packet { @@ -509,6 +519,9 @@ impl Builder { let f = MaybeDangling::new(f); let main = move || { + // Immediately store the thread handle to avoid setting it or its ID + // twice, which would cause an abort. + set_current(their_thread.clone()); if let Some(name) = their_thread.cname() { imp::Thread::set_name(name); } @@ -516,7 +529,6 @@ impl Builder { crate::io::set_output_capture(output_capture); let f = f.into_inner(); - set_current(their_thread); let try_result = panic::catch_unwind(panic::AssertUnwindSafe(|| { crate::sys::backtrace::__rust_begin_short_backtrace(f) })); @@ -690,84 +702,6 @@ where Builder::new().spawn(f).expect("failed to spawn thread") } -thread_local! { - // Invariant: `CURRENT` and `CURRENT_ID` will always be initialized together. - // If `CURRENT` is initialized, then `CURRENT_ID` will hold the same value - // as `CURRENT.id()`. - static CURRENT: OnceCell = const { OnceCell::new() }; - static CURRENT_ID: Cell> = const { Cell::new(None) }; -} - -/// Sets the thread handle for the current thread. -/// -/// Aborts if the handle has been set already to reduce code size. -pub(crate) fn set_current(thread: Thread) { - let tid = thread.id(); - // Using `unwrap` here can add ~3kB to the binary size. We have complete - // control over where this is called, so just abort if there is a bug. - CURRENT.with(|current| match current.set(thread) { - Ok(()) => CURRENT_ID.set(Some(tid)), - Err(_) => rtabort!("thread::set_current should only be called once per thread"), - }); -} - -/// Gets a handle to the thread that invokes it. -/// -/// In contrast to the public `current` function, this will not panic if called -/// from inside a TLS destructor. -pub(crate) fn try_current() -> Option { - CURRENT - .try_with(|current| { - current - .get_or_init(|| { - let thread = Thread::new_unnamed(); - CURRENT_ID.set(Some(thread.id())); - thread - }) - .clone() - }) - .ok() -} - -/// Gets the id of the thread that invokes it. -#[inline] -pub(crate) fn current_id() -> ThreadId { - CURRENT_ID.get().unwrap_or_else(|| { - // If `CURRENT_ID` isn't initialized yet, then `CURRENT` must also not be initialized. - // `current()` will initialize both `CURRENT` and `CURRENT_ID` so subsequent calls to - // `current_id()` will succeed immediately. - current().id() - }) -} - -/// Gets a handle to the thread that invokes it. -/// -/// # Examples -/// -/// Getting a handle to the current thread with `thread::current()`: -/// -/// ``` -/// use std::thread; -/// -/// let handler = thread::Builder::new() -/// .name("named thread".into()) -/// .spawn(|| { -/// let handle = thread::current(); -/// assert_eq!(handle.name(), Some("named thread")); -/// }) -/// .unwrap(); -/// -/// handler.join().unwrap(); -/// ``` -#[must_use] -#[stable(feature = "rust1", since = "1.0.0")] -pub fn current() -> Thread { - try_current().expect( - "use of std::thread::current() is not possible \ - after the thread's local data has been destroyed", - ) -} - /// Cooperatively gives up a timeslice to the OS scheduler. /// /// This calls the underlying OS scheduler's yield primitive, signaling @@ -1225,8 +1159,11 @@ pub fn park_timeout(dur: Duration) { pub struct ThreadId(NonZero); impl ThreadId { + // DO NOT rely on this value. + const MAIN_THREAD: ThreadId = ThreadId(unsafe { NonZero::new_unchecked(1) }); + // Generate a new unique thread ID. - fn new() -> ThreadId { + pub(crate) fn new() -> ThreadId { #[cold] fn exhausted() -> ! { panic!("failed to generate unique thread ID: bitspace exhausted") @@ -1236,7 +1173,7 @@ impl ThreadId { if #[cfg(target_has_atomic = "64")] { use crate::sync::atomic::AtomicU64; - static COUNTER: AtomicU64 = AtomicU64::new(0); + static COUNTER: AtomicU64 = AtomicU64::new(1); let mut last = COUNTER.load(Ordering::Relaxed); loop { @@ -1252,7 +1189,7 @@ impl ThreadId { } else { use crate::sync::{Mutex, PoisonError}; - static COUNTER: Mutex = Mutex::new(0); + static COUNTER: Mutex = Mutex::new(1); let mut counter = COUNTER.lock().unwrap_or_else(PoisonError::into_inner); let Some(id) = counter.checked_add(1) else { @@ -1269,6 +1206,11 @@ impl ThreadId { } } + #[cfg(not(target_thread_local))] + fn from_u64(v: u64) -> Option { + NonZero::new(v).map(ThreadId) + } + /// This returns a numeric identifier for the thread identified by this /// `ThreadId`. /// @@ -1369,27 +1311,27 @@ impl Inner { /// should instead use a function like `spawn` to create new threads, see the /// docs of [`Builder`] and [`spawn`] for more details. /// -/// [`thread::current`]: current +/// [`thread::current`]: current::current pub struct Thread { inner: Pin>, } impl Thread { /// Used only internally to construct a thread object without spawning. - pub(crate) fn new(name: String) -> Thread { - Self::new_inner(ThreadName::Other(name.into())) + pub(crate) fn new(id: ThreadId, name: String) -> Thread { + Self::new_inner(id, ThreadName::Other(name.into())) } - pub(crate) fn new_unnamed() -> Thread { - Self::new_inner(ThreadName::Unnamed) + pub(crate) fn new_unnamed(id: ThreadId) -> Thread { + Self::new_inner(id, ThreadName::Unnamed) } // Used in runtime to construct main thread pub(crate) fn new_main() -> Thread { - Self::new_inner(ThreadName::Main) + Self::new_inner(ThreadId::MAIN_THREAD, ThreadName::Main) } - fn new_inner(name: ThreadName) -> Thread { + fn new_inner(id: ThreadId, name: ThreadName) -> Thread { // We have to use `unsafe` here to construct the `Parker` in-place, // which is required for the UNIX implementation. // @@ -1399,7 +1341,7 @@ impl Thread { let mut arc = Arc::::new_uninit(); let ptr = Arc::get_mut_unchecked(&mut arc).as_mut_ptr(); (&raw mut (*ptr).name).write(name); - (&raw mut (*ptr).id).write(ThreadId::new()); + (&raw mut (*ptr).id).write(id); Parker::new_in_place(&raw mut (*ptr).parker); Pin::new_unchecked(arc.assume_init()) }; From a71ba0cf02cdd196b9c88fd1087ccdf7048c4c21 Mon Sep 17 00:00:00 2001 From: joboet Date: Sat, 20 Jul 2024 13:00:00 +0200 Subject: [PATCH 099/120] bless miri tests --- src/tools/miri/tests/fail/tail_calls/cc-mismatch.stderr | 6 +++--- src/tools/miri/tests/pass/backtrace/backtrace-api-v0.stderr | 2 +- src/tools/miri/tests/pass/backtrace/backtrace-api-v1.stderr | 2 +- .../miri/tests/pass/backtrace/backtrace-global-alloc.stderr | 2 +- src/tools/miri/tests/pass/backtrace/backtrace-std.stderr | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/tools/miri/tests/fail/tail_calls/cc-mismatch.stderr b/src/tools/miri/tests/fail/tail_calls/cc-mismatch.stderr index b157e9f0b211a..b416f0eb68976 100644 --- a/src/tools/miri/tests/fail/tail_calls/cc-mismatch.stderr +++ b/src/tools/miri/tests/fail/tail_calls/cc-mismatch.stderr @@ -15,9 +15,9 @@ LL | extern "rust-call" fn call_once(self, args: Args) -> Self::Output; = note: inside `std::panicking::r#try:: i32 + std::marker::Sync + std::panic::RefUnwindSafe>` at RUSTLIB/std/src/panicking.rs:LL:CC = note: inside `std::panic::catch_unwind::<&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe, i32>` at RUSTLIB/std/src/panic.rs:LL:CC = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC - = note: inside `std::panicking::r#try::do_call::<{closure@std::rt::lang_start_internal::{closure#2}}, isize>` at RUSTLIB/std/src/panicking.rs:LL:CC - = note: inside `std::panicking::r#try::` at RUSTLIB/std/src/panicking.rs:LL:CC - = note: inside `std::panic::catch_unwind::<{closure@std::rt::lang_start_internal::{closure#2}}, isize>` at RUSTLIB/std/src/panic.rs:LL:CC + = note: inside `std::panicking::r#try::do_call::<{closure@std::rt::lang_start_internal::{closure#1}}, isize>` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panicking::r#try::` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panic::catch_unwind::<{closure@std::rt::lang_start_internal::{closure#1}}, isize>` at RUSTLIB/std/src/panic.rs:LL:CC = note: inside `std::rt::lang_start_internal` at RUSTLIB/std/src/rt.rs:LL:CC = note: inside `std::rt::lang_start::<()>` at RUSTLIB/std/src/rt.rs:LL:CC diff --git a/src/tools/miri/tests/pass/backtrace/backtrace-api-v0.stderr b/src/tools/miri/tests/pass/backtrace/backtrace-api-v0.stderr index b87063431ad88..9849a1aa74ea0 100644 --- a/src/tools/miri/tests/pass/backtrace/backtrace-api-v0.stderr +++ b/src/tools/miri/tests/pass/backtrace/backtrace-api-v0.stderr @@ -10,7 +10,7 @@ RUSTLIB/core/src/ops/function.rs:LL:CC (std::ops::function::impls::call_once) RUSTLIB/std/src/panicking.rs:LL:CC (std::panicking::r#try::do_call) RUSTLIB/std/src/panicking.rs:LL:CC (std::panicking::r#try) RUSTLIB/std/src/panic.rs:LL:CC (std::panic::catch_unwind) -RUSTLIB/std/src/rt.rs:LL:CC (std::rt::lang_start_internal::{closure#2}) +RUSTLIB/std/src/rt.rs:LL:CC (std::rt::lang_start_internal::{closure#1}) RUSTLIB/std/src/panicking.rs:LL:CC (std::panicking::r#try::do_call) RUSTLIB/std/src/panicking.rs:LL:CC (std::panicking::r#try) RUSTLIB/std/src/panic.rs:LL:CC (std::panic::catch_unwind) diff --git a/src/tools/miri/tests/pass/backtrace/backtrace-api-v1.stderr b/src/tools/miri/tests/pass/backtrace/backtrace-api-v1.stderr index 2c729c49ee05d..48d9649c9201f 100644 --- a/src/tools/miri/tests/pass/backtrace/backtrace-api-v1.stderr +++ b/src/tools/miri/tests/pass/backtrace/backtrace-api-v1.stderr @@ -10,7 +10,7 @@ RUSTLIB/core/src/ops/function.rs:LL:CC (std::ops::function::impls::call_once) RUSTLIB/std/src/panicking.rs:LL:CC (std::panicking::r#try::do_call) RUSTLIB/std/src/panicking.rs:LL:CC (std::panicking::r#try) RUSTLIB/std/src/panic.rs:LL:CC (std::panic::catch_unwind) -RUSTLIB/std/src/rt.rs:LL:CC (std::rt::lang_start_internal::{closure#2}) +RUSTLIB/std/src/rt.rs:LL:CC (std::rt::lang_start_internal::{closure#1}) RUSTLIB/std/src/panicking.rs:LL:CC (std::panicking::r#try::do_call) RUSTLIB/std/src/panicking.rs:LL:CC (std::panicking::r#try) RUSTLIB/std/src/panic.rs:LL:CC (std::panic::catch_unwind) diff --git a/src/tools/miri/tests/pass/backtrace/backtrace-global-alloc.stderr b/src/tools/miri/tests/pass/backtrace/backtrace-global-alloc.stderr index d9414aa651835..667ee04e62413 100644 --- a/src/tools/miri/tests/pass/backtrace/backtrace-global-alloc.stderr +++ b/src/tools/miri/tests/pass/backtrace/backtrace-global-alloc.stderr @@ -14,7 +14,7 @@ at RUSTLIB/std/src/panicking.rs:LL:CC 7: std::panic::catch_unwind at RUSTLIB/std/src/panic.rs:LL:CC - 8: std::rt::lang_start_internal::{closure#2} + 8: std::rt::lang_start_internal::{closure#1} at RUSTLIB/std/src/rt.rs:LL:CC 9: std::panicking::r#try::do_call at RUSTLIB/std/src/panicking.rs:LL:CC diff --git a/src/tools/miri/tests/pass/backtrace/backtrace-std.stderr b/src/tools/miri/tests/pass/backtrace/backtrace-std.stderr index d6d69ee837ede..b3a3a9d654a78 100644 --- a/src/tools/miri/tests/pass/backtrace/backtrace-std.stderr +++ b/src/tools/miri/tests/pass/backtrace/backtrace-std.stderr @@ -22,7 +22,7 @@ at RUSTLIB/std/src/panicking.rs:LL:CC 11: std::panic::catch_unwind at RUSTLIB/std/src/panic.rs:LL:CC - 12: std::rt::lang_start_internal::{closure#2} + 12: std::rt::lang_start_internal::{closure#1} at RUSTLIB/std/src/rt.rs:LL:CC 13: std::panicking::r#try::do_call at RUSTLIB/std/src/panicking.rs:LL:CC From 746c3225925ee44b37c1665617ada24c8b173b20 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Wed, 2 Oct 2024 19:30:55 +0200 Subject: [PATCH 100/120] Fix target_vendor for aarch64-nintendo-switch-freestanding Previously set to `target_vendor = "unknown"`, but Nintendo is clearly the vendor of the Switch, and is also reflected in the target name itself. --- .../src/spec/targets/aarch64_nintendo_switch_freestanding.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/rustc_target/src/spec/targets/aarch64_nintendo_switch_freestanding.rs b/compiler/rustc_target/src/spec/targets/aarch64_nintendo_switch_freestanding.rs index d6d49a4a07044..1f1cd96632675 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_nintendo_switch_freestanding.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_nintendo_switch_freestanding.rs @@ -23,6 +23,7 @@ pub(crate) fn target() -> Target { linker: Some("rust-lld".into()), link_script: Some(LINKER_SCRIPT.into()), os: "horizon".into(), + vendor: "nintendo".into(), max_atomic_width: Some(128), stack_probes: StackProbeType::Inline, panic_strategy: PanicStrategy::Abort, From 0ae796440a476a4ffe982fbb7cc54a9097e7f161 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Wed, 2 Oct 2024 20:54:09 +0200 Subject: [PATCH 101/120] Fix target_abi in SOLID targets The `armv7a-kmc-solid_asp3-eabi` and `armv7a-kmc-solid_asp3-eabihf` targets clearly have the ABI in their name, so it should also be exposed in Rust's `target_abi` cfg variable. --- .../rustc_target/src/spec/targets/armv7a_kmc_solid_asp3_eabi.rs | 1 + .../src/spec/targets/armv7a_kmc_solid_asp3_eabihf.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/compiler/rustc_target/src/spec/targets/armv7a_kmc_solid_asp3_eabi.rs b/compiler/rustc_target/src/spec/targets/armv7a_kmc_solid_asp3_eabi.rs index 7e14c5efe7191..e5ae1064d9716 100644 --- a/compiler/rustc_target/src/spec/targets/armv7a_kmc_solid_asp3_eabi.rs +++ b/compiler/rustc_target/src/spec/targets/armv7a_kmc_solid_asp3_eabi.rs @@ -14,6 +14,7 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), arch: "arm".into(), options: TargetOptions { + abi: "eabi".into(), linker: Some("arm-kmc-eabi-gcc".into()), features: "+v7,+soft-float,+thumb2,-neon".into(), relocation_model: RelocModel::Static, diff --git a/compiler/rustc_target/src/spec/targets/armv7a_kmc_solid_asp3_eabihf.rs b/compiler/rustc_target/src/spec/targets/armv7a_kmc_solid_asp3_eabihf.rs index 1958f4a7c30a2..0879fa24a1b73 100644 --- a/compiler/rustc_target/src/spec/targets/armv7a_kmc_solid_asp3_eabihf.rs +++ b/compiler/rustc_target/src/spec/targets/armv7a_kmc_solid_asp3_eabihf.rs @@ -14,6 +14,7 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), arch: "arm".into(), options: TargetOptions { + abi: "eabihf".into(), linker: Some("arm-kmc-eabi-gcc".into()), features: "+v7,+vfp3,-d32,+thumb2,-neon".into(), relocation_model: RelocModel::Static, From f95bdf453e4f4c0a5ac244ff5878264b61ccaa8e Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 2 Oct 2024 21:52:44 -0400 Subject: [PATCH 102/120] Remove redundant in_trait from hir::TyKind::OpaqueDef --- compiler/rustc_ast_lowering/src/lib.rs | 1 - compiler/rustc_borrowck/src/diagnostics/region_name.rs | 2 +- compiler/rustc_hir/src/hir.rs | 4 ++-- compiler/rustc_hir/src/intravisit.rs | 2 +- compiler/rustc_hir_analysis/src/collect/predicates_of.rs | 2 +- compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs | 2 +- compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs | 4 ++-- compiler/rustc_middle/src/ty/diagnostics.rs | 2 +- compiler/rustc_passes/src/dead.rs | 2 +- .../infer/nice_region_error/static_impl_trait.rs | 2 +- .../rustc_trait_selection/src/error_reporting/infer/region.rs | 2 +- compiler/rustc_ty_utils/src/assoc.rs | 2 +- src/librustdoc/clean/mod.rs | 2 +- .../clippy/clippy_lints/src/extra_unused_type_parameters.rs | 2 +- src/tools/clippy/clippy_lints/src/lifetimes.rs | 2 +- src/tools/clippy/clippy_lints/src/manual_async_fn.rs | 2 +- src/tools/clippy/clippy_utils/src/hir_utils.rs | 3 +-- 17 files changed, 18 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index c6cb7aa7dd5c2..8f8fe9e85809e 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1776,7 +1776,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { hir::TyKind::OpaqueDef( hir::ItemId { owner_id: hir::OwnerId { def_id: opaque_ty_def_id } }, generic_args, - in_trait, ) } diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index 2f22e1532c196..d4598a1f582b6 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -832,7 +832,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { fn get_future_inner_return_ty(&self, hir_ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> { let hir = self.infcx.tcx.hir(); - let hir::TyKind::OpaqueDef(id, _, _) = hir_ty.kind else { + let hir::TyKind::OpaqueDef(id, _) = hir_ty.kind else { span_bug!( hir_ty.span, "lowered return type of async fn is not OpaqueDef: {:?}", diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 71216023ecc33..ac23443fee2a9 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -2632,7 +2632,7 @@ impl<'hir> Ty<'hir> { } TyKind::Tup(tys) => tys.iter().any(Self::is_suggestable_infer_ty), TyKind::Ptr(mut_ty) | TyKind::Ref(_, mut_ty) => mut_ty.ty.is_suggestable_infer_ty(), - TyKind::OpaqueDef(_, generic_args, _) => are_suggestable_generic_args(generic_args), + TyKind::OpaqueDef(_, generic_args) => are_suggestable_generic_args(generic_args), TyKind::Path(QPath::TypeRelative(ty, segment)) => { ty.is_suggestable_infer_ty() || are_suggestable_generic_args(segment.args().args) } @@ -2856,7 +2856,7 @@ pub enum TyKind<'hir> { /// possibly parameters) that are actually bound on the `impl Trait`. /// /// The last parameter specifies whether this opaque appears in a trait definition. - OpaqueDef(ItemId, &'hir [GenericArg<'hir>], bool), + OpaqueDef(ItemId, &'hir [GenericArg<'hir>]), /// A trait object type `Bound1 + Bound2 + Bound3` /// where `Bound` is a trait or a lifetime. TraitObject( diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 4da3224578519..d0a8aaa85bb29 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -894,7 +894,7 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) -> V::Resul TyKind::Path(ref qpath) => { try_visit!(visitor.visit_qpath(qpath, typ.hir_id, typ.span)); } - TyKind::OpaqueDef(item_id, lifetimes, _in_trait) => { + TyKind::OpaqueDef(item_id, lifetimes) => { try_visit!(visitor.visit_nested_item(item_id)); walk_list!(visitor, visit_generic_arg, lifetimes); } diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index 9e9704622052f..33f6623edfdf3 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -332,7 +332,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen // and the duplicated parameter, to ensure that they do not get out of sync. if let Node::Item(&Item { kind: ItemKind::OpaqueTy(..), .. }) = node { let opaque_ty_node = tcx.parent_hir_node(hir_id); - let Node::Ty(&hir::Ty { kind: TyKind::OpaqueDef(_, lifetimes, _), .. }) = opaque_ty_node + let Node::Ty(&hir::Ty { kind: TyKind::OpaqueDef(_, lifetimes), .. }) = opaque_ty_node else { bug!("unexpected {opaque_ty_node:?}") }; diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index c9b949ad88d54..30944b5e9908b 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -689,7 +689,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { }; self.with(scope, |this| this.visit_ty(mt.ty)); } - hir::TyKind::OpaqueDef(item_id, lifetimes, _in_trait) => { + hir::TyKind::OpaqueDef(item_id, lifetimes) => { // Resolve the lifetimes in the bounds to the lifetime defs in the generics. // `fn foo<'a>() -> impl MyTrait<'a> { ... }` desugars to // `type MyAnonTy<'b> = impl MyTrait<'b>;` diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 2186952720f33..496f54e31d2c0 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -2087,11 +2087,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let opt_self_ty = maybe_qself.as_ref().map(|qself| self.lower_ty(qself)); self.lower_path(opt_self_ty, path, hir_ty.hir_id, false) } - &hir::TyKind::OpaqueDef(item_id, lifetimes, in_trait) => { + &hir::TyKind::OpaqueDef(item_id, lifetimes) => { let opaque_ty = tcx.hir().item(item_id); match opaque_ty.kind { - hir::ItemKind::OpaqueTy(&hir::OpaqueTy { .. }) => { + hir::ItemKind::OpaqueTy(&hir::OpaqueTy { in_trait, .. }) => { let local_def_id = item_id.owner_id.def_id; // If this is an RPITIT and we are using the new RPITIT lowering scheme, we // generate the def_id of an associated type for the trait and return as diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index 992eb264163ba..d98e18c1b0c2e 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -510,7 +510,7 @@ impl<'v> hir::intravisit::Visitor<'v> for TraitObjectVisitor<'v> { ) => { self.0.push(ty); } - hir::TyKind::OpaqueDef(item_id, _, _) => { + hir::TyKind::OpaqueDef(item_id, _) => { self.0.push(ty); let item = self.1.item(item_id); hir::intravisit::walk_item(self, item); diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index aa329fc546ecb..100f3e80603e5 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -656,7 +656,7 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> { } fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) { - if let TyKind::OpaqueDef(item_id, _, _) = ty.kind { + if let TyKind::OpaqueDef(item_id, _) = ty.kind { let item = self.tcx.hir().item(item_id); intravisit::walk_item(self, item); } diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs index 4bde120cba9d0..31256bca55e93 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs @@ -284,7 +284,7 @@ pub fn suggest_new_region_bound( } match fn_return.kind { // FIXME(precise_captures): Suggest adding to `use<...>` list instead. - TyKind::OpaqueDef(item_id, _, _) => { + TyKind::OpaqueDef(item_id, _) => { let item = tcx.hir().item(item_id); let ItemKind::OpaqueTy(opaque) = &item.kind else { return; diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs index 41fe8a2bf2266..4c3a011248808 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs @@ -857,7 +857,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } fn visit_ty(&mut self, ty: &'hir hir::Ty<'hir>) { - let hir::TyKind::OpaqueDef(item_id, _, _) = ty.kind else { + let hir::TyKind::OpaqueDef(item_id, _) = ty.kind else { return hir::intravisit::walk_ty(self, ty); }; let opaque_ty = self.tcx.hir().item(item_id).expect_opaque_ty(); diff --git a/compiler/rustc_ty_utils/src/assoc.rs b/compiler/rustc_ty_utils/src/assoc.rs index 6726db8bb54df..45fe199c335b0 100644 --- a/compiler/rustc_ty_utils/src/assoc.rs +++ b/compiler/rustc_ty_utils/src/assoc.rs @@ -323,7 +323,7 @@ fn associated_types_for_impl_traits_in_associated_fn( impl<'tcx> Visitor<'tcx> for RPITVisitor<'tcx> { fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) { - if let hir::TyKind::OpaqueDef(item_id, _, _) = ty.kind + if let hir::TyKind::OpaqueDef(item_id, _) = ty.kind && self.rpits.insert(item_id.owner_id.def_id) { let opaque_item = diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 3d845cf878f4a..b79458eaa7890 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1828,7 +1828,7 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T Array(Box::new(clean_ty(ty, cx)), length.into()) } TyKind::Tup(tys) => Tuple(tys.iter().map(|ty| clean_ty(ty, cx)).collect()), - TyKind::OpaqueDef(item_id, _, _) => { + TyKind::OpaqueDef(item_id, _) => { let item = cx.tcx.hir().item(item_id); if let hir::ItemKind::OpaqueTy(ty) = item.kind { ImplTrait(ty.bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect()) diff --git a/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs b/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs index bf9388b4a70fd..a7fb9535e7904 100644 --- a/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs +++ b/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs @@ -199,7 +199,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for TypeWalker<'cx, 'tcx> { fn visit_ty(&mut self, t: &'tcx Ty<'tcx>) { if let Some((def_id, _)) = t.peel_refs().as_generic_param() { self.ty_params.remove(&def_id); - } else if let TyKind::OpaqueDef(id, _, _) = t.kind { + } else if let TyKind::OpaqueDef(id, _) = t.kind { // Explicitly walk OpaqueDef. Normally `walk_ty` would do the job, but it calls // `visit_nested_item`, which checks that `Self::NestedFilter::INTER` is set. We're // using `OnlyBodies`, so the check ends up failing and the type isn't fully walked. diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs index 755afe959b37a..d3cc5ea628c72 100644 --- a/src/tools/clippy/clippy_lints/src/lifetimes.rs +++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs @@ -523,7 +523,7 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> { fn visit_ty(&mut self, ty: &'tcx Ty<'_>) { match ty.kind { - TyKind::OpaqueDef(item, bounds, _) => { + TyKind::OpaqueDef(item, bounds) => { let map = self.cx.tcx.hir(); let item = map.item(item); let len = self.lts.len(); diff --git a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs index fc3bba9e51232..7097c85156c34 100644 --- a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs +++ b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs @@ -105,7 +105,7 @@ fn future_trait_ref<'tcx>( cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>, ) -> Option<(&'tcx TraitRef<'tcx>, Vec)> { - if let TyKind::OpaqueDef(item_id, bounds, false) = ty.kind + if let TyKind::OpaqueDef(item_id, bounds) = ty.kind && let item = cx.tcx.hir().item(item_id) && let ItemKind::OpaqueTy(opaque) = &item.kind && let Some(trait_ref) = opaque.bounds.iter().find_map(|bound| { diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index 76900379ac787..b2d75125ccdb1 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -1115,9 +1115,8 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { } }, TyKind::Path(ref qpath) => self.hash_qpath(qpath), - TyKind::OpaqueDef(_, arg_list, in_trait) => { + TyKind::OpaqueDef(_, arg_list) => { self.hash_generic_args(arg_list); - in_trait.hash(&mut self.s); }, TyKind::TraitObject(_, lifetime, _) => { self.hash_lifetime(lifetime); From cb7e3695e8e7ea04377f60977b65ba324273b63d Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 2 Oct 2024 22:04:18 -0400 Subject: [PATCH 103/120] Use named fields for OpaqueTyOrigin --- compiler/rustc_ast_lowering/src/lib.rs | 12 +++++++----- .../src/region_infer/opaque_types.rs | 4 ++-- compiler/rustc_hir/src/hir.rs | 10 ++++++++-- compiler/rustc_hir_analysis/src/check/check.rs | 16 ++++++++-------- .../src/check/compare_impl_item/refine.rs | 4 ++-- .../src/collect/generics_of.rs | 3 ++- .../src/collect/item_bounds.rs | 4 ++-- .../src/collect/resolve_bound_vars.rs | 4 ++-- .../rustc_hir_analysis/src/collect/type_of.rs | 3 ++- compiler/rustc_hir_typeck/src/_match.rs | 3 ++- .../rustc_lint/src/impl_trait_overcaptures.rs | 4 ++-- .../src/opaque_hidden_inferred_bound.rs | 6 +++--- compiler/rustc_metadata/src/rmeta/encoder.rs | 6 +++--- .../src/error_reporting/infer/region.rs | 2 +- .../error_reporting/traits/fulfillment_errors.rs | 2 +- compiler/rustc_ty_utils/src/assoc.rs | 3 ++- compiler/rustc_ty_utils/src/opaque_types.rs | 3 ++- src/librustdoc/visit_ast.rs | 2 +- src/tools/clippy/clippy_lints/src/len_zero.rs | 2 +- 19 files changed, 53 insertions(+), 40 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 8f8fe9e85809e..c8aa8d701b9b0 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1555,7 +1555,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { .map(|(ident, id, _)| Lifetime { id, ident }) .collect() } - hir::OpaqueTyOrigin::FnReturn(..) => { + hir::OpaqueTyOrigin::FnReturn { .. } => { if matches!( fn_kind.expect("expected RPITs to be lowered with a FnKind"), FnDeclKind::Impl | FnDeclKind::Trait @@ -1576,7 +1576,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { lifetime_collector::lifetimes_in_bounds(self.resolver, bounds) } } - hir::OpaqueTyOrigin::AsyncFn(..) => { + hir::OpaqueTyOrigin::AsyncFn { .. } => { unreachable!("should be using `lower_async_fn_ret_ty`") } } @@ -1867,7 +1867,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { | FnDeclKind::Inherent | FnDeclKind::Trait | FnDeclKind::Impl => ImplTraitContext::OpaqueTy { - origin: hir::OpaqueTyOrigin::FnReturn(self.local_def_id(fn_node_id)), + origin: hir::OpaqueTyOrigin::FnReturn { + parent: self.local_def_id(fn_node_id), + }, fn_kind: Some(kind), }, FnDeclKind::ExternFn => { @@ -1952,7 +1954,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let opaque_ty_ref = self.lower_opaque_inner( opaque_ty_node_id, - hir::OpaqueTyOrigin::AsyncFn(fn_def_id), + hir::OpaqueTyOrigin::AsyncFn { parent: fn_def_id }, matches!(fn_kind, FnDeclKind::Trait), captured_lifetimes, span, @@ -1963,7 +1965,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { coro, opaque_ty_span, ImplTraitContext::OpaqueTy { - origin: hir::OpaqueTyOrigin::FnReturn(fn_def_id), + origin: hir::OpaqueTyOrigin::FnReturn { parent: fn_def_id }, fn_kind: Some(fn_kind), }, ); diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index 3cf21d4a36b83..1f6aa59805948 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -503,8 +503,8 @@ impl<'tcx> LazyOpaqueTyEnv<'tcx> { let &Self { tcx, def_id, .. } = self; let origin = tcx.opaque_type_origin(def_id); let parent = match origin { - hir::OpaqueTyOrigin::FnReturn(parent) - | hir::OpaqueTyOrigin::AsyncFn(parent) + hir::OpaqueTyOrigin::FnReturn { parent } + | hir::OpaqueTyOrigin::AsyncFn { parent } | hir::OpaqueTyOrigin::TyAlias { parent, .. } => parent, }; let param_env = tcx.param_env(parent); diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index ac23443fee2a9..68195c46f5c40 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -2806,9 +2806,15 @@ pub struct PreciseCapturingNonLifetimeArg { #[derive(Copy, Clone, PartialEq, Eq, Debug, HashStable_Generic)] pub enum OpaqueTyOrigin { /// `-> impl Trait` - FnReturn(LocalDefId), + FnReturn { + /// The defining function. + parent: LocalDefId, + }, /// `async fn` - AsyncFn(LocalDefId), + AsyncFn { + /// The defining function. + parent: LocalDefId, + }, /// type aliases: `type Foo = impl Trait;` TyAlias { /// The type alias or associated type parent of the TAIT/ATPIT diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index d725772a5b369..feba484229b87 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -336,9 +336,9 @@ fn check_opaque_meets_bounds<'tcx>( origin: &hir::OpaqueTyOrigin, ) -> Result<(), ErrorGuaranteed> { let defining_use_anchor = match *origin { - hir::OpaqueTyOrigin::FnReturn(did) - | hir::OpaqueTyOrigin::AsyncFn(did) - | hir::OpaqueTyOrigin::TyAlias { parent: did, .. } => did, + hir::OpaqueTyOrigin::FnReturn { parent } + | hir::OpaqueTyOrigin::AsyncFn { parent } + | hir::OpaqueTyOrigin::TyAlias { parent, .. } => parent, }; let param_env = tcx.param_env(defining_use_anchor); @@ -346,8 +346,8 @@ fn check_opaque_meets_bounds<'tcx>( let ocx = ObligationCtxt::new_with_diagnostics(&infcx); let args = match *origin { - hir::OpaqueTyOrigin::FnReturn(parent) - | hir::OpaqueTyOrigin::AsyncFn(parent) + hir::OpaqueTyOrigin::FnReturn { parent } + | hir::OpaqueTyOrigin::AsyncFn { parent } | hir::OpaqueTyOrigin::TyAlias { parent, .. } => GenericArgs::identity_for_item( tcx, parent, ) @@ -409,7 +409,7 @@ fn check_opaque_meets_bounds<'tcx>( let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds); ocx.resolve_regions_and_report_errors(defining_use_anchor, &outlives_env)?; - if let hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) = origin { + if let hir::OpaqueTyOrigin::FnReturn { .. } | hir::OpaqueTyOrigin::AsyncFn { .. } = origin { // HACK: this should also fall through to the hidden type check below, but the original // implementation had a bug where equivalent lifetimes are not identical. This caused us // to reject existing stable code that is otherwise completely fine. The real fix is to @@ -736,8 +736,8 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) { check_opaque_precise_captures(tcx, def_id); let origin = tcx.opaque_type_origin(def_id); - if let hir::OpaqueTyOrigin::FnReturn(fn_def_id) - | hir::OpaqueTyOrigin::AsyncFn(fn_def_id) = origin + if let hir::OpaqueTyOrigin::FnReturn { parent: fn_def_id } + | hir::OpaqueTyOrigin::AsyncFn { parent: fn_def_id } = origin && let hir::Node::TraitItem(trait_item) = tcx.hir_node_by_def_id(fn_def_id) && let (_, hir::TraitFn::Required(..)) = trait_item.expect_fn() { diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs index e07b587508a68..3375fc303f156 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs @@ -94,8 +94,8 @@ pub(super) fn check_refining_return_position_impl_trait_in_trait<'tcx>( if !tcx.hir().get_if_local(impl_opaque.def_id).is_some_and(|node| { matches!( node.expect_item().expect_opaque_ty().origin, - hir::OpaqueTyOrigin::AsyncFn(def_id) | hir::OpaqueTyOrigin::FnReturn(def_id) - if def_id == impl_m.def_id.expect_local() + hir::OpaqueTyOrigin::AsyncFn { parent } | hir::OpaqueTyOrigin::FnReturn { parent } + if parent == impl_m.def_id.expect_local() ) }) { report_mismatched_rpitit_signature( diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs index 0a8eef2006d5a..73e98c67cdc30 100644 --- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs @@ -210,7 +210,8 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { Node::Item(item) => match item.kind { ItemKind::OpaqueTy(&hir::OpaqueTy { origin: - hir::OpaqueTyOrigin::FnReturn(fn_def_id) | hir::OpaqueTyOrigin::AsyncFn(fn_def_id), + hir::OpaqueTyOrigin::FnReturn { parent: fn_def_id } + | hir::OpaqueTyOrigin::AsyncFn { parent: fn_def_id }, in_trait, .. }) => { diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs index f44b4728ad533..07a8c3a9e84db 100644 --- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs +++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs @@ -388,8 +388,8 @@ pub(super) fn explicit_item_bounds_with_filter( span, .. }) => { - let (hir::OpaqueTyOrigin::FnReturn(fn_def_id) - | hir::OpaqueTyOrigin::AsyncFn(fn_def_id)) = *origin + let (hir::OpaqueTyOrigin::FnReturn { parent: fn_def_id } + | hir::OpaqueTyOrigin::AsyncFn { parent: fn_def_id }) = *origin else { span_bug!(*span, "RPITIT cannot be a TAIT, but got origin {origin:?}"); }; diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index 30944b5e9908b..60788641da0cd 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -515,8 +515,8 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { } hir::ItemKind::OpaqueTy(&hir::OpaqueTy { origin: - hir::OpaqueTyOrigin::FnReturn(parent) - | hir::OpaqueTyOrigin::AsyncFn(parent) + hir::OpaqueTyOrigin::FnReturn { parent } + | hir::OpaqueTyOrigin::AsyncFn { parent } | hir::OpaqueTyOrigin::TyAlias { parent, .. }, generics, .. diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index 48b5e87cbd033..313d7dd62654a 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -618,7 +618,8 @@ pub(super) fn type_of_opaque( // Opaque types desugared from `impl Trait`. ItemKind::OpaqueTy(&OpaqueTy { origin: - hir::OpaqueTyOrigin::FnReturn(owner) | hir::OpaqueTyOrigin::AsyncFn(owner), + hir::OpaqueTyOrigin::FnReturn { parent: owner } + | hir::OpaqueTyOrigin::AsyncFn { parent: owner }, in_trait, .. }) => { diff --git a/compiler/rustc_hir_typeck/src/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs index bf8ed017cf703..204138ffd44a4 100644 --- a/compiler/rustc_hir_typeck/src/_match.rs +++ b/compiler/rustc_hir_typeck/src/_match.rs @@ -602,7 +602,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .map(|(k, _)| (k.def_id, k.args))?, _ => return None, }; - let hir::OpaqueTyOrigin::FnReturn(parent_def_id) = self.tcx.opaque_type_origin(def_id) + let hir::OpaqueTyOrigin::FnReturn { parent: parent_def_id } = + self.tcx.opaque_type_origin(def_id) else { return None; }; diff --git a/compiler/rustc_lint/src/impl_trait_overcaptures.rs b/compiler/rustc_lint/src/impl_trait_overcaptures.rs index a073d16f634d8..c13f4c7746d08 100644 --- a/compiler/rustc_lint/src/impl_trait_overcaptures.rs +++ b/compiler/rustc_lint/src/impl_trait_overcaptures.rs @@ -259,8 +259,8 @@ where // If it's owned by this function && let opaque = self.tcx.hir_node_by_def_id(opaque_def_id).expect_item().expect_opaque_ty() - && let hir::OpaqueTyOrigin::FnReturn(parent_def_id) = opaque.origin - && parent_def_id == self.parent_def_id + && let hir::OpaqueTyOrigin::FnReturn { parent } = opaque.origin + && parent == self.parent_def_id { let opaque_span = self.tcx.def_span(opaque_def_id); let new_capture_rules = diff --git a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs index 83652bbf54631..87fa38a282ed1 100644 --- a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs +++ b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs @@ -77,7 +77,7 @@ impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound { // That's because although we may have an opaque type on the function, // it won't have a hidden type, so proving predicates about it is // not really meaningful. - if let hir::OpaqueTyOrigin::FnReturn(method_def_id) = opaque.origin + if let hir::OpaqueTyOrigin::FnReturn { parent: method_def_id } = opaque.origin && let hir::Node::TraitItem(trait_item) = cx.tcx.hir_node_by_def_id(method_def_id) && !trait_item.defaultness.has_value() { @@ -103,7 +103,7 @@ impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound { && cx.tcx.parent(opaque_ty.def_id) == def_id && matches!( opaque.origin, - hir::OpaqueTyOrigin::FnReturn(_) | hir::OpaqueTyOrigin::AsyncFn(_) + hir::OpaqueTyOrigin::FnReturn { .. } | hir::OpaqueTyOrigin::AsyncFn { .. } ) { return; @@ -114,7 +114,7 @@ impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound { // return type is well-formed in traits even when `Self` isn't sized. if let ty::Param(param_ty) = *proj_term.kind() && param_ty.name == kw::SelfUpper - && matches!(opaque.origin, hir::OpaqueTyOrigin::AsyncFn(_)) + && matches!(opaque.origin, hir::OpaqueTyOrigin::AsyncFn { .. }) && opaque.in_trait { return; diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 5f756672b049e..61eb7260384ef 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1186,9 +1186,9 @@ fn should_encode_type(tcx: TyCtxt<'_>, def_id: LocalDefId, def_kind: DefKind) -> DefKind::OpaqueTy => { let origin = tcx.opaque_type_origin(def_id); - if let hir::OpaqueTyOrigin::FnReturn(fn_def_id) - | hir::OpaqueTyOrigin::AsyncFn(fn_def_id) = origin - && let hir::Node::TraitItem(trait_item) = tcx.hir_node_by_def_id(fn_def_id) + if let hir::OpaqueTyOrigin::FnReturn { parent } + | hir::OpaqueTyOrigin::AsyncFn { parent } = origin + && let hir::Node::TraitItem(trait_item) = tcx.hir_node_by_def_id(parent) && let (_, hir::TraitFn::Required(..)) = trait_item.expect_fn() { false diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs index 4c3a011248808..673756076b12f 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs @@ -1271,7 +1271,7 @@ fn suggest_precise_capturing<'tcx>( let hir::OpaqueTy { bounds, origin, .. } = tcx.hir_node_by_def_id(opaque_def_id).expect_item().expect_opaque_ty(); - let hir::OpaqueTyOrigin::FnReturn(fn_def_id) = *origin else { + let hir::OpaqueTyOrigin::FnReturn { parent: fn_def_id } = *origin else { return; }; diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 34a0f182ab45f..43762b1004baa 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -2580,7 +2580,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { def_id: DefId, ) -> ErrorGuaranteed { let name = match self.tcx.opaque_type_origin(def_id.expect_local()) { - hir::OpaqueTyOrigin::FnReturn(_) | hir::OpaqueTyOrigin::AsyncFn(_) => { + hir::OpaqueTyOrigin::FnReturn { .. } | hir::OpaqueTyOrigin::AsyncFn { .. } => { "opaque type".to_string() } hir::OpaqueTyOrigin::TyAlias { .. } => { diff --git a/compiler/rustc_ty_utils/src/assoc.rs b/compiler/rustc_ty_utils/src/assoc.rs index 45fe199c335b0..f8cfeb500a5ec 100644 --- a/compiler/rustc_ty_utils/src/assoc.rs +++ b/compiler/rustc_ty_utils/src/assoc.rs @@ -379,7 +379,8 @@ fn associated_type_for_impl_trait_in_trait( tcx: TyCtxt<'_>, opaque_ty_def_id: LocalDefId, ) -> LocalDefId { - let (hir::OpaqueTyOrigin::FnReturn(fn_def_id) | hir::OpaqueTyOrigin::AsyncFn(fn_def_id)) = + let (hir::OpaqueTyOrigin::FnReturn { parent: fn_def_id } + | hir::OpaqueTyOrigin::AsyncFn { parent: fn_def_id }) = tcx.opaque_type_origin(opaque_ty_def_id) else { bug!("expected opaque for {opaque_ty_def_id:?}"); diff --git a/compiler/rustc_ty_utils/src/opaque_types.rs b/compiler/rustc_ty_utils/src/opaque_types.rs index bac0d020d721b..7c4b4887b2d75 100644 --- a/compiler/rustc_ty_utils/src/opaque_types.rs +++ b/compiler/rustc_ty_utils/src/opaque_types.rs @@ -141,7 +141,8 @@ impl<'tcx> OpaqueTypeCollector<'tcx> { let origin = self.tcx.opaque_type_origin(alias_ty.def_id.expect_local()); trace!(?origin); match origin { - rustc_hir::OpaqueTyOrigin::FnReturn(_) | rustc_hir::OpaqueTyOrigin::AsyncFn(_) => {} + rustc_hir::OpaqueTyOrigin::FnReturn { .. } + | rustc_hir::OpaqueTyOrigin::AsyncFn { .. } => {} rustc_hir::OpaqueTyOrigin::TyAlias { in_assoc_ty, .. } => { if !in_assoc_ty && !self.check_tait_defining_scope(alias_ty.def_id.expect_local()) { return; diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index 2cf703f57c0b6..c44e5ecaba81c 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -515,7 +515,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { self.add_to_current_mod(item, renamed, import_id); } hir::ItemKind::OpaqueTy(hir::OpaqueTy { - origin: hir::OpaqueTyOrigin::AsyncFn(_) | hir::OpaqueTyOrigin::FnReturn(_), + origin: hir::OpaqueTyOrigin::AsyncFn { .. } | hir::OpaqueTyOrigin::FnReturn { .. }, .. }) => { // return-position impl traits are never nameable, and should never be documented. diff --git a/src/tools/clippy/clippy_lints/src/len_zero.rs b/src/tools/clippy/clippy_lints/src/len_zero.rs index 8bc2a56af9938..0cbcdbeb90ae1 100644 --- a/src/tools/clippy/clippy_lints/src/len_zero.rs +++ b/src/tools/clippy/clippy_lints/src/len_zero.rs @@ -313,7 +313,7 @@ fn extract_future_output<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<& kind: ItemKind::OpaqueTy(opaque), .. } = item - && let OpaqueTyOrigin::AsyncFn(_) = opaque.origin + && let OpaqueTyOrigin::AsyncFn { .. } = opaque.origin && let [GenericBound::Trait(trait_ref, _)] = &opaque.bounds && let Some(segment) = trait_ref.trait_ref.path.segments.last() && let Some(generic_args) = segment.args From 7cd466a03606313dad4fa22fd5cf444204138fc8 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 2 Oct 2024 22:21:37 -0400 Subject: [PATCH 104/120] Move in_trait into OpaqueTyOrigin --- compiler/rustc_ast_lowering/src/item.rs | 2 - compiler/rustc_ast_lowering/src/lib.rs | 80 +++++++++---------- .../src/region_infer/opaque_types.rs | 4 +- compiler/rustc_hir/src/hir.rs | 14 +++- .../rustc_hir_analysis/src/check/check.rs | 12 +-- .../src/check/compare_impl_item/refine.rs | 2 +- .../src/collect/generics_of.rs | 7 +- .../src/collect/item_bounds.rs | 70 +++++++++------- .../src/collect/resolve_bound_vars.rs | 4 +- .../rustc_hir_analysis/src/collect/type_of.rs | 9 ++- .../src/hir_ty_lowering/mod.rs | 34 ++++++-- compiler/rustc_hir_typeck/src/_match.rs | 2 +- .../rustc_lint/src/impl_trait_overcaptures.rs | 2 +- .../src/opaque_hidden_inferred_bound.rs | 8 +- compiler/rustc_metadata/src/rmeta/encoder.rs | 4 +- compiler/rustc_middle/src/hir/map/mod.rs | 8 +- compiler/rustc_privacy/src/lib.rs | 3 +- .../src/error_reporting/infer/region.rs | 2 +- compiler/rustc_ty_utils/src/assoc.rs | 4 +- 19 files changed, 146 insertions(+), 125 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 7bb3b2fa29095..1273b50dff816 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -286,7 +286,6 @@ impl<'hir> LoweringContext<'_, 'hir> { parent: this.local_def_id(id), in_assoc_ty: false, }, - fn_kind: None, }), }, ); @@ -983,7 +982,6 @@ impl<'hir> LoweringContext<'_, 'hir> { parent: this.local_def_id(i.id), in_assoc_ty: true, }, - fn_kind: None, }); hir::ImplItemKind::Type(ty) } diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index c8aa8d701b9b0..9275308cccbce 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -288,12 +288,7 @@ enum ImplTraitContext { /// Example: `fn foo() -> impl Debug`, where `impl Debug` is conceptually /// equivalent to a new opaque type like `type T = impl Debug; fn foo() -> T`. /// - OpaqueTy { - origin: hir::OpaqueTyOrigin, - /// Only used to change the lifetime capture rules, since - /// RPITIT captures all in scope, RPIT does not. - fn_kind: Option, - }, + OpaqueTy { origin: hir::OpaqueTyOrigin }, /// `impl Trait` is unstably accepted in this position. FeatureGated(ImplTraitPosition, Symbol), /// `impl Trait` is not accepted in this position. @@ -1404,14 +1399,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { TyKind::ImplTrait(def_node_id, bounds) => { let span = t.span; match itctx { - ImplTraitContext::OpaqueTy { origin, fn_kind } => self.lower_opaque_impl_trait( - span, - origin, - *def_node_id, - bounds, - fn_kind, - itctx, - ), + ImplTraitContext::OpaqueTy { origin } => { + self.lower_opaque_impl_trait(span, origin, *def_node_id, bounds, itctx) + } ImplTraitContext::Universal => { if let Some(span) = bounds.iter().find_map(|bound| match *bound { ast::GenericBound::Use(_, span) => Some(span), @@ -1513,7 +1503,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { origin: hir::OpaqueTyOrigin, opaque_ty_node_id: NodeId, bounds: &GenericBounds, - fn_kind: Option, itctx: ImplTraitContext, ) -> hir::TyKind<'hir> { // Make sure we know that some funky desugaring has been going on here. @@ -1555,11 +1544,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { .map(|(ident, id, _)| Lifetime { id, ident }) .collect() } - hir::OpaqueTyOrigin::FnReturn { .. } => { - if matches!( - fn_kind.expect("expected RPITs to be lowered with a FnKind"), - FnDeclKind::Impl | FnDeclKind::Trait - ) || self.tcx.features().lifetime_capture_rules_2024 + hir::OpaqueTyOrigin::FnReturn { in_trait_or_impl, .. } => { + if in_trait_or_impl.is_some() + || self.tcx.features().lifetime_capture_rules_2024 || span.at_least_rust_2024() { // return-position impl trait in trait was decided to capture all @@ -1583,9 +1570,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { }; debug!(?captured_lifetimes_to_duplicate); - match fn_kind { - // Deny `use<>` on RPITIT in trait/trait-impl for now. - Some(FnDeclKind::Trait | FnDeclKind::Impl) => { + // Feature gate for RPITIT + use<..> + match origin { + rustc_hir::OpaqueTyOrigin::FnReturn { in_trait_or_impl: Some(_), .. } => { if let Some(span) = bounds.iter().find_map(|bound| match *bound { ast::GenericBound::Use(_, span) => Some(span), _ => None, @@ -1593,20 +1580,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { self.tcx.dcx().emit_err(errors::NoPreciseCapturesOnRpitit { span }); } } - None - | Some( - FnDeclKind::Fn - | FnDeclKind::Inherent - | FnDeclKind::ExternFn - | FnDeclKind::Closure - | FnDeclKind::Pointer, - ) => {} + _ => {} } self.lower_opaque_inner( opaque_ty_node_id, origin, - matches!(fn_kind, Some(FnDeclKind::Trait)), captured_lifetimes_to_duplicate, span, opaque_ty_span, @@ -1618,7 +1597,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { &mut self, opaque_ty_node_id: NodeId, origin: hir::OpaqueTyOrigin, - in_trait: bool, captured_lifetimes_to_duplicate: FxIndexSet, span: Span, opaque_ty_span: Span, @@ -1747,7 +1725,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { bounds, origin, lifetime_mapping, - in_trait, }; // Generate an `type Foo = impl Trait;` declaration. @@ -1863,14 +1840,23 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { None => match &decl.output { FnRetTy::Ty(ty) => { let itctx = match kind { - FnDeclKind::Fn - | FnDeclKind::Inherent - | FnDeclKind::Trait - | FnDeclKind::Impl => ImplTraitContext::OpaqueTy { + FnDeclKind::Fn | FnDeclKind::Inherent => ImplTraitContext::OpaqueTy { + origin: hir::OpaqueTyOrigin::FnReturn { + parent: self.local_def_id(fn_node_id), + in_trait_or_impl: None, + }, + }, + FnDeclKind::Trait => ImplTraitContext::OpaqueTy { origin: hir::OpaqueTyOrigin::FnReturn { parent: self.local_def_id(fn_node_id), + in_trait_or_impl: Some(hir::RpitContext::Trait), + }, + }, + FnDeclKind::Impl => ImplTraitContext::OpaqueTy { + origin: hir::OpaqueTyOrigin::FnReturn { + parent: self.local_def_id(fn_node_id), + in_trait_or_impl: Some(hir::RpitContext::TraitImpl), }, - fn_kind: Some(kind), }, FnDeclKind::ExternFn => { ImplTraitContext::Disallowed(ImplTraitPosition::ExternFnReturn) @@ -1952,10 +1938,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { .map(|(ident, id, _)| Lifetime { id, ident }) .collect(); + let in_trait_or_impl = match fn_kind { + FnDeclKind::Trait => Some(hir::RpitContext::Trait), + FnDeclKind::Impl => Some(hir::RpitContext::TraitImpl), + FnDeclKind::Fn | FnDeclKind::Inherent => None, + FnDeclKind::ExternFn | FnDeclKind::Closure | FnDeclKind::Pointer => unreachable!(), + }; + let opaque_ty_ref = self.lower_opaque_inner( opaque_ty_node_id, - hir::OpaqueTyOrigin::AsyncFn { parent: fn_def_id }, - matches!(fn_kind, FnDeclKind::Trait), + hir::OpaqueTyOrigin::AsyncFn { parent: fn_def_id, in_trait_or_impl }, captured_lifetimes, span, opaque_ty_span, @@ -1965,8 +1957,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { coro, opaque_ty_span, ImplTraitContext::OpaqueTy { - origin: hir::OpaqueTyOrigin::FnReturn { parent: fn_def_id }, - fn_kind: Some(fn_kind), + origin: hir::OpaqueTyOrigin::FnReturn { + parent: fn_def_id, + in_trait_or_impl, + }, }, ); arena_vec![this; bound] diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index 1f6aa59805948..2f90e91628141 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -503,8 +503,8 @@ impl<'tcx> LazyOpaqueTyEnv<'tcx> { let &Self { tcx, def_id, .. } = self; let origin = tcx.opaque_type_origin(def_id); let parent = match origin { - hir::OpaqueTyOrigin::FnReturn { parent } - | hir::OpaqueTyOrigin::AsyncFn { parent } + hir::OpaqueTyOrigin::FnReturn { parent, .. } + | hir::OpaqueTyOrigin::AsyncFn { parent, .. } | hir::OpaqueTyOrigin::TyAlias { parent, .. } => parent, }; let param_env = tcx.param_env(parent); diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 68195c46f5c40..f58ec22aea971 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -2762,10 +2762,6 @@ pub struct OpaqueTy<'hir> { /// This mapping associated a captured lifetime (first parameter) with the new /// early-bound lifetime that was generated for the opaque. pub lifetime_mapping: &'hir [(&'hir Lifetime, LocalDefId)], - /// Whether the opaque is a return-position impl trait (or async future) - /// originating from a trait method. This makes it so that the opaque is - /// lowered as an associated type. - pub in_trait: bool, } #[derive(Debug, Clone, Copy, HashStable_Generic)] @@ -2802,6 +2798,12 @@ pub struct PreciseCapturingNonLifetimeArg { pub res: Res, } +#[derive(Copy, Clone, PartialEq, Eq, Debug, HashStable_Generic)] +pub enum RpitContext { + Trait, + TraitImpl, +} + /// From whence the opaque type came. #[derive(Copy, Clone, PartialEq, Eq, Debug, HashStable_Generic)] pub enum OpaqueTyOrigin { @@ -2809,11 +2811,15 @@ pub enum OpaqueTyOrigin { FnReturn { /// The defining function. parent: LocalDefId, + // Whether this is an RPITIT (return position impl trait in trait) + in_trait_or_impl: Option, }, /// `async fn` AsyncFn { /// The defining function. parent: LocalDefId, + // Whether this is an AFIT (async fn in trait) + in_trait_or_impl: Option, }, /// type aliases: `type Foo = impl Trait;` TyAlias { diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index feba484229b87..312212232bcdc 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -336,8 +336,8 @@ fn check_opaque_meets_bounds<'tcx>( origin: &hir::OpaqueTyOrigin, ) -> Result<(), ErrorGuaranteed> { let defining_use_anchor = match *origin { - hir::OpaqueTyOrigin::FnReturn { parent } - | hir::OpaqueTyOrigin::AsyncFn { parent } + hir::OpaqueTyOrigin::FnReturn { parent, .. } + | hir::OpaqueTyOrigin::AsyncFn { parent, .. } | hir::OpaqueTyOrigin::TyAlias { parent, .. } => parent, }; let param_env = tcx.param_env(defining_use_anchor); @@ -346,8 +346,8 @@ fn check_opaque_meets_bounds<'tcx>( let ocx = ObligationCtxt::new_with_diagnostics(&infcx); let args = match *origin { - hir::OpaqueTyOrigin::FnReturn { parent } - | hir::OpaqueTyOrigin::AsyncFn { parent } + hir::OpaqueTyOrigin::FnReturn { parent, .. } + | hir::OpaqueTyOrigin::AsyncFn { parent, .. } | hir::OpaqueTyOrigin::TyAlias { parent, .. } => GenericArgs::identity_for_item( tcx, parent, ) @@ -736,8 +736,8 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) { check_opaque_precise_captures(tcx, def_id); let origin = tcx.opaque_type_origin(def_id); - if let hir::OpaqueTyOrigin::FnReturn { parent: fn_def_id } - | hir::OpaqueTyOrigin::AsyncFn { parent: fn_def_id } = origin + if let hir::OpaqueTyOrigin::FnReturn { parent: fn_def_id, .. } + | hir::OpaqueTyOrigin::AsyncFn { parent: fn_def_id, .. } = origin && let hir::Node::TraitItem(trait_item) = tcx.hir_node_by_def_id(fn_def_id) && let (_, hir::TraitFn::Required(..)) = trait_item.expect_fn() { diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs index 3375fc303f156..35c2b7e7ce2fb 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs @@ -94,7 +94,7 @@ pub(super) fn check_refining_return_position_impl_trait_in_trait<'tcx>( if !tcx.hir().get_if_local(impl_opaque.def_id).is_some_and(|node| { matches!( node.expect_item().expect_opaque_ty().origin, - hir::OpaqueTyOrigin::AsyncFn { parent } | hir::OpaqueTyOrigin::FnReturn { parent } + hir::OpaqueTyOrigin::AsyncFn { parent, .. } | hir::OpaqueTyOrigin::FnReturn { parent, .. } if parent == impl_m.def_id.expect_local() ) }) { diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs index 73e98c67cdc30..8ff9640a87489 100644 --- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs @@ -210,12 +210,11 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { Node::Item(item) => match item.kind { ItemKind::OpaqueTy(&hir::OpaqueTy { origin: - hir::OpaqueTyOrigin::FnReturn { parent: fn_def_id } - | hir::OpaqueTyOrigin::AsyncFn { parent: fn_def_id }, - in_trait, + hir::OpaqueTyOrigin::FnReturn { parent: fn_def_id, in_trait_or_impl } + | hir::OpaqueTyOrigin::AsyncFn { parent: fn_def_id, in_trait_or_impl }, .. }) => { - if in_trait { + if in_trait_or_impl.is_some() { assert_matches!(tcx.def_kind(fn_def_id), DefKind::AssocFn); } else { assert_matches!(tcx.def_kind(fn_def_id), DefKind::AssocFn | DefKind::Fn); diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs index 07a8c3a9e84db..2418037ae962f 100644 --- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs +++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs @@ -370,39 +370,47 @@ pub(super) fn explicit_item_bounds_with_filter( .. }) => associated_type_bounds(tcx, def_id, bounds, *span, filter), hir::Node::Item(hir::Item { - kind: hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds, in_trait: false, .. }), + kind: hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds, origin, .. }), span, .. - }) => { - let args = GenericArgs::identity_for_item(tcx, def_id); - let item_ty = Ty::new_opaque(tcx, def_id.to_def_id(), args); - let bounds = opaque_type_bounds(tcx, def_id, bounds, item_ty, *span, filter); - assert_only_contains_predicates_from(filter, bounds, item_ty); - bounds - } - // Since RPITITs are lowered as projections in `::lower_ty`, when we're - // asking for the item bounds of the *opaques* in a trait's default method signature, we - // need to map these projections back to opaques. - hir::Node::Item(hir::Item { - kind: hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds, in_trait: true, origin, .. }), - span, - .. - }) => { - let (hir::OpaqueTyOrigin::FnReturn { parent: fn_def_id } - | hir::OpaqueTyOrigin::AsyncFn { parent: fn_def_id }) = *origin - else { - span_bug!(*span, "RPITIT cannot be a TAIT, but got origin {origin:?}"); - }; - let args = GenericArgs::identity_for_item(tcx, def_id); - let item_ty = Ty::new_opaque(tcx, def_id.to_def_id(), args); - let bounds = &*tcx.arena.alloc_slice( - &opaque_type_bounds(tcx, def_id, bounds, item_ty, *span, filter) - .to_vec() - .fold_with(&mut AssocTyToOpaque { tcx, fn_def_id: fn_def_id.to_def_id() }), - ); - assert_only_contains_predicates_from(filter, bounds, item_ty); - bounds - } + }) => match origin { + // Since RPITITs are lowered as projections in `::lower_ty`, + // when we're asking for the item bounds of the *opaques* in a trait's default + // method signature, we need to map these projections back to opaques. + rustc_hir::OpaqueTyOrigin::FnReturn { + parent, + in_trait_or_impl: Some(hir::RpitContext::Trait), + } + | rustc_hir::OpaqueTyOrigin::AsyncFn { + parent, + in_trait_or_impl: Some(hir::RpitContext::Trait), + } => { + let args = GenericArgs::identity_for_item(tcx, def_id); + let item_ty = Ty::new_opaque(tcx, def_id.to_def_id(), args); + let bounds = &*tcx.arena.alloc_slice( + &opaque_type_bounds(tcx, def_id, bounds, item_ty, *span, filter) + .to_vec() + .fold_with(&mut AssocTyToOpaque { tcx, fn_def_id: parent.to_def_id() }), + ); + assert_only_contains_predicates_from(filter, bounds, item_ty); + bounds + } + rustc_hir::OpaqueTyOrigin::FnReturn { + parent: _, + in_trait_or_impl: None | Some(hir::RpitContext::TraitImpl), + } + | rustc_hir::OpaqueTyOrigin::AsyncFn { + parent: _, + in_trait_or_impl: None | Some(hir::RpitContext::TraitImpl), + } + | rustc_hir::OpaqueTyOrigin::TyAlias { parent: _, .. } => { + let args = GenericArgs::identity_for_item(tcx, def_id); + let item_ty = Ty::new_opaque(tcx, def_id.to_def_id(), args); + let bounds = opaque_type_bounds(tcx, def_id, bounds, item_ty, *span, filter); + assert_only_contains_predicates_from(filter, bounds, item_ty); + bounds + } + }, hir::Node::Item(hir::Item { kind: hir::ItemKind::TyAlias(..), .. }) => &[], _ => bug!("item_bounds called on {:?}", def_id), }; diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index 60788641da0cd..a15621bf28b9b 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -515,8 +515,8 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { } hir::ItemKind::OpaqueTy(&hir::OpaqueTy { origin: - hir::OpaqueTyOrigin::FnReturn { parent } - | hir::OpaqueTyOrigin::AsyncFn { parent } + hir::OpaqueTyOrigin::FnReturn { parent, .. } + | hir::OpaqueTyOrigin::AsyncFn { parent, .. } | hir::OpaqueTyOrigin::TyAlias { parent, .. }, generics, .. diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index 313d7dd62654a..3af4d1f5edad5 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -618,12 +618,13 @@ pub(super) fn type_of_opaque( // Opaque types desugared from `impl Trait`. ItemKind::OpaqueTy(&OpaqueTy { origin: - hir::OpaqueTyOrigin::FnReturn { parent: owner } - | hir::OpaqueTyOrigin::AsyncFn { parent: owner }, - in_trait, + hir::OpaqueTyOrigin::FnReturn { parent: owner, in_trait_or_impl } + | hir::OpaqueTyOrigin::AsyncFn { parent: owner, in_trait_or_impl }, .. }) => { - if in_trait && !tcx.defaultness(owner).has_value() { + if in_trait_or_impl == Some(hir::RpitContext::Trait) + && !tcx.defaultness(owner).has_value() + { span_bug!( tcx.def_span(def_id), "tried to get type of this RPITIT with no definition" diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 496f54e31d2c0..24a8fdf0f6bcc 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -2091,17 +2091,37 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let opaque_ty = tcx.hir().item(item_id); match opaque_ty.kind { - hir::ItemKind::OpaqueTy(&hir::OpaqueTy { in_trait, .. }) => { + hir::ItemKind::OpaqueTy(&hir::OpaqueTy { origin, .. }) => { let local_def_id = item_id.owner_id.def_id; // If this is an RPITIT and we are using the new RPITIT lowering scheme, we // generate the def_id of an associated type for the trait and return as // type a projection. - let def_id = if in_trait { - tcx.associated_type_for_impl_trait_in_trait(local_def_id).to_def_id() - } else { - local_def_id.to_def_id() - }; - self.lower_opaque_ty(def_id, lifetimes, in_trait) + match origin { + hir::OpaqueTyOrigin::FnReturn { + in_trait_or_impl: Some(hir::RpitContext::Trait), + .. + } + | hir::OpaqueTyOrigin::AsyncFn { + in_trait_or_impl: Some(hir::RpitContext::Trait), + .. + } => self.lower_opaque_ty( + tcx.associated_type_for_impl_trait_in_trait(local_def_id) + .to_def_id(), + lifetimes, + true, + ), + hir::OpaqueTyOrigin::FnReturn { + in_trait_or_impl: None | Some(hir::RpitContext::TraitImpl), + .. + } + | hir::OpaqueTyOrigin::AsyncFn { + in_trait_or_impl: None | Some(hir::RpitContext::TraitImpl), + .. + } + | hir::OpaqueTyOrigin::TyAlias { .. } => { + self.lower_opaque_ty(local_def_id.to_def_id(), lifetimes, false) + } + } } ref i => bug!("`impl Trait` pointed to non-opaque type?? {:#?}", i), } diff --git a/compiler/rustc_hir_typeck/src/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs index 204138ffd44a4..2dbadf8198b3f 100644 --- a/compiler/rustc_hir_typeck/src/_match.rs +++ b/compiler/rustc_hir_typeck/src/_match.rs @@ -602,7 +602,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .map(|(k, _)| (k.def_id, k.args))?, _ => return None, }; - let hir::OpaqueTyOrigin::FnReturn { parent: parent_def_id } = + let hir::OpaqueTyOrigin::FnReturn { parent: parent_def_id, .. } = self.tcx.opaque_type_origin(def_id) else { return None; diff --git a/compiler/rustc_lint/src/impl_trait_overcaptures.rs b/compiler/rustc_lint/src/impl_trait_overcaptures.rs index c13f4c7746d08..5aeaad420699e 100644 --- a/compiler/rustc_lint/src/impl_trait_overcaptures.rs +++ b/compiler/rustc_lint/src/impl_trait_overcaptures.rs @@ -259,7 +259,7 @@ where // If it's owned by this function && let opaque = self.tcx.hir_node_by_def_id(opaque_def_id).expect_item().expect_opaque_ty() - && let hir::OpaqueTyOrigin::FnReturn { parent } = opaque.origin + && let hir::OpaqueTyOrigin::FnReturn { parent, .. } = opaque.origin && parent == self.parent_def_id { let opaque_span = self.tcx.def_span(opaque_def_id); diff --git a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs index 87fa38a282ed1..342ebfa0b06cc 100644 --- a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs +++ b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs @@ -77,7 +77,7 @@ impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound { // That's because although we may have an opaque type on the function, // it won't have a hidden type, so proving predicates about it is // not really meaningful. - if let hir::OpaqueTyOrigin::FnReturn { parent: method_def_id } = opaque.origin + if let hir::OpaqueTyOrigin::FnReturn { parent: method_def_id, .. } = opaque.origin && let hir::Node::TraitItem(trait_item) = cx.tcx.hir_node_by_def_id(method_def_id) && !trait_item.defaultness.has_value() { @@ -114,8 +114,10 @@ impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound { // return type is well-formed in traits even when `Self` isn't sized. if let ty::Param(param_ty) = *proj_term.kind() && param_ty.name == kw::SelfUpper - && matches!(opaque.origin, hir::OpaqueTyOrigin::AsyncFn { .. }) - && opaque.in_trait + && matches!(opaque.origin, hir::OpaqueTyOrigin::AsyncFn { + in_trait_or_impl: Some(hir::RpitContext::Trait), + .. + }) { return; } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 61eb7260384ef..610c682d3a461 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1186,8 +1186,8 @@ fn should_encode_type(tcx: TyCtxt<'_>, def_id: LocalDefId, def_kind: DefKind) -> DefKind::OpaqueTy => { let origin = tcx.opaque_type_origin(def_id); - if let hir::OpaqueTyOrigin::FnReturn { parent } - | hir::OpaqueTyOrigin::AsyncFn { parent } = origin + if let hir::OpaqueTyOrigin::FnReturn { parent, .. } + | hir::OpaqueTyOrigin::AsyncFn { parent, .. } = origin && let hir::Node::TraitItem(trait_item) = tcx.hir_node_by_def_id(parent) && let (_, hir::TraitFn::Required(..)) = trait_item.expect_fn() { diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index e11361a615f4c..72e6c96e6f646 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -1139,13 +1139,7 @@ fn hir_id_to_string(map: Map<'_>, id: HirId) -> String { ItemKind::ForeignMod { .. } => "foreign mod", ItemKind::GlobalAsm(..) => "global asm", ItemKind::TyAlias(..) => "ty", - ItemKind::OpaqueTy(opaque) => { - if opaque.in_trait { - "opaque type in trait" - } else { - "opaque type" - } - } + ItemKind::OpaqueTy(..) => "opaque type", ItemKind::Enum(..) => "enum", ItemKind::Struct(..) => "struct", ItemKind::Union(..) => "union", diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 9094b00fbfb44..015e45ad67014 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -636,8 +636,7 @@ impl<'tcx> EmbargoVisitor<'tcx> { impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> { fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { if self.impl_trait_pass - && let hir::ItemKind::OpaqueTy(opaque) = item.kind - && !opaque.in_trait + && let hir::ItemKind::OpaqueTy(..) = item.kind { // FIXME: This is some serious pessimization intended to workaround deficiencies // in the reachability pass (`middle/reachable.rs`). Types are marked as link-time diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs index 673756076b12f..a2d717817db14 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs @@ -1271,7 +1271,7 @@ fn suggest_precise_capturing<'tcx>( let hir::OpaqueTy { bounds, origin, .. } = tcx.hir_node_by_def_id(opaque_def_id).expect_item().expect_opaque_ty(); - let hir::OpaqueTyOrigin::FnReturn { parent: fn_def_id } = *origin else { + let hir::OpaqueTyOrigin::FnReturn { parent: fn_def_id, .. } = *origin else { return; }; diff --git a/compiler/rustc_ty_utils/src/assoc.rs b/compiler/rustc_ty_utils/src/assoc.rs index f8cfeb500a5ec..e41f2c8ce4869 100644 --- a/compiler/rustc_ty_utils/src/assoc.rs +++ b/compiler/rustc_ty_utils/src/assoc.rs @@ -379,8 +379,8 @@ fn associated_type_for_impl_trait_in_trait( tcx: TyCtxt<'_>, opaque_ty_def_id: LocalDefId, ) -> LocalDefId { - let (hir::OpaqueTyOrigin::FnReturn { parent: fn_def_id } - | hir::OpaqueTyOrigin::AsyncFn { parent: fn_def_id }) = + let (hir::OpaqueTyOrigin::FnReturn { parent: fn_def_id, .. } + | hir::OpaqueTyOrigin::AsyncFn { parent: fn_def_id, .. }) = tcx.opaque_type_origin(opaque_ty_def_id) else { bug!("expected opaque for {opaque_ty_def_id:?}"); From 6e8573c520086db6cc7993a1a2608cc9347832a4 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 2 Oct 2024 23:00:08 -0400 Subject: [PATCH 105/120] Visit in embargo visitor if trait method has body --- compiler/rustc_privacy/src/lib.rs | 48 ++++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 015e45ad67014..d00e7eff75212 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -636,17 +636,45 @@ impl<'tcx> EmbargoVisitor<'tcx> { impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> { fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { if self.impl_trait_pass - && let hir::ItemKind::OpaqueTy(..) = item.kind + && let hir::ItemKind::OpaqueTy(opaque) = item.kind { - // FIXME: This is some serious pessimization intended to workaround deficiencies - // in the reachability pass (`middle/reachable.rs`). Types are marked as link-time - // reachable if they are returned via `impl Trait`, even from private functions. - let pub_ev = EffectiveVisibility::from_vis(ty::Visibility::Public); - self.reach_through_impl_trait(item.owner_id.def_id, pub_ev) - .generics() - .predicates() - .ty(); - return; + let should_visit = match opaque.origin { + hir::OpaqueTyOrigin::FnReturn { + parent, + in_trait_or_impl: Some(hir::RpitContext::Trait), + } + | hir::OpaqueTyOrigin::AsyncFn { + parent, + in_trait_or_impl: Some(hir::RpitContext::Trait), + } => match self.tcx.hir_node_by_def_id(parent).expect_trait_item().expect_fn().1 { + hir::TraitFn::Required(_) => false, + hir::TraitFn::Provided(..) => true, + }, + + // Always visit RPITs in functions that have definitions, + // and all TAITs. + hir::OpaqueTyOrigin::FnReturn { + in_trait_or_impl: None | Some(hir::RpitContext::TraitImpl), + .. + } + | hir::OpaqueTyOrigin::AsyncFn { + in_trait_or_impl: None | Some(hir::RpitContext::TraitImpl), + .. + } + | hir::OpaqueTyOrigin::TyAlias { .. } => true, + }; + + if should_visit { + // FIXME: This is some serious pessimization intended to workaround deficiencies + // in the reachability pass (`middle/reachable.rs`). Types are marked as link-time + // reachable if they are returned via `impl Trait`, even from private functions. + let pub_ev = EffectiveVisibility::from_vis(ty::Visibility::Public); + self.reach_through_impl_trait(item.owner_id.def_id, pub_ev) + .generics() + .predicates() + .ty(); + return; + } } // Update levels of nested things and mark all items From 11f738fcb2ae4ecd6821309290a468a6ebcbaaa9 Mon Sep 17 00:00:00 2001 From: ltdk Date: Sat, 17 Aug 2024 20:33:31 -0400 Subject: [PATCH 106/120] impl Default for Hash{Map,Set} iterators that don't already have it --- library/std/src/collections/hash/map.rs | 64 +++++++++++++++++++++++++ library/std/src/collections/hash/set.rs | 16 +++++++ 2 files changed, 80 insertions(+) diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs index 6f2b4100620ee..f2e523dca779c 100644 --- a/library/std/src/collections/hash/map.rs +++ b/library/std/src/collections/hash/map.rs @@ -1438,6 +1438,14 @@ impl Clone for Iter<'_, K, V> { } } +#[stable(feature = "default_iters_hash", since = "CURRENT_RUSTC_VERSION")] +impl Default for Iter<'_, K, V> { + #[inline] + fn default() -> Self { + Iter { base: Default::default() } + } +} + #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for Iter<'_, K, V> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -1476,6 +1484,14 @@ impl<'a, K, V> IterMut<'a, K, V> { } } +#[stable(feature = "default_iters_hash", since = "CURRENT_RUSTC_VERSION")] +impl Default for IterMut<'_, K, V> { + #[inline] + fn default() -> Self { + IterMut { base: Default::default() } + } +} + /// An owning iterator over the entries of a `HashMap`. /// /// This `struct` is created by the [`into_iter`] method on [`HashMap`] @@ -1506,6 +1522,14 @@ impl IntoIter { } } +#[stable(feature = "default_iters_hash", since = "CURRENT_RUSTC_VERSION")] +impl Default for IntoIter { + #[inline] + fn default() -> Self { + IntoIter { base: Default::default() } + } +} + /// An iterator over the keys of a `HashMap`. /// /// This `struct` is created by the [`keys`] method on [`HashMap`]. See its @@ -1538,6 +1562,14 @@ impl Clone for Keys<'_, K, V> { } } +#[stable(feature = "default_iters_hash", since = "CURRENT_RUSTC_VERSION")] +impl Default for Keys<'_, K, V> { + #[inline] + fn default() -> Self { + Keys { inner: Default::default() } + } +} + #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for Keys<'_, K, V> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -1577,6 +1609,14 @@ impl Clone for Values<'_, K, V> { } } +#[stable(feature = "default_iters_hash", since = "CURRENT_RUSTC_VERSION")] +impl Default for Values<'_, K, V> { + #[inline] + fn default() -> Self { + Values { inner: Default::default() } + } +} + #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for Values<'_, K, V> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -1665,6 +1705,14 @@ pub struct ValuesMut<'a, K: 'a, V: 'a> { inner: IterMut<'a, K, V>, } +#[stable(feature = "default_iters_hash", since = "CURRENT_RUSTC_VERSION")] +impl Default for ValuesMut<'_, K, V> { + #[inline] + fn default() -> Self { + ValuesMut { inner: Default::default() } + } +} + /// An owning iterator over the keys of a `HashMap`. /// /// This `struct` is created by the [`into_keys`] method on [`HashMap`]. @@ -1687,6 +1735,14 @@ pub struct IntoKeys { inner: IntoIter, } +#[stable(feature = "default_iters_hash", since = "CURRENT_RUSTC_VERSION")] +impl Default for IntoKeys { + #[inline] + fn default() -> Self { + IntoKeys { inner: Default::default() } + } +} + /// An owning iterator over the values of a `HashMap`. /// /// This `struct` is created by the [`into_values`] method on [`HashMap`]. @@ -1709,6 +1765,14 @@ pub struct IntoValues { inner: IntoIter, } +#[stable(feature = "default_iters_hash", since = "CURRENT_RUSTC_VERSION")] +impl Default for IntoValues { + #[inline] + fn default() -> Self { + IntoValues { inner: Default::default() } + } +} + /// A builder for computing where in a HashMap a key-value pair would be stored. /// /// See the [`HashMap::raw_entry_mut`] docs for usage examples. diff --git a/library/std/src/collections/hash/set.rs b/library/std/src/collections/hash/set.rs index e69fb0878e7d0..210f57152250c 100644 --- a/library/std/src/collections/hash/set.rs +++ b/library/std/src/collections/hash/set.rs @@ -1244,6 +1244,14 @@ pub struct Iter<'a, K: 'a> { base: base::Iter<'a, K>, } +#[stable(feature = "default_iters_hash", since = "CURRENT_RUSTC_VERSION")] +impl Default for Iter<'_, K> { + #[inline] + fn default() -> Self { + Iter { base: Default::default() } + } +} + /// An owning iterator over the items of a `HashSet`. /// /// This `struct` is created by the [`into_iter`] method on [`HashSet`] @@ -1265,6 +1273,14 @@ pub struct IntoIter { base: base::IntoIter, } +#[stable(feature = "default_iters_hash", since = "CURRENT_RUSTC_VERSION")] +impl Default for IntoIter { + #[inline] + fn default() -> Self { + IntoIter { base: Default::default() } + } +} + /// A draining iterator over the items of a `HashSet`. /// /// This `struct` is created by the [`drain`] method on [`HashSet`]. From 4b48d72eaa7f4d934f56bcf2b6d0348954977823 Mon Sep 17 00:00:00 2001 From: Jaken Herman Date: Wed, 2 Oct 2024 00:24:42 -0500 Subject: [PATCH 107/120] Add `get_line` confusable to `Stdin::read_line()` Add tests for addition of `#[rustc_confusables("get_line")]` --- library/std/src/io/stdio.rs | 1 + tests/ui/attributes/rustc_confusables_std_cases.rs | 4 ++++ .../attributes/rustc_confusables_std_cases.stderr | 13 ++++++++++++- 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs index 6de069a518e3d..bf242e715bd94 100644 --- a/library/std/src/io/stdio.rs +++ b/library/std/src/io/stdio.rs @@ -394,6 +394,7 @@ impl Stdin { /// in which case it will wait for the Enter key to be pressed before /// continuing #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_confusables("get_line")] pub fn read_line(&self, buf: &mut String) -> io::Result { self.lock().read_line(buf) } diff --git a/tests/ui/attributes/rustc_confusables_std_cases.rs b/tests/ui/attributes/rustc_confusables_std_cases.rs index d9121695950b2..4f6baea26dfd7 100644 --- a/tests/ui/attributes/rustc_confusables_std_cases.rs +++ b/tests/ui/attributes/rustc_confusables_std_cases.rs @@ -23,4 +23,8 @@ fn main() { //~^ HELP you might have meant to use `push_str` String::new().append(""); //~ ERROR E0599 //~^ HELP you might have meant to use `push_str` + let mut buffer = String::new(); + let stdin = std::io::stdin(); + stdin.get_line(&mut buffer).unwrap(); //~ ERROR E0599 + //~^ HELP you might have meant to use `read_line` } diff --git a/tests/ui/attributes/rustc_confusables_std_cases.stderr b/tests/ui/attributes/rustc_confusables_std_cases.stderr index f4b6947ccd94a..7bf96241ca725 100644 --- a/tests/ui/attributes/rustc_confusables_std_cases.stderr +++ b/tests/ui/attributes/rustc_confusables_std_cases.stderr @@ -106,7 +106,18 @@ help: you might have meant to use `push_str` LL | String::new().push_str(""); | ~~~~~~~~ -error: aborting due to 8 previous errors +error[E0599]: no method named `get_line` found for struct `Stdin` in the current scope + --> $DIR/rustc_confusables_std_cases.rs:28:11 + | +LL | stdin.get_line(&mut buffer).unwrap(); + | ^^^^^^^^ method not found in `Stdin` + | +help: you might have meant to use `read_line` + | +LL | stdin.read_line(&mut buffer).unwrap(); + | ~~~~~~~~~ + +error: aborting due to 9 previous errors Some errors have detailed explanations: E0308, E0599. For more information about an error, try `rustc --explain E0308`. From 3d8bd6bbc5dc51fbcc8d912a46f117611c98ee0d Mon Sep 17 00:00:00 2001 From: ismailarilik Date: Thu, 3 Oct 2024 08:23:30 +0300 Subject: [PATCH 108/120] Handle `rustc_metadata` cases of `rustc::potential_query_instability` lint --- compiler/rustc_metadata/src/lib.rs | 1 - compiler/rustc_metadata/src/locator.rs | 20 ++++++++++---------- compiler/rustc_middle/src/ty/mod.rs | 4 ++-- compiler/rustc_resolve/src/lib.rs | 4 ++-- 4 files changed, 14 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_metadata/src/lib.rs b/compiler/rustc_metadata/src/lib.rs index 10f2087d1e6f1..f206dba6cf4bc 100644 --- a/compiler/rustc_metadata/src/lib.rs +++ b/compiler/rustc_metadata/src/lib.rs @@ -1,6 +1,5 @@ // tidy-alphabetical-start #![allow(internal_features)] -#![allow(rustc::potential_query_instability)] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(control_flow_enum)] diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index 089ac060ba848..99c673b021a90 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -218,7 +218,7 @@ use std::ops::Deref; use std::path::{Path, PathBuf}; use std::{cmp, fmt}; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_data_structures::memmap::Mmap; use rustc_data_structures::owned_slice::slice_owned; use rustc_data_structures::svh::Svh; @@ -385,7 +385,7 @@ impl<'a> CrateLocator<'a> { let dylib_suffix = &self.target.dll_suffix; let staticlib_suffix = &self.target.staticlib_suffix; - let mut candidates: FxHashMap<_, (FxHashMap<_, _>, FxHashMap<_, _>, FxHashMap<_, _>)> = + let mut candidates: FxIndexMap<_, (FxIndexMap<_, _>, FxIndexMap<_, _>, FxIndexMap<_, _>)> = Default::default(); // First, find all possible candidate rlibs and dylibs purely based on @@ -460,7 +460,7 @@ impl<'a> CrateLocator<'a> { // A Library candidate is created if the metadata for the set of // libraries corresponds to the crate id and hash criteria that this // search is being performed for. - let mut libraries = FxHashMap::default(); + let mut libraries = FxIndexMap::default(); for (_hash, (rlibs, rmetas, dylibs)) in candidates { if let Some((svh, lib)) = self.extract_lib(rlibs, rmetas, dylibs)? { libraries.insert(svh, lib); @@ -494,9 +494,9 @@ impl<'a> CrateLocator<'a> { fn extract_lib( &mut self, - rlibs: FxHashMap, - rmetas: FxHashMap, - dylibs: FxHashMap, + rlibs: FxIndexMap, + rmetas: FxIndexMap, + dylibs: FxIndexMap, ) -> Result, CrateError> { let mut slot = None; // Order here matters, rmeta should come first. See comment in @@ -534,7 +534,7 @@ impl<'a> CrateLocator<'a> { // The `PathBuf` in `slot` will only be used for diagnostic purposes. fn extract_one( &mut self, - m: FxHashMap, + m: FxIndexMap, flavor: CrateFlavor, slot: &mut Option<(Svh, MetadataBlob, PathBuf)>, ) -> Result, CrateError> { @@ -702,9 +702,9 @@ impl<'a> CrateLocator<'a> { // First, filter out all libraries that look suspicious. We only accept // files which actually exist that have the correct naming scheme for // rlibs/dylibs. - let mut rlibs = FxHashMap::default(); - let mut rmetas = FxHashMap::default(); - let mut dylibs = FxHashMap::default(); + let mut rlibs = FxIndexMap::default(); + let mut rmetas = FxIndexMap::default(); + let mut dylibs = FxIndexMap::default(); for loc in &self.exact_paths { if !loc.canonicalized().exists() { return Err(CrateError::ExternLocationNotExist( diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index c4a28845085d9..f32daee7c44f2 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -187,8 +187,8 @@ pub struct ResolverGlobalCtxt { /// Mapping from ident span to path span for paths that don't exist as written, but that /// exist under `std`. For example, wrote `str::from_utf8` instead of `std::str::from_utf8`. pub confused_type_with_std_module: FxIndexMap, - pub doc_link_resolutions: FxHashMap, - pub doc_link_traits_in_scope: FxHashMap>, + pub doc_link_resolutions: FxIndexMap, + pub doc_link_traits_in_scope: FxIndexMap>, pub all_macro_rules: FxHashMap>, pub stripped_cfg_items: Steal>, } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 0c1a0038f9c79..fcbbb523cfc9f 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -1196,8 +1196,8 @@ pub struct Resolver<'ra, 'tcx> { stripped_cfg_items: Vec>, effective_visibilities: EffectiveVisibilities, - doc_link_resolutions: FxHashMap, - doc_link_traits_in_scope: FxHashMap>, + doc_link_resolutions: FxIndexMap, + doc_link_traits_in_scope: FxIndexMap>, all_macro_rules: FxHashMap, /// Invocation ids of all glob delegations. From 3a0ed0c6e3209ae1bce3079eea944c07b58f225f Mon Sep 17 00:00:00 2001 From: Orion Gonzalez Date: Wed, 2 Oct 2024 23:35:17 +0200 Subject: [PATCH 109/120] add direnv to gitignore --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index d6d8b99f6023c..2f88b27530128 100644 --- a/.gitignore +++ b/.gitignore @@ -86,4 +86,8 @@ package.json ## Rustdoc GUI tests tests/rustdoc-gui/src/**.lock +## direnv +.envrc +.direnv/ + # Before adding new lines, see the comment at the top. From 510e262deb6c5eba94e0aad2f609448271f80264 Mon Sep 17 00:00:00 2001 From: Orion Gonzalez Date: Wed, 2 Oct 2024 23:35:48 +0200 Subject: [PATCH 110/120] add nix files to gitignore --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index 2f88b27530128..b170dca88fa95 100644 --- a/.gitignore +++ b/.gitignore @@ -90,4 +90,9 @@ tests/rustdoc-gui/src/**.lock .envrc .direnv/ +## nix +flake.nix +flake.lock +default.nix + # Before adding new lines, see the comment at the top. From 83d0d9f292d4c98d26cd007f294ab91789b1378d Mon Sep 17 00:00:00 2001 From: ismailarilik Date: Thu, 3 Oct 2024 12:47:08 +0300 Subject: [PATCH 111/120] Handle `rustc_query_impl` cases of `rustc::potential_query_instability` lint --- compiler/rustc_query_impl/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs index 69742bb49b1e8..df898e0587ff9 100644 --- a/compiler/rustc_query_impl/src/lib.rs +++ b/compiler/rustc_query_impl/src/lib.rs @@ -2,7 +2,7 @@ // tidy-alphabetical-start #![allow(internal_features)] -#![allow(rustc::potential_query_instability, unused_parens)] +#![allow(unused_parens)] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(min_specialization)] From 8e382ba022fc7f8f6f7e0abca5e8052039a81202 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Thu, 3 Oct 2024 16:53:51 +1000 Subject: [PATCH 112/120] Avoid ICE in coverage builds with bad `#[coverage(..)]` attributes This code can sometimes witness malformed coverage attributes in builds that are going to fail, so use `span_delayed_bug` to avoid an inappropriate ICE in that case. --- .../rustc_mir_transform/src/coverage/query.rs | 3 ++- tests/crashes/127880.rs | 5 ---- .../ui/coverage-attr/bad-attr-ice.feat.stderr | 15 +++++++++++ .../coverage-attr/bad-attr-ice.nofeat.stderr | 26 +++++++++++++++++++ tests/ui/coverage-attr/bad-attr-ice.rs | 16 ++++++++++++ 5 files changed, 59 insertions(+), 6 deletions(-) delete mode 100644 tests/crashes/127880.rs create mode 100644 tests/ui/coverage-attr/bad-attr-ice.feat.stderr create mode 100644 tests/ui/coverage-attr/bad-attr-ice.nofeat.stderr create mode 100644 tests/ui/coverage-attr/bad-attr-ice.rs diff --git a/compiler/rustc_mir_transform/src/coverage/query.rs b/compiler/rustc_mir_transform/src/coverage/query.rs index e65a5fdd5e7f1..df151f8cca35e 100644 --- a/compiler/rustc_mir_transform/src/coverage/query.rs +++ b/compiler/rustc_mir_transform/src/coverage/query.rs @@ -63,7 +63,8 @@ fn coverage_attr_on(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { Some([item]) if item.has_name(sym::on) => return true, Some(_) | None => { // Other possibilities should have been rejected by `rustc_parse::validate_attr`. - tcx.dcx().span_bug(attr.span, "unexpected value of coverage attribute"); + // Use `span_delayed_bug` to avoid an ICE in failing builds (#127880). + tcx.dcx().span_delayed_bug(attr.span, "unexpected value of coverage attribute"); } } } diff --git a/tests/crashes/127880.rs b/tests/crashes/127880.rs deleted file mode 100644 index 6c625eac6914a..0000000000000 --- a/tests/crashes/127880.rs +++ /dev/null @@ -1,5 +0,0 @@ -//@ known-bug: #127880 -//@ compile-flags: -Cinstrument-coverage - -#[coverage] -fn main() {} diff --git a/tests/ui/coverage-attr/bad-attr-ice.feat.stderr b/tests/ui/coverage-attr/bad-attr-ice.feat.stderr new file mode 100644 index 0000000000000..9e3cd41c2777d --- /dev/null +++ b/tests/ui/coverage-attr/bad-attr-ice.feat.stderr @@ -0,0 +1,15 @@ +error: malformed `coverage` attribute input + --> $DIR/bad-attr-ice.rs:10:1 + | +LL | #[coverage] + | ^^^^^^^^^^^ + | +help: the following are the possible correct uses + | +LL | #[coverage(off)] + | +LL | #[coverage(on)] + | + +error: aborting due to 1 previous error + diff --git a/tests/ui/coverage-attr/bad-attr-ice.nofeat.stderr b/tests/ui/coverage-attr/bad-attr-ice.nofeat.stderr new file mode 100644 index 0000000000000..d73636e158bae --- /dev/null +++ b/tests/ui/coverage-attr/bad-attr-ice.nofeat.stderr @@ -0,0 +1,26 @@ +error: malformed `coverage` attribute input + --> $DIR/bad-attr-ice.rs:10:1 + | +LL | #[coverage] + | ^^^^^^^^^^^ + | +help: the following are the possible correct uses + | +LL | #[coverage(off)] + | +LL | #[coverage(on)] + | + +error[E0658]: the `#[coverage]` attribute is an experimental feature + --> $DIR/bad-attr-ice.rs:10:1 + | +LL | #[coverage] + | ^^^^^^^^^^^ + | + = note: see issue #84605 for more information + = help: add `#![feature(coverage_attribute)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/coverage-attr/bad-attr-ice.rs b/tests/ui/coverage-attr/bad-attr-ice.rs new file mode 100644 index 0000000000000..ae4d27d65ebd6 --- /dev/null +++ b/tests/ui/coverage-attr/bad-attr-ice.rs @@ -0,0 +1,16 @@ +#![cfg_attr(feat, feature(coverage_attribute))] +//@ revisions: feat nofeat +//@ compile-flags: -Cinstrument-coverage +//@ needs-profiler-support + +// Malformed `#[coverage(..)]` attributes should not cause an ICE when built +// with `-Cinstrument-coverage`. +// Regression test for . + +#[coverage] +//~^ ERROR malformed `coverage` attribute input +//[nofeat]~| the `#[coverage]` attribute is an experimental feature +fn main() {} + +// FIXME(#130766): When the `#[coverage(..)]` attribute is stabilized, +// get rid of the revisions and just make this a normal test. From c994a60fd4f848005beeef17bf6a13e1347103b8 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 3 Oct 2024 14:48:03 +0200 Subject: [PATCH 113/120] Bump nightly version -> 2024-10-03 --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index b431599c224ef..f0c8651efce4e 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,4 +1,4 @@ [toolchain] -channel = "nightly-2024-09-22" +channel = "nightly-2024-10-03" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" From 9c91a4ef1652e5f01742185f43541ab22e6f01db Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 3 Oct 2024 09:40:53 -0400 Subject: [PATCH 114/120] Failing diff test --- ...itwise_not.JumpThreading.panic-unwind.diff | 47 +++++++++++++++++++ tests/mir-opt/jump_threading.rs | 11 +++++ 2 files changed, 58 insertions(+) create mode 100644 tests/mir-opt/jump_threading.bitwise_not.JumpThreading.panic-unwind.diff diff --git a/tests/mir-opt/jump_threading.bitwise_not.JumpThreading.panic-unwind.diff b/tests/mir-opt/jump_threading.bitwise_not.JumpThreading.panic-unwind.diff new file mode 100644 index 0000000000000..fb64fff7e11e5 --- /dev/null +++ b/tests/mir-opt/jump_threading.bitwise_not.JumpThreading.panic-unwind.diff @@ -0,0 +1,47 @@ +- // MIR for `bitwise_not` before JumpThreading ++ // MIR for `bitwise_not` after JumpThreading + + fn bitwise_not() -> i32 { + let mut _0: i32; + let mut _1: i32; + let mut _2: bool; + let mut _3: i32; + let mut _4: i32; + scope 1 { + debug a => _1; + } + + bb0: { + StorageLive(_1); + _1 = const 0_i32; + _1 = const 1_i32; + StorageLive(_2); + StorageLive(_3); + StorageLive(_4); + _4 = copy _1; + _3 = Not(move _4); + StorageDead(_4); + _2 = Eq(move _3, const 0_i32); +- switchInt(move _2) -> [0: bb2, otherwise: bb1]; ++ goto -> bb1; + } + + bb1: { + StorageDead(_3); + _0 = const 1_i32; + goto -> bb3; + } + + bb2: { + StorageDead(_3); + _0 = const 0_i32; + goto -> bb3; + } + + bb3: { + StorageDead(_2); + StorageDead(_1); + return; + } + } + diff --git a/tests/mir-opt/jump_threading.rs b/tests/mir-opt/jump_threading.rs index 9487a4e7e5ffe..4094dec0cbf1e 100644 --- a/tests/mir-opt/jump_threading.rs +++ b/tests/mir-opt/jump_threading.rs @@ -531,6 +531,16 @@ fn floats() -> u32 { if x == 0.0 { 0 } else { 1 } } +pub fn bitwise_not() -> i32 { + // CHECK-LABEL: fn bitwise_not( + // CHECK: switchInt( + + // Test for #131195, which was optimizing `!a == b` into `a != b`. + let mut a: i32 = 0; + a = 1; + if !a == 0 { 1 } else { 0 } +} + fn main() { // CHECK-LABEL: fn main( too_complex(Ok(0)); @@ -562,3 +572,4 @@ fn main() { // EMIT_MIR jump_threading.assume.JumpThreading.diff // EMIT_MIR jump_threading.aggregate_copy.JumpThreading.diff // EMIT_MIR jump_threading.floats.JumpThreading.diff +// EMIT_MIR jump_threading.bitwise_not.JumpThreading.diff From d47e388843f682f57262f020edd9909c850a0c49 Mon Sep 17 00:00:00 2001 From: EFanZh Date: Thu, 3 Oct 2024 22:15:52 +0800 Subject: [PATCH 115/120] Avoid emptiness check in `PeekMut::pop` --- library/alloc/src/collections/binary_heap/mod.rs | 5 ++++- tests/codegen/binary-heap-peek-mut-pop-no-panic.rs | 13 +++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 tests/codegen/binary-heap-peek-mut-pop-no-panic.rs diff --git a/library/alloc/src/collections/binary_heap/mod.rs b/library/alloc/src/collections/binary_heap/mod.rs index 5e59abf54ee0f..59f10b09c73fd 100644 --- a/library/alloc/src/collections/binary_heap/mod.rs +++ b/library/alloc/src/collections/binary_heap/mod.rs @@ -374,7 +374,10 @@ impl<'a, T: Ord, A: Allocator> PeekMut<'a, T, A> { // the caller could've mutated the element. It is removed from the // heap on the next line and pop() is not sensitive to its value. } - this.heap.pop().unwrap() + + // SAFETY: Have a `PeekMut` element proves that the associated binary heap being non-empty, + // so the `pop` operation will not fail. + unsafe { this.heap.pop().unwrap_unchecked() } } } diff --git a/tests/codegen/binary-heap-peek-mut-pop-no-panic.rs b/tests/codegen/binary-heap-peek-mut-pop-no-panic.rs new file mode 100644 index 0000000000000..9cf4f210e527a --- /dev/null +++ b/tests/codegen/binary-heap-peek-mut-pop-no-panic.rs @@ -0,0 +1,13 @@ +//@ compile-flags: -O +//@ ignore-debug +#![crate_type = "lib"] + +use std::collections::binary_heap::PeekMut; + +// CHECK-LABEL: @peek_mut_pop +#[no_mangle] +pub fn peek_mut_pop(peek_mut: PeekMut) -> u32 { + // CHECK-NOT: panic + // CHECK-NOT: unwrap_failed + PeekMut::pop(peek_mut) +} From bc5f9520c117b372b7ad7326fda1252cd9da3f4e Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 3 Oct 2024 15:19:23 -0400 Subject: [PATCH 116/120] Remove crashes, add comment --- .../traits/fulfillment_errors.rs | 2 ++ tests/crashes/125881.rs | 8 ----- tests/crashes/126377.rs | 29 ------------------- tests/crashes/130413.rs | 17 ----------- 4 files changed, 2 insertions(+), 54 deletions(-) delete mode 100644 tests/crashes/125881.rs delete mode 100644 tests/crashes/126377.rs delete mode 100644 tests/crashes/130413.rs diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index f494d9637f324..e2d067e012c77 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -2224,6 +2224,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ) -> GetSafeTransmuteErrorAndReason { use rustc_transmute::Answer; + // We don't assemble a transmutability candidate for types that are generic + // and we should have ambiguity for types that still have non-region infer. if obligation.predicate.has_non_region_param() || obligation.has_non_region_infer() { return GetSafeTransmuteErrorAndReason::Default; } diff --git a/tests/crashes/125881.rs b/tests/crashes/125881.rs deleted file mode 100644 index a38f1891b61d9..0000000000000 --- a/tests/crashes/125881.rs +++ /dev/null @@ -1,8 +0,0 @@ -//@ known-bug: rust-lang/rust#125881 -#![crate_type = "lib"] -#![feature(transmutability)] -#![feature(unboxed_closures,effects)] - -const fn test() -> impl std::mem::TransmuteFrom() { - || {} -} diff --git a/tests/crashes/126377.rs b/tests/crashes/126377.rs deleted file mode 100644 index f6727bcc0a494..0000000000000 --- a/tests/crashes/126377.rs +++ /dev/null @@ -1,29 +0,0 @@ -//@ known-bug: rust-lang/rust#126377 - -#![feature(effects)] -#![feature(generic_const_exprs)] - -mod assert { - use std::mem::{Assume, TransmuteFrom}; - - pub fn is_transmutable< - Src, - Dst, - const ASSUME_ALIGNMENT: bool, - const ASSUME_LIFETIMES: bool, - const ASSUME_SAFETY: bool, - const ASSUME_VALIDITY: bool, - >() - where - Dst: TransmuteFrom< - Src, - { } - >, - {} -} - -const fn from_options() -> Assume { - #[repr(C)] struct Src; - #[repr(C)] struct Dst; - assert::is_transmutable::(); -} diff --git a/tests/crashes/130413.rs b/tests/crashes/130413.rs deleted file mode 100644 index 08435ac6450c0..0000000000000 --- a/tests/crashes/130413.rs +++ /dev/null @@ -1,17 +0,0 @@ -//@ known-bug: #130413 - -#![feature(transmutability)] -trait Aaa { - type Y; -} - -trait Bbb { - type B: std::mem::TransmuteFrom<()>; -} - -impl Bbb for T -where - T: Aaa, -{ - type B = T::Y; -} From f0bfba258330884df9c9d14a0aafd741ca9bbdc9 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 3 Oct 2024 09:47:06 -0400 Subject: [PATCH 117/120] Disable jump threading UnOp::Not for non-bool --- .../rustc_mir_transform/src/jump_threading.rs | 8 ++++ ...bitwise_not.JumpThreading.panic-abort.diff | 46 +++++++++++++++++++ ...itwise_not.JumpThreading.panic-unwind.diff | 3 +- 3 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 tests/mir-opt/jump_threading.bitwise_not.JumpThreading.panic-abort.diff diff --git a/compiler/rustc_mir_transform/src/jump_threading.rs b/compiler/rustc_mir_transform/src/jump_threading.rs index 1844b97887ab9..9b9b0b705bfec 100644 --- a/compiler/rustc_mir_transform/src/jump_threading.rs +++ b/compiler/rustc_mir_transform/src/jump_threading.rs @@ -494,8 +494,16 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { } // Transfer the conditions on the copy rhs, after inversing polarity. Rvalue::UnaryOp(UnOp::Not, Operand::Move(place) | Operand::Copy(place)) => { + if !place.ty(self.body, self.tcx).ty.is_bool() { + // Constructing the conditions by inverting the polarity + // of equality is only correct for bools. That is to say, + // `!a == b` is not `a != b` for integers greater than 1 bit. + return; + } let Some(conditions) = state.try_get_idx(lhs, &self.map) else { return }; let Some(place) = self.map.find(place.as_ref()) else { return }; + // FIXME: I think This could be generalized to not bool if we + // actually perform a logical not on the condition's value. let conds = conditions.map(self.arena, Condition::inv); state.insert_value_idx(place, conds, &self.map); } diff --git a/tests/mir-opt/jump_threading.bitwise_not.JumpThreading.panic-abort.diff b/tests/mir-opt/jump_threading.bitwise_not.JumpThreading.panic-abort.diff new file mode 100644 index 0000000000000..047441e609913 --- /dev/null +++ b/tests/mir-opt/jump_threading.bitwise_not.JumpThreading.panic-abort.diff @@ -0,0 +1,46 @@ +- // MIR for `bitwise_not` before JumpThreading ++ // MIR for `bitwise_not` after JumpThreading + + fn bitwise_not() -> i32 { + let mut _0: i32; + let mut _1: i32; + let mut _2: bool; + let mut _3: i32; + let mut _4: i32; + scope 1 { + debug a => _1; + } + + bb0: { + StorageLive(_1); + _1 = const 0_i32; + _1 = const 1_i32; + StorageLive(_2); + StorageLive(_3); + StorageLive(_4); + _4 = copy _1; + _3 = Not(move _4); + StorageDead(_4); + _2 = Eq(move _3, const 0_i32); + switchInt(move _2) -> [0: bb2, otherwise: bb1]; + } + + bb1: { + StorageDead(_3); + _0 = const 1_i32; + goto -> bb3; + } + + bb2: { + StorageDead(_3); + _0 = const 0_i32; + goto -> bb3; + } + + bb3: { + StorageDead(_2); + StorageDead(_1); + return; + } + } + diff --git a/tests/mir-opt/jump_threading.bitwise_not.JumpThreading.panic-unwind.diff b/tests/mir-opt/jump_threading.bitwise_not.JumpThreading.panic-unwind.diff index fb64fff7e11e5..047441e609913 100644 --- a/tests/mir-opt/jump_threading.bitwise_not.JumpThreading.panic-unwind.diff +++ b/tests/mir-opt/jump_threading.bitwise_not.JumpThreading.panic-unwind.diff @@ -22,8 +22,7 @@ _3 = Not(move _4); StorageDead(_4); _2 = Eq(move _3, const 0_i32); -- switchInt(move _2) -> [0: bb2, otherwise: bb1]; -+ goto -> bb1; + switchInt(move _2) -> [0: bb2, otherwise: bb1]; } bb1: { From 0aab10135dd3d4e43e44ba5c375f5fae382cae49 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 3 Oct 2024 16:20:26 +1000 Subject: [PATCH 118/120] Merge `rustc_infer::infer::relate::{glb,lub}`. Most of the code in these two modules is duplicated in the other module. This commit eliminates the duplication by replacing them with a new module `lattice_op`. The new `LatticeOpKind` enum is used to distinguish between glb and lub in the few places where the behaviour differs. --- .../rustc_infer/src/infer/relate/combine.rs | 13 +- compiler/rustc_infer/src/infer/relate/glb.rs | 159 -------------- .../rustc_infer/src/infer/relate/lattice.rs | 197 +++++++++++++++++- compiler/rustc_infer/src/infer/relate/lub.rs | 158 -------------- compiler/rustc_infer/src/infer/relate/mod.rs | 2 - 5 files changed, 196 insertions(+), 333 deletions(-) delete mode 100644 compiler/rustc_infer/src/infer/relate/glb.rs delete mode 100644 compiler/rustc_infer/src/infer/relate/lub.rs diff --git a/compiler/rustc_infer/src/infer/relate/combine.rs b/compiler/rustc_infer/src/infer/relate/combine.rs index e75d7b7db1453..d9e59d67ba32b 100644 --- a/compiler/rustc_infer/src/infer/relate/combine.rs +++ b/compiler/rustc_infer/src/infer/relate/combine.rs @@ -1,4 +1,4 @@ -//! There are four type combiners: [TypeRelating], [Lub], and [Glb], +//! There are four type combiners: [TypeRelating], `Lub`, and `Glb`, //! and `NllTypeRelating` in rustc_borrowck, which is only used for NLL. //! //! Each implements the trait [TypeRelation] and contains methods for @@ -26,8 +26,7 @@ use rustc_middle::ty::{self, InferConst, IntType, Ty, TyCtxt, TypeVisitableExt, pub use rustc_next_trait_solver::relate::combine::*; use tracing::debug; -use super::glb::Glb; -use super::lub::Lub; +use super::lattice::{LatticeOp, LatticeOpKind}; use super::type_relating::TypeRelating; use super::{RelateResult, StructurallyRelateAliases}; use crate::infer::{DefineOpaqueTypes, InferCtxt, TypeTrace, relate}; @@ -298,12 +297,12 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> { TypeRelating::new(self, StructurallyRelateAliases::No, ty::Contravariant) } - pub fn lub<'a>(&'a mut self) -> Lub<'a, 'infcx, 'tcx> { - Lub::new(self) + pub(crate) fn lub<'a>(&'a mut self) -> LatticeOp<'a, 'infcx, 'tcx> { + LatticeOp::new(self, LatticeOpKind::Lub) } - pub fn glb<'a>(&'a mut self) -> Glb<'a, 'infcx, 'tcx> { - Glb::new(self) + pub(crate) fn glb<'a>(&'a mut self) -> LatticeOp<'a, 'infcx, 'tcx> { + LatticeOp::new(self, LatticeOpKind::Glb) } pub fn register_obligations( diff --git a/compiler/rustc_infer/src/infer/relate/glb.rs b/compiler/rustc_infer/src/infer/relate/glb.rs deleted file mode 100644 index ed108f42969db..0000000000000 --- a/compiler/rustc_infer/src/infer/relate/glb.rs +++ /dev/null @@ -1,159 +0,0 @@ -//! Greatest lower bound. See [`lattice`]. - -use rustc_middle::traits::solve::Goal; -use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation}; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; -use rustc_span::Span; -use tracing::{debug, instrument}; - -use super::StructurallyRelateAliases; -use super::combine::{CombineFields, PredicateEmittingRelation}; -use super::lattice::{self, LatticeDir}; -use crate::infer::{DefineOpaqueTypes, InferCtxt, SubregionOrigin}; -use crate::traits::ObligationCause; - -/// "Greatest lower bound" (common subtype) -pub struct Glb<'combine, 'infcx, 'tcx> { - fields: &'combine mut CombineFields<'infcx, 'tcx>, -} - -impl<'combine, 'infcx, 'tcx> Glb<'combine, 'infcx, 'tcx> { - pub fn new(fields: &'combine mut CombineFields<'infcx, 'tcx>) -> Glb<'combine, 'infcx, 'tcx> { - Glb { fields } - } -} - -impl<'tcx> TypeRelation> for Glb<'_, '_, 'tcx> { - fn cx(&self) -> TyCtxt<'tcx> { - self.fields.tcx() - } - - fn relate_with_variance>>( - &mut self, - variance: ty::Variance, - _info: ty::VarianceDiagInfo>, - a: T, - b: T, - ) -> RelateResult<'tcx, T> { - match variance { - ty::Invariant => self.fields.equate(StructurallyRelateAliases::No).relate(a, b), - ty::Covariant => self.relate(a, b), - // FIXME(#41044) -- not correct, need test - ty::Bivariant => Ok(a), - ty::Contravariant => self.fields.lub().relate(a, b), - } - } - - #[instrument(skip(self), level = "trace")] - fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { - lattice::super_lattice_tys(self, a, b) - } - - #[instrument(skip(self), level = "trace")] - fn regions( - &mut self, - a: ty::Region<'tcx>, - b: ty::Region<'tcx>, - ) -> RelateResult<'tcx, ty::Region<'tcx>> { - let origin = SubregionOrigin::Subtype(Box::new(self.fields.trace.clone())); - // GLB(&'static u8, &'a u8) == &RegionLUB('static, 'a) u8 == &'static u8 - Ok(self.fields.infcx.inner.borrow_mut().unwrap_region_constraints().lub_regions( - self.cx(), - origin, - a, - b, - )) - } - - #[instrument(skip(self), level = "trace")] - fn consts( - &mut self, - a: ty::Const<'tcx>, - b: ty::Const<'tcx>, - ) -> RelateResult<'tcx, ty::Const<'tcx>> { - self.fields.infcx.super_combine_consts(self, a, b) - } - - fn binders( - &mut self, - a: ty::Binder<'tcx, T>, - b: ty::Binder<'tcx, T>, - ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> - where - T: Relate>, - { - // GLB of a binder and itself is just itself - if a == b { - return Ok(a); - } - - debug!("binders(a={:?}, b={:?})", a, b); - if a.skip_binder().has_escaping_bound_vars() || b.skip_binder().has_escaping_bound_vars() { - // When higher-ranked types are involved, computing the GLB is - // very challenging, switch to invariance. This is obviously - // overly conservative but works ok in practice. - self.relate_with_variance(ty::Invariant, ty::VarianceDiagInfo::default(), a, b)?; - Ok(a) - } else { - Ok(ty::Binder::dummy(self.relate(a.skip_binder(), b.skip_binder())?)) - } - } -} - -impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for Glb<'combine, 'infcx, 'tcx> { - fn infcx(&self) -> &'infcx InferCtxt<'tcx> { - self.fields.infcx - } - - fn cause(&self) -> &ObligationCause<'tcx> { - &self.fields.trace.cause - } - - fn relate_bound(&mut self, v: Ty<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, ()> { - let mut sub = self.fields.sub(); - sub.relate(v, a)?; - sub.relate(v, b)?; - Ok(()) - } - - fn define_opaque_types(&self) -> DefineOpaqueTypes { - self.fields.define_opaque_types - } -} - -impl<'tcx> PredicateEmittingRelation> for Glb<'_, '_, 'tcx> { - fn span(&self) -> Span { - self.fields.trace.span() - } - - fn structurally_relate_aliases(&self) -> StructurallyRelateAliases { - StructurallyRelateAliases::No - } - - fn param_env(&self) -> ty::ParamEnv<'tcx> { - self.fields.param_env - } - - fn register_predicates( - &mut self, - obligations: impl IntoIterator, ty::Predicate<'tcx>>>, - ) { - self.fields.register_predicates(obligations); - } - - fn register_goals( - &mut self, - obligations: impl IntoIterator>>, - ) { - self.fields.register_obligations(obligations); - } - - fn register_alias_relate_predicate(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) { - self.register_predicates([ty::Binder::dummy(ty::PredicateKind::AliasRelate( - a.into(), - b.into(), - // FIXME(deferred_projection_equality): This isn't right, I think? - ty::AliasRelationDirection::Equate, - ))]); - } -} diff --git a/compiler/rustc_infer/src/infer/relate/lattice.rs b/compiler/rustc_infer/src/infer/relate/lattice.rs index 1d3f45465d63d..1e4db70898686 100644 --- a/compiler/rustc_infer/src/infer/relate/lattice.rs +++ b/compiler/rustc_infer/src/infer/relate/lattice.rs @@ -17,12 +17,15 @@ //! //! [lattices]: https://en.wikipedia.org/wiki/Lattice_(order) -use rustc_middle::ty::relate::RelateResult; -use rustc_middle::ty::{self, Ty, TyVar}; -use tracing::instrument; +use rustc_middle::traits::solve::Goal; +use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation}; +use rustc_middle::ty::{self, Ty, TyCtxt, TyVar, TypeVisitableExt}; +use rustc_span::Span; +use tracing::{debug, instrument}; -use super::combine::PredicateEmittingRelation; -use crate::infer::{DefineOpaqueTypes, InferCtxt}; +use super::StructurallyRelateAliases; +use super::combine::{CombineFields, PredicateEmittingRelation}; +use crate::infer::{DefineOpaqueTypes, InferCtxt, SubregionOrigin}; use crate::traits::ObligationCause; /// Trait for returning data about a lattice, and for abstracting @@ -30,7 +33,7 @@ use crate::traits::ObligationCause; /// /// GLB moves "down" the lattice (to smaller values); LUB moves /// "up" the lattice (to bigger values). -pub(crate) trait LatticeDir<'f, 'tcx>: PredicateEmittingRelation> { +trait LatticeDir<'f, 'tcx>: PredicateEmittingRelation> { fn infcx(&self) -> &'f InferCtxt<'tcx>; fn cause(&self) -> &ObligationCause<'tcx>; @@ -48,7 +51,7 @@ pub(crate) trait LatticeDir<'f, 'tcx>: PredicateEmittingRelation /// Relates two types using a given lattice. #[instrument(skip(this), level = "debug")] -pub fn super_lattice_tys<'a, 'tcx: 'a, L>( +fn super_lattice_tys<'a, 'tcx: 'a, L>( this: &mut L, a: Ty<'tcx>, b: Ty<'tcx>, @@ -113,3 +116,183 @@ where _ => infcx.super_combine_tys(this, a, b), } } + +#[derive(Clone, Copy)] +pub(crate) enum LatticeOpKind { + Glb, + Lub, +} + +impl LatticeOpKind { + fn invert(self) -> Self { + match self { + LatticeOpKind::Glb => LatticeOpKind::Lub, + LatticeOpKind::Lub => LatticeOpKind::Glb, + } + } +} + +/// A greatest lower bound" (common subtype) or least upper bound (common supertype). +pub(crate) struct LatticeOp<'combine, 'infcx, 'tcx> { + fields: &'combine mut CombineFields<'infcx, 'tcx>, + kind: LatticeOpKind, +} + +impl<'combine, 'infcx, 'tcx> LatticeOp<'combine, 'infcx, 'tcx> { + pub(crate) fn new( + fields: &'combine mut CombineFields<'infcx, 'tcx>, + kind: LatticeOpKind, + ) -> LatticeOp<'combine, 'infcx, 'tcx> { + LatticeOp { fields, kind } + } +} + +impl<'tcx> TypeRelation> for LatticeOp<'_, '_, 'tcx> { + fn cx(&self) -> TyCtxt<'tcx> { + self.fields.tcx() + } + + fn relate_with_variance>>( + &mut self, + variance: ty::Variance, + _info: ty::VarianceDiagInfo>, + a: T, + b: T, + ) -> RelateResult<'tcx, T> { + match variance { + ty::Invariant => self.fields.equate(StructurallyRelateAliases::No).relate(a, b), + ty::Covariant => self.relate(a, b), + // FIXME(#41044) -- not correct, need test + ty::Bivariant => Ok(a), + ty::Contravariant => { + self.kind = self.kind.invert(); + let res = self.relate(a, b); + self.kind = self.kind.invert(); + res + } + } + } + + #[instrument(skip(self), level = "trace")] + fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { + super_lattice_tys(self, a, b) + } + + #[instrument(skip(self), level = "trace")] + fn regions( + &mut self, + a: ty::Region<'tcx>, + b: ty::Region<'tcx>, + ) -> RelateResult<'tcx, ty::Region<'tcx>> { + let origin = SubregionOrigin::Subtype(Box::new(self.fields.trace.clone())); + let mut inner = self.fields.infcx.inner.borrow_mut(); + let mut constraints = inner.unwrap_region_constraints(); + Ok(match self.kind { + // GLB(&'static u8, &'a u8) == &RegionLUB('static, 'a) u8 == &'static u8 + LatticeOpKind::Glb => constraints.lub_regions(self.cx(), origin, a, b), + + // LUB(&'static u8, &'a u8) == &RegionGLB('static, 'a) u8 == &'a u8 + LatticeOpKind::Lub => constraints.glb_regions(self.cx(), origin, a, b), + }) + } + + #[instrument(skip(self), level = "trace")] + fn consts( + &mut self, + a: ty::Const<'tcx>, + b: ty::Const<'tcx>, + ) -> RelateResult<'tcx, ty::Const<'tcx>> { + self.fields.infcx.super_combine_consts(self, a, b) + } + + fn binders( + &mut self, + a: ty::Binder<'tcx, T>, + b: ty::Binder<'tcx, T>, + ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> + where + T: Relate>, + { + // GLB/LUB of a binder and itself is just itself + if a == b { + return Ok(a); + } + + debug!("binders(a={:?}, b={:?})", a, b); + if a.skip_binder().has_escaping_bound_vars() || b.skip_binder().has_escaping_bound_vars() { + // When higher-ranked types are involved, computing the GLB/LUB is + // very challenging, switch to invariance. This is obviously + // overly conservative but works ok in practice. + self.relate_with_variance(ty::Invariant, ty::VarianceDiagInfo::default(), a, b)?; + Ok(a) + } else { + Ok(ty::Binder::dummy(self.relate(a.skip_binder(), b.skip_binder())?)) + } + } +} + +impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for LatticeOp<'combine, 'infcx, 'tcx> { + fn infcx(&self) -> &'infcx InferCtxt<'tcx> { + self.fields.infcx + } + + fn cause(&self) -> &ObligationCause<'tcx> { + &self.fields.trace.cause + } + + fn relate_bound(&mut self, v: Ty<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, ()> { + let mut sub = self.fields.sub(); + match self.kind { + LatticeOpKind::Glb => { + sub.relate(v, a)?; + sub.relate(v, b)?; + } + LatticeOpKind::Lub => { + sub.relate(a, v)?; + sub.relate(b, v)?; + } + } + Ok(()) + } + + fn define_opaque_types(&self) -> DefineOpaqueTypes { + self.fields.define_opaque_types + } +} + +impl<'tcx> PredicateEmittingRelation> for LatticeOp<'_, '_, 'tcx> { + fn span(&self) -> Span { + self.fields.trace.span() + } + + fn structurally_relate_aliases(&self) -> StructurallyRelateAliases { + StructurallyRelateAliases::No + } + + fn param_env(&self) -> ty::ParamEnv<'tcx> { + self.fields.param_env + } + + fn register_predicates( + &mut self, + obligations: impl IntoIterator, ty::Predicate<'tcx>>>, + ) { + self.fields.register_predicates(obligations); + } + + fn register_goals( + &mut self, + obligations: impl IntoIterator>>, + ) { + self.fields.register_obligations(obligations); + } + + fn register_alias_relate_predicate(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) { + self.register_predicates([ty::Binder::dummy(ty::PredicateKind::AliasRelate( + a.into(), + b.into(), + // FIXME(deferred_projection_equality): This isn't right, I think? + ty::AliasRelationDirection::Equate, + ))]); + } +} diff --git a/compiler/rustc_infer/src/infer/relate/lub.rs b/compiler/rustc_infer/src/infer/relate/lub.rs deleted file mode 100644 index 35c7ab5000d70..0000000000000 --- a/compiler/rustc_infer/src/infer/relate/lub.rs +++ /dev/null @@ -1,158 +0,0 @@ -//! Least upper bound. See [`lattice`]. - -use rustc_middle::traits::solve::Goal; -use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation}; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; -use rustc_span::Span; -use tracing::{debug, instrument}; - -use super::StructurallyRelateAliases; -use super::combine::{CombineFields, PredicateEmittingRelation}; -use super::lattice::{self, LatticeDir}; -use crate::infer::{DefineOpaqueTypes, InferCtxt, SubregionOrigin}; -use crate::traits::ObligationCause; - -/// "Least upper bound" (common supertype) -pub struct Lub<'combine, 'infcx, 'tcx> { - fields: &'combine mut CombineFields<'infcx, 'tcx>, -} - -impl<'combine, 'infcx, 'tcx> Lub<'combine, 'infcx, 'tcx> { - pub fn new(fields: &'combine mut CombineFields<'infcx, 'tcx>) -> Lub<'combine, 'infcx, 'tcx> { - Lub { fields } - } -} - -impl<'tcx> TypeRelation> for Lub<'_, '_, 'tcx> { - fn cx(&self) -> TyCtxt<'tcx> { - self.fields.tcx() - } - - fn relate_with_variance>>( - &mut self, - variance: ty::Variance, - _info: ty::VarianceDiagInfo>, - a: T, - b: T, - ) -> RelateResult<'tcx, T> { - match variance { - ty::Invariant => self.fields.equate(StructurallyRelateAliases::No).relate(a, b), - ty::Covariant => self.relate(a, b), - // FIXME(#41044) -- not correct, need test - ty::Bivariant => Ok(a), - ty::Contravariant => self.fields.glb().relate(a, b), - } - } - - fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { - lattice::super_lattice_tys(self, a, b) - } - - #[instrument(skip(self), level = "trace")] - fn regions( - &mut self, - a: ty::Region<'tcx>, - b: ty::Region<'tcx>, - ) -> RelateResult<'tcx, ty::Region<'tcx>> { - let origin = SubregionOrigin::Subtype(Box::new(self.fields.trace.clone())); - // LUB(&'static u8, &'a u8) == &RegionGLB('static, 'a) u8 == &'a u8 - Ok(self.fields.infcx.inner.borrow_mut().unwrap_region_constraints().glb_regions( - self.cx(), - origin, - a, - b, - )) - } - - #[instrument(skip(self), level = "trace")] - fn consts( - &mut self, - a: ty::Const<'tcx>, - b: ty::Const<'tcx>, - ) -> RelateResult<'tcx, ty::Const<'tcx>> { - self.fields.infcx.super_combine_consts(self, a, b) - } - - fn binders( - &mut self, - a: ty::Binder<'tcx, T>, - b: ty::Binder<'tcx, T>, - ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> - where - T: Relate>, - { - // LUB of a binder and itself is just itself - if a == b { - return Ok(a); - } - - debug!("binders(a={:?}, b={:?})", a, b); - if a.skip_binder().has_escaping_bound_vars() || b.skip_binder().has_escaping_bound_vars() { - // When higher-ranked types are involved, computing the LUB is - // very challenging, switch to invariance. This is obviously - // overly conservative but works ok in practice. - self.relate_with_variance(ty::Invariant, ty::VarianceDiagInfo::default(), a, b)?; - Ok(a) - } else { - Ok(ty::Binder::dummy(self.relate(a.skip_binder(), b.skip_binder())?)) - } - } -} - -impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for Lub<'combine, 'infcx, 'tcx> { - fn infcx(&self) -> &'infcx InferCtxt<'tcx> { - self.fields.infcx - } - - fn cause(&self) -> &ObligationCause<'tcx> { - &self.fields.trace.cause - } - - fn relate_bound(&mut self, v: Ty<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, ()> { - let mut sub = self.fields.sub(); - sub.relate(a, v)?; - sub.relate(b, v)?; - Ok(()) - } - - fn define_opaque_types(&self) -> DefineOpaqueTypes { - self.fields.define_opaque_types - } -} - -impl<'tcx> PredicateEmittingRelation> for Lub<'_, '_, 'tcx> { - fn span(&self) -> Span { - self.fields.trace.span() - } - - fn structurally_relate_aliases(&self) -> StructurallyRelateAliases { - StructurallyRelateAliases::No - } - - fn param_env(&self) -> ty::ParamEnv<'tcx> { - self.fields.param_env - } - - fn register_predicates( - &mut self, - obligations: impl IntoIterator, ty::Predicate<'tcx>>>, - ) { - self.fields.register_predicates(obligations); - } - - fn register_goals( - &mut self, - obligations: impl IntoIterator>>, - ) { - self.fields.register_obligations(obligations) - } - - fn register_alias_relate_predicate(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) { - self.register_predicates([ty::Binder::dummy(ty::PredicateKind::AliasRelate( - a.into(), - b.into(), - // FIXME(deferred_projection_equality): This isn't right, I think? - ty::AliasRelationDirection::Equate, - ))]); - } -} diff --git a/compiler/rustc_infer/src/infer/relate/mod.rs b/compiler/rustc_infer/src/infer/relate/mod.rs index 183ea5b3309a9..edc0c4f078ab2 100644 --- a/compiler/rustc_infer/src/infer/relate/mod.rs +++ b/compiler/rustc_infer/src/infer/relate/mod.rs @@ -10,8 +10,6 @@ pub use self::combine::{CombineFields, PredicateEmittingRelation}; #[allow(hidden_glob_reexports)] pub(super) mod combine; mod generalize; -mod glb; mod higher_ranked; mod lattice; -mod lub; mod type_relating; From ee227dec8c84fc5a65ef798af93863a97794502e Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 3 Oct 2024 17:41:32 +1000 Subject: [PATCH 119/120] Remove `LatticeDir` trait. It's no longer necessary now that the `glb` and `lub` modules have been merged. --- .../rustc_infer/src/infer/relate/lattice.rs | 173 +++++++----------- 1 file changed, 69 insertions(+), 104 deletions(-) diff --git a/compiler/rustc_infer/src/infer/relate/lattice.rs b/compiler/rustc_infer/src/infer/relate/lattice.rs index 1e4db70898686..9564baa6ab287 100644 --- a/compiler/rustc_infer/src/infer/relate/lattice.rs +++ b/compiler/rustc_infer/src/infer/relate/lattice.rs @@ -26,96 +26,6 @@ use tracing::{debug, instrument}; use super::StructurallyRelateAliases; use super::combine::{CombineFields, PredicateEmittingRelation}; use crate::infer::{DefineOpaqueTypes, InferCtxt, SubregionOrigin}; -use crate::traits::ObligationCause; - -/// Trait for returning data about a lattice, and for abstracting -/// over the "direction" of the lattice operation (LUB/GLB). -/// -/// GLB moves "down" the lattice (to smaller values); LUB moves -/// "up" the lattice (to bigger values). -trait LatticeDir<'f, 'tcx>: PredicateEmittingRelation> { - fn infcx(&self) -> &'f InferCtxt<'tcx>; - - fn cause(&self) -> &ObligationCause<'tcx>; - - fn define_opaque_types(&self) -> DefineOpaqueTypes; - - // Relates the type `v` to `a` and `b` such that `v` represents - // the LUB/GLB of `a` and `b` as appropriate. - // - // Subtle hack: ordering *may* be significant here. This method - // relates `v` to `a` first, which may help us to avoid unnecessary - // type variable obligations. See caller for details. - fn relate_bound(&mut self, v: Ty<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, ()>; -} - -/// Relates two types using a given lattice. -#[instrument(skip(this), level = "debug")] -fn super_lattice_tys<'a, 'tcx: 'a, L>( - this: &mut L, - a: Ty<'tcx>, - b: Ty<'tcx>, -) -> RelateResult<'tcx, Ty<'tcx>> -where - L: LatticeDir<'a, 'tcx>, -{ - if a == b { - return Ok(a); - } - - let infcx = this.infcx(); - - let a = infcx.shallow_resolve(a); - let b = infcx.shallow_resolve(b); - - match (a.kind(), b.kind()) { - // If one side is known to be a variable and one is not, - // create a variable (`v`) to represent the LUB. Make sure to - // relate `v` to the non-type-variable first (by passing it - // first to `relate_bound`). Otherwise, we would produce a - // subtype obligation that must then be processed. - // - // Example: if the LHS is a type variable, and RHS is - // `Box`, then we current compare `v` to the RHS first, - // which will instantiate `v` with `Box`. Then when `v` - // is compared to the LHS, we instantiate LHS with `Box`. - // But if we did in reverse order, we would create a `v <: - // LHS` (or vice versa) constraint and then instantiate - // `v`. This would require further processing to achieve same - // end-result; in particular, this screws up some of the logic - // in coercion, which expects LUB to figure out that the LHS - // is (e.g.) `Box`. A more obvious solution might be to - // iterate on the subtype obligations that are returned, but I - // think this suffices. -nmatsakis - (&ty::Infer(TyVar(..)), _) => { - let v = infcx.next_ty_var(this.cause().span); - this.relate_bound(v, b, a)?; - Ok(v) - } - (_, &ty::Infer(TyVar(..))) => { - let v = infcx.next_ty_var(this.cause().span); - this.relate_bound(v, a, b)?; - Ok(v) - } - - ( - &ty::Alias(ty::Opaque, ty::AliasTy { def_id: a_def_id, .. }), - &ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, .. }), - ) if a_def_id == b_def_id => infcx.super_combine_tys(this, a, b), - - (&ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }), _) - | (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. })) - if this.define_opaque_types() == DefineOpaqueTypes::Yes - && def_id.is_local() - && !this.infcx().next_trait_solver() => - { - this.register_goals(infcx.handle_opaque_type(a, b, this.span(), this.param_env())?); - Ok(a) - } - - _ => infcx.super_combine_tys(this, a, b), - } -} #[derive(Clone, Copy)] pub(crate) enum LatticeOpKind { @@ -173,9 +83,70 @@ impl<'tcx> TypeRelation> for LatticeOp<'_, '_, 'tcx> { } } + /// Relates two types using a given lattice. #[instrument(skip(self), level = "trace")] fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { - super_lattice_tys(self, a, b) + if a == b { + return Ok(a); + } + + let infcx = self.fields.infcx; + + let a = infcx.shallow_resolve(a); + let b = infcx.shallow_resolve(b); + + match (a.kind(), b.kind()) { + // If one side is known to be a variable and one is not, + // create a variable (`v`) to represent the LUB. Make sure to + // relate `v` to the non-type-variable first (by passing it + // first to `relate_bound`). Otherwise, we would produce a + // subtype obligation that must then be processed. + // + // Example: if the LHS is a type variable, and RHS is + // `Box`, then we current compare `v` to the RHS first, + // which will instantiate `v` with `Box`. Then when `v` + // is compared to the LHS, we instantiate LHS with `Box`. + // But if we did in reverse order, we would create a `v <: + // LHS` (or vice versa) constraint and then instantiate + // `v`. This would require further processing to achieve same + // end-result; in particular, this screws up some of the logic + // in coercion, which expects LUB to figure out that the LHS + // is (e.g.) `Box`. A more obvious solution might be to + // iterate on the subtype obligations that are returned, but I + // think this suffices. -nmatsakis + (&ty::Infer(TyVar(..)), _) => { + let v = infcx.next_ty_var(self.fields.trace.cause.span); + self.relate_bound(v, b, a)?; + Ok(v) + } + (_, &ty::Infer(TyVar(..))) => { + let v = infcx.next_ty_var(self.fields.trace.cause.span); + self.relate_bound(v, a, b)?; + Ok(v) + } + + ( + &ty::Alias(ty::Opaque, ty::AliasTy { def_id: a_def_id, .. }), + &ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, .. }), + ) if a_def_id == b_def_id => infcx.super_combine_tys(self, a, b), + + (&ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }), _) + | (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. })) + if self.fields.define_opaque_types == DefineOpaqueTypes::Yes + && def_id.is_local() + && !infcx.next_trait_solver() => + { + self.register_goals(infcx.handle_opaque_type( + a, + b, + self.span(), + self.param_env(), + )?); + Ok(a) + } + + _ => infcx.super_combine_tys(self, a, b), + } } #[instrument(skip(self), level = "trace")] @@ -231,15 +202,13 @@ impl<'tcx> TypeRelation> for LatticeOp<'_, '_, 'tcx> { } } -impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for LatticeOp<'combine, 'infcx, 'tcx> { - fn infcx(&self) -> &'infcx InferCtxt<'tcx> { - self.fields.infcx - } - - fn cause(&self) -> &ObligationCause<'tcx> { - &self.fields.trace.cause - } - +impl<'combine, 'infcx, 'tcx> LatticeOp<'combine, 'infcx, 'tcx> { + // Relates the type `v` to `a` and `b` such that `v` represents + // the LUB/GLB of `a` and `b` as appropriate. + // + // Subtle hack: ordering *may* be significant here. This method + // relates `v` to `a` first, which may help us to avoid unnecessary + // type variable obligations. See caller for details. fn relate_bound(&mut self, v: Ty<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, ()> { let mut sub = self.fields.sub(); match self.kind { @@ -254,10 +223,6 @@ impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for LatticeOp<'combine, 'i } Ok(()) } - - fn define_opaque_types(&self) -> DefineOpaqueTypes { - self.fields.define_opaque_types - } } impl<'tcx> PredicateEmittingRelation> for LatticeOp<'_, '_, 'tcx> { From eefe3200cd26a1810a50c7016d07d28862e0744e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20D=C3=B6nszelmann?= Date: Fri, 4 Oct 2024 09:38:14 +0200 Subject: [PATCH 120/120] remove blank issue template --- .github/ISSUE_TEMPLATE/blank_issue.md | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/blank_issue.md diff --git a/.github/ISSUE_TEMPLATE/blank_issue.md b/.github/ISSUE_TEMPLATE/blank_issue.md deleted file mode 100644 index 9aef3ebe637a1..0000000000000 --- a/.github/ISSUE_TEMPLATE/blank_issue.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -name: Blank Issue -about: Create a blank issue. ----