From dbb22d247f201d9e2a3f07fa75f90db39e966ff7 Mon Sep 17 00:00:00 2001 From: Jane Lusby Date: Thu, 10 Mar 2022 16:59:08 -0800 Subject: [PATCH 0001/1135] update library tracking issue template --- .github/ISSUE_TEMPLATE/library_tracking_issue.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/library_tracking_issue.md b/.github/ISSUE_TEMPLATE/library_tracking_issue.md index 32fccfcfb1696..91c06402ca10a 100644 --- a/.github/ISSUE_TEMPLATE/library_tracking_issue.md +++ b/.github/ISSUE_TEMPLATE/library_tracking_issue.md @@ -50,7 +50,7 @@ If the feature is changed later, please add those PRs here as well. --> - [ ] Implementation: #... -- [ ] Final comment period (FCP) +- [ ] Final comment period (FCP)[^1] - [ ] Stabilization PR - None yet. + +[^1]: https://std-dev-guide.rust-lang.org/feature-lifecycle/stabilization.html From 3cd49a0fa8599bbf1705fede777ec2e82b7e3239 Mon Sep 17 00:00:00 2001 From: CAD97 Date: Thu, 24 Mar 2022 23:49:46 -0500 Subject: [PATCH 0002/1135] Enforce that layout size fits in isize in Layout --- library/core/src/alloc/layout.rs | 53 ++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 19 deletions(-) diff --git a/library/core/src/alloc/layout.rs b/library/core/src/alloc/layout.rs index ea639268652c3..749959c34ed5e 100644 --- a/library/core/src/alloc/layout.rs +++ b/library/core/src/alloc/layout.rs @@ -1,4 +1,5 @@ use crate::cmp; +use crate::convert::TryFrom; use crate::fmt; use crate::mem; use crate::num::NonZeroUsize; @@ -53,8 +54,8 @@ impl Layout { /// * `align` must be a power of two, /// /// * `size`, when rounded up to the nearest multiple of `align`, - /// must not overflow (i.e., the rounded value must be less than - /// or equal to `usize::MAX`). + /// must not overflow isize (i.e., the rounded value must be + /// less than or equal to `isize::MAX`). #[stable(feature = "alloc_layout", since = "1.28.0")] #[rustc_const_stable(feature = "const_alloc_layout", since = "1.50.0")] #[inline] @@ -77,7 +78,7 @@ impl Layout { // // Above implies that checking for summation overflow is both // necessary and sufficient. - if size > usize::MAX - (align - 1) { + if size > isize::MAX as usize - (align - 1) { return Err(LayoutError); } @@ -277,8 +278,8 @@ impl Layout { let pad = self.padding_needed_for(self.align()); // This cannot overflow. Quoting from the invariant of Layout: // > `size`, when rounded up to the nearest multiple of `align`, - // > must not overflow (i.e., the rounded value must be less than - // > `usize::MAX`) + // > must not overflow isize (i.e., the rounded value must be + // > less than or equal to `isize::MAX`) let new_size = self.size() + pad; // SAFETY: self.align is already known to be valid and new_size has been @@ -299,14 +300,21 @@ impl Layout { pub fn repeat(&self, n: usize) -> Result<(Self, usize), LayoutError> { // This cannot overflow. Quoting from the invariant of Layout: // > `size`, when rounded up to the nearest multiple of `align`, - // > must not overflow (i.e., the rounded value must be less than - // > `usize::MAX`) + // > must not overflow isize (i.e., the rounded value must be + // > less than or equal to `isize::MAX`) let padded_size = self.size() + self.padding_needed_for(self.align()); - let alloc_size = padded_size.checked_mul(n).ok_or(LayoutError)?; + // Size manipulation is done in isize space to avoid overflowing isize. + let n = isize::try_from(n).map_err(|_| LayoutError)?; + let alloc_size = (padded_size as isize).checked_mul(n).ok_or(LayoutError)?; // SAFETY: self.align is already known to be valid and alloc_size has been // padded already. - unsafe { Ok((Layout::from_size_align_unchecked(alloc_size, self.align()), padded_size)) } + unsafe { + Ok(( + Layout::from_size_align_unchecked(alloc_size as usize, self.align()), + padded_size as usize, + )) + } } /// Creates a layout describing the record for `self` followed by @@ -360,11 +368,12 @@ impl Layout { let new_align = cmp::max(self.align(), next.align()); let pad = self.padding_needed_for(next.align()); - let offset = self.size().checked_add(pad).ok_or(LayoutError)?; - let new_size = offset.checked_add(next.size()).ok_or(LayoutError)?; + // Size manipulation is done in isize space to avoid overflowing isize. + let offset = (self.size() as isize).checked_add(pad as isize).ok_or(LayoutError)?; + let new_size = offset.checked_add(next.size() as isize).ok_or(LayoutError)?; - let layout = Layout::from_size_align(new_size, new_align)?; - Ok((layout, offset)) + let layout = Layout::from_size_align(new_size as usize, new_align)?; + Ok((layout, offset as usize)) } /// Creates a layout describing the record for `n` instances of @@ -382,8 +391,10 @@ impl Layout { #[unstable(feature = "alloc_layout_extra", issue = "55724")] #[inline] pub fn repeat_packed(&self, n: usize) -> Result { - let size = self.size().checked_mul(n).ok_or(LayoutError)?; - Layout::from_size_align(size, self.align()) + // Size manipulation is done in isize space to avoid overflowing isize. + let n = isize::try_from(n).map_err(|_| LayoutError)?; + let size = (self.size() as isize).checked_mul(n).ok_or(LayoutError)?; + Layout::from_size_align(size as usize, self.align()) } /// Creates a layout describing the record for `self` followed by @@ -395,8 +406,10 @@ impl Layout { #[unstable(feature = "alloc_layout_extra", issue = "55724")] #[inline] pub fn extend_packed(&self, next: Self) -> Result { - let new_size = self.size().checked_add(next.size()).ok_or(LayoutError)?; - Layout::from_size_align(new_size, self.align()) + // Size manipulation is done in isize space to avoid overflowing isize. + let new_size = + (self.size() as isize).checked_add(next.size() as isize).ok_or(LayoutError)?; + Layout::from_size_align(new_size as usize, self.align()) } /// Creates a layout describing the record for a `[T; n]`. @@ -405,7 +418,9 @@ impl Layout { #[stable(feature = "alloc_layout_manipulation", since = "1.44.0")] #[inline] pub fn array(n: usize) -> Result { - let array_size = mem::size_of::().checked_mul(n).ok_or(LayoutError)?; + // Size manipulation is done in isize space to avoid overflowing isize. + let n = isize::try_from(n).map_err(|_| LayoutError)?; + let array_size = (mem::size_of::() as isize).checked_mul(n).ok_or(LayoutError)?; // SAFETY: // - Size: `array_size` cannot be too big because `size_of::()` must @@ -415,7 +430,7 @@ impl Layout { // just checked by the `checked_mul()`. // - Alignment: `align_of::()` will always give an acceptable // (non-zero, power of two) alignment. - Ok(unsafe { Layout::from_size_align_unchecked(array_size, mem::align_of::()) }) + Ok(unsafe { Layout::from_size_align_unchecked(array_size as usize, mem::align_of::()) }) } } From 7b58193f90185a5730732a727362576a9bdca26b Mon Sep 17 00:00:00 2001 From: CAD97 Date: Fri, 25 Mar 2022 00:57:05 -0500 Subject: [PATCH 0003/1135] Adjust tests for isize::MAX allocation always being checked --- library/alloc/tests/string.rs | 126 +++++++---------------- library/alloc/tests/vec.rs | 166 ++++++++++--------------------- library/alloc/tests/vec_deque.rs | 161 ++++++++++-------------------- 3 files changed, 142 insertions(+), 311 deletions(-) diff --git a/library/alloc/tests/string.rs b/library/alloc/tests/string.rs index b6836fdc88ee8..99d1296a4c925 100644 --- a/library/alloc/tests/string.rs +++ b/library/alloc/tests/string.rs @@ -693,12 +693,6 @@ fn test_try_reserve() { const MAX_CAP: usize = isize::MAX as usize; const MAX_USIZE: usize = usize::MAX; - // On 16/32-bit, we check that allocations don't exceed isize::MAX, - // on 64-bit, we assume the OS will give an OOM for such a ridiculous size. - // Any platform that succeeds for these requests is technically broken with - // ptr::offset because LLVM is the worst. - let guards_against_isize = usize::BITS < 64; - { // Note: basic stuff is checked by test_reserve let mut empty_string: String = String::new(); @@ -712,35 +706,19 @@ fn test_try_reserve() { panic!("isize::MAX shouldn't trigger an overflow!"); } - if guards_against_isize { - // Check isize::MAX + 1 does count as overflow - assert_matches!( - empty_string.try_reserve(MAX_CAP + 1).map_err(|e| e.kind()), - Err(CapacityOverflow), - "isize::MAX + 1 should trigger an overflow!" - ); - - // Check usize::MAX does count as overflow - assert_matches!( - empty_string.try_reserve(MAX_USIZE).map_err(|e| e.kind()), - Err(CapacityOverflow), - "usize::MAX should trigger an overflow!" - ); - } else { - // Check isize::MAX + 1 is an OOM - assert_matches!( - empty_string.try_reserve(MAX_CAP + 1).map_err(|e| e.kind()), - Err(AllocError { .. }), - "isize::MAX + 1 should trigger an OOM!" - ); - - // Check usize::MAX is an OOM - assert_matches!( - empty_string.try_reserve(MAX_USIZE).map_err(|e| e.kind()), - Err(AllocError { .. }), - "usize::MAX should trigger an OOM!" - ); - } + // Check isize::MAX + 1 does count as overflow + assert_matches!( + empty_string.try_reserve(MAX_CAP + 1).map_err(|e| e.kind()), + Err(CapacityOverflow), + "isize::MAX + 1 should trigger an overflow!" + ); + + // Check usize::MAX does count as overflow + assert_matches!( + empty_string.try_reserve(MAX_USIZE).map_err(|e| e.kind()), + Err(CapacityOverflow), + "usize::MAX should trigger an overflow!" + ); } { @@ -753,19 +731,13 @@ fn test_try_reserve() { if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_CAP - 10).map_err(|e| e.kind()) { panic!("isize::MAX shouldn't trigger an overflow!"); } - if guards_against_isize { - assert_matches!( - ten_bytes.try_reserve(MAX_CAP - 9).map_err(|e| e.kind()), - Err(CapacityOverflow), - "isize::MAX + 1 should trigger an overflow!" - ); - } else { - assert_matches!( - ten_bytes.try_reserve(MAX_CAP - 9).map_err(|e| e.kind()), - Err(AllocError { .. }), - "isize::MAX + 1 should trigger an OOM!" - ); - } + + assert_matches!( + ten_bytes.try_reserve(MAX_CAP - 9).map_err(|e| e.kind()), + Err(CapacityOverflow), + "isize::MAX + 1 should trigger an overflow!" + ); + // Should always overflow in the add-to-len assert_matches!( ten_bytes.try_reserve(MAX_USIZE).map_err(|e| e.kind()), @@ -785,8 +757,6 @@ fn test_try_reserve_exact() { const MAX_CAP: usize = isize::MAX as usize; const MAX_USIZE: usize = usize::MAX; - let guards_against_isize = usize::BITS < 64; - { let mut empty_string: String = String::new(); @@ -799,31 +769,17 @@ fn test_try_reserve_exact() { panic!("isize::MAX shouldn't trigger an overflow!"); } - if guards_against_isize { - assert_matches!( - empty_string.try_reserve_exact(MAX_CAP + 1).map_err(|e| e.kind()), - Err(CapacityOverflow), - "isize::MAX + 1 should trigger an overflow!" - ); - - assert_matches!( - empty_string.try_reserve_exact(MAX_USIZE).map_err(|e| e.kind()), - Err(CapacityOverflow), - "usize::MAX should trigger an overflow!" - ); - } else { - assert_matches!( - empty_string.try_reserve_exact(MAX_CAP + 1).map_err(|e| e.kind()), - Err(AllocError { .. }), - "isize::MAX + 1 should trigger an OOM!" - ); - - assert_matches!( - empty_string.try_reserve_exact(MAX_USIZE).map_err(|e| e.kind()), - Err(AllocError { .. }), - "usize::MAX should trigger an OOM!" - ); - } + assert_matches!( + empty_string.try_reserve_exact(MAX_CAP + 1).map_err(|e| e.kind()), + Err(CapacityOverflow), + "isize::MAX + 1 should trigger an overflow!" + ); + + assert_matches!( + empty_string.try_reserve_exact(MAX_USIZE).map_err(|e| e.kind()), + Err(CapacityOverflow), + "usize::MAX should trigger an overflow!" + ); } { @@ -839,19 +795,13 @@ fn test_try_reserve_exact() { { panic!("isize::MAX shouldn't trigger an overflow!"); } - if guards_against_isize { - assert_matches!( - ten_bytes.try_reserve_exact(MAX_CAP - 9).map_err(|e| e.kind()), - Err(CapacityOverflow), - "isize::MAX + 1 should trigger an overflow!" - ); - } else { - assert_matches!( - ten_bytes.try_reserve_exact(MAX_CAP - 9).map_err(|e| e.kind()), - Err(AllocError { .. }), - "isize::MAX + 1 should trigger an OOM!" - ); - } + + assert_matches!( + ten_bytes.try_reserve_exact(MAX_CAP - 9).map_err(|e| e.kind()), + Err(CapacityOverflow), + "isize::MAX + 1 should trigger an overflow!" + ); + assert_matches!( ten_bytes.try_reserve_exact(MAX_USIZE).map_err(|e| e.kind()), Err(CapacityOverflow), diff --git a/library/alloc/tests/vec.rs b/library/alloc/tests/vec.rs index ca0fcc855c7b8..2f93250bce24a 100644 --- a/library/alloc/tests/vec.rs +++ b/library/alloc/tests/vec.rs @@ -1489,12 +1489,6 @@ fn test_try_reserve() { const MAX_CAP: usize = isize::MAX as usize; const MAX_USIZE: usize = usize::MAX; - // On 16/32-bit, we check that allocations don't exceed isize::MAX, - // on 64-bit, we assume the OS will give an OOM for such a ridiculous size. - // Any platform that succeeds for these requests is technically broken with - // ptr::offset because LLVM is the worst. - let guards_against_isize = usize::BITS < 64; - { // Note: basic stuff is checked by test_reserve let mut empty_bytes: Vec = Vec::new(); @@ -1508,35 +1502,19 @@ fn test_try_reserve() { panic!("isize::MAX shouldn't trigger an overflow!"); } - if guards_against_isize { - // Check isize::MAX + 1 does count as overflow - assert_matches!( - empty_bytes.try_reserve(MAX_CAP + 1).map_err(|e| e.kind()), - Err(CapacityOverflow), - "isize::MAX + 1 should trigger an overflow!" - ); - - // Check usize::MAX does count as overflow - assert_matches!( - empty_bytes.try_reserve(MAX_USIZE).map_err(|e| e.kind()), - Err(CapacityOverflow), - "usize::MAX should trigger an overflow!" - ); - } else { - // Check isize::MAX + 1 is an OOM - assert_matches!( - empty_bytes.try_reserve(MAX_CAP + 1).map_err(|e| e.kind()), - Err(AllocError { .. }), - "isize::MAX + 1 should trigger an OOM!" - ); - - // Check usize::MAX is an OOM - assert_matches!( - empty_bytes.try_reserve(MAX_USIZE).map_err(|e| e.kind()), - Err(AllocError { .. }), - "usize::MAX should trigger an OOM!" - ); - } + // Check isize::MAX + 1 does count as overflow + assert_matches!( + empty_bytes.try_reserve(MAX_CAP + 1).map_err(|e| e.kind()), + Err(CapacityOverflow), + "isize::MAX + 1 should trigger an overflow!" + ); + + // Check usize::MAX does count as overflow + assert_matches!( + empty_bytes.try_reserve(MAX_USIZE).map_err(|e| e.kind()), + Err(CapacityOverflow), + "usize::MAX should trigger an overflow!" + ); } { @@ -1549,19 +1527,13 @@ fn test_try_reserve() { if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_CAP - 10).map_err(|e| e.kind()) { panic!("isize::MAX shouldn't trigger an overflow!"); } - if guards_against_isize { - assert_matches!( - ten_bytes.try_reserve(MAX_CAP - 9).map_err(|e| e.kind()), - Err(CapacityOverflow), - "isize::MAX + 1 should trigger an overflow!" - ); - } else { - assert_matches!( - ten_bytes.try_reserve(MAX_CAP - 9).map_err(|e| e.kind()), - Err(AllocError { .. }), - "isize::MAX + 1 should trigger an OOM!" - ); - } + + assert_matches!( + ten_bytes.try_reserve(MAX_CAP - 9).map_err(|e| e.kind()), + Err(CapacityOverflow), + "isize::MAX + 1 should trigger an overflow!" + ); + // Should always overflow in the add-to-len assert_matches!( ten_bytes.try_reserve(MAX_USIZE).map_err(|e| e.kind()), @@ -1582,19 +1554,13 @@ fn test_try_reserve() { { panic!("isize::MAX shouldn't trigger an overflow!"); } - if guards_against_isize { - assert_matches!( - ten_u32s.try_reserve(MAX_CAP / 4 - 9).map_err(|e| e.kind()), - Err(CapacityOverflow), - "isize::MAX + 1 should trigger an overflow!" - ); - } else { - assert_matches!( - ten_u32s.try_reserve(MAX_CAP / 4 - 9).map_err(|e| e.kind()), - Err(AllocError { .. }), - "isize::MAX + 1 should trigger an OOM!" - ); - } + + assert_matches!( + ten_u32s.try_reserve(MAX_CAP / 4 - 9).map_err(|e| e.kind()), + Err(CapacityOverflow), + "isize::MAX + 1 should trigger an overflow!" + ); + // Should fail in the mul-by-size assert_matches!( ten_u32s.try_reserve(MAX_USIZE - 20).map_err(|e| e.kind()), @@ -1614,8 +1580,6 @@ fn test_try_reserve_exact() { const MAX_CAP: usize = isize::MAX as usize; const MAX_USIZE: usize = usize::MAX; - let guards_against_isize = size_of::() < 8; - { let mut empty_bytes: Vec = Vec::new(); @@ -1628,31 +1592,17 @@ fn test_try_reserve_exact() { panic!("isize::MAX shouldn't trigger an overflow!"); } - if guards_against_isize { - assert_matches!( - empty_bytes.try_reserve_exact(MAX_CAP + 1).map_err(|e| e.kind()), - Err(CapacityOverflow), - "isize::MAX + 1 should trigger an overflow!" - ); - - assert_matches!( - empty_bytes.try_reserve_exact(MAX_USIZE).map_err(|e| e.kind()), - Err(CapacityOverflow), - "usize::MAX should trigger an overflow!" - ); - } else { - assert_matches!( - empty_bytes.try_reserve_exact(MAX_CAP + 1).map_err(|e| e.kind()), - Err(AllocError { .. }), - "isize::MAX + 1 should trigger an OOM!" - ); - - assert_matches!( - empty_bytes.try_reserve_exact(MAX_USIZE).map_err(|e| e.kind()), - Err(AllocError { .. }), - "usize::MAX should trigger an OOM!" - ); - } + assert_matches!( + empty_bytes.try_reserve_exact(MAX_CAP + 1).map_err(|e| e.kind()), + Err(CapacityOverflow), + "isize::MAX + 1 should trigger an overflow!" + ); + + assert_matches!( + empty_bytes.try_reserve_exact(MAX_USIZE).map_err(|e| e.kind()), + Err(CapacityOverflow), + "usize::MAX should trigger an overflow!" + ); } { @@ -1668,19 +1618,13 @@ fn test_try_reserve_exact() { { panic!("isize::MAX shouldn't trigger an overflow!"); } - if guards_against_isize { - assert_matches!( - ten_bytes.try_reserve_exact(MAX_CAP - 9).map_err(|e| e.kind()), - Err(CapacityOverflow), - "isize::MAX + 1 should trigger an overflow!" - ); - } else { - assert_matches!( - ten_bytes.try_reserve_exact(MAX_CAP - 9).map_err(|e| e.kind()), - Err(AllocError { .. }), - "isize::MAX + 1 should trigger an OOM!" - ); - } + + assert_matches!( + ten_bytes.try_reserve_exact(MAX_CAP - 9).map_err(|e| e.kind()), + Err(CapacityOverflow), + "isize::MAX + 1 should trigger an overflow!" + ); + assert_matches!( ten_bytes.try_reserve_exact(MAX_USIZE).map_err(|e| e.kind()), Err(CapacityOverflow), @@ -1701,19 +1645,13 @@ fn test_try_reserve_exact() { { panic!("isize::MAX shouldn't trigger an overflow!"); } - if guards_against_isize { - assert_matches!( - ten_u32s.try_reserve_exact(MAX_CAP / 4 - 9).map_err(|e| e.kind()), - Err(CapacityOverflow), - "isize::MAX + 1 should trigger an overflow!" - ); - } else { - assert_matches!( - ten_u32s.try_reserve_exact(MAX_CAP / 4 - 9).map_err(|e| e.kind()), - Err(AllocError { .. }), - "isize::MAX + 1 should trigger an OOM!" - ); - } + + assert_matches!( + ten_u32s.try_reserve_exact(MAX_CAP / 4 - 9).map_err(|e| e.kind()), + Err(CapacityOverflow), + "isize::MAX + 1 should trigger an overflow!" + ); + assert_matches!( ten_u32s.try_reserve_exact(MAX_USIZE - 20).map_err(|e| e.kind()), Err(CapacityOverflow), diff --git a/library/alloc/tests/vec_deque.rs b/library/alloc/tests/vec_deque.rs index 89cc7f905be2c..019d73c0b16bb 100644 --- a/library/alloc/tests/vec_deque.rs +++ b/library/alloc/tests/vec_deque.rs @@ -2,7 +2,6 @@ use std::assert_matches::assert_matches; use std::collections::TryReserveErrorKind::*; use std::collections::{vec_deque::Drain, VecDeque}; use std::fmt::Debug; -use std::mem::size_of; use std::ops::Bound::*; use std::panic::{catch_unwind, AssertUnwindSafe}; @@ -1161,12 +1160,6 @@ fn test_try_reserve() { const MAX_CAP: usize = (isize::MAX as usize + 1) / 2 - 1; const MAX_USIZE: usize = usize::MAX; - // On 16/32-bit, we check that allocations don't exceed isize::MAX, - // on 64-bit, we assume the OS will give an OOM for such a ridiculous size. - // Any platform that succeeds for these requests is technically broken with - // ptr::offset because LLVM is the worst. - let guards_against_isize = size_of::() < 8; - { // Note: basic stuff is checked by test_reserve let mut empty_bytes: VecDeque = VecDeque::new(); @@ -1180,31 +1173,19 @@ fn test_try_reserve() { panic!("isize::MAX shouldn't trigger an overflow!"); } - if guards_against_isize { - // Check isize::MAX + 1 does count as overflow - assert_matches!( - empty_bytes.try_reserve(MAX_CAP + 1).map_err(|e| e.kind()), - Err(CapacityOverflow), - "isize::MAX + 1 should trigger an overflow!" - ); - - // Check usize::MAX does count as overflow - assert_matches!( - empty_bytes.try_reserve(MAX_USIZE).map_err(|e| e.kind()), - Err(CapacityOverflow), - "usize::MAX should trigger an overflow!" - ); - } else { - // Check isize::MAX is an OOM - // VecDeque starts with capacity 7, always adds 1 to the capacity - // and also rounds the number to next power of 2 so this is the - // furthest we can go without triggering CapacityOverflow - assert_matches!( - empty_bytes.try_reserve(MAX_CAP).map_err(|e| e.kind()), - Err(AllocError { .. }), - "isize::MAX + 1 should trigger an OOM!" - ); - } + // Check isize::MAX + 1 does count as overflow + assert_matches!( + empty_bytes.try_reserve(MAX_CAP + 1).map_err(|e| e.kind()), + Err(CapacityOverflow), + "isize::MAX + 1 should trigger an overflow!" + ); + + // Check usize::MAX does count as overflow + assert_matches!( + empty_bytes.try_reserve(MAX_USIZE).map_err(|e| e.kind()), + Err(CapacityOverflow), + "usize::MAX should trigger an overflow!" + ); } { @@ -1217,19 +1198,13 @@ fn test_try_reserve() { if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_CAP - 10).map_err(|e| e.kind()) { panic!("isize::MAX shouldn't trigger an overflow!"); } - if guards_against_isize { - assert_matches!( - ten_bytes.try_reserve(MAX_CAP - 9).map_err(|e| e.kind()), - Err(CapacityOverflow), - "isize::MAX + 1 should trigger an overflow!" - ); - } else { - assert_matches!( - ten_bytes.try_reserve(MAX_CAP - 9).map_err(|e| e.kind()), - Err(AllocError { .. }), - "isize::MAX + 1 should trigger an OOM!" - ); - } + + assert_matches!( + ten_bytes.try_reserve(MAX_CAP - 9).map_err(|e| e.kind()), + Err(CapacityOverflow), + "isize::MAX + 1 should trigger an overflow!" + ); + // Should always overflow in the add-to-len assert_matches!( ten_bytes.try_reserve(MAX_USIZE).map_err(|e| e.kind()), @@ -1250,19 +1225,13 @@ fn test_try_reserve() { { panic!("isize::MAX shouldn't trigger an overflow!"); } - if guards_against_isize { - assert_matches!( - ten_u32s.try_reserve(MAX_CAP / 4 - 9).map_err(|e| e.kind()), - Err(CapacityOverflow), - "isize::MAX + 1 should trigger an overflow!" - ); - } else { - assert_matches!( - ten_u32s.try_reserve(MAX_CAP / 4 - 9).map_err(|e| e.kind()), - Err(AllocError { .. }), - "isize::MAX + 1 should trigger an OOM!" - ); - } + + assert_matches!( + ten_u32s.try_reserve(MAX_CAP / 4 - 9).map_err(|e| e.kind()), + Err(CapacityOverflow), + "isize::MAX + 1 should trigger an overflow!" + ); + // Should fail in the mul-by-size assert_matches!( ten_u32s.try_reserve(MAX_USIZE - 20).map_err(|e| e.kind()), @@ -1282,8 +1251,6 @@ fn test_try_reserve_exact() { const MAX_CAP: usize = (isize::MAX as usize + 1) / 2 - 1; const MAX_USIZE: usize = usize::MAX; - let guards_against_isize = size_of::() < 8; - { let mut empty_bytes: VecDeque = VecDeque::new(); @@ -1296,29 +1263,17 @@ fn test_try_reserve_exact() { panic!("isize::MAX shouldn't trigger an overflow!"); } - if guards_against_isize { - assert_matches!( - empty_bytes.try_reserve_exact(MAX_CAP + 1).map_err(|e| e.kind()), - Err(CapacityOverflow), - "isize::MAX + 1 should trigger an overflow!" - ); - - assert_matches!( - empty_bytes.try_reserve_exact(MAX_USIZE).map_err(|e| e.kind()), - Err(CapacityOverflow), - "usize::MAX should trigger an overflow!" - ); - } else { - // Check isize::MAX is an OOM - // VecDeque starts with capacity 7, always adds 1 to the capacity - // and also rounds the number to next power of 2 so this is the - // furthest we can go without triggering CapacityOverflow - assert_matches!( - empty_bytes.try_reserve_exact(MAX_CAP).map_err(|e| e.kind()), - Err(AllocError { .. }), - "isize::MAX + 1 should trigger an OOM!" - ); - } + assert_matches!( + empty_bytes.try_reserve_exact(MAX_CAP + 1).map_err(|e| e.kind()), + Err(CapacityOverflow), + "isize::MAX + 1 should trigger an overflow!" + ); + + assert_matches!( + empty_bytes.try_reserve_exact(MAX_USIZE).map_err(|e| e.kind()), + Err(CapacityOverflow), + "usize::MAX should trigger an overflow!" + ); } { @@ -1334,19 +1289,13 @@ fn test_try_reserve_exact() { { panic!("isize::MAX shouldn't trigger an overflow!"); } - if guards_against_isize { - assert_matches!( - ten_bytes.try_reserve_exact(MAX_CAP - 9).map_err(|e| e.kind()), - Err(CapacityOverflow), - "isize::MAX + 1 should trigger an overflow!" - ); - } else { - assert_matches!( - ten_bytes.try_reserve_exact(MAX_CAP - 9).map_err(|e| e.kind()), - Err(AllocError { .. }), - "isize::MAX + 1 should trigger an OOM!" - ); - } + + assert_matches!( + ten_bytes.try_reserve_exact(MAX_CAP - 9).map_err(|e| e.kind()), + Err(CapacityOverflow), + "isize::MAX + 1 should trigger an overflow!" + ); + assert_matches!( ten_bytes.try_reserve_exact(MAX_USIZE).map_err(|e| e.kind()), Err(CapacityOverflow), @@ -1367,19 +1316,13 @@ fn test_try_reserve_exact() { { panic!("isize::MAX shouldn't trigger an overflow!"); } - if guards_against_isize { - assert_matches!( - ten_u32s.try_reserve_exact(MAX_CAP / 4 - 9).map_err(|e| e.kind()), - Err(CapacityOverflow), - "isize::MAX + 1 should trigger an overflow!" - ); - } else { - assert_matches!( - ten_u32s.try_reserve_exact(MAX_CAP / 4 - 9).map_err(|e| e.kind()), - Err(AllocError { .. }), - "isize::MAX + 1 should trigger an OOM!" - ); - } + + assert_matches!( + ten_u32s.try_reserve_exact(MAX_CAP / 4 - 9).map_err(|e| e.kind()), + Err(CapacityOverflow), + "isize::MAX + 1 should trigger an overflow!" + ); + assert_matches!( ten_u32s.try_reserve_exact(MAX_USIZE - 20).map_err(|e| e.kind()), Err(CapacityOverflow), From 98190b716829fd4edbf1b364a1a662de9a9ca7bb Mon Sep 17 00:00:00 2001 From: "Cliff L. Biffle" Date: Mon, 4 Apr 2022 11:03:03 -0700 Subject: [PATCH 0004/1135] Revert "Work around invalid DWARF bugs for fat LTO" Since September, the toolchain has not been generating reliable DWARF information for static variables when LTO is on. This has affected projects in the embedded space where the use of LTO is typical. In our case, it has kept us from bumping past the 2021-09-22 nightly toolchain lest our debugger break. This has been a pretty dramatic regression for people using debuggers and static variables. See #90357 for more info and a repro case. This commit is a mechanical revert of d5de680e20def848751cb3c11e1182408112b1d3 from PR #89041, which caused the issue. (Note on that PR that the commit's author has requested it be reverted.) I have locally verified that this fixes #90357 by restoring the functionality of both the repro case I posted on that bug, and debugger behavior on real programs. There do not appear to be test cases for this in the toolchain; if I've missed them, point me at 'em and I'll update them. --- compiler/rustc_codegen_llvm/src/back/lto.rs | 18 ++---------------- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 8 ++++++-- .../rustc_llvm/llvm-wrapper/PassWrapper.cpp | 4 ++-- 3 files changed, 10 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 0f5b1c08ec2dc..266ed6d8b6f30 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -325,20 +325,6 @@ fn fat_lto( drop(linker); save_temp_bitcode(cgcx, &module, "lto.input"); - // Fat LTO also suffers from the invalid DWARF issue similar to Thin LTO. - // Here we rewrite all `DICompileUnit` pointers if there is only one `DICompileUnit`. - // This only works around the problem when codegen-units = 1. - // Refer to the comments in the `optimize_thin_module` function for more details. - let mut cu1 = ptr::null_mut(); - let mut cu2 = ptr::null_mut(); - unsafe { llvm::LLVMRustLTOGetDICompileUnit(llmod, &mut cu1, &mut cu2) }; - if !cu2.is_null() { - let _timer = - cgcx.prof.generic_activity_with_arg("LLVM_fat_lto_patch_debuginfo", &*module.name); - unsafe { llvm::LLVMRustLTOPatchDICompileUnit(llmod, cu1) }; - save_temp_bitcode(cgcx, &module, "fat-lto-after-patch"); - } - // Internalize everything below threshold to help strip out more modules and such. unsafe { let ptr = symbols_below_threshold.as_ptr(); @@ -757,7 +743,7 @@ pub unsafe fn optimize_thin_module( // an error. let mut cu1 = ptr::null_mut(); let mut cu2 = ptr::null_mut(); - llvm::LLVMRustLTOGetDICompileUnit(llmod, &mut cu1, &mut cu2); + llvm::LLVMRustThinLTOGetDICompileUnit(llmod, &mut cu1, &mut cu2); if !cu2.is_null() { let msg = "multiple source DICompileUnits found"; return Err(write::llvm_err(&diag_handler, msg)); @@ -846,7 +832,7 @@ pub unsafe fn optimize_thin_module( let _timer = cgcx .prof .generic_activity_with_arg("LLVM_thin_lto_patch_debuginfo", thin_module.name()); - llvm::LLVMRustLTOPatchDICompileUnit(llmod, cu1); + llvm::LLVMRustThinLTOPatchDICompileUnit(llmod, cu1); save_temp_bitcode(cgcx, &module, "thin-lto-after-patch"); } diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 375b9927c8672..db018aeac1385 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -2506,8 +2506,12 @@ extern "C" { len: usize, out_len: &mut usize, ) -> *const u8; - pub fn LLVMRustLTOGetDICompileUnit(M: &Module, CU1: &mut *mut c_void, CU2: &mut *mut c_void); - pub fn LLVMRustLTOPatchDICompileUnit(M: &Module, CU: *mut c_void); + pub fn LLVMRustThinLTOGetDICompileUnit( + M: &Module, + CU1: &mut *mut c_void, + CU2: &mut *mut c_void, + ); + pub fn LLVMRustThinLTOPatchDICompileUnit(M: &Module, CU: *mut c_void); pub fn LLVMRustLinkerNew(M: &Module) -> &mut Linker<'_>; pub fn LLVMRustLinkerAdd( diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 7030fd53704dd..e3ee21c80859e 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -1611,7 +1611,7 @@ LLVMRustGetBitcodeSliceFromObjectData(const char *data, // Rewrite all `DICompileUnit` pointers to the `DICompileUnit` specified. See // the comment in `back/lto.rs` for why this exists. extern "C" void -LLVMRustLTOGetDICompileUnit(LLVMModuleRef Mod, +LLVMRustThinLTOGetDICompileUnit(LLVMModuleRef Mod, DICompileUnit **A, DICompileUnit **B) { Module *M = unwrap(Mod); @@ -1629,7 +1629,7 @@ LLVMRustLTOGetDICompileUnit(LLVMModuleRef Mod, // Rewrite all `DICompileUnit` pointers to the `DICompileUnit` specified. See // the comment in `back/lto.rs` for why this exists. extern "C" void -LLVMRustLTOPatchDICompileUnit(LLVMModuleRef Mod, DICompileUnit *Unit) { +LLVMRustThinLTOPatchDICompileUnit(LLVMModuleRef Mod, DICompileUnit *Unit) { Module *M = unwrap(Mod); // If the original source module didn't have a `DICompileUnit` then try to From 42af197431fd3185767b6f3064336d65b0e62869 Mon Sep 17 00:00:00 2001 From: "Cliff L. Biffle" Date: Tue, 5 Apr 2022 10:35:52 -0700 Subject: [PATCH 0005/1135] Improve debuginfo test coverage for simple statics. - Re-enabled basic-types-globals which has been disabled since 2018 - Updated its now-rotted assertions about GDB's output to pass - Rewrote header comment describing some previous state of GDB behavior that didn't match either the checked-in assertions _or_ the current behavior, and so I assume has just been wrong for some time. - Copy-pasta'd the test into a version that uses LTO to check for regression on #90357, because I don't see a way to matrix the same test into several build configurations. --- src/test/debuginfo/basic-types-globals-lto.rs | 80 +++++++++++++++++++ src/test/debuginfo/basic-types-globals.rs | 10 +-- 2 files changed, 83 insertions(+), 7 deletions(-) create mode 100644 src/test/debuginfo/basic-types-globals-lto.rs diff --git a/src/test/debuginfo/basic-types-globals-lto.rs b/src/test/debuginfo/basic-types-globals-lto.rs new file mode 100644 index 0000000000000..03770eb6f01c7 --- /dev/null +++ b/src/test/debuginfo/basic-types-globals-lto.rs @@ -0,0 +1,80 @@ +// Caveat - gdb doesn't know about UTF-32 character encoding and will print a +// rust char as only its numerical value. + +// min-lldb-version: 310 + +// no-prefer-dynamic +// compile-flags:-g -C lto +// gdb-command:run +// gdbg-command:print 'basic_types_globals::B' +// gdbr-command:print B +// gdb-check:$1 = false +// gdbg-command:print 'basic_types_globals::I' +// gdbr-command:print I +// gdb-check:$2 = -1 +// gdbg-command:print 'basic_types_globals::C' +// gdbr-command:print C +// gdbg-check:$3 = 97 +// gdbr-check:$3 = 97 +// gdbg-command:print/d 'basic_types_globals::I8' +// gdbr-command:print I8 +// gdb-check:$4 = 68 +// gdbg-command:print 'basic_types_globals::I16' +// gdbr-command:print I16 +// gdb-check:$5 = -16 +// gdbg-command:print 'basic_types_globals::I32' +// gdbr-command:print I32 +// gdb-check:$6 = -32 +// gdbg-command:print 'basic_types_globals::I64' +// gdbr-command:print I64 +// gdb-check:$7 = -64 +// gdbg-command:print 'basic_types_globals::U' +// gdbr-command:print U +// gdb-check:$8 = 1 +// gdbg-command:print/d 'basic_types_globals::U8' +// gdbr-command:print U8 +// gdb-check:$9 = 100 +// gdbg-command:print 'basic_types_globals::U16' +// gdbr-command:print U16 +// gdb-check:$10 = 16 +// gdbg-command:print 'basic_types_globals::U32' +// gdbr-command:print U32 +// gdb-check:$11 = 32 +// gdbg-command:print 'basic_types_globals::U64' +// gdbr-command:print U64 +// gdb-check:$12 = 64 +// gdbg-command:print 'basic_types_globals::F32' +// gdbr-command:print F32 +// gdb-check:$13 = 2.5 +// gdbg-command:print 'basic_types_globals::F64' +// gdbr-command:print F64 +// gdb-check:$14 = 3.5 +// gdb-command:continue + +#![allow(unused_variables)] +#![feature(omit_gdb_pretty_printer_section)] +#![omit_gdb_pretty_printer_section] + +// N.B. These are `mut` only so they don't constant fold away. +static mut B: bool = false; +static mut I: isize = -1; +static mut C: char = 'a'; +static mut I8: i8 = 68; +static mut I16: i16 = -16; +static mut I32: i32 = -32; +static mut I64: i64 = -64; +static mut U: usize = 1; +static mut U8: u8 = 100; +static mut U16: u16 = 16; +static mut U32: u32 = 32; +static mut U64: u64 = 64; +static mut F32: f32 = 2.5; +static mut F64: f64 = 3.5; + +fn main() { + _zzz(); // #break + + let a = unsafe { (B, I, C, I8, I16, I32, I64, U, U8, U16, U32, U64, F32, F64) }; +} + +fn _zzz() {()} diff --git a/src/test/debuginfo/basic-types-globals.rs b/src/test/debuginfo/basic-types-globals.rs index 389b2cf015cac..11637d2294a83 100644 --- a/src/test/debuginfo/basic-types-globals.rs +++ b/src/test/debuginfo/basic-types-globals.rs @@ -1,11 +1,7 @@ -// Caveats - gdb prints any 8-bit value (meaning rust I8 and u8 values) -// as its numerical value along with its associated ASCII char, there -// doesn't seem to be any way around this. Also, gdb doesn't know -// about UTF-32 character encoding and will print a rust char as only -// its numerical value. +// Caveat - gdb doesn't know about UTF-32 character encoding and will print a +// rust char as only its numerical value. // min-lldb-version: 310 -// ignore-gdb // Test temporarily ignored due to debuginfo tests being disabled, see PR 47155 // compile-flags:-g // gdb-command:run @@ -18,7 +14,7 @@ // gdbg-command:print 'basic_types_globals::C' // gdbr-command:print C // gdbg-check:$3 = 97 -// gdbr-check:$3 = 97 'a' +// gdbr-check:$3 = 97 // gdbg-command:print/d 'basic_types_globals::I8' // gdbr-command:print I8 // gdb-check:$4 = 68 From 8b1f85caede44808e62542cfdff04787d70f8f7f Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Tue, 26 Apr 2022 00:13:24 +0100 Subject: [PATCH 0006/1135] Windows: Iterative `remove_dir_all` This will allow better strategies for use of memory and File handles. However, fully taking advantage of that is left to future work. --- library/std/src/sys/windows/fs.rs | 144 ++++++++++++++---------------- 1 file changed, 67 insertions(+), 77 deletions(-) diff --git a/library/std/src/sys/windows/fs.rs b/library/std/src/sys/windows/fs.rs index 95903899297b6..618cbbb1817ec 100644 --- a/library/std/src/sys/windows/fs.rs +++ b/library/std/src/sys/windows/fs.rs @@ -680,7 +680,7 @@ impl<'a> DirBuffIter<'a> { } } impl<'a> Iterator for DirBuffIter<'a> { - type Item = &'a [u16]; + type Item = (&'a [u16], bool); fn next(&mut self) -> Option { use crate::mem::size_of; let buffer = &self.buffer?[self.cursor..]; @@ -689,14 +689,16 @@ impl<'a> Iterator for DirBuffIter<'a> { // SAFETY: The buffer contains a `FILE_ID_BOTH_DIR_INFO` struct but the // last field (the file name) is unsized. So an offset has to be // used to get the file name slice. - let (name, next_entry) = unsafe { + let (name, is_directory, next_entry) = unsafe { let info = buffer.as_ptr().cast::(); let next_entry = (*info).NextEntryOffset as usize; let name = crate::slice::from_raw_parts( (*info).FileName.as_ptr().cast::(), (*info).FileNameLength as usize / size_of::(), ); - (name, next_entry) + let is_directory = ((*info).FileAttributes & c::FILE_ATTRIBUTE_DIRECTORY) != 0; + + (name, is_directory, next_entry) }; if next_entry == 0 { @@ -709,7 +711,7 @@ impl<'a> Iterator for DirBuffIter<'a> { const DOT: u16 = b'.' as u16; match name { [DOT] | [DOT, DOT] => self.next(), - _ => Some(name), + _ => Some((name, is_directory)), } } } @@ -994,89 +996,77 @@ pub fn remove_dir_all(path: &Path) -> io::Result<()> { if (file.basic_info()?.FileAttributes & c::FILE_ATTRIBUTE_DIRECTORY) == 0 { return Err(io::Error::from_raw_os_error(c::ERROR_DIRECTORY as _)); } - let mut delete: fn(&File) -> io::Result<()> = File::posix_delete; - let result = match delete(&file) { - Err(e) if e.kind() == io::ErrorKind::DirectoryNotEmpty => { - match remove_dir_all_recursive(&file, delete) { - // Return unexpected errors. - Err(e) if e.kind() != io::ErrorKind::DirectoryNotEmpty => return Err(e), - result => result, - } - } - // If POSIX delete is not supported for this filesystem then fallback to win32 delete. - Err(e) - if e.raw_os_error() == Some(c::ERROR_NOT_SUPPORTED as i32) - || e.raw_os_error() == Some(c::ERROR_INVALID_PARAMETER as i32) => - { - delete = File::win32_delete; - Err(e) - } - result => result, - }; - if result.is_ok() { - Ok(()) - } else { - // This is a fallback to make sure the directory is actually deleted. - // Otherwise this function is prone to failing with `DirectoryNotEmpty` - // due to possible delays between marking a file for deletion and the - // file actually being deleted from the filesystem. - // - // So we retry a few times before giving up. - for _ in 0..5 { - match remove_dir_all_recursive(&file, delete) { - Err(e) if e.kind() == io::ErrorKind::DirectoryNotEmpty => {} - result => return result, + + match remove_dir_all_iterative(&file, File::posix_delete) { + Err(e) => { + if let Some(code) = e.raw_os_error() { + match code as u32 { + // If POSIX delete is not supported for this filesystem then fallback to win32 delete. + c::ERROR_NOT_SUPPORTED + | c::ERROR_INVALID_FUNCTION + | c::ERROR_INVALID_PARAMETER => { + remove_dir_all_iterative(&file, File::win32_delete) + } + _ => Err(e), + } + } else { + Err(e) } } - // Try one last time. - delete(&file) + ok => ok, } } -fn remove_dir_all_recursive(f: &File, delete: fn(&File) -> io::Result<()>) -> io::Result<()> { +fn remove_dir_all_iterative(f: &File, delete: fn(&File) -> io::Result<()>) -> io::Result<()> { let mut buffer = DirBuff::new(); - let mut restart = true; - // Fill the buffer and iterate the entries. - while f.fill_dir_buff(&mut buffer, restart)? { - for name in buffer.iter() { - // Open the file without following symlinks and try deleting it. - // We try opening will all needed permissions and if that is denied - // fallback to opening without `FILE_LIST_DIRECTORY` permission. - // Note `SYNCHRONIZE` permission is needed for synchronous access. - let mut result = - open_link_no_reparse(&f, name, c::SYNCHRONIZE | c::DELETE | c::FILE_LIST_DIRECTORY); - if matches!(&result, Err(e) if e.kind() == io::ErrorKind::PermissionDenied) { - result = open_link_no_reparse(&f, name, c::SYNCHRONIZE | c::DELETE); - } - match result { - Ok(file) => match delete(&file) { - Err(e) if e.kind() == io::ErrorKind::DirectoryNotEmpty => { - // Iterate the directory's files. - // Ignore `DirectoryNotEmpty` errors here. They will be - // caught when `remove_dir_all` tries to delete the top - // level directory. It can then decide if to retry or not. - match remove_dir_all_recursive(&file, delete) { - Err(e) if e.kind() == io::ErrorKind::DirectoryNotEmpty => {} - result => result?, - } + let mut dirlist = vec![f.duplicate()?]; + + // FIXME: This is a hack so we can push to the dirlist vec after borrowing from it. + fn copy_handle(f: &File) -> mem::ManuallyDrop { + unsafe { mem::ManuallyDrop::new(File::from_raw_handle(f.as_raw_handle())) } + } + + while let Some(dir) = dirlist.last() { + let dir = copy_handle(dir); + + // Fill the buffer and iterate the entries. + let more_data = dir.fill_dir_buff(&mut buffer, false)?; + for (name, is_directory) in buffer.iter() { + if is_directory { + let child_dir = open_link_no_reparse( + &dir, + name, + c::SYNCHRONIZE | c::DELETE | c::FILE_LIST_DIRECTORY, + )?; + dirlist.push(child_dir); + } else { + const MAX_RETRIES: u32 = 10; + for i in 1..=MAX_RETRIES { + let result = open_link_no_reparse(&dir, name, c::SYNCHRONIZE | c::DELETE); + match result { + Ok(f) => delete(&f)?, + // Already deleted, so skip. + Err(e) if e.kind() == io::ErrorKind::NotFound => break, + // Retry a few times if the file is locked or a delete is already in progress. + Err(e) + if i < MAX_RETRIES + && (e.raw_os_error() == Some(c::ERROR_DELETE_PENDING as _) + || e.raw_os_error() + == Some(c::ERROR_SHARING_VIOLATION as _)) => {} + // Otherwise return the error. + Err(e) => return Err(e), } - result => result?, - }, - // Ignore error if a delete is already in progress or the file - // has already been deleted. It also ignores sharing violations - // (where a file is locked by another process) as these are - // usually temporary. - Err(e) - if e.raw_os_error() == Some(c::ERROR_DELETE_PENDING as _) - || e.kind() == io::ErrorKind::NotFound - || e.raw_os_error() == Some(c::ERROR_SHARING_VIOLATION as _) => {} - Err(e) => return Err(e), + } + } + } + // If there were no more files then delete the directory. + if !more_data { + if let Some(dir) = dirlist.pop() { + delete(&dir)?; } } - // Continue reading directory entries without restarting from the beginning, - restart = false; } - delete(&f) + Ok(()) } pub fn readlink(path: &Path) -> io::Result { From 8dc4696b3b8e048ddbc4a25953cccb567b27570c Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Tue, 26 Apr 2022 01:08:46 +0100 Subject: [PATCH 0007/1135] Retry deleting a directory It's possible that a file in the directory is pending deletion. In that case we might succeed after a few attempts. --- library/std/src/sys/windows/fs.rs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/library/std/src/sys/windows/fs.rs b/library/std/src/sys/windows/fs.rs index 618cbbb1817ec..e6f0f0f302391 100644 --- a/library/std/src/sys/windows/fs.rs +++ b/library/std/src/sys/windows/fs.rs @@ -1018,6 +1018,10 @@ pub fn remove_dir_all(path: &Path) -> io::Result<()> { } fn remove_dir_all_iterative(f: &File, delete: fn(&File) -> io::Result<()>) -> io::Result<()> { + // When deleting files we may loop this many times when certain error conditions occur. + // This allows remove_dir_all to succeed when the error is temporary. + const MAX_RETRIES: u32 = 10; + let mut buffer = DirBuff::new(); let mut dirlist = vec![f.duplicate()?]; @@ -1040,7 +1044,6 @@ fn remove_dir_all_iterative(f: &File, delete: fn(&File) -> io::Result<()>) -> io )?; dirlist.push(child_dir); } else { - const MAX_RETRIES: u32 = 10; for i in 1..=MAX_RETRIES { let result = open_link_no_reparse(&dir, name, c::SYNCHRONIZE | c::DELETE); match result { @@ -1062,7 +1065,17 @@ fn remove_dir_all_iterative(f: &File, delete: fn(&File) -> io::Result<()>) -> io // If there were no more files then delete the directory. if !more_data { if let Some(dir) = dirlist.pop() { - delete(&dir)?; + // Retry deleting a few times in case we need to wait for a file to be deleted. + for i in 1..=MAX_RETRIES { + let result = delete(&dir); + if let Err(e) = result { + if i == MAX_RETRIES || e.kind() != io::ErrorKind::DirectoryNotEmpty { + return Err(e); + } + } else { + break; + } + } } } } From d579665bd1e1649c9597cdaf3da3c2ca7f54e793 Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Thu, 28 Apr 2022 18:46:33 +0100 Subject: [PATCH 0008/1135] Yield the thread when waiting to delete a file --- library/std/src/sys/windows/fs.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/library/std/src/sys/windows/fs.rs b/library/std/src/sys/windows/fs.rs index e6f0f0f302391..5e055307c291e 100644 --- a/library/std/src/sys/windows/fs.rs +++ b/library/std/src/sys/windows/fs.rs @@ -14,6 +14,7 @@ use crate::sys::handle::Handle; use crate::sys::time::SystemTime; use crate::sys::{c, cvt}; use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::thread; use super::path::maybe_verbatim; use super::to_u16s; @@ -1059,6 +1060,7 @@ fn remove_dir_all_iterative(f: &File, delete: fn(&File) -> io::Result<()>) -> io // Otherwise return the error. Err(e) => return Err(e), } + thread::yield_now(); } } } @@ -1072,6 +1074,7 @@ fn remove_dir_all_iterative(f: &File, delete: fn(&File) -> io::Result<()>) -> io if i == MAX_RETRIES || e.kind() != io::ErrorKind::DirectoryNotEmpty { return Err(e); } + thread::yield_now(); } else { break; } From 430575b61a95fc559f5021e246c04c3468acd1d6 Mon Sep 17 00:00:00 2001 From: Elliot Bobrow Date: Wed, 6 Apr 2022 09:28:49 -0700 Subject: [PATCH 0009/1135] add `manual_find` lint for function return case --- CHANGELOG.md | 1 + clippy_lints/src/lib.register_all.rs | 1 + clippy_lints/src/lib.register_complexity.rs | 1 + clippy_lints/src/lib.register_lints.rs | 1 + clippy_lints/src/loops/manual_find.rs | 158 +++++++++++++ clippy_lints/src/loops/mod.rs | 34 +++ clippy_lints/src/non_expressive_names.rs | 10 +- tests/ui/manual_find.rs | 22 ++ tests/ui/manual_find.stderr | 29 +++ tests/ui/manual_find_fixable.fixed | 182 +++++++++++++++ tests/ui/manual_find_fixable.rs | 242 ++++++++++++++++++++ tests/ui/manual_find_fixable.stderr | 142 ++++++++++++ tests/ui/while_let_on_iterator.fixed | 3 +- tests/ui/while_let_on_iterator.rs | 3 +- tests/ui/while_let_on_iterator.stderr | 46 ++-- 15 files changed, 844 insertions(+), 31 deletions(-) create mode 100644 clippy_lints/src/loops/manual_find.rs create mode 100644 tests/ui/manual_find.rs create mode 100644 tests/ui/manual_find.stderr create mode 100644 tests/ui/manual_find_fixable.fixed create mode 100644 tests/ui/manual_find_fixable.rs create mode 100644 tests/ui/manual_find_fixable.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index fd288285532ea..0011f50da20bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3384,6 +3384,7 @@ Released 2018-09-13 [`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn [`manual_bits`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits [`manual_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map +[`manual_find`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find [`manual_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find_map [`manual_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_flatten [`manual_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_map diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 132a466267626..844008254a8d7 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -109,6 +109,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(loops::FOR_KV_MAP), LintId::of(loops::FOR_LOOPS_OVER_FALLIBLES), LintId::of(loops::ITER_NEXT_LOOP), + LintId::of(loops::MANUAL_FIND), LintId::of(loops::MANUAL_FLATTEN), LintId::of(loops::MANUAL_MEMCPY), LintId::of(loops::MISSING_SPIN_LOOP), diff --git a/clippy_lints/src/lib.register_complexity.rs b/clippy_lints/src/lib.register_complexity.rs index a2ce69065f94d..64f5b2df0043c 100644 --- a/clippy_lints/src/lib.register_complexity.rs +++ b/clippy_lints/src/lib.register_complexity.rs @@ -21,6 +21,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(lifetimes::EXTRA_UNUSED_LIFETIMES), LintId::of(lifetimes::NEEDLESS_LIFETIMES), LintId::of(loops::EXPLICIT_COUNTER_LOOP), + LintId::of(loops::MANUAL_FIND), LintId::of(loops::MANUAL_FLATTEN), LintId::of(loops::SINGLE_ELEMENT_LOOP), LintId::of(loops::WHILE_LET_LOOP), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 21f1ef562b5a3..d5ddb95d25f8a 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -222,6 +222,7 @@ store.register_lints(&[ loops::FOR_KV_MAP, loops::FOR_LOOPS_OVER_FALLIBLES, loops::ITER_NEXT_LOOP, + loops::MANUAL_FIND, loops::MANUAL_FLATTEN, loops::MANUAL_MEMCPY, loops::MISSING_SPIN_LOOP, diff --git a/clippy_lints/src/loops/manual_find.rs b/clippy_lints/src/loops/manual_find.rs new file mode 100644 index 0000000000000..33736d6d4e650 --- /dev/null +++ b/clippy_lints/src/loops/manual_find.rs @@ -0,0 +1,158 @@ +use super::utils::make_iterator_snippet; +use super::MANUAL_FIND; +use clippy_utils::{ + diagnostics::span_lint_and_then, higher, is_lang_ctor, path_res, peel_blocks_with_stmt, + source::snippet_with_applicability, ty::implements_trait, +}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{ + def::Res, lang_items::LangItem, BindingAnnotation, Block, Expr, ExprKind, HirId, Node, Pat, PatKind, Stmt, StmtKind, +}; +use rustc_lint::LateContext; +use rustc_span::source_map::Span; + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + pat: &'tcx Pat<'_>, + arg: &'tcx Expr<'_>, + body: &'tcx Expr<'_>, + span: Span, + expr: &'tcx Expr<'_>, +) { + let inner_expr = peel_blocks_with_stmt(body); + // Check for the specific case that the result is returned and optimize suggestion for that (more + // cases can be added later) + if_chain! { + if let Some(higher::If { cond, then, r#else: None, }) = higher::If::hir(inner_expr); + if let Some(binding_id) = get_binding(pat); + if let ExprKind::Block(block, _) = then.kind; + if let [stmt] = block.stmts; + if let StmtKind::Semi(semi) = stmt.kind; + if let ExprKind::Ret(Some(ret_value)) = semi.kind; + if let ExprKind::Call(Expr { kind: ExprKind::Path(ctor), .. }, [inner_ret]) = ret_value.kind; + if is_lang_ctor(cx, ctor, LangItem::OptionSome); + if path_res(cx, inner_ret) == Res::Local(binding_id); + if let Some((last_stmt, last_ret)) = last_stmt_and_ret(cx, expr); + then { + let mut applicability = Applicability::MachineApplicable; + let mut snippet = make_iterator_snippet(cx, arg, &mut applicability); + // Checks if `pat` is a single reference to a binding (`&x`) + let is_ref_to_binding = + matches!(pat.kind, PatKind::Ref(inner, _) if matches!(inner.kind, PatKind::Binding(..))); + // If `pat` is not a binding or a reference to a binding (`x` or `&x`) + // we need to map it to the binding returned by the function (i.e. `.map(|(x, _)| x)`) + if !(matches!(pat.kind, PatKind::Binding(..)) || is_ref_to_binding) { + snippet.push_str( + &format!( + ".map(|{}| {})", + snippet_with_applicability(cx, pat.span, "..", &mut applicability), + snippet_with_applicability(cx, inner_ret.span, "..", &mut applicability), + )[..], + ); + } + let ty = cx.typeck_results().expr_ty(inner_ret); + if cx.tcx.lang_items().copy_trait().map_or(false, |id| implements_trait(cx, ty, id, &[])) { + snippet.push_str( + &format!( + ".find(|{}{}| {})", + "&".repeat(1 + usize::from(is_ref_to_binding)), + snippet_with_applicability(cx, inner_ret.span, "..", &mut applicability), + snippet_with_applicability(cx, cond.span, "..", &mut applicability), + )[..], + ); + if is_ref_to_binding { + snippet.push_str(".copied()"); + } + } else { + applicability = Applicability::MaybeIncorrect; + snippet.push_str( + &format!( + ".find(|{}| {})", + snippet_with_applicability(cx, inner_ret.span, "..", &mut applicability), + snippet_with_applicability(cx, cond.span, "..", &mut applicability), + )[..], + ); + } + // Extends to `last_stmt` to include semicolon in case of `return None;` + let lint_span = span.to(last_stmt.span).to(last_ret.span); + span_lint_and_then( + cx, + MANUAL_FIND, + lint_span, + "manual implementation of `Iterator::find`", + |diag| { + if applicability == Applicability::MaybeIncorrect { + diag.note("you may need to dereference some variables"); + } + diag.span_suggestion( + lint_span, + "replace with an iterator", + snippet, + applicability, + ); + }, + ); + } + } +} + +fn get_binding(pat: &Pat<'_>) -> Option { + let mut hir_id = None; + let mut count = 0; + pat.each_binding(|annotation, id, _, _| { + count += 1; + if count > 1 { + hir_id = None; + return; + } + if let BindingAnnotation::Unannotated = annotation { + hir_id = Some(id); + } + }); + hir_id +} + +// Returns the last statement and last return if function fits format for lint +fn last_stmt_and_ret<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, +) -> Option<(&'tcx Stmt<'tcx>, &'tcx Expr<'tcx>)> { + // Returns last non-return statement and the last return + fn extract<'tcx>(block: &Block<'tcx>) -> Option<(&'tcx Stmt<'tcx>, &'tcx Expr<'tcx>)> { + if let [.., last_stmt] = block.stmts { + if let Some(ret) = block.expr { + return Some((last_stmt, ret)); + } + if_chain! { + if let [.., snd_last, _] = block.stmts; + if let StmtKind::Semi(last_expr) = last_stmt.kind; + if let ExprKind::Ret(Some(ret)) = last_expr.kind; + then { + return Some((snd_last, ret)); + } + } + } + None + } + let mut parent_iter = cx.tcx.hir().parent_iter(expr.hir_id); + if_chain! { + // This should be the loop + if let Some((node_hir, Node::Stmt(..))) = parent_iter.next(); + // This should be the funciton body + if let Some((_, Node::Block(block))) = parent_iter.next(); + if let Some((last_stmt, last_ret)) = extract(block); + if last_stmt.hir_id == node_hir; + if let ExprKind::Path(path) = &last_ret.kind; + if is_lang_ctor(cx, path, LangItem::OptionNone); + if let Some((_, Node::Expr(_block))) = parent_iter.next(); + // This includes the function header + if let Some((_, func)) = parent_iter.next(); + if func.fn_kind().is_some(); + then { + Some((block.stmts.last().unwrap(), last_ret)) + } else { + None + } + } +} diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index f029067d36715..2f5d176b2de0b 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -5,6 +5,7 @@ mod explicit_iter_loop; mod for_kv_map; mod for_loops_over_fallibles; mod iter_next_loop; +mod manual_find; mod manual_flatten; mod manual_memcpy; mod missing_spin_loop; @@ -597,6 +598,37 @@ declare_clippy_lint! { "An empty busy waiting loop" } +declare_clippy_lint! { + /// ### What it does + /// Check for manual implementations of Iterator::find + /// + /// ### Why is this bad? + /// It doesn't affect performance, but using `find` is shorter and easier to read. + /// + /// ### Example + /// + /// ```rust + /// fn example(arr: Vec) -> Option { + /// for el in arr { + /// if el == 1 { + /// return Some(el); + /// } + /// } + /// None + /// } + /// ``` + /// Use instead: + /// ```rust + /// fn example(arr: Vec) -> Option { + /// arr.into_iter().find(|&el| el == 1) + /// } + /// ``` + #[clippy::version = "1.61.0"] + pub MANUAL_FIND, + complexity, + "manual implementation of `Iterator::find`" +} + declare_lint_pass!(Loops => [ MANUAL_MEMCPY, MANUAL_FLATTEN, @@ -617,6 +649,7 @@ declare_lint_pass!(Loops => [ SAME_ITEM_PUSH, SINGLE_ELEMENT_LOOP, MISSING_SPIN_LOOP, + MANUAL_FIND, ]); impl<'tcx> LateLintPass<'tcx> for Loops { @@ -692,6 +725,7 @@ fn check_for_loop<'tcx>( single_element_loop::check(cx, pat, arg, body, expr); same_item_push::check(cx, pat, arg, body, expr); manual_flatten::check(cx, pat, arg, body, span); + manual_find::check(cx, pat, arg, body, span, expr); } fn check_for_loop_arg(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>) { diff --git a/clippy_lints/src/non_expressive_names.rs b/clippy_lints/src/non_expressive_names.rs index 0d0c88b02c78b..4a3edbb9d37ce 100644 --- a/clippy_lints/src/non_expressive_names.rs +++ b/clippy_lints/src/non_expressive_names.rs @@ -159,12 +159,10 @@ impl<'a, 'tcx, 'b> Visitor<'tcx> for SimilarNamesNameVisitor<'a, 'tcx, 'b> { #[must_use] fn get_exemptions(interned_name: &str) -> Option<&'static [&'static str]> { - for &list in ALLOWED_TO_BE_SIMILAR { - if allowed_to_be_similar(interned_name, list) { - return Some(list); - } - } - None + ALLOWED_TO_BE_SIMILAR + .iter() + .find(|&&list| allowed_to_be_similar(interned_name, list)) + .copied() } #[must_use] diff --git a/tests/ui/manual_find.rs b/tests/ui/manual_find.rs new file mode 100644 index 0000000000000..257fe045f78a1 --- /dev/null +++ b/tests/ui/manual_find.rs @@ -0,0 +1,22 @@ +#![allow(unused)] +#![warn(clippy::manual_find)] + +fn vec_string(strings: Vec) -> Option { + for s in strings { + if s == String::new() { + return Some(s); + } + } + None +} + +fn tuple(arr: Vec<(String, i32)>) -> Option { + for (s, _) in arr { + if s == String::new() { + return Some(s); + } + } + None +} + +fn main() {} diff --git a/tests/ui/manual_find.stderr b/tests/ui/manual_find.stderr new file mode 100644 index 0000000000000..da0fd4aaef7d6 --- /dev/null +++ b/tests/ui/manual_find.stderr @@ -0,0 +1,29 @@ +error: manual implementation of `Iterator::find` + --> $DIR/manual_find.rs:5:5 + | +LL | / for s in strings { +LL | | if s == String::new() { +LL | | return Some(s); +LL | | } +LL | | } +LL | | None + | |________^ help: replace with an iterator: `strings.into_iter().find(|s| s == String::new())` + | + = note: `-D clippy::manual-find` implied by `-D warnings` + = note: you may need to dereference some variables + +error: manual implementation of `Iterator::find` + --> $DIR/manual_find.rs:14:5 + | +LL | / for (s, _) in arr { +LL | | if s == String::new() { +LL | | return Some(s); +LL | | } +LL | | } +LL | | None + | |________^ help: replace with an iterator: `arr.into_iter().map(|(s, _)| s).find(|s| s == String::new())` + | + = note: you may need to dereference some variables + +error: aborting due to 2 previous errors + diff --git a/tests/ui/manual_find_fixable.fixed b/tests/ui/manual_find_fixable.fixed new file mode 100644 index 0000000000000..36d1644c22bda --- /dev/null +++ b/tests/ui/manual_find_fixable.fixed @@ -0,0 +1,182 @@ +// run-rustfix + +#![allow(unused, clippy::needless_return)] +#![warn(clippy::manual_find)] + +use std::collections::HashMap; + +const ARRAY: &[u32; 5] = &[2, 7, 1, 9, 3]; + +fn lookup(n: u32) -> Option { + ARRAY.iter().find(|&&v| v == n).copied() +} + +fn with_pat(arr: Vec<(u32, u32)>) -> Option { + arr.into_iter().map(|(a, _)| a).find(|&a| a % 2 == 0) +} + +struct Data { + name: String, + is_true: bool, +} +fn with_struct(arr: Vec) -> Option { + arr.into_iter().find(|el| el.name.len() == 10) +} + +struct Tuple(usize, usize); +fn with_tuple_struct(arr: Vec) -> Option { + arr.into_iter().map(|Tuple(a, _)| a).find(|&a| a >= 3) +} + +struct A; +impl A { + fn should_keep(&self) -> bool { + true + } +} +fn with_method_call(arr: Vec) -> Option { + arr.into_iter().find(|el| el.should_keep()) +} + +fn with_closure(arr: Vec) -> Option { + let f = |el: u32| -> u32 { el + 10 }; + arr.into_iter().find(|&el| f(el) == 20) +} + +fn with_closure2(arr: HashMap) -> Option { + let f = |el: i32| -> bool { el == 10 }; + arr.values().find(|&&el| f(el)).copied() +} + +fn with_bool(arr: Vec) -> Option { + arr.into_iter().find(|el| el.is_true) +} + +fn with_side_effects(arr: Vec) -> Option { + for v in arr { + if v == 1 { + println!("side effect"); + return Some(v); + } + } + None +} + +fn with_else(arr: Vec) -> Option { + for el in arr { + if el % 2 == 0 { + return Some(el); + } else { + println!("{}", el); + } + } + None +} + +fn tuple_with_ref(v: [(u8, &u8); 3]) -> Option { + v.into_iter().map(|(_, &x)| x).find(|&x| x > 10) +} + +fn ref_to_tuple_with_ref(v: &[(u8, &u8)]) -> Option { + v.iter().map(|&(_, &x)| x).find(|&x| x > 10) +} + +fn explicit_ret(arr: Vec) -> Option { + arr.into_iter().find(|&x| x >= 5) +} + +fn plus_one(a: i32) -> Option { + Some(a + 1) +} +fn fn_instead_of_some(a: &[i32]) -> Option { + for &x in a { + if x == 1 { + return plus_one(x); + } + } + None +} + +fn for_in_condition(a: &[i32], b: bool) -> Option { + if b { + for &x in a { + if x == 1 { + return Some(x); + } + } + } + None +} + +fn intermediate_statements(a: &[i32]) -> Option { + for &x in a { + if x == 1 { + return Some(x); + } + } + + println!("side effect"); + + None +} + +fn mixed_binding_modes(arr: Vec<(i32, String)>) -> Option { + for (x, mut s) in arr { + if x == 1 && s.as_mut_str().len() == 2 { + return Some(x); + } + } + None +} + +fn as_closure() { + #[rustfmt::skip] + let f = |arr: Vec| -> Option { + arr.into_iter().find(|&x| x < 1) + }; +} + +fn in_block(a: &[i32]) -> Option { + let should_be_none = { + for &x in a { + if x == 1 { + return Some(x); + } + } + None + }; + + assert!(should_be_none.is_none()); + + should_be_none +} + +// Not handled yet +fn mut_binding(v: Vec) -> Option { + for mut s in v { + if s.as_mut_str().len() > 1 { + return Some(s); + } + } + None +} + +fn subpattern(v: Vec<[u32; 32]>) -> Option<[u32; 32]> { + for a @ [first, ..] in v { + if a[12] == first { + return Some(a); + } + } + None +} + +fn two_bindings(v: Vec<(u8, u8)>) -> Option { + for (a, n) in v { + if a == n { + return Some(a); + } + } + None +} + +fn main() {} diff --git a/tests/ui/manual_find_fixable.rs b/tests/ui/manual_find_fixable.rs new file mode 100644 index 0000000000000..ed277ddaa722d --- /dev/null +++ b/tests/ui/manual_find_fixable.rs @@ -0,0 +1,242 @@ +// run-rustfix + +#![allow(unused, clippy::needless_return)] +#![warn(clippy::manual_find)] + +use std::collections::HashMap; + +const ARRAY: &[u32; 5] = &[2, 7, 1, 9, 3]; + +fn lookup(n: u32) -> Option { + for &v in ARRAY { + if v == n { + return Some(v); + } + } + None +} + +fn with_pat(arr: Vec<(u32, u32)>) -> Option { + for (a, _) in arr { + if a % 2 == 0 { + return Some(a); + } + } + None +} + +struct Data { + name: String, + is_true: bool, +} +fn with_struct(arr: Vec) -> Option { + for el in arr { + if el.name.len() == 10 { + return Some(el); + } + } + None +} + +struct Tuple(usize, usize); +fn with_tuple_struct(arr: Vec) -> Option { + for Tuple(a, _) in arr { + if a >= 3 { + return Some(a); + } + } + None +} + +struct A; +impl A { + fn should_keep(&self) -> bool { + true + } +} +fn with_method_call(arr: Vec) -> Option { + for el in arr { + if el.should_keep() { + return Some(el); + } + } + None +} + +fn with_closure(arr: Vec) -> Option { + let f = |el: u32| -> u32 { el + 10 }; + for el in arr { + if f(el) == 20 { + return Some(el); + } + } + None +} + +fn with_closure2(arr: HashMap) -> Option { + let f = |el: i32| -> bool { el == 10 }; + for &el in arr.values() { + if f(el) { + return Some(el); + } + } + None +} + +fn with_bool(arr: Vec) -> Option { + for el in arr { + if el.is_true { + return Some(el); + } + } + None +} + +fn with_side_effects(arr: Vec) -> Option { + for v in arr { + if v == 1 { + println!("side effect"); + return Some(v); + } + } + None +} + +fn with_else(arr: Vec) -> Option { + for el in arr { + if el % 2 == 0 { + return Some(el); + } else { + println!("{}", el); + } + } + None +} + +fn tuple_with_ref(v: [(u8, &u8); 3]) -> Option { + for (_, &x) in v { + if x > 10 { + return Some(x); + } + } + None +} + +fn ref_to_tuple_with_ref(v: &[(u8, &u8)]) -> Option { + for &(_, &x) in v { + if x > 10 { + return Some(x); + } + } + None +} + +fn explicit_ret(arr: Vec) -> Option { + for x in arr { + if x >= 5 { + return Some(x); + } + } + return None; +} + +fn plus_one(a: i32) -> Option { + Some(a + 1) +} +fn fn_instead_of_some(a: &[i32]) -> Option { + for &x in a { + if x == 1 { + return plus_one(x); + } + } + None +} + +fn for_in_condition(a: &[i32], b: bool) -> Option { + if b { + for &x in a { + if x == 1 { + return Some(x); + } + } + } + None +} + +fn intermediate_statements(a: &[i32]) -> Option { + for &x in a { + if x == 1 { + return Some(x); + } + } + + println!("side effect"); + + None +} + +fn mixed_binding_modes(arr: Vec<(i32, String)>) -> Option { + for (x, mut s) in arr { + if x == 1 && s.as_mut_str().len() == 2 { + return Some(x); + } + } + None +} + +fn as_closure() { + #[rustfmt::skip] + let f = |arr: Vec| -> Option { + for x in arr { + if x < 1 { + return Some(x); + } + } + None + }; +} + +fn in_block(a: &[i32]) -> Option { + let should_be_none = { + for &x in a { + if x == 1 { + return Some(x); + } + } + None + }; + + assert!(should_be_none.is_none()); + + should_be_none +} + +// Not handled yet +fn mut_binding(v: Vec) -> Option { + for mut s in v { + if s.as_mut_str().len() > 1 { + return Some(s); + } + } + None +} + +fn subpattern(v: Vec<[u32; 32]>) -> Option<[u32; 32]> { + for a @ [first, ..] in v { + if a[12] == first { + return Some(a); + } + } + None +} + +fn two_bindings(v: Vec<(u8, u8)>) -> Option { + for (a, n) in v { + if a == n { + return Some(a); + } + } + None +} + +fn main() {} diff --git a/tests/ui/manual_find_fixable.stderr b/tests/ui/manual_find_fixable.stderr new file mode 100644 index 0000000000000..dbc4ff69a740b --- /dev/null +++ b/tests/ui/manual_find_fixable.stderr @@ -0,0 +1,142 @@ +error: manual implementation of `Iterator::find` + --> $DIR/manual_find_fixable.rs:11:5 + | +LL | / for &v in ARRAY { +LL | | if v == n { +LL | | return Some(v); +LL | | } +LL | | } +LL | | None + | |________^ help: replace with an iterator: `ARRAY.iter().find(|&&v| v == n).copied()` + | + = note: `-D clippy::manual-find` implied by `-D warnings` + +error: manual implementation of `Iterator::find` + --> $DIR/manual_find_fixable.rs:20:5 + | +LL | / for (a, _) in arr { +LL | | if a % 2 == 0 { +LL | | return Some(a); +LL | | } +LL | | } +LL | | None + | |________^ help: replace with an iterator: `arr.into_iter().map(|(a, _)| a).find(|&a| a % 2 == 0)` + +error: manual implementation of `Iterator::find` + --> $DIR/manual_find_fixable.rs:33:5 + | +LL | / for el in arr { +LL | | if el.name.len() == 10 { +LL | | return Some(el); +LL | | } +LL | | } +LL | | None + | |________^ help: replace with an iterator: `arr.into_iter().find(|el| el.name.len() == 10)` + | + = note: you may need to dereference some variables + +error: manual implementation of `Iterator::find` + --> $DIR/manual_find_fixable.rs:43:5 + | +LL | / for Tuple(a, _) in arr { +LL | | if a >= 3 { +LL | | return Some(a); +LL | | } +LL | | } +LL | | None + | |________^ help: replace with an iterator: `arr.into_iter().map(|Tuple(a, _)| a).find(|&a| a >= 3)` + +error: manual implementation of `Iterator::find` + --> $DIR/manual_find_fixable.rs:58:5 + | +LL | / for el in arr { +LL | | if el.should_keep() { +LL | | return Some(el); +LL | | } +LL | | } +LL | | None + | |________^ help: replace with an iterator: `arr.into_iter().find(|el| el.should_keep())` + | + = note: you may need to dereference some variables + +error: manual implementation of `Iterator::find` + --> $DIR/manual_find_fixable.rs:68:5 + | +LL | / for el in arr { +LL | | if f(el) == 20 { +LL | | return Some(el); +LL | | } +LL | | } +LL | | None + | |________^ help: replace with an iterator: `arr.into_iter().find(|&el| f(el) == 20)` + +error: manual implementation of `Iterator::find` + --> $DIR/manual_find_fixable.rs:78:5 + | +LL | / for &el in arr.values() { +LL | | if f(el) { +LL | | return Some(el); +LL | | } +LL | | } +LL | | None + | |________^ help: replace with an iterator: `arr.values().find(|&&el| f(el)).copied()` + +error: manual implementation of `Iterator::find` + --> $DIR/manual_find_fixable.rs:87:5 + | +LL | / for el in arr { +LL | | if el.is_true { +LL | | return Some(el); +LL | | } +LL | | } +LL | | None + | |________^ help: replace with an iterator: `arr.into_iter().find(|el| el.is_true)` + | + = note: you may need to dereference some variables + +error: manual implementation of `Iterator::find` + --> $DIR/manual_find_fixable.rs:117:5 + | +LL | / for (_, &x) in v { +LL | | if x > 10 { +LL | | return Some(x); +LL | | } +LL | | } +LL | | None + | |________^ help: replace with an iterator: `v.into_iter().map(|(_, &x)| x).find(|&x| x > 10)` + +error: manual implementation of `Iterator::find` + --> $DIR/manual_find_fixable.rs:126:5 + | +LL | / for &(_, &x) in v { +LL | | if x > 10 { +LL | | return Some(x); +LL | | } +LL | | } +LL | | None + | |________^ help: replace with an iterator: `v.iter().map(|&(_, &x)| x).find(|&x| x > 10)` + +error: manual implementation of `Iterator::find` + --> $DIR/manual_find_fixable.rs:135:5 + | +LL | / for x in arr { +LL | | if x >= 5 { +LL | | return Some(x); +LL | | } +LL | | } +LL | | return None; + | |________________^ help: replace with an iterator: `arr.into_iter().find(|&x| x >= 5)` + +error: manual implementation of `Iterator::find` + --> $DIR/manual_find_fixable.rs:190:9 + | +LL | / for x in arr { +LL | | if x < 1 { +LL | | return Some(x); +LL | | } +LL | | } +LL | | None + | |____________^ help: replace with an iterator: `arr.into_iter().find(|&x| x < 1)` + +error: aborting due to 12 previous errors + diff --git a/tests/ui/while_let_on_iterator.fixed b/tests/ui/while_let_on_iterator.fixed index cb8892a3f009b..e9ff64431e19d 100644 --- a/tests/ui/while_let_on_iterator.fixed +++ b/tests/ui/while_let_on_iterator.fixed @@ -6,7 +6,8 @@ unreachable_code, unused_mut, dead_code, - clippy::equatable_if_let + clippy::equatable_if_let, + clippy::manual_find )] fn base() { diff --git a/tests/ui/while_let_on_iterator.rs b/tests/ui/while_let_on_iterator.rs index d91571844959e..03da39526b2fd 100644 --- a/tests/ui/while_let_on_iterator.rs +++ b/tests/ui/while_let_on_iterator.rs @@ -6,7 +6,8 @@ unreachable_code, unused_mut, dead_code, - clippy::equatable_if_let + clippy::equatable_if_let, + clippy::manual_find )] fn base() { diff --git a/tests/ui/while_let_on_iterator.stderr b/tests/ui/while_let_on_iterator.stderr index fb2b0f2467c0d..42859243855a6 100644 --- a/tests/ui/while_let_on_iterator.stderr +++ b/tests/ui/while_let_on_iterator.stderr @@ -1,5 +1,5 @@ error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:14:5 + --> $DIR/while_let_on_iterator.rs:15:5 | LL | while let Option::Some(x) = iter.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in iter` @@ -7,85 +7,85 @@ LL | while let Option::Some(x) = iter.next() { = note: `-D clippy::while-let-on-iterator` implied by `-D warnings` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:19:5 + --> $DIR/while_let_on_iterator.rs:20:5 | LL | while let Some(x) = iter.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in iter` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:24:5 + --> $DIR/while_let_on_iterator.rs:25:5 | LL | while let Some(_) = iter.next() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in iter` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:100:9 + --> $DIR/while_let_on_iterator.rs:101:9 | LL | while let Some([..]) = it.next() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for [..] in it` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:107:9 + --> $DIR/while_let_on_iterator.rs:108:9 | LL | while let Some([_x]) = it.next() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for [_x] in it` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:120:9 + --> $DIR/while_let_on_iterator.rs:121:9 | LL | while let Some(x @ [_]) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x @ [_] in it` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:140:9 + --> $DIR/while_let_on_iterator.rs:141:9 | LL | while let Some(_) = y.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in y` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:197:9 + --> $DIR/while_let_on_iterator.rs:198:9 | LL | while let Some(m) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it.by_ref()` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:208:5 + --> $DIR/while_let_on_iterator.rs:209:5 | LL | while let Some(n) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for n in it` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:210:9 + --> $DIR/while_let_on_iterator.rs:211:9 | LL | while let Some(m) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:219:9 + --> $DIR/while_let_on_iterator.rs:220:9 | LL | while let Some(m) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:228:9 + --> $DIR/while_let_on_iterator.rs:229:9 | LL | while let Some(m) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it.by_ref()` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:245:9 + --> $DIR/while_let_on_iterator.rs:246:9 | LL | while let Some(m) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it.by_ref()` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:260:13 + --> $DIR/while_let_on_iterator.rs:261:13 | LL | while let Some(i) = self.0.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for i in self.0.by_ref()` error: manual `!RangeInclusive::contains` implementation - --> $DIR/while_let_on_iterator.rs:261:20 + --> $DIR/while_let_on_iterator.rs:262:20 | LL | if i < 3 || i > 7 { | ^^^^^^^^^^^^^^ help: use: `!(3..=7).contains(&i)` @@ -93,49 +93,49 @@ LL | if i < 3 || i > 7 { = note: `-D clippy::manual-range-contains` implied by `-D warnings` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:292:13 + --> $DIR/while_let_on_iterator.rs:293:13 | LL | while let Some(i) = self.0.0.0.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for i in self.0.0.0.by_ref()` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:321:5 + --> $DIR/while_let_on_iterator.rs:322:5 | LL | while let Some(n) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for n in it.by_ref()` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:333:9 + --> $DIR/while_let_on_iterator.rs:334:9 | LL | while let Some(x) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it.by_ref()` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:347:5 + --> $DIR/while_let_on_iterator.rs:348:5 | LL | while let Some(x) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it.by_ref()` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:358:5 + --> $DIR/while_let_on_iterator.rs:359:5 | LL | while let Some(x) = it.0.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it.0.by_ref()` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:393:5 + --> $DIR/while_let_on_iterator.rs:394:5 | LL | while let Some(x) = s.x.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in s.x.by_ref()` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:400:5 + --> $DIR/while_let_on_iterator.rs:401:5 | LL | while let Some(x) = x[0].next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in x[0].by_ref()` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:407:5 + --> $DIR/while_let_on_iterator.rs:408:5 | LL | while let Some(..) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in it` From fd76552a4b41d132701dc0857a9fee9a3850088e Mon Sep 17 00:00:00 2001 From: joboet Date: Wed, 18 May 2022 12:18:51 +0200 Subject: [PATCH 0010/1135] std: use an event flag based thread parker on SOLID --- library/std/src/sys/itron/abi.rs | 48 +++++++++- library/std/src/sys/itron/wait_flag.rs | 67 +++++++++++++ library/std/src/sys/solid/mod.rs | 2 + .../std/src/sys_common/thread_parker/mod.rs | 7 +- .../src/sys_common/thread_parker/wait_flag.rs | 96 +++++++++++++++++++ 5 files changed, 214 insertions(+), 6 deletions(-) create mode 100644 library/std/src/sys/itron/wait_flag.rs create mode 100644 library/std/src/sys_common/thread_parker/wait_flag.rs diff --git a/library/std/src/sys/itron/abi.rs b/library/std/src/sys/itron/abi.rs index f99ee4fa897ea..5eb14bb7e534b 100644 --- a/library/std/src/sys/itron/abi.rs +++ b/library/std/src/sys/itron/abi.rs @@ -30,15 +30,32 @@ pub type ER = int_t; /// Error code type, `ID` on success pub type ER_ID = int_t; +/// Service call operational mode +pub type MODE = uint_t; + +/// OR waiting condition for an eventflag +pub const TWF_ORW: MODE = 0x01; + +/// Object attributes +pub type ATR = uint_t; + +/// FIFO wait order +pub const TA_FIFO: ATR = 0; +/// Only one task is allowed to be in the waiting state for the eventflag +pub const TA_WSGL: ATR = 0; +/// The eventflag’s bit pattern is cleared when a task is released from the +/// waiting state for that eventflag. +pub const TA_CLR: ATR = 0x04; + +/// Bit pattern of an eventflag +pub type FLGPTN = uint_t; + /// Task or interrupt priority pub type PRI = int_t; /// The special value of `PRI` representing the current task's priority. pub const TPRI_SELF: PRI = 0; -/// Object attributes -pub type ATR = uint_t; - /// Use the priority inheritance protocol #[cfg(target_os = "solid_asp3")] pub const TA_INHERIT: ATR = 0x02; @@ -90,6 +107,13 @@ pub struct T_CSEM { pub maxsem: uint_t, } +#[derive(Clone, Copy)] +#[repr(C)] +pub struct T_CFLG { + pub flgatr: ATR, + pub iflgptn: FLGPTN, +} + #[derive(Clone, Copy)] #[repr(C)] pub struct T_CMTX { @@ -139,6 +163,24 @@ extern "C" { pub fn sns_dsp() -> bool_t; #[link_name = "__asp3_get_tim"] pub fn get_tim(p_systim: *mut SYSTIM) -> ER; + #[link_name = "__asp3_acre_flg"] + pub fn acre_flg(pk_cflg: *const T_CFLG) -> ER_ID; + #[link_name = "__asp3_del_flg"] + pub fn del_flg(flgid: ID) -> ER; + #[link_name = "__asp3_set_flg"] + pub fn set_flg(flgid: ID, setptn: FLGPTN) -> ER; + #[link_name = "__asp3_clr_flg"] + pub fn clr_flg(flgid: ID, clrptn: FLGPTN) -> ER; + #[link_name = "__asp3_wai_flg"] + pub fn wai_flg(flgid: ID, waiptn: FLGPTN, wfmode: MODE, p_flgptn: *mut FLGPTN) -> ER; + #[link_name = "__asp3_twai_flg"] + pub fn twai_flg( + flgid: ID, + waiptn: FLGPTN, + wfmode: MODE, + p_flgptn: *mut FLGPTN, + tmout: TMO, + ) -> ER; #[link_name = "__asp3_acre_mtx"] pub fn acre_mtx(pk_cmtx: *const T_CMTX) -> ER_ID; #[link_name = "__asp3_del_mtx"] diff --git a/library/std/src/sys/itron/wait_flag.rs b/library/std/src/sys/itron/wait_flag.rs new file mode 100644 index 0000000000000..805f85a69b6c7 --- /dev/null +++ b/library/std/src/sys/itron/wait_flag.rs @@ -0,0 +1,67 @@ +use crate::mem::MaybeUninit; +use crate::time::Duration; + +use super::{ + abi, + error::{expect_success, fail}, + time::with_tmos, +}; + +const CLEAR: abi::FLGPTN = 0; +const RAISED: abi::FLGPTN = 1; + +/// A thread parking primitive that is not susceptible to race conditions, +/// but provides no atomic ordering guarantees and allows only one `raise` per wait. +pub struct WaitFlag { + flag: abi::ID, +} + +impl WaitFlag { + /// Creates a new wait flag. + pub fn new() -> WaitFlag { + let flag = expect_success( + unsafe { + abi::acre_flg(&abi::T_CFLG { + flgatr: abi::TA_FIFO | abi::TA_WSGL | abi::TA_CLR, + iflgptn: CLEAR, + }) + }, + &"acre_flg", + ); + + WaitFlag { flag } + } + + /// Wait for the wait flag to be raised. + pub fn wait(&self) { + let mut token = MaybeUninit::uninit(); + expect_success( + unsafe { abi::wai_flg(self.flag, RAISED, abi::TWF_ORW, token.as_mut_ptr()) }, + &"wai_flg", + ); + } + + /// Wait for the wait flag to be raised or the timeout to occur. + pub fn wait_timeout(&self, dur: Duration) { + let mut token = MaybeUninit::uninit(); + let er = with_tmos(dur, |tmout| unsafe { + abi::twai_flg(self.flag, RAISED, abi::TWF_ORW, token.as_mut_ptr(), tmout) + }); + if er != abi::E_OK && er != abi::E_TMOUT { + fail(er, &"twai_flg"); + } + } + + /// Raise the wait flag. + /// + /// Calls to this function should be balanced with the number of successful waits. + pub fn raise(&self) { + expect_success(unsafe { abi::set_flg(self.flag, RAISED) }, &"set_flg"); + } +} + +impl Drop for WaitFlag { + fn drop(&mut self) { + expect_success(unsafe { abi::del_flg(self.flag) }, &"del_flg"); + } +} diff --git a/library/std/src/sys/solid/mod.rs b/library/std/src/sys/solid/mod.rs index 5ffa381f2e50f..2d21e4764fc21 100644 --- a/library/std/src/sys/solid/mod.rs +++ b/library/std/src/sys/solid/mod.rs @@ -15,6 +15,7 @@ mod itron { pub mod thread; pub(super) mod time; use super::unsupported; + pub mod wait_flag; } pub mod alloc; @@ -43,6 +44,7 @@ pub mod memchr; pub mod thread_local_dtor; pub mod thread_local_key; pub mod time; +pub use self::itron::wait_flag; mod rwlock; diff --git a/library/std/src/sys_common/thread_parker/mod.rs b/library/std/src/sys_common/thread_parker/mod.rs index c789a388e05ad..79d5a498e054f 100644 --- a/library/std/src/sys_common/thread_parker/mod.rs +++ b/library/std/src/sys_common/thread_parker/mod.rs @@ -9,9 +9,10 @@ cfg_if::cfg_if! { ))] { mod futex; pub use futex::Parker; - } else if #[cfg(windows)] { - pub use crate::sys::thread_parker::Parker; - } else if #[cfg(target_family = "unix")] { + } else if #[cfg(target_os = "solid_asp3")] { + mod wait_flag; + pub use wait_flag::Parker; + } else if #[cfg(any(windows, target_family = "unix"))] { pub use crate::sys::thread_parker::Parker; } else { mod generic; diff --git a/library/std/src/sys_common/thread_parker/wait_flag.rs b/library/std/src/sys_common/thread_parker/wait_flag.rs new file mode 100644 index 0000000000000..39a0df1cd3de4 --- /dev/null +++ b/library/std/src/sys_common/thread_parker/wait_flag.rs @@ -0,0 +1,96 @@ +//! A wait-flag-based thread parker. +//! +//! Some operating systems provide low-level parking primitives like wait counts, +//! event flags or semaphores which are not susceptible to race conditions (meaning +//! the wakeup can occure before the wait operation). To implement the `std` thread +//! parker on top of these primitives, we only have to ensure that parking is fast +//! when the thread token is available, the atomic ordering guarantees are maintained +//! and spurious wakeups are minimized. +//! +//! To achieve this, this parker uses an atomic variable with three states: `EMPTY`, +//! `PARKED` and `NOTIFIED`: +//! * `EMPTY` means the token has not been made available, but the thread is not +//! currently waiting on it. +//! * `PARKED` means the token is not available and the thread is parked. +//! * `NOTIFIED` means the token is available. +//! +//! `park` and `park_timeout` change the state from `EMPTY` to `PARKED` and from +//! `NOTIFIED` to `EMPTY`. If the state was `NOTIFIED`, the thread was unparked and +//! execution can continue without calling into the OS. If the state was `EMPTY`, +//! the token is not available and the thread waits on the primitive (here called +//! "wait flag"). +//! +//! `unpark` changes the state to `NOTIFIED`. If the state was `PARKED`, the thread +//! is or will be sleeping on the wait flag, so we raise it. Only the first thread +//! to call `unpark` will raise the wait flag, so spurious wakeups are avoided +//! (this is especially important for semaphores). + +use crate::pin::Pin; +use crate::sync::atomic::AtomicI8; +use crate::sync::atomic::Ordering::SeqCst; +use crate::sys::wait_flag::WaitFlag; +use crate::time::Duration; + +const EMPTY: i8 = 0; +const PARKED: i8 = -1; +const NOTIFIED: i8 = 1; + +pub struct Parker { + state: AtomicI8, + wait_flag: WaitFlag, +} + +impl Parker { + /// Construct a parker for the current thread. The UNIX parker + /// implementation requires this to happen in-place. + pub unsafe fn new(parker: *mut Parker) { + parker.write(Parker { state: AtomicI8::new(EMPTY), wait_flag: WaitFlag::new() }) + } + + // This implementation doesn't require `unsafe` and `Pin`, but other implementations do. + pub unsafe fn park(self: Pin<&Self>) { + // The state values are chosen so that this subtraction changes + // `NOTIFIED` to `EMPTY` and `EMPTY` to `PARKED`. + let state = self.state.fetch_sub(1, SeqCst); + match state { + EMPTY => (), + NOTIFIED => return, + _ => panic!("inconsistent park state"), + } + + self.wait_flag.wait(); + + // We need to do a load here to use `Acquire` ordering. + self.state.swap(EMPTY, SeqCst); + } + + // This implementation doesn't require `unsafe` and `Pin`, but other implementations do. + pub unsafe fn park_timeout(self: Pin<&Self>, dur: Duration) { + let state = self.state.fetch_sub(1, SeqCst); + match state { + EMPTY => (), + NOTIFIED => return, + _ => panic!("inconsistent park state"), + } + + self.wait_flag.wait_timeout(dur); + let state = self.state.swap(EMPTY, SeqCst); + if state == NOTIFIED { + // The token was made available after the timeout occurred, but before + // we reset the state, so we need to reset the wait flag to avoid + // spurious wakeups. This wait has no timeout, but we know it will + // return quickly, as the unparking thread will definitely raise the + // flag if it has not already done so. + self.wait_flag.wait(); + } + } + + // This implementation doesn't require `Pin`, but other implementations do. + pub fn unpark(self: Pin<&Self>) { + let state = self.state.swap(NOTIFIED, SeqCst); + + if state == PARKED { + self.wait_flag.raise(); + } + } +} From 3b6ae15058dbb68710f92697265580c7e957629f Mon Sep 17 00:00:00 2001 From: joboet Date: Thu, 19 May 2022 14:37:29 +0200 Subject: [PATCH 0011/1135] std: fix deadlock in `Parker` --- library/std/src/sys/itron/wait_flag.rs | 13 +++++++++---- .../std/src/sys_common/thread_parker/wait_flag.rs | 8 ++++---- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/library/std/src/sys/itron/wait_flag.rs b/library/std/src/sys/itron/wait_flag.rs index 805f85a69b6c7..e432edd207754 100644 --- a/library/std/src/sys/itron/wait_flag.rs +++ b/library/std/src/sys/itron/wait_flag.rs @@ -42,13 +42,18 @@ impl WaitFlag { } /// Wait for the wait flag to be raised or the timeout to occur. - pub fn wait_timeout(&self, dur: Duration) { + /// + /// Returns whether the flag was raised (`true`) or the operation timed out (`false`). + pub fn wait_timeout(&self, dur: Duration) -> bool { let mut token = MaybeUninit::uninit(); - let er = with_tmos(dur, |tmout| unsafe { + let res = with_tmos(dur, |tmout| unsafe { abi::twai_flg(self.flag, RAISED, abi::TWF_ORW, token.as_mut_ptr(), tmout) }); - if er != abi::E_OK && er != abi::E_TMOUT { - fail(er, &"twai_flg"); + + match res { + abi::E_OK => true, + abi::E_TMOUT => false, + error => fail(error, &"twai_flg"), } } diff --git a/library/std/src/sys_common/thread_parker/wait_flag.rs b/library/std/src/sys_common/thread_parker/wait_flag.rs index 39a0df1cd3de4..f9581ff5d5774 100644 --- a/library/std/src/sys_common/thread_parker/wait_flag.rs +++ b/library/std/src/sys_common/thread_parker/wait_flag.rs @@ -2,7 +2,7 @@ //! //! Some operating systems provide low-level parking primitives like wait counts, //! event flags or semaphores which are not susceptible to race conditions (meaning -//! the wakeup can occure before the wait operation). To implement the `std` thread +//! the wakeup can occur before the wait operation). To implement the `std` thread //! parker on top of these primitives, we only have to ensure that parking is fast //! when the thread token is available, the atomic ordering guarantees are maintained //! and spurious wakeups are minimized. @@ -73,10 +73,10 @@ impl Parker { _ => panic!("inconsistent park state"), } - self.wait_flag.wait_timeout(dur); + let wakeup = self.wait_flag.wait_timeout(dur); let state = self.state.swap(EMPTY, SeqCst); - if state == NOTIFIED { - // The token was made available after the timeout occurred, but before + if state == NOTIFIED && !wakeup { + // The token was made available after the wait timed out, but before // we reset the state, so we need to reset the wait flag to avoid // spurious wakeups. This wait has no timeout, but we know it will // return quickly, as the unparking thread will definitely raise the From 369555c85a038d4cad396301822cf89627a7e5c6 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 23 May 2022 02:33:27 +0000 Subject: [PATCH 0012/1135] Implement `FusedIterator` for `std::net::[Into]Incoming` They never return `None`, so they trivially fulfill the contract. --- library/std/src/net/tcp.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/library/std/src/net/tcp.rs b/library/std/src/net/tcp.rs index 06300035633b9..2cbc9f22baf78 100644 --- a/library/std/src/net/tcp.rs +++ b/library/std/src/net/tcp.rs @@ -7,6 +7,7 @@ use crate::io::prelude::*; use crate::fmt; use crate::io::{self, IoSlice, IoSliceMut}; +use crate::iter::FusedIterator; use crate::net::{Shutdown, SocketAddr, ToSocketAddrs}; use crate::sys_common::net as net_imp; use crate::sys_common::{AsInner, FromInner, IntoInner}; @@ -1009,6 +1010,9 @@ impl<'a> Iterator for Incoming<'a> { } } +#[stable(feature = "tcp_listener_incoming_fused_iterator", since = "1.63.0")] +impl FusedIterator for Incoming<'_> {} + #[unstable(feature = "tcplistener_into_incoming", issue = "88339")] impl Iterator for IntoIncoming { type Item = io::Result; @@ -1017,6 +1021,9 @@ impl Iterator for IntoIncoming { } } +#[unstable(feature = "tcplistener_into_incoming", issue = "88339")] +impl FusedIterator for IntoIncoming {} + impl AsInner for TcpListener { fn as_inner(&self) -> &net_imp::TcpListener { &self.0 From 47080eacf83debe4cbd9bb8dbedaf2c29e084cbc Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Wed, 25 May 2022 11:44:44 +0200 Subject: [PATCH 0013/1135] Improve invalid memory ordering diagnostics. --- compiler/rustc_lint/src/types.rs | 143 +++++++++++++------------------ 1 file changed, 61 insertions(+), 82 deletions(-) diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 55b1ba9cd964a..170d85f5cd50b 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -4,7 +4,6 @@ use rustc_attr as attr; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_hir::def_id::DefId; use rustc_hir::{is_range_literal, Expr, ExprKind, Node}; use rustc_middle::ty::layout::{IntegerExt, LayoutOf, SizeSkeleton}; use rustc_middle::ty::subst::SubstsRef; @@ -1483,39 +1482,32 @@ impl InvalidAtomicOrdering { None } - fn matches_ordering(cx: &LateContext<'_>, did: DefId, orderings: &[Symbol]) -> bool { + fn match_ordering(cx: &LateContext<'_>, ord_arg: &Expr<'_>) -> Option { + let ExprKind::Path(ref ord_qpath) = ord_arg.kind else { return None }; + let did = cx.qpath_res(ord_qpath, ord_arg.hir_id).opt_def_id()?; let tcx = cx.tcx; let atomic_ordering = tcx.get_diagnostic_item(sym::Ordering); - orderings.iter().any(|ordering| { - tcx.item_name(did) == *ordering && { - let parent = tcx.parent(did); - Some(parent) == atomic_ordering - // needed in case this is a ctor, not a variant - || tcx.opt_parent(parent) == atomic_ordering - } - }) - } - - fn opt_ordering_defid(cx: &LateContext<'_>, ord_arg: &Expr<'_>) -> Option { - if let ExprKind::Path(ref ord_qpath) = ord_arg.kind { - cx.qpath_res(ord_qpath, ord_arg.hir_id).opt_def_id() - } else { - None - } + let name = tcx.item_name(did); + let parent = tcx.parent(did); + [sym::Relaxed, sym::Release, sym::Acquire, sym::AcqRel, sym::SeqCst].into_iter().find( + |&ordering| { + name == ordering + && (Some(parent) == atomic_ordering + // needed in case this is a ctor, not a variant + || tcx.opt_parent(parent) == atomic_ordering) + }, + ) } fn check_atomic_load_store(cx: &LateContext<'_>, expr: &Expr<'_>) { - use rustc_hir::def::{DefKind, Res}; - use rustc_hir::QPath; if let Some((method, args)) = Self::inherent_atomic_method_call(cx, expr, &[sym::load, sym::store]) && let Some((ordering_arg, invalid_ordering)) = match method { sym::load => Some((&args[1], sym::Release)), sym::store => Some((&args[2], sym::Acquire)), _ => None, } - && let ExprKind::Path(QPath::Resolved(_, path)) = ordering_arg.kind - && let Res::Def(DefKind::Ctor(..), ctor_id) = path.res - && Self::matches_ordering(cx, ctor_id, &[invalid_ordering, sym::AcqRel]) + && let Some(ordering) = Self::match_ordering(cx, ordering_arg) + && (ordering == invalid_ordering || ordering == sym::AcqRel) { cx.struct_span_lint(INVALID_ATOMIC_ORDERING, ordering_arg.span, |diag| { if method == sym::load { @@ -1537,9 +1529,7 @@ impl InvalidAtomicOrdering { && let ExprKind::Path(ref func_qpath) = func.kind && let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id() && matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::fence | sym::compiler_fence)) - && let ExprKind::Path(ref ordering_qpath) = &args[0].kind - && let Some(ordering_def_id) = cx.qpath_res(ordering_qpath, args[0].hir_id).opt_def_id() - && Self::matches_ordering(cx, ordering_def_id, &[sym::Relaxed]) + && Self::match_ordering(cx, &args[0]) == Some(sym::Relaxed) { cx.struct_span_lint(INVALID_ATOMIC_ORDERING, args[0].span, |diag| { diag.build("memory fences cannot have `Relaxed` ordering") @@ -1550,62 +1540,51 @@ impl InvalidAtomicOrdering { } fn check_atomic_compare_exchange(cx: &LateContext<'_>, expr: &Expr<'_>) { - if let Some((method, args)) = Self::inherent_atomic_method_call(cx, expr, &[sym::fetch_update, sym::compare_exchange, sym::compare_exchange_weak]) - && let Some((success_order_arg, failure_order_arg)) = match method { - sym::fetch_update => Some((&args[1], &args[2])), - sym::compare_exchange | sym::compare_exchange_weak => Some((&args[3], &args[4])), - _ => None, - } - && let Some(fail_ordering_def_id) = Self::opt_ordering_defid(cx, failure_order_arg) - { - // Helper type holding on to some checking and error reporting data. Has - // - (success ordering, - // - list of failure orderings forbidden by the success order, - // - suggestion message) - type OrdLintInfo = (Symbol, &'static [Symbol], &'static str); - const RELAXED: OrdLintInfo = (sym::Relaxed, &[sym::SeqCst, sym::Acquire], "ordering mode `Relaxed`"); - const ACQUIRE: OrdLintInfo = (sym::Acquire, &[sym::SeqCst], "ordering modes `Acquire` or `Relaxed`"); - const SEQ_CST: OrdLintInfo = (sym::SeqCst, &[], "ordering modes `Acquire`, `SeqCst` or `Relaxed`"); - const RELEASE: OrdLintInfo = (sym::Release, RELAXED.1, RELAXED.2); - const ACQREL: OrdLintInfo = (sym::AcqRel, ACQUIRE.1, ACQUIRE.2); - const SEARCH: [OrdLintInfo; 5] = [RELAXED, ACQUIRE, SEQ_CST, RELEASE, ACQREL]; - - let success_lint_info = Self::opt_ordering_defid(cx, success_order_arg) - .and_then(|success_ord_def_id| -> Option { - SEARCH - .iter() - .copied() - .find(|(ordering, ..)| { - Self::matches_ordering(cx, success_ord_def_id, &[*ordering]) - }) - }); - if Self::matches_ordering(cx, fail_ordering_def_id, &[sym::Release, sym::AcqRel]) { - // If we don't know the success order is, use what we'd suggest - // if it were maximally permissive. - let suggested = success_lint_info.unwrap_or(SEQ_CST).2; - cx.struct_span_lint(INVALID_ATOMIC_ORDERING, failure_order_arg.span, |diag| { - let msg = format!( - "{}'s failure ordering may not be `Release` or `AcqRel`", - method, - ); - diag.build(&msg) - .help(&format!("consider using {} instead", suggested)) - .emit(); - }); - } else if let Some((success_ord, bad_ords_given_success, suggested)) = success_lint_info { - if Self::matches_ordering(cx, fail_ordering_def_id, bad_ords_given_success) { - cx.struct_span_lint(INVALID_ATOMIC_ORDERING, failure_order_arg.span, |diag| { - let msg = format!( - "{}'s failure ordering may not be stronger than the success ordering of `{}`", - method, - success_ord, - ); - diag.build(&msg) - .help(&format!("consider using {} instead", suggested)) - .emit(); - }); - } - } + let Some((method, args)) = Self::inherent_atomic_method_call(cx, expr, &[sym::fetch_update, sym::compare_exchange, sym::compare_exchange_weak]) + else {return }; + + let (success_order_arg, fail_order_arg) = match method { + sym::fetch_update => (&args[1], &args[2]), + sym::compare_exchange | sym::compare_exchange_weak => (&args[3], &args[4]), + _ => return, + }; + + let Some(fail_ordering) = Self::match_ordering(cx, fail_order_arg) else { return }; + + if matches!(fail_ordering, sym::Release | sym::AcqRel) { + cx.struct_span_lint(INVALID_ATOMIC_ORDERING, fail_order_arg.span, |diag| { + diag.build(&format!( + "{method}'s failure ordering may not be `Release` or `AcqRel`, \ + since a failed {method} does not result in a write", + )) + .span_label(fail_order_arg.span, "invalid failure ordering") + .help("consider using Acquire or Relaxed failure ordering instead") + .emit(); + }); + } + + let Some(success_ordering) = Self::match_ordering(cx, success_order_arg) else { return }; + + if matches!( + (success_ordering, fail_ordering), + (sym::Relaxed | sym::Release, sym::Acquire) + | (sym::Relaxed | sym::Release | sym::Acquire | sym::AcqRel, sym::SeqCst) + ) { + let success_suggestion = + if success_ordering == sym::Release && fail_ordering == sym::Acquire { + sym::AcqRel + } else { + fail_ordering + }; + cx.struct_span_lint(INVALID_ATOMIC_ORDERING, success_order_arg.span, |diag| { + diag.build(&format!( + "{method}'s success ordering must be at least as strong as its failure ordering" + )) + .span_label(fail_order_arg.span, format!("{fail_ordering} failure ordering")) + .span_label(success_order_arg.span, format!("{success_ordering} success ordering")) + .help(format!("consider using {success_suggestion} success ordering instead")) + .emit(); + }); } } } From 1b83dcffcee773bc94db998f8c37e1b1bfb67698 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Wed, 25 May 2022 11:49:02 +0200 Subject: [PATCH 0014/1135] Update tests. --- ...t-invalid-atomic-ordering-exchange-weak.rs | 12 +- ...valid-atomic-ordering-exchange-weak.stderr | 120 ++++++++++-------- .../lint-invalid-atomic-ordering-exchange.rs | 12 +- ...nt-invalid-atomic-ordering-exchange.stderr | 120 ++++++++++-------- ...nt-invalid-atomic-ordering-fetch-update.rs | 12 +- ...nvalid-atomic-ordering-fetch-update.stderr | 120 ++++++++++-------- 6 files changed, 216 insertions(+), 180 deletions(-) diff --git a/src/test/ui/lint/lint-invalid-atomic-ordering-exchange-weak.rs b/src/test/ui/lint/lint-invalid-atomic-ordering-exchange-weak.rs index c79c1daf77410..1f870d1ef72ce 100644 --- a/src/test/ui/lint/lint-invalid-atomic-ordering-exchange-weak.rs +++ b/src/test/ui/lint/lint-invalid-atomic-ordering-exchange-weak.rs @@ -44,19 +44,19 @@ fn main() { // Release success order forbids failure order of Acquire or SeqCst let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Release, Ordering::Acquire); - //~^ ERROR compare_exchange_weak's failure ordering may not be stronger + //~^ ERROR compare_exchange_weak's success ordering must be at least as strong as let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Release, Ordering::SeqCst); - //~^ ERROR compare_exchange_weak's failure ordering may not be stronger + //~^ ERROR compare_exchange_weak's success ordering must be at least as strong as // Relaxed success order also forbids failure order of Acquire or SeqCst let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering::SeqCst); - //~^ ERROR compare_exchange_weak's failure ordering may not be stronger + //~^ ERROR compare_exchange_weak's success ordering must be at least as strong as let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering::Acquire); - //~^ ERROR compare_exchange_weak's failure ordering may not be stronger + //~^ ERROR compare_exchange_weak's success ordering must be at least as strong as // Acquire/AcqRel forbids failure order of SeqCst let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Acquire, Ordering::SeqCst); - //~^ ERROR compare_exchange_weak's failure ordering may not be stronger + //~^ ERROR compare_exchange_weak's success ordering must be at least as strong as let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::AcqRel, Ordering::SeqCst); - //~^ ERROR compare_exchange_weak's failure ordering may not be stronger + //~^ ERROR compare_exchange_weak's success ordering must be at least as strong as } diff --git a/src/test/ui/lint/lint-invalid-atomic-ordering-exchange-weak.stderr b/src/test/ui/lint/lint-invalid-atomic-ordering-exchange-weak.stderr index 13350ab0b9c02..8e816f7c48251 100644 --- a/src/test/ui/lint/lint-invalid-atomic-ordering-exchange-weak.stderr +++ b/src/test/ui/lint/lint-invalid-atomic-ordering-exchange-weak.stderr @@ -1,131 +1,143 @@ -error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel`, since a failed compare_exchange_weak does not result in a write --> $DIR/lint-invalid-atomic-ordering-exchange-weak.rs:22:67 | LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Relaxed, Ordering::AcqRel); - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ invalid failure ordering | = note: `#[deny(invalid_atomic_ordering)]` on by default - = help: consider using ordering mode `Relaxed` instead + = help: consider using Acquire or Relaxed failure ordering instead -error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel`, since a failed compare_exchange_weak does not result in a write --> $DIR/lint-invalid-atomic-ordering-exchange-weak.rs:24:67 | LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Acquire, Ordering::AcqRel); - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ invalid failure ordering | - = help: consider using ordering modes `Acquire` or `Relaxed` instead + = help: consider using Acquire or Relaxed failure ordering instead -error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel`, since a failed compare_exchange_weak does not result in a write --> $DIR/lint-invalid-atomic-ordering-exchange-weak.rs:26:67 | LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Release, Ordering::AcqRel); - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ invalid failure ordering | - = help: consider using ordering mode `Relaxed` instead + = help: consider using Acquire or Relaxed failure ordering instead -error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel`, since a failed compare_exchange_weak does not result in a write --> $DIR/lint-invalid-atomic-ordering-exchange-weak.rs:28:66 | LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::AcqRel, Ordering::AcqRel); - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ invalid failure ordering | - = help: consider using ordering modes `Acquire` or `Relaxed` instead + = help: consider using Acquire or Relaxed failure ordering instead -error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel`, since a failed compare_exchange_weak does not result in a write --> $DIR/lint-invalid-atomic-ordering-exchange-weak.rs:30:66 | LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::SeqCst, Ordering::AcqRel); - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ invalid failure ordering | - = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead + = help: consider using Acquire or Relaxed failure ordering instead -error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel`, since a failed compare_exchange_weak does not result in a write --> $DIR/lint-invalid-atomic-ordering-exchange-weak.rs:34:67 | LL | let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering::Release); - | ^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^ invalid failure ordering | - = help: consider using ordering mode `Relaxed` instead + = help: consider using Acquire or Relaxed failure ordering instead -error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel`, since a failed compare_exchange_weak does not result in a write --> $DIR/lint-invalid-atomic-ordering-exchange-weak.rs:36:67 | LL | let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Acquire, Ordering::Release); - | ^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^ invalid failure ordering | - = help: consider using ordering modes `Acquire` or `Relaxed` instead + = help: consider using Acquire or Relaxed failure ordering instead -error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel`, since a failed compare_exchange_weak does not result in a write --> $DIR/lint-invalid-atomic-ordering-exchange-weak.rs:38:67 | LL | let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Release, Ordering::Release); - | ^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^ invalid failure ordering | - = help: consider using ordering mode `Relaxed` instead + = help: consider using Acquire or Relaxed failure ordering instead -error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel`, since a failed compare_exchange_weak does not result in a write --> $DIR/lint-invalid-atomic-ordering-exchange-weak.rs:40:66 | LL | let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::AcqRel, Ordering::Release); - | ^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^ invalid failure ordering | - = help: consider using ordering modes `Acquire` or `Relaxed` instead + = help: consider using Acquire or Relaxed failure ordering instead -error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel`, since a failed compare_exchange_weak does not result in a write --> $DIR/lint-invalid-atomic-ordering-exchange-weak.rs:42:66 | LL | let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::SeqCst, Ordering::Release); - | ^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^ invalid failure ordering | - = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead + = help: consider using Acquire or Relaxed failure ordering instead -error: compare_exchange_weak's failure ordering may not be stronger than the success ordering of `Release` - --> $DIR/lint-invalid-atomic-ordering-exchange-weak.rs:46:67 +error: compare_exchange_weak's success ordering must be at least as strong as its failure ordering + --> $DIR/lint-invalid-atomic-ordering-exchange-weak.rs:46:48 | LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Release, Ordering::Acquire); - | ^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^ ----------------- Acquire failure ordering + | | + | Release success ordering | - = help: consider using ordering mode `Relaxed` instead + = help: consider using AcqRel success ordering instead -error: compare_exchange_weak's failure ordering may not be stronger than the success ordering of `Release` - --> $DIR/lint-invalid-atomic-ordering-exchange-weak.rs:48:67 +error: compare_exchange_weak's success ordering must be at least as strong as its failure ordering + --> $DIR/lint-invalid-atomic-ordering-exchange-weak.rs:48:48 | LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Release, Ordering::SeqCst); - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^ ---------------- SeqCst failure ordering + | | + | Release success ordering | - = help: consider using ordering mode `Relaxed` instead + = help: consider using SeqCst success ordering instead -error: compare_exchange_weak's failure ordering may not be stronger than the success ordering of `Relaxed` - --> $DIR/lint-invalid-atomic-ordering-exchange-weak.rs:52:67 +error: compare_exchange_weak's success ordering must be at least as strong as its failure ordering + --> $DIR/lint-invalid-atomic-ordering-exchange-weak.rs:52:48 | LL | let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering::SeqCst); - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^ ---------------- SeqCst failure ordering + | | + | Relaxed success ordering | - = help: consider using ordering mode `Relaxed` instead + = help: consider using SeqCst success ordering instead -error: compare_exchange_weak's failure ordering may not be stronger than the success ordering of `Relaxed` - --> $DIR/lint-invalid-atomic-ordering-exchange-weak.rs:54:67 +error: compare_exchange_weak's success ordering must be at least as strong as its failure ordering + --> $DIR/lint-invalid-atomic-ordering-exchange-weak.rs:54:48 | LL | let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering::Acquire); - | ^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^ ----------------- Acquire failure ordering + | | + | Relaxed success ordering | - = help: consider using ordering mode `Relaxed` instead + = help: consider using Acquire success ordering instead -error: compare_exchange_weak's failure ordering may not be stronger than the success ordering of `Acquire` - --> $DIR/lint-invalid-atomic-ordering-exchange-weak.rs:58:67 +error: compare_exchange_weak's success ordering must be at least as strong as its failure ordering + --> $DIR/lint-invalid-atomic-ordering-exchange-weak.rs:58:48 | LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Acquire, Ordering::SeqCst); - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^ ---------------- SeqCst failure ordering + | | + | Acquire success ordering | - = help: consider using ordering modes `Acquire` or `Relaxed` instead + = help: consider using SeqCst success ordering instead -error: compare_exchange_weak's failure ordering may not be stronger than the success ordering of `AcqRel` - --> $DIR/lint-invalid-atomic-ordering-exchange-weak.rs:60:66 +error: compare_exchange_weak's success ordering must be at least as strong as its failure ordering + --> $DIR/lint-invalid-atomic-ordering-exchange-weak.rs:60:48 | LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::AcqRel, Ordering::SeqCst); - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ ---------------- SeqCst failure ordering + | | + | AcqRel success ordering | - = help: consider using ordering modes `Acquire` or `Relaxed` instead + = help: consider using SeqCst success ordering instead error: aborting due to 16 previous errors diff --git a/src/test/ui/lint/lint-invalid-atomic-ordering-exchange.rs b/src/test/ui/lint/lint-invalid-atomic-ordering-exchange.rs index 8ef3a400cf040..1a5cb3dac6b5c 100644 --- a/src/test/ui/lint/lint-invalid-atomic-ordering-exchange.rs +++ b/src/test/ui/lint/lint-invalid-atomic-ordering-exchange.rs @@ -42,19 +42,19 @@ fn main() { // Release success order forbids failure order of Acquire or SeqCst let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::Acquire); - //~^ ERROR compare_exchange's failure ordering may not be stronger + //~^ ERROR compare_exchange's success ordering must be at least as strong as let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::SeqCst); - //~^ ERROR compare_exchange's failure ordering may not be stronger + //~^ ERROR compare_exchange's success ordering must be at least as strong as // Relaxed success order also forbids failure order of Acquire or SeqCst let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::SeqCst); - //~^ ERROR compare_exchange's failure ordering may not be stronger + //~^ ERROR compare_exchange's success ordering must be at least as strong as let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::Acquire); - //~^ ERROR compare_exchange's failure ordering may not be stronger + //~^ ERROR compare_exchange's success ordering must be at least as strong as // Acquire/AcqRel forbids failure order of SeqCst let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::SeqCst); - //~^ ERROR compare_exchange's failure ordering may not be stronger + //~^ ERROR compare_exchange's success ordering must be at least as strong as let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::SeqCst); - //~^ ERROR compare_exchange's failure ordering may not be stronger + //~^ ERROR compare_exchange's success ordering must be at least as strong as } diff --git a/src/test/ui/lint/lint-invalid-atomic-ordering-exchange.stderr b/src/test/ui/lint/lint-invalid-atomic-ordering-exchange.stderr index daedfec743093..eeeeb1fb5a94e 100644 --- a/src/test/ui/lint/lint-invalid-atomic-ordering-exchange.stderr +++ b/src/test/ui/lint/lint-invalid-atomic-ordering-exchange.stderr @@ -1,131 +1,143 @@ -error: compare_exchange's failure ordering may not be `Release` or `AcqRel` +error: compare_exchange's failure ordering may not be `Release` or `AcqRel`, since a failed compare_exchange does not result in a write --> $DIR/lint-invalid-atomic-ordering-exchange.rs:20:57 | LL | let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::AcqRel); - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ invalid failure ordering | = note: `#[deny(invalid_atomic_ordering)]` on by default - = help: consider using ordering mode `Relaxed` instead + = help: consider using Acquire or Relaxed failure ordering instead -error: compare_exchange's failure ordering may not be `Release` or `AcqRel` +error: compare_exchange's failure ordering may not be `Release` or `AcqRel`, since a failed compare_exchange does not result in a write --> $DIR/lint-invalid-atomic-ordering-exchange.rs:22:57 | LL | let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::AcqRel); - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ invalid failure ordering | - = help: consider using ordering modes `Acquire` or `Relaxed` instead + = help: consider using Acquire or Relaxed failure ordering instead -error: compare_exchange's failure ordering may not be `Release` or `AcqRel` +error: compare_exchange's failure ordering may not be `Release` or `AcqRel`, since a failed compare_exchange does not result in a write --> $DIR/lint-invalid-atomic-ordering-exchange.rs:24:57 | LL | let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::AcqRel); - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ invalid failure ordering | - = help: consider using ordering mode `Relaxed` instead + = help: consider using Acquire or Relaxed failure ordering instead -error: compare_exchange's failure ordering may not be `Release` or `AcqRel` +error: compare_exchange's failure ordering may not be `Release` or `AcqRel`, since a failed compare_exchange does not result in a write --> $DIR/lint-invalid-atomic-ordering-exchange.rs:26:56 | LL | let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::AcqRel); - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ invalid failure ordering | - = help: consider using ordering modes `Acquire` or `Relaxed` instead + = help: consider using Acquire or Relaxed failure ordering instead -error: compare_exchange's failure ordering may not be `Release` or `AcqRel` +error: compare_exchange's failure ordering may not be `Release` or `AcqRel`, since a failed compare_exchange does not result in a write --> $DIR/lint-invalid-atomic-ordering-exchange.rs:28:56 | LL | let _ = x.compare_exchange(0, 0, Ordering::SeqCst, Ordering::AcqRel); - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ invalid failure ordering | - = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead + = help: consider using Acquire or Relaxed failure ordering instead -error: compare_exchange's failure ordering may not be `Release` or `AcqRel` +error: compare_exchange's failure ordering may not be `Release` or `AcqRel`, since a failed compare_exchange does not result in a write --> $DIR/lint-invalid-atomic-ordering-exchange.rs:32:57 | LL | let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::Release); - | ^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^ invalid failure ordering | - = help: consider using ordering mode `Relaxed` instead + = help: consider using Acquire or Relaxed failure ordering instead -error: compare_exchange's failure ordering may not be `Release` or `AcqRel` +error: compare_exchange's failure ordering may not be `Release` or `AcqRel`, since a failed compare_exchange does not result in a write --> $DIR/lint-invalid-atomic-ordering-exchange.rs:34:57 | LL | let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::Release); - | ^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^ invalid failure ordering | - = help: consider using ordering modes `Acquire` or `Relaxed` instead + = help: consider using Acquire or Relaxed failure ordering instead -error: compare_exchange's failure ordering may not be `Release` or `AcqRel` +error: compare_exchange's failure ordering may not be `Release` or `AcqRel`, since a failed compare_exchange does not result in a write --> $DIR/lint-invalid-atomic-ordering-exchange.rs:36:57 | LL | let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::Release); - | ^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^ invalid failure ordering | - = help: consider using ordering mode `Relaxed` instead + = help: consider using Acquire or Relaxed failure ordering instead -error: compare_exchange's failure ordering may not be `Release` or `AcqRel` +error: compare_exchange's failure ordering may not be `Release` or `AcqRel`, since a failed compare_exchange does not result in a write --> $DIR/lint-invalid-atomic-ordering-exchange.rs:38:56 | LL | let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::Release); - | ^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^ invalid failure ordering | - = help: consider using ordering modes `Acquire` or `Relaxed` instead + = help: consider using Acquire or Relaxed failure ordering instead -error: compare_exchange's failure ordering may not be `Release` or `AcqRel` +error: compare_exchange's failure ordering may not be `Release` or `AcqRel`, since a failed compare_exchange does not result in a write --> $DIR/lint-invalid-atomic-ordering-exchange.rs:40:56 | LL | let _ = x.compare_exchange(0, 0, Ordering::SeqCst, Ordering::Release); - | ^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^ invalid failure ordering | - = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead + = help: consider using Acquire or Relaxed failure ordering instead -error: compare_exchange's failure ordering may not be stronger than the success ordering of `Release` - --> $DIR/lint-invalid-atomic-ordering-exchange.rs:44:57 +error: compare_exchange's success ordering must be at least as strong as its failure ordering + --> $DIR/lint-invalid-atomic-ordering-exchange.rs:44:38 | LL | let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::Acquire); - | ^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^ ----------------- Acquire failure ordering + | | + | Release success ordering | - = help: consider using ordering mode `Relaxed` instead + = help: consider using AcqRel success ordering instead -error: compare_exchange's failure ordering may not be stronger than the success ordering of `Release` - --> $DIR/lint-invalid-atomic-ordering-exchange.rs:46:57 +error: compare_exchange's success ordering must be at least as strong as its failure ordering + --> $DIR/lint-invalid-atomic-ordering-exchange.rs:46:38 | LL | let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::SeqCst); - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^ ---------------- SeqCst failure ordering + | | + | Release success ordering | - = help: consider using ordering mode `Relaxed` instead + = help: consider using SeqCst success ordering instead -error: compare_exchange's failure ordering may not be stronger than the success ordering of `Relaxed` - --> $DIR/lint-invalid-atomic-ordering-exchange.rs:50:57 +error: compare_exchange's success ordering must be at least as strong as its failure ordering + --> $DIR/lint-invalid-atomic-ordering-exchange.rs:50:38 | LL | let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::SeqCst); - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^ ---------------- SeqCst failure ordering + | | + | Relaxed success ordering | - = help: consider using ordering mode `Relaxed` instead + = help: consider using SeqCst success ordering instead -error: compare_exchange's failure ordering may not be stronger than the success ordering of `Relaxed` - --> $DIR/lint-invalid-atomic-ordering-exchange.rs:52:57 +error: compare_exchange's success ordering must be at least as strong as its failure ordering + --> $DIR/lint-invalid-atomic-ordering-exchange.rs:52:38 | LL | let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::Acquire); - | ^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^ ----------------- Acquire failure ordering + | | + | Relaxed success ordering | - = help: consider using ordering mode `Relaxed` instead + = help: consider using Acquire success ordering instead -error: compare_exchange's failure ordering may not be stronger than the success ordering of `Acquire` - --> $DIR/lint-invalid-atomic-ordering-exchange.rs:56:57 +error: compare_exchange's success ordering must be at least as strong as its failure ordering + --> $DIR/lint-invalid-atomic-ordering-exchange.rs:56:38 | LL | let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::SeqCst); - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^ ---------------- SeqCst failure ordering + | | + | Acquire success ordering | - = help: consider using ordering modes `Acquire` or `Relaxed` instead + = help: consider using SeqCst success ordering instead -error: compare_exchange's failure ordering may not be stronger than the success ordering of `AcqRel` - --> $DIR/lint-invalid-atomic-ordering-exchange.rs:58:56 +error: compare_exchange's success ordering must be at least as strong as its failure ordering + --> $DIR/lint-invalid-atomic-ordering-exchange.rs:58:38 | LL | let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::SeqCst); - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ ---------------- SeqCst failure ordering + | | + | AcqRel success ordering | - = help: consider using ordering modes `Acquire` or `Relaxed` instead + = help: consider using SeqCst success ordering instead error: aborting due to 16 previous errors diff --git a/src/test/ui/lint/lint-invalid-atomic-ordering-fetch-update.rs b/src/test/ui/lint/lint-invalid-atomic-ordering-fetch-update.rs index 938ca0359f845..dcf277dfaccfd 100644 --- a/src/test/ui/lint/lint-invalid-atomic-ordering-fetch-update.rs +++ b/src/test/ui/lint/lint-invalid-atomic-ordering-fetch-update.rs @@ -42,19 +42,19 @@ fn main() { // Release success order forbids failure order of Acquire or SeqCst let _ = x.fetch_update(Ordering::Release, Ordering::Acquire, |old| Some(old + 1)); - //~^ ERROR fetch_update's failure ordering may not be stronger + //~^ ERROR fetch_update's success ordering must be at least as strong as let _ = x.fetch_update(Ordering::Release, Ordering::SeqCst, |old| Some(old + 1)); - //~^ ERROR fetch_update's failure ordering may not be stronger + //~^ ERROR fetch_update's success ordering must be at least as strong as // Relaxed success order also forbids failure order of Acquire or SeqCst let _ = x.fetch_update(Ordering::Relaxed, Ordering::SeqCst, |old| Some(old + 1)); - //~^ ERROR fetch_update's failure ordering may not be stronger + //~^ ERROR fetch_update's success ordering must be at least as strong as let _ = x.fetch_update(Ordering::Relaxed, Ordering::Acquire, |old| Some(old + 1)); - //~^ ERROR fetch_update's failure ordering may not be stronger + //~^ ERROR fetch_update's success ordering must be at least as strong as // Acquire/AcqRel forbids failure order of SeqCst let _ = x.fetch_update(Ordering::Acquire, Ordering::SeqCst, |old| Some(old + 1)); - //~^ ERROR fetch_update's failure ordering may not be stronger + //~^ ERROR fetch_update's success ordering must be at least as strong as let _ = x.fetch_update(Ordering::AcqRel, Ordering::SeqCst, |old| Some(old + 1)); - //~^ ERROR fetch_update's failure ordering may not be stronger + //~^ ERROR fetch_update's success ordering must be at least as strong as } diff --git a/src/test/ui/lint/lint-invalid-atomic-ordering-fetch-update.stderr b/src/test/ui/lint/lint-invalid-atomic-ordering-fetch-update.stderr index dabc1da7e55c4..f63d059d42915 100644 --- a/src/test/ui/lint/lint-invalid-atomic-ordering-fetch-update.stderr +++ b/src/test/ui/lint/lint-invalid-atomic-ordering-fetch-update.stderr @@ -1,131 +1,143 @@ -error: fetch_update's failure ordering may not be `Release` or `AcqRel` +error: fetch_update's failure ordering may not be `Release` or `AcqRel`, since a failed fetch_update does not result in a write --> $DIR/lint-invalid-atomic-ordering-fetch-update.rs:20:47 | LL | let _ = x.fetch_update(Ordering::Relaxed, Ordering::AcqRel, |old| Some(old + 1)); - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ invalid failure ordering | = note: `#[deny(invalid_atomic_ordering)]` on by default - = help: consider using ordering mode `Relaxed` instead + = help: consider using Acquire or Relaxed failure ordering instead -error: fetch_update's failure ordering may not be `Release` or `AcqRel` +error: fetch_update's failure ordering may not be `Release` or `AcqRel`, since a failed fetch_update does not result in a write --> $DIR/lint-invalid-atomic-ordering-fetch-update.rs:22:47 | LL | let _ = x.fetch_update(Ordering::Acquire, Ordering::AcqRel, |old| Some(old + 1)); - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ invalid failure ordering | - = help: consider using ordering modes `Acquire` or `Relaxed` instead + = help: consider using Acquire or Relaxed failure ordering instead -error: fetch_update's failure ordering may not be `Release` or `AcqRel` +error: fetch_update's failure ordering may not be `Release` or `AcqRel`, since a failed fetch_update does not result in a write --> $DIR/lint-invalid-atomic-ordering-fetch-update.rs:24:47 | LL | let _ = x.fetch_update(Ordering::Release, Ordering::AcqRel, |old| Some(old + 1)); - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ invalid failure ordering | - = help: consider using ordering mode `Relaxed` instead + = help: consider using Acquire or Relaxed failure ordering instead -error: fetch_update's failure ordering may not be `Release` or `AcqRel` +error: fetch_update's failure ordering may not be `Release` or `AcqRel`, since a failed fetch_update does not result in a write --> $DIR/lint-invalid-atomic-ordering-fetch-update.rs:26:46 | LL | let _ = x.fetch_update(Ordering::AcqRel, Ordering::AcqRel, |old| Some(old + 1)); - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ invalid failure ordering | - = help: consider using ordering modes `Acquire` or `Relaxed` instead + = help: consider using Acquire or Relaxed failure ordering instead -error: fetch_update's failure ordering may not be `Release` or `AcqRel` +error: fetch_update's failure ordering may not be `Release` or `AcqRel`, since a failed fetch_update does not result in a write --> $DIR/lint-invalid-atomic-ordering-fetch-update.rs:28:46 | LL | let _ = x.fetch_update(Ordering::SeqCst, Ordering::AcqRel, |old| Some(old + 1)); - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ invalid failure ordering | - = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead + = help: consider using Acquire or Relaxed failure ordering instead -error: fetch_update's failure ordering may not be `Release` or `AcqRel` +error: fetch_update's failure ordering may not be `Release` or `AcqRel`, since a failed fetch_update does not result in a write --> $DIR/lint-invalid-atomic-ordering-fetch-update.rs:32:47 | LL | let _ = x.fetch_update(Ordering::Relaxed, Ordering::Release, |old| Some(old + 1)); - | ^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^ invalid failure ordering | - = help: consider using ordering mode `Relaxed` instead + = help: consider using Acquire or Relaxed failure ordering instead -error: fetch_update's failure ordering may not be `Release` or `AcqRel` +error: fetch_update's failure ordering may not be `Release` or `AcqRel`, since a failed fetch_update does not result in a write --> $DIR/lint-invalid-atomic-ordering-fetch-update.rs:34:47 | LL | let _ = x.fetch_update(Ordering::Acquire, Ordering::Release, |old| Some(old + 1)); - | ^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^ invalid failure ordering | - = help: consider using ordering modes `Acquire` or `Relaxed` instead + = help: consider using Acquire or Relaxed failure ordering instead -error: fetch_update's failure ordering may not be `Release` or `AcqRel` +error: fetch_update's failure ordering may not be `Release` or `AcqRel`, since a failed fetch_update does not result in a write --> $DIR/lint-invalid-atomic-ordering-fetch-update.rs:36:47 | LL | let _ = x.fetch_update(Ordering::Release, Ordering::Release, |old| Some(old + 1)); - | ^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^ invalid failure ordering | - = help: consider using ordering mode `Relaxed` instead + = help: consider using Acquire or Relaxed failure ordering instead -error: fetch_update's failure ordering may not be `Release` or `AcqRel` +error: fetch_update's failure ordering may not be `Release` or `AcqRel`, since a failed fetch_update does not result in a write --> $DIR/lint-invalid-atomic-ordering-fetch-update.rs:38:46 | LL | let _ = x.fetch_update(Ordering::AcqRel, Ordering::Release, |old| Some(old + 1)); - | ^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^ invalid failure ordering | - = help: consider using ordering modes `Acquire` or `Relaxed` instead + = help: consider using Acquire or Relaxed failure ordering instead -error: fetch_update's failure ordering may not be `Release` or `AcqRel` +error: fetch_update's failure ordering may not be `Release` or `AcqRel`, since a failed fetch_update does not result in a write --> $DIR/lint-invalid-atomic-ordering-fetch-update.rs:40:46 | LL | let _ = x.fetch_update(Ordering::SeqCst, Ordering::Release, |old| Some(old + 1)); - | ^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^ invalid failure ordering | - = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead + = help: consider using Acquire or Relaxed failure ordering instead -error: fetch_update's failure ordering may not be stronger than the success ordering of `Release` - --> $DIR/lint-invalid-atomic-ordering-fetch-update.rs:44:47 +error: fetch_update's success ordering must be at least as strong as its failure ordering + --> $DIR/lint-invalid-atomic-ordering-fetch-update.rs:44:28 | LL | let _ = x.fetch_update(Ordering::Release, Ordering::Acquire, |old| Some(old + 1)); - | ^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^ ----------------- Acquire failure ordering + | | + | Release success ordering | - = help: consider using ordering mode `Relaxed` instead + = help: consider using AcqRel success ordering instead -error: fetch_update's failure ordering may not be stronger than the success ordering of `Release` - --> $DIR/lint-invalid-atomic-ordering-fetch-update.rs:46:47 +error: fetch_update's success ordering must be at least as strong as its failure ordering + --> $DIR/lint-invalid-atomic-ordering-fetch-update.rs:46:28 | LL | let _ = x.fetch_update(Ordering::Release, Ordering::SeqCst, |old| Some(old + 1)); - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^ ---------------- SeqCst failure ordering + | | + | Release success ordering | - = help: consider using ordering mode `Relaxed` instead + = help: consider using SeqCst success ordering instead -error: fetch_update's failure ordering may not be stronger than the success ordering of `Relaxed` - --> $DIR/lint-invalid-atomic-ordering-fetch-update.rs:50:47 +error: fetch_update's success ordering must be at least as strong as its failure ordering + --> $DIR/lint-invalid-atomic-ordering-fetch-update.rs:50:28 | LL | let _ = x.fetch_update(Ordering::Relaxed, Ordering::SeqCst, |old| Some(old + 1)); - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^ ---------------- SeqCst failure ordering + | | + | Relaxed success ordering | - = help: consider using ordering mode `Relaxed` instead + = help: consider using SeqCst success ordering instead -error: fetch_update's failure ordering may not be stronger than the success ordering of `Relaxed` - --> $DIR/lint-invalid-atomic-ordering-fetch-update.rs:52:47 +error: fetch_update's success ordering must be at least as strong as its failure ordering + --> $DIR/lint-invalid-atomic-ordering-fetch-update.rs:52:28 | LL | let _ = x.fetch_update(Ordering::Relaxed, Ordering::Acquire, |old| Some(old + 1)); - | ^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^ ----------------- Acquire failure ordering + | | + | Relaxed success ordering | - = help: consider using ordering mode `Relaxed` instead + = help: consider using Acquire success ordering instead -error: fetch_update's failure ordering may not be stronger than the success ordering of `Acquire` - --> $DIR/lint-invalid-atomic-ordering-fetch-update.rs:56:47 +error: fetch_update's success ordering must be at least as strong as its failure ordering + --> $DIR/lint-invalid-atomic-ordering-fetch-update.rs:56:28 | LL | let _ = x.fetch_update(Ordering::Acquire, Ordering::SeqCst, |old| Some(old + 1)); - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^ ---------------- SeqCst failure ordering + | | + | Acquire success ordering | - = help: consider using ordering modes `Acquire` or `Relaxed` instead + = help: consider using SeqCst success ordering instead -error: fetch_update's failure ordering may not be stronger than the success ordering of `AcqRel` - --> $DIR/lint-invalid-atomic-ordering-fetch-update.rs:58:46 +error: fetch_update's success ordering must be at least as strong as its failure ordering + --> $DIR/lint-invalid-atomic-ordering-fetch-update.rs:58:28 | LL | let _ = x.fetch_update(Ordering::AcqRel, Ordering::SeqCst, |old| Some(old + 1)); - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ ---------------- SeqCst failure ordering + | | + | AcqRel success ordering | - = help: consider using ordering modes `Acquire` or `Relaxed` instead + = help: consider using SeqCst success ordering instead error: aborting due to 16 previous errors From 1c1f221845f41e1c5663ec7f963181c9b6be95d0 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Wed, 25 May 2022 12:18:48 +0200 Subject: [PATCH 0015/1135] Tweak memory ordering diagnostic. --- compiler/rustc_lint/src/types.rs | 7 ++++++- ...nvalid-atomic-ordering-exchange-weak.stderr | 18 ++++++------------ ...int-invalid-atomic-ordering-exchange.stderr | 18 ++++++------------ ...invalid-atomic-ordering-fetch-update.stderr | 18 ++++++------------ 4 files changed, 24 insertions(+), 37 deletions(-) diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 170d85f5cd50b..b3802b719fe36 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -1582,7 +1582,12 @@ impl InvalidAtomicOrdering { )) .span_label(fail_order_arg.span, format!("{fail_ordering} failure ordering")) .span_label(success_order_arg.span, format!("{success_ordering} success ordering")) - .help(format!("consider using {success_suggestion} success ordering instead")) + .span_suggestion_short( + success_order_arg.span, + format!("consider using {success_suggestion} success ordering instead"), + success_suggestion.to_string(), + Applicability::MaybeIncorrect, + ) .emit(); }); } diff --git a/src/test/ui/lint/lint-invalid-atomic-ordering-exchange-weak.stderr b/src/test/ui/lint/lint-invalid-atomic-ordering-exchange-weak.stderr index 8e816f7c48251..030b065cf4f8b 100644 --- a/src/test/ui/lint/lint-invalid-atomic-ordering-exchange-weak.stderr +++ b/src/test/ui/lint/lint-invalid-atomic-ordering-exchange-weak.stderr @@ -86,8 +86,7 @@ LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Release, Ordering: | ^^^^^^^^^^^^^^^^^ ----------------- Acquire failure ordering | | | Release success ordering - | - = help: consider using AcqRel success ordering instead + | help: consider using AcqRel success ordering instead error: compare_exchange_weak's success ordering must be at least as strong as its failure ordering --> $DIR/lint-invalid-atomic-ordering-exchange-weak.rs:48:48 @@ -96,8 +95,7 @@ LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Release, Ordering: | ^^^^^^^^^^^^^^^^^ ---------------- SeqCst failure ordering | | | Release success ordering - | - = help: consider using SeqCst success ordering instead + | help: consider using SeqCst success ordering instead error: compare_exchange_weak's success ordering must be at least as strong as its failure ordering --> $DIR/lint-invalid-atomic-ordering-exchange-weak.rs:52:48 @@ -106,8 +104,7 @@ LL | let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering: | ^^^^^^^^^^^^^^^^^ ---------------- SeqCst failure ordering | | | Relaxed success ordering - | - = help: consider using SeqCst success ordering instead + | help: consider using SeqCst success ordering instead error: compare_exchange_weak's success ordering must be at least as strong as its failure ordering --> $DIR/lint-invalid-atomic-ordering-exchange-weak.rs:54:48 @@ -116,8 +113,7 @@ LL | let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering: | ^^^^^^^^^^^^^^^^^ ----------------- Acquire failure ordering | | | Relaxed success ordering - | - = help: consider using Acquire success ordering instead + | help: consider using Acquire success ordering instead error: compare_exchange_weak's success ordering must be at least as strong as its failure ordering --> $DIR/lint-invalid-atomic-ordering-exchange-weak.rs:58:48 @@ -126,8 +122,7 @@ LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Acquire, Ordering: | ^^^^^^^^^^^^^^^^^ ---------------- SeqCst failure ordering | | | Acquire success ordering - | - = help: consider using SeqCst success ordering instead + | help: consider using SeqCst success ordering instead error: compare_exchange_weak's success ordering must be at least as strong as its failure ordering --> $DIR/lint-invalid-atomic-ordering-exchange-weak.rs:60:48 @@ -136,8 +131,7 @@ LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::AcqRel, Ordering:: | ^^^^^^^^^^^^^^^^ ---------------- SeqCst failure ordering | | | AcqRel success ordering - | - = help: consider using SeqCst success ordering instead + | help: consider using SeqCst success ordering instead error: aborting due to 16 previous errors diff --git a/src/test/ui/lint/lint-invalid-atomic-ordering-exchange.stderr b/src/test/ui/lint/lint-invalid-atomic-ordering-exchange.stderr index eeeeb1fb5a94e..7bb12a8168346 100644 --- a/src/test/ui/lint/lint-invalid-atomic-ordering-exchange.stderr +++ b/src/test/ui/lint/lint-invalid-atomic-ordering-exchange.stderr @@ -86,8 +86,7 @@ LL | let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::Acquire); | ^^^^^^^^^^^^^^^^^ ----------------- Acquire failure ordering | | | Release success ordering - | - = help: consider using AcqRel success ordering instead + | help: consider using AcqRel success ordering instead error: compare_exchange's success ordering must be at least as strong as its failure ordering --> $DIR/lint-invalid-atomic-ordering-exchange.rs:46:38 @@ -96,8 +95,7 @@ LL | let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::SeqCst); | ^^^^^^^^^^^^^^^^^ ---------------- SeqCst failure ordering | | | Release success ordering - | - = help: consider using SeqCst success ordering instead + | help: consider using SeqCst success ordering instead error: compare_exchange's success ordering must be at least as strong as its failure ordering --> $DIR/lint-invalid-atomic-ordering-exchange.rs:50:38 @@ -106,8 +104,7 @@ LL | let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::SeqCst); | ^^^^^^^^^^^^^^^^^ ---------------- SeqCst failure ordering | | | Relaxed success ordering - | - = help: consider using SeqCst success ordering instead + | help: consider using SeqCst success ordering instead error: compare_exchange's success ordering must be at least as strong as its failure ordering --> $DIR/lint-invalid-atomic-ordering-exchange.rs:52:38 @@ -116,8 +113,7 @@ LL | let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::Acquire); | ^^^^^^^^^^^^^^^^^ ----------------- Acquire failure ordering | | | Relaxed success ordering - | - = help: consider using Acquire success ordering instead + | help: consider using Acquire success ordering instead error: compare_exchange's success ordering must be at least as strong as its failure ordering --> $DIR/lint-invalid-atomic-ordering-exchange.rs:56:38 @@ -126,8 +122,7 @@ LL | let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::SeqCst); | ^^^^^^^^^^^^^^^^^ ---------------- SeqCst failure ordering | | | Acquire success ordering - | - = help: consider using SeqCst success ordering instead + | help: consider using SeqCst success ordering instead error: compare_exchange's success ordering must be at least as strong as its failure ordering --> $DIR/lint-invalid-atomic-ordering-exchange.rs:58:38 @@ -136,8 +131,7 @@ LL | let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::SeqCst); | ^^^^^^^^^^^^^^^^ ---------------- SeqCst failure ordering | | | AcqRel success ordering - | - = help: consider using SeqCst success ordering instead + | help: consider using SeqCst success ordering instead error: aborting due to 16 previous errors diff --git a/src/test/ui/lint/lint-invalid-atomic-ordering-fetch-update.stderr b/src/test/ui/lint/lint-invalid-atomic-ordering-fetch-update.stderr index f63d059d42915..f79936cbda931 100644 --- a/src/test/ui/lint/lint-invalid-atomic-ordering-fetch-update.stderr +++ b/src/test/ui/lint/lint-invalid-atomic-ordering-fetch-update.stderr @@ -86,8 +86,7 @@ LL | let _ = x.fetch_update(Ordering::Release, Ordering::Acquire, |old| Some | ^^^^^^^^^^^^^^^^^ ----------------- Acquire failure ordering | | | Release success ordering - | - = help: consider using AcqRel success ordering instead + | help: consider using AcqRel success ordering instead error: fetch_update's success ordering must be at least as strong as its failure ordering --> $DIR/lint-invalid-atomic-ordering-fetch-update.rs:46:28 @@ -96,8 +95,7 @@ LL | let _ = x.fetch_update(Ordering::Release, Ordering::SeqCst, |old| Some( | ^^^^^^^^^^^^^^^^^ ---------------- SeqCst failure ordering | | | Release success ordering - | - = help: consider using SeqCst success ordering instead + | help: consider using SeqCst success ordering instead error: fetch_update's success ordering must be at least as strong as its failure ordering --> $DIR/lint-invalid-atomic-ordering-fetch-update.rs:50:28 @@ -106,8 +104,7 @@ LL | let _ = x.fetch_update(Ordering::Relaxed, Ordering::SeqCst, |old| Some( | ^^^^^^^^^^^^^^^^^ ---------------- SeqCst failure ordering | | | Relaxed success ordering - | - = help: consider using SeqCst success ordering instead + | help: consider using SeqCst success ordering instead error: fetch_update's success ordering must be at least as strong as its failure ordering --> $DIR/lint-invalid-atomic-ordering-fetch-update.rs:52:28 @@ -116,8 +113,7 @@ LL | let _ = x.fetch_update(Ordering::Relaxed, Ordering::Acquire, |old| Some | ^^^^^^^^^^^^^^^^^ ----------------- Acquire failure ordering | | | Relaxed success ordering - | - = help: consider using Acquire success ordering instead + | help: consider using Acquire success ordering instead error: fetch_update's success ordering must be at least as strong as its failure ordering --> $DIR/lint-invalid-atomic-ordering-fetch-update.rs:56:28 @@ -126,8 +122,7 @@ LL | let _ = x.fetch_update(Ordering::Acquire, Ordering::SeqCst, |old| Some( | ^^^^^^^^^^^^^^^^^ ---------------- SeqCst failure ordering | | | Acquire success ordering - | - = help: consider using SeqCst success ordering instead + | help: consider using SeqCst success ordering instead error: fetch_update's success ordering must be at least as strong as its failure ordering --> $DIR/lint-invalid-atomic-ordering-fetch-update.rs:58:28 @@ -136,8 +131,7 @@ LL | let _ = x.fetch_update(Ordering::AcqRel, Ordering::SeqCst, |old| Some(o | ^^^^^^^^^^^^^^^^ ---------------- SeqCst failure ordering | | | AcqRel success ordering - | - = help: consider using SeqCst success ordering instead + | help: consider using SeqCst success ordering instead error: aborting due to 16 previous errors From 655798558e2ba9801fc055a536a59dbbffa23c88 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 21 May 2022 13:57:52 +0200 Subject: [PATCH 0016/1135] Improve display of `
` in doc blocks --- src/librustdoc/html/static/css/rustdoc.css | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index d0229bdb5f23c..ed22a1a353e46 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -158,8 +158,8 @@ h1.fqn { Underlines elsewhere in the documentation break up visual flow and tend to invert section hierarchies. */ h2, -.top-doc h3, -.top-doc h4 { +.top-doc .docblock > h3, +.top-doc .docblock > h4 { border-bottom: 1px solid; } h3.code-header { @@ -1669,6 +1669,18 @@ details.rustdoc-toggle[open] > summary.hideme::after { content: "Collapse"; } +.docblock details summary { + display: flex; + list-style: none; + align-items: center; +} +.docblock details[open] summary::before { + content: "► "; +} +.docblock details[open] summary::before { + content: "▼ "; +} + /* Media Queries */ @media (min-width: 701px) { From 07596fefc114335c5f4c8cc3ed5b7361b17cf91b Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 21 May 2022 15:05:19 +0200 Subject: [PATCH 0017/1135] Fix display of `
`/`` in doc blocks --- src/librustdoc/html/static/css/rustdoc.css | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index ed22a1a353e46..c7e1330e86acf 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -1669,16 +1669,9 @@ details.rustdoc-toggle[open] > summary.hideme::after { content: "Collapse"; } -.docblock details summary { - display: flex; - list-style: none; - align-items: center; -} -.docblock details[open] summary::before { - content: "► "; -} -.docblock details[open] summary::before { - content: "▼ "; +/* This is needed in docblocks to have the "▶" element to be on the same line. */ +.docblock summary > * { + display: inline-block; } /* Media Queries */ From b8db8cc10f5d3a28d75f89680f2734e24cd1490c Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 21 May 2022 15:05:56 +0200 Subject: [PATCH 0018/1135] Add GUI test for `
`/`` display --- src/test/rustdoc-gui/docblock-details.goml | 23 ++++++++++++++++++++++ src/test/rustdoc-gui/src/test_docs/lib.rs | 12 +++++++++++ 2 files changed, 35 insertions(+) create mode 100644 src/test/rustdoc-gui/docblock-details.goml diff --git a/src/test/rustdoc-gui/docblock-details.goml b/src/test/rustdoc-gui/docblock-details.goml new file mode 100644 index 0000000000000..2edbf1e4e2d8c --- /dev/null +++ b/src/test/rustdoc-gui/docblock-details.goml @@ -0,0 +1,23 @@ +// This ensures that the `
`/`` elements are displayed as expected. +goto: file://|DOC_PATH|/test_docs/details/struct.Details.html +show-text: true +local-storage: {"rustdoc-theme": "dark", "rustdoc-use-system-theme": "false"} +reload: + +// We first check that the headers in the `.top-doc` doc block still have their +// bottom border. +assert-text: (".top-doc .docblock > h3", "Hello") +assert-css: ( + ".top-doc .docblock > h3", + {"border-bottom": "1px solid rgb(221, 221, 221)"}, +) +// We now check that the `` doesn't have a bottom border and has the correct display. +assert-css: ( + ".top-doc .docblock summary h4", + {"border-bottom": "0px none rgb(221, 221, 221)"}, +) +// This allows to ensure that summary is on one line only! +assert-property: (".top-doc .docblock summary h4", {"offsetHeight": "33"}) +assert-css: (".top-doc .docblock summary h4", {"margin-top": "15px", "margin-bottom": "5px"}) +// So `33 + 15 + 5` == `53` +assert-property: (".top-doc .docblock summary", {"offsetHeight": "53"}) diff --git a/src/test/rustdoc-gui/src/test_docs/lib.rs b/src/test/rustdoc-gui/src/test_docs/lib.rs index b6fe9eb2565bd..aa2f78289bea2 100644 --- a/src/test/rustdoc-gui/src/test_docs/lib.rs +++ b/src/test/rustdoc-gui/src/test_docs/lib.rs @@ -277,3 +277,15 @@ pub use macros::*; #[doc(alias = "AliasForTheStdReexport")] pub use ::std as TheStdReexport; + +pub mod details { + /// We check the appearance of the `
`/`` in here. + /// + /// ## Hello + /// + ///
+ ///

I'm a summary

+ ///
I'm the content of the details!
+ ///
+ pub struct Details; +} From 907ea5584173464b31326480dd11d9a71cbd4141 Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Fri, 3 Jun 2022 22:46:01 +0200 Subject: [PATCH 0019/1135] Adapt tests to be able to run in miri Decrease the Ns of bug loops to a smaller N, which makes them a lot faster in miri. --- compiler/rustc_arena/src/tests.rs | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_arena/src/tests.rs b/compiler/rustc_arena/src/tests.rs index 911e577c1edc7..ad61464343a4a 100644 --- a/compiler/rustc_arena/src/tests.rs +++ b/compiler/rustc_arena/src/tests.rs @@ -79,7 +79,11 @@ fn test_arena_alloc_nested() { #[test] pub fn test_copy() { let arena = TypedArena::default(); - for _ in 0..100000 { + #[cfg(not(miri))] + const N: usize = 100000; + #[cfg(miri)] + const N: usize = 1000; + for _ in 0..N { arena.alloc(Point { x: 1, y: 2, z: 3 }); } } @@ -106,7 +110,11 @@ struct Noncopy { #[test] pub fn test_noncopy() { let arena = TypedArena::default(); - for _ in 0..100000 { + #[cfg(not(miri))] + const N: usize = 100000; + #[cfg(miri)] + const N: usize = 1000; + for _ in 0..N { arena.alloc(Noncopy { string: "hello world".to_string(), array: vec![1, 2, 3, 4, 5] }); } } @@ -114,7 +122,11 @@ pub fn test_noncopy() { #[test] pub fn test_typed_arena_zero_sized() { let arena = TypedArena::default(); - for _ in 0..100000 { + #[cfg(not(miri))] + const N: usize = 100000; + #[cfg(miri)] + const N: usize = 1000; + for _ in 0..N { arena.alloc(()); } } @@ -124,7 +136,11 @@ pub fn test_typed_arena_clear() { let mut arena = TypedArena::default(); for _ in 0..10 { arena.clear(); - for _ in 0..10000 { + #[cfg(not(miri))] + const N: usize = 10000; + #[cfg(miri)] + const N: usize = 100; + for _ in 0..N { arena.alloc(Point { x: 1, y: 2, z: 3 }); } } From 5a70d88af5ff96897b7ddfd01d13e039a86c775c Mon Sep 17 00:00:00 2001 From: DevAccentor Date: Sun, 5 Jun 2022 11:01:54 +0200 Subject: [PATCH 0020/1135] add vec.capacity() to slow_vec_initialization --- .../src/slow_vector_initialization.rs | 39 ++++++++++--------- tests/ui/slow_vector_initialization.rs | 6 +++ tests/ui/slow_vector_initialization.stderr | 28 ++++++++++--- 3 files changed, 49 insertions(+), 24 deletions(-) diff --git a/clippy_lints/src/slow_vector_initialization.rs b/clippy_lints/src/slow_vector_initialization.rs index b4ad5dcbe3e9a..e5b30937d16b4 100644 --- a/clippy_lints/src/slow_vector_initialization.rs +++ b/clippy_lints/src/slow_vector_initialization.rs @@ -28,6 +28,9 @@ declare_clippy_lint! { /// let mut vec1 = Vec::with_capacity(len); /// vec1.resize(len, 0); /// + /// let mut vec1 = Vec::with_capacity(len); + /// vec1.resize(vec1.capacity(), 0); + /// /// let mut vec2 = Vec::with_capacity(len); /// vec2.extend(repeat(0).take(len)); /// @@ -210,23 +213,20 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> { /// Checks if the given expression is resizing a vector with 0 fn search_slow_resize_filling(&mut self, expr: &'tcx Expr<'_>) { - if_chain! { - if self.initialization_found; - if let ExprKind::MethodCall(path, [self_arg, len_arg, fill_arg], _) = expr.kind; - if path_to_local_id(self_arg, self.vec_alloc.local_id); - if path.ident.name == sym!(resize); - + if self.initialization_found + && let ExprKind::MethodCall(path, [self_arg, len_arg, fill_arg], _) = expr.kind + && path_to_local_id(self_arg, self.vec_alloc.local_id) + && path.ident.name == sym!(resize) // Check that is filled with 0 - if let ExprKind::Lit(ref lit) = fill_arg.kind; - if let LitKind::Int(0, _) = lit.node; - - // Check that len expression is equals to `with_capacity` expression - if SpanlessEq::new(self.cx).eq_expr(len_arg, self.vec_alloc.len_expr); - - then { - self.slow_expression = Some(InitializationType::Resize(expr)); + && let ExprKind::Lit(ref lit) = fill_arg.kind + && let LitKind::Int(0, _) = lit.node { + // Check that len expression is equals to `with_capacity` expression + if SpanlessEq::new(self.cx).eq_expr(len_arg, self.vec_alloc.len_expr) { + self.slow_expression = Some(InitializationType::Resize(expr)); + } else if let ExprKind::MethodCall(path, _, _) = len_arg.kind && path.ident.as_str() == "capacity" { + self.slow_expression = Some(InitializationType::Resize(expr)); + } } - } } /// Returns `true` if give expression is `repeat(0).take(...)` @@ -239,12 +239,15 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> { if let Some(repeat_expr) = take_args.get(0); if self.is_repeat_zero(repeat_expr); - // Check that len expression is equals to `with_capacity` expression if let Some(len_arg) = take_args.get(1); - if SpanlessEq::new(self.cx).eq_expr(len_arg, self.vec_alloc.len_expr); then { - return true; + // Check that len expression is equals to `with_capacity` expression + if SpanlessEq::new(self.cx).eq_expr(len_arg, self.vec_alloc.len_expr) { + return true; + } else if let ExprKind::MethodCall(path, _, _) = len_arg.kind && path.ident.as_str() == "capacity" { + return true; + } } } diff --git a/tests/ui/slow_vector_initialization.rs b/tests/ui/slow_vector_initialization.rs index 7e3d357ae50dc..16be9f6d203aa 100644 --- a/tests/ui/slow_vector_initialization.rs +++ b/tests/ui/slow_vector_initialization.rs @@ -19,6 +19,9 @@ fn extend_vector() { // Extend with mismatching expression should not be warned let mut vec3 = Vec::with_capacity(24322); vec3.extend(repeat(0).take(2)); + + let mut vec4 = Vec::with_capacity(len); + vec4.extend(repeat(0).take(vec4.capacity())); } fn mixed_extend_resize_vector() { @@ -48,6 +51,9 @@ fn resize_vector() { let mut vec3 = Vec::with_capacity(len - 10); vec3.resize(len - 10, 0); + let mut vec4 = Vec::with_capacity(len); + vec4.resize(vec4.capacity(), 0); + // Reinitialization should be warned vec1 = Vec::with_capacity(10); vec1.resize(10, 0); diff --git a/tests/ui/slow_vector_initialization.stderr b/tests/ui/slow_vector_initialization.stderr index 5d2788ec26086..cb3ce3e95a7af 100644 --- a/tests/ui/slow_vector_initialization.stderr +++ b/tests/ui/slow_vector_initialization.stderr @@ -17,7 +17,15 @@ LL | vec2.extend(repeat(0).take(len - 10)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: slow zero-filling initialization - --> $DIR/slow_vector_initialization.rs:31:5 + --> $DIR/slow_vector_initialization.rs:24:5 + | +LL | let mut vec4 = Vec::with_capacity(len); + | ----------------------- help: consider replace allocation with: `vec![0; len]` +LL | vec4.extend(repeat(0).take(vec4.capacity())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: slow zero-filling initialization + --> $DIR/slow_vector_initialization.rs:34:5 | LL | let mut resized_vec = Vec::with_capacity(30); | ---------------------- help: consider replace allocation with: `vec![0; 30]` @@ -25,7 +33,7 @@ LL | resized_vec.resize(30, 0); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: slow zero-filling initialization - --> $DIR/slow_vector_initialization.rs:34:5 + --> $DIR/slow_vector_initialization.rs:37:5 | LL | let mut extend_vec = Vec::with_capacity(30); | ---------------------- help: consider replace allocation with: `vec![0; 30]` @@ -33,7 +41,7 @@ LL | extend_vec.extend(repeat(0).take(30)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: slow zero-filling initialization - --> $DIR/slow_vector_initialization.rs:41:5 + --> $DIR/slow_vector_initialization.rs:44:5 | LL | let mut vec1 = Vec::with_capacity(len); | ----------------------- help: consider replace allocation with: `vec![0; len]` @@ -41,7 +49,7 @@ LL | vec1.resize(len, 0); | ^^^^^^^^^^^^^^^^^^^ error: slow zero-filling initialization - --> $DIR/slow_vector_initialization.rs:49:5 + --> $DIR/slow_vector_initialization.rs:52:5 | LL | let mut vec3 = Vec::with_capacity(len - 10); | ---------------------------- help: consider replace allocation with: `vec![0; len - 10]` @@ -49,12 +57,20 @@ LL | vec3.resize(len - 10, 0); | ^^^^^^^^^^^^^^^^^^^^^^^^ error: slow zero-filling initialization - --> $DIR/slow_vector_initialization.rs:53:5 + --> $DIR/slow_vector_initialization.rs:55:5 + | +LL | let mut vec4 = Vec::with_capacity(len); + | ----------------------- help: consider replace allocation with: `vec![0; len]` +LL | vec4.resize(vec4.capacity(), 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: slow zero-filling initialization + --> $DIR/slow_vector_initialization.rs:59:5 | LL | vec1 = Vec::with_capacity(10); | ---------------------- help: consider replace allocation with: `vec![0; 10]` LL | vec1.resize(10, 0); | ^^^^^^^^^^^^^^^^^^ -error: aborting due to 7 previous errors +error: aborting due to 9 previous errors From b9660de664cc491b3f22a5c1dadee5a03e506b2a Mon Sep 17 00:00:00 2001 From: joboet Date: Sat, 4 Jun 2022 20:57:25 +0200 Subject: [PATCH 0021/1135] std: solve priority issue for Parker --- .../src/sys_common/thread_parker/wait_flag.rs | 55 +++++++++++-------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/library/std/src/sys_common/thread_parker/wait_flag.rs b/library/std/src/sys_common/thread_parker/wait_flag.rs index f9581ff5d5774..8db12693ef734 100644 --- a/library/std/src/sys_common/thread_parker/wait_flag.rs +++ b/library/std/src/sys_common/thread_parker/wait_flag.rs @@ -21,13 +21,11 @@ //! "wait flag"). //! //! `unpark` changes the state to `NOTIFIED`. If the state was `PARKED`, the thread -//! is or will be sleeping on the wait flag, so we raise it. Only the first thread -//! to call `unpark` will raise the wait flag, so spurious wakeups are avoided -//! (this is especially important for semaphores). +//! is or will be sleeping on the wait flag, so we raise it. use crate::pin::Pin; use crate::sync::atomic::AtomicI8; -use crate::sync::atomic::Ordering::SeqCst; +use crate::sync::atomic::Ordering::{Relaxed, SeqCst}; use crate::sys::wait_flag::WaitFlag; use crate::time::Duration; @@ -49,39 +47,48 @@ impl Parker { // This implementation doesn't require `unsafe` and `Pin`, but other implementations do. pub unsafe fn park(self: Pin<&Self>) { - // The state values are chosen so that this subtraction changes - // `NOTIFIED` to `EMPTY` and `EMPTY` to `PARKED`. - let state = self.state.fetch_sub(1, SeqCst); - match state { - EMPTY => (), + match self.state.fetch_sub(1, SeqCst) { + // NOTIFIED => EMPTY NOTIFIED => return, + // EMPTY => PARKED + EMPTY => (), _ => panic!("inconsistent park state"), } - self.wait_flag.wait(); + // Avoid waking up from spurious wakeups (these are quite likely, see below). + loop { + self.wait_flag.wait(); - // We need to do a load here to use `Acquire` ordering. - self.state.swap(EMPTY, SeqCst); + match self.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, Relaxed) { + Ok(_) => return, + Err(PARKED) => (), + Err(_) => panic!("inconsistent park state"), + } + } } // This implementation doesn't require `unsafe` and `Pin`, but other implementations do. pub unsafe fn park_timeout(self: Pin<&Self>, dur: Duration) { - let state = self.state.fetch_sub(1, SeqCst); - match state { - EMPTY => (), + match self.state.fetch_sub(1, SeqCst) { NOTIFIED => return, + EMPTY => (), _ => panic!("inconsistent park state"), } - let wakeup = self.wait_flag.wait_timeout(dur); - let state = self.state.swap(EMPTY, SeqCst); - if state == NOTIFIED && !wakeup { - // The token was made available after the wait timed out, but before - // we reset the state, so we need to reset the wait flag to avoid - // spurious wakeups. This wait has no timeout, but we know it will - // return quickly, as the unparking thread will definitely raise the - // flag if it has not already done so. - self.wait_flag.wait(); + self.wait_flag.wait_timeout(dur); + + // Either a wakeup or a timeout occurred. Wakeups may be spurious, as there can be + // a race condition when `unpark` is performed between receiving the timeout and + // resetting the state, resulting in the eventflag being set unnecessarily. `park` + // is protected against this by looping until the token is actually given, but + // here we cannot easily tell. + + // Use `swap` to provide acquire ordering (not strictly necessary, but all other + // implementations do). + match self.state.swap(EMPTY, SeqCst) { + NOTIFIED => (), + PARKED => (), + _ => panic!("inconsistent park state"), } } From b96d1e45f188010f2cc6fff956902a455eb2178a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 3 Jun 2022 17:06:29 -0400 Subject: [PATCH 0022/1135] change ptr::swap methods to do untyped copies --- library/core/src/ptr/mod.rs | 24 ++++++++++++------------ library/core/tests/ptr.rs | 25 +++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 5b04ae7b07e69..3b2b7ba8531ca 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -905,15 +905,15 @@ pub const unsafe fn swap_nonoverlapping(x: *mut T, y: *mut T, count: usize) { if mem::align_of::() >= mem::align_of::<$ChunkTy>() && mem::size_of::() % mem::size_of::<$ChunkTy>() == 0 { - let x: *mut MaybeUninit<$ChunkTy> = x.cast(); - let y: *mut MaybeUninit<$ChunkTy> = y.cast(); + let x: *mut $ChunkTy = x.cast(); + let y: *mut $ChunkTy = y.cast(); let count = count * (mem::size_of::() / mem::size_of::<$ChunkTy>()); // SAFETY: these are the same bytes that the caller promised were // ok, just typed as `MaybeUninit`s instead of as `T`s. // The `if` condition above ensures that we're not violating // alignment requirements, and that the division is exact so // that we don't lose any bytes off the end. - return unsafe { swap_nonoverlapping_simple(x, y, count) }; + return unsafe { swap_nonoverlapping_simple_untyped(x, y, count) }; } }; } @@ -946,7 +946,7 @@ pub const unsafe fn swap_nonoverlapping(x: *mut T, y: *mut T, count: usize) { } // SAFETY: Same preconditions as this function - unsafe { swap_nonoverlapping_simple(x, y, count) } + unsafe { swap_nonoverlapping_simple_untyped(x, y, count) } } /// Same behaviour and safety conditions as [`swap_nonoverlapping`] @@ -955,16 +955,16 @@ pub const unsafe fn swap_nonoverlapping(x: *mut T, y: *mut T, count: usize) { /// `swap_nonoverlapping` tries to use) so no need to manually SIMD it. #[inline] #[rustc_const_unstable(feature = "const_swap", issue = "83163")] -const unsafe fn swap_nonoverlapping_simple(x: *mut T, y: *mut T, count: usize) { +const unsafe fn swap_nonoverlapping_simple_untyped(x: *mut T, y: *mut T, count: usize) { + let x = x.cast::>(); + let y = y.cast::>(); let mut i = 0; while i < count { - let x: &mut T = - // SAFETY: By precondition, `i` is in-bounds because it's below `n` - unsafe { &mut *x.add(i) }; - let y: &mut T = - // SAFETY: By precondition, `i` is in-bounds because it's below `n` - // and it's distinct from `x` since the ranges are non-overlapping - unsafe { &mut *y.add(i) }; + // SAFETY: By precondition, `i` is in-bounds because it's below `n` + let x = unsafe { &mut *x.add(i) }; + // SAFETY: By precondition, `i` is in-bounds because it's below `n` + // and it's distinct from `x` since the ranges are non-overlapping + let y = unsafe { &mut *y.add(i) }; mem::swap_simple(x, y); i += 1; diff --git a/library/core/tests/ptr.rs b/library/core/tests/ptr.rs index 40b2b49bdbd7d..082d438128e20 100644 --- a/library/core/tests/ptr.rs +++ b/library/core/tests/ptr.rs @@ -783,6 +783,31 @@ fn nonnull_tagged_pointer_with_provenance() { } } +#[test] +fn swap_copy_untyped() { + // We call `{swap,copy}{,_nonoverlapping}` at `bool` type on data that is not a valid bool. + // These should all do untyped copies, so this should work fine. + let mut x = 5u8; + let mut y = 6u8; + + let ptr1 = &mut x as *mut u8 as *mut bool; + let ptr2 = &mut y as *mut u8 as *mut bool; + + unsafe { + ptr::swap(ptr1, ptr2); + ptr::swap_nonoverlapping(ptr1, ptr2, 1); + } + assert_eq!(x, 5); + assert_eq!(y, 6); + + unsafe { + ptr::copy(ptr1, ptr2, 1); + ptr::copy_nonoverlapping(ptr1, ptr2, 1); + } + assert_eq!(x, 5); + assert_eq!(y, 5); +} + #[test] fn test_const_copy() { const { From cb7cd97641b7a2d1646520b7bf785934f9c6aaeb Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 3 Jun 2022 17:10:12 -0400 Subject: [PATCH 0023/1135] promise that ptr::copy and ptr::swap are doing untyped copies --- library/core/src/intrinsics.rs | 6 ++++++ library/core/src/ptr/mod.rs | 10 ++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index 0b76790c0097e..9bed758c10a1d 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -2043,6 +2043,9 @@ pub(crate) fn is_nonoverlapping(src: *const T, dst: *const T, count: usize) - /// `copy_nonoverlapping` is semantically equivalent to C's [`memcpy`], but /// with the argument order swapped. /// +/// The copy is "untyped" in the sense that data may be uninitialized or otherwise violate the +/// requirements of `T`. The initialization state is preserved exactly. +/// /// [`memcpy`]: https://en.cppreference.com/w/c/string/byte/memcpy /// /// # Safety @@ -2148,6 +2151,9 @@ pub const unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: us /// order swapped. Copying takes place as if the bytes were copied from `src` /// to a temporary array and then copied from the array to `dst`. /// +/// The copy is "untyped" in the sense that data may be uninitialized or otherwise violate the +/// requirements of `T`. The initialization state is preserved exactly. +/// /// [`memmove`]: https://en.cppreference.com/w/c/string/byte/memmove /// /// # Safety diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 3b2b7ba8531ca..1035fdbf12dd5 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -774,7 +774,7 @@ pub const fn slice_from_raw_parts_mut(data: *mut T, len: usize) -> *mut [T] { /// Swaps the values at two mutable locations of the same type, without /// deinitializing either. /// -/// But for the following two exceptions, this function is semantically +/// But for the following exceptions, this function is semantically /// equivalent to [`mem::swap`]: /// /// * It operates on raw pointers instead of references. When references are @@ -784,6 +784,9 @@ pub const fn slice_from_raw_parts_mut(data: *mut T, len: usize) -> *mut [T] { /// overlapping region of memory from `x` will be used. This is demonstrated /// in the second example below. /// +/// * The operation is "untyped" in the sense that data may be uninitialized or otherwise violate +/// the requirements of `T`. The initialization state is preserved exactly. +/// /// # Safety /// /// Behavior is undefined if any of the following conditions are violated: @@ -860,6 +863,9 @@ pub const unsafe fn swap(x: *mut T, y: *mut T) { /// Swaps `count * size_of::()` bytes between the two regions of memory /// beginning at `x` and `y`. The two regions must *not* overlap. /// +/// The operation is "untyped" in the sense that data may be uninitialized or otherwise violate the +/// requirements of `T`. The initialization state is preserved exactly. +/// /// # Safety /// /// Behavior is undefined if any of the following conditions are violated: @@ -965,7 +971,7 @@ const unsafe fn swap_nonoverlapping_simple_untyped(x: *mut T, y: *mut T, coun // SAFETY: By precondition, `i` is in-bounds because it's below `n` // and it's distinct from `x` since the ranges are non-overlapping let y = unsafe { &mut *y.add(i) }; - mem::swap_simple(x, y); + mem::swap_simple::>(x, y); i += 1; } From c1f4f980f4706365d56136f8761537f8e9b56bb4 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 5 Jun 2022 23:04:37 -0700 Subject: [PATCH 0024/1135] Do wfcheck on ADT field before Sized check --- compiler/rustc_middle/src/traits/mod.rs | 2 +- compiler/rustc_typeck/src/check/wfcheck.rs | 19 +++++++++--------- .../bugs/issue-80626.stderr | 13 ++++-------- src/test/ui/union/issue-81199.stderr | 20 +++++-------------- src/test/ui/wf/issue-96810.rs | 12 +++++++++++ src/test/ui/wf/issue-96810.stderr | 19 ++++++++++++++++++ 6 files changed, 51 insertions(+), 34 deletions(-) create mode 100644 src/test/ui/wf/issue-96810.rs create mode 100644 src/test/ui/wf/issue-96810.stderr diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 4006b2fcf177a..c963dc1689380 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -403,7 +403,7 @@ pub enum ObligationCauseCode<'tcx> { QuestionMark, /// Well-formed checking. If a `WellFormedLoc` is provided, - /// then it will be used to eprform HIR-based wf checking + /// then it will be used to perform HIR-based wf checking /// after an error occurs, in order to generate a more precise error span. /// This is purely for diagnostic purposes - it is always /// correct to use `MiscObligation` instead, or to specify diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs index 20ef97c085f18..e6c21608c8ae2 100644 --- a/compiler/rustc_typeck/src/check/wfcheck.rs +++ b/compiler/rustc_typeck/src/check/wfcheck.rs @@ -1008,6 +1008,15 @@ fn check_type_defn<'tcx, F>( let packed = tcx.adt_def(item.def_id).repr().packed(); for variant in &variants { + // All field types must be well-formed. + for field in &variant.fields { + fcx.register_wf_obligation( + field.ty.into(), + field.span, + ObligationCauseCode::WellFormed(Some(WellFormedLoc::Ty(field.def_id))), + ) + } + // For DST, or when drop needs to copy things around, all // intermediate types must be sized. let needs_drop_copy = || { @@ -1024,6 +1033,7 @@ fn check_type_defn<'tcx, F>( } } }; + // All fields (except for possibly the last) should be sized. let all_sized = all_sized || variant.fields.is_empty() || needs_drop_copy(); let unsized_len = if all_sized { 0 } else { 1 }; for (idx, field) in @@ -1048,15 +1058,6 @@ fn check_type_defn<'tcx, F>( ); } - // All field types must be well-formed. - for field in &variant.fields { - fcx.register_wf_obligation( - field.ty.into(), - field.span, - ObligationCauseCode::WellFormed(Some(WellFormedLoc::Ty(field.def_id))), - ) - } - // Explicit `enum` discriminant values must const-evaluate successfully. if let Some(discr_def_id) = variant.explicit_discr { let discr_substs = InternalSubsts::identity_for_item(tcx, discr_def_id.to_def_id()); diff --git a/src/test/ui/generic-associated-types/bugs/issue-80626.stderr b/src/test/ui/generic-associated-types/bugs/issue-80626.stderr index 8b0cc78e99949..487b83dfa3fcc 100644 --- a/src/test/ui/generic-associated-types/bugs/issue-80626.stderr +++ b/src/test/ui/generic-associated-types/bugs/issue-80626.stderr @@ -4,16 +4,11 @@ error[E0275]: overflow evaluating the requirement `LinkedList: Sized` LL | Next(A::Allocated) | ^^^^^^^^^^^^^^^^^^ | - = note: no field of an enum variant may have a dynamically sized type - = help: change the field's type to have a statically known size -help: borrowed types always have a statically known size +note: required by a bound in `Allocator::Allocated` + --> $DIR/issue-80626.rs:9:20 | -LL | Next(&A::Allocated) - | + -help: the `Box` type always has a statically known size and allocates its contents in the heap - | -LL | Next(Box>) - | ++++ + +LL | type Allocated; + | ^ required by this bound in `Allocator::Allocated` error: aborting due to previous error diff --git a/src/test/ui/union/issue-81199.stderr b/src/test/ui/union/issue-81199.stderr index f26bfe3a0b060..5bb98675361a0 100644 --- a/src/test/ui/union/issue-81199.stderr +++ b/src/test/ui/union/issue-81199.stderr @@ -1,28 +1,18 @@ -error[E0277]: the trait bound `T: Pointee` is not satisfied in `PtrComponents` +error[E0277]: the trait bound `T: Pointee` is not satisfied --> $DIR/issue-81199.rs:5:17 | LL | components: PtrComponents, - | ^^^^^^^^^^^^^^^^ within `PtrComponents`, the trait `Pointee` is not implemented for `T` + | ^^^^^^^^^^^^^^^^ the trait `Pointee` is not implemented for `T` | -note: required because it appears within the type `PtrComponents` - --> $DIR/issue-81199.rs:10:8 +note: required by a bound in `PtrComponents` + --> $DIR/issue-81199.rs:10:25 | LL | struct PtrComponents { - | ^^^^^^^^^^^^^ - = note: no field of a union may have a dynamically sized type - = help: change the field's type to have a statically known size + | ^^^^^^^ required by this bound in `PtrComponents` help: consider further restricting this bound | LL | union PtrRepr { | +++++++++ -help: borrowed types always have a statically known size - | -LL | components: &PtrComponents, - | + -help: the `Box` type always has a statically known size and allocates its contents in the heap - | -LL | components: Box>, - | ++++ + error: aborting due to previous error diff --git a/src/test/ui/wf/issue-96810.rs b/src/test/ui/wf/issue-96810.rs new file mode 100644 index 0000000000000..c2948086b200b --- /dev/null +++ b/src/test/ui/wf/issue-96810.rs @@ -0,0 +1,12 @@ +struct S(T::Assoc); + +trait Tr { + type Assoc; +} + +struct Hoge { + s: S, //~ ERROR the trait bound `K: Tr` is not satisfied + a: u32, +} + +fn main() {} diff --git a/src/test/ui/wf/issue-96810.stderr b/src/test/ui/wf/issue-96810.stderr new file mode 100644 index 0000000000000..1407e62b1e139 --- /dev/null +++ b/src/test/ui/wf/issue-96810.stderr @@ -0,0 +1,19 @@ +error[E0277]: the trait bound `K: Tr` is not satisfied + --> $DIR/issue-96810.rs:8:8 + | +LL | s: S, + | ^^^^ the trait `Tr` is not implemented for `K` + | +note: required by a bound in `S` + --> $DIR/issue-96810.rs:1:13 + | +LL | struct S(T::Assoc); + | ^^ required by this bound in `S` +help: consider restricting type parameter `K` + | +LL | struct Hoge { + | ++++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. From 246a80c32cca2c5e07f205cbcd327c3b2d9b4db1 Mon Sep 17 00:00:00 2001 From: Nikolai Vazquez Date: Tue, 7 Jun 2022 14:53:59 -0400 Subject: [PATCH 0025/1135] Inline Windows `OsStrExt::encode_wide` User crates currently produce much more code than necessary because the optimizer fails to make assumptions about this method. --- library/std/src/os/windows/ffi.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/library/std/src/os/windows/ffi.rs b/library/std/src/os/windows/ffi.rs index a9493a94cac26..96bab59d3f8d7 100644 --- a/library/std/src/os/windows/ffi.rs +++ b/library/std/src/os/windows/ffi.rs @@ -129,6 +129,7 @@ pub trait OsStrExt: Sealed { #[stable(feature = "rust1", since = "1.0.0")] impl OsStrExt for OsStr { + #[inline] fn encode_wide(&self) -> EncodeWide<'_> { self.as_inner().inner.encode_wide() } From 63d1c8623000782ebc1bb6ae662a10af9f067cab Mon Sep 17 00:00:00 2001 From: Gus Wynn Date: Wed, 1 Jun 2022 11:36:20 -0700 Subject: [PATCH 0026/1135] [core] add Exclusive to sync --- library/core/src/sync/exclusive.rs | 173 +++++++++++++++++++++++++++++ library/core/src/sync/mod.rs | 3 + library/std/src/lib.rs | 1 + library/std/src/sync/mod.rs | 2 + 4 files changed, 179 insertions(+) create mode 100644 library/core/src/sync/exclusive.rs diff --git a/library/core/src/sync/exclusive.rs b/library/core/src/sync/exclusive.rs new file mode 100644 index 0000000000000..6ada07fd576a1 --- /dev/null +++ b/library/core/src/sync/exclusive.rs @@ -0,0 +1,173 @@ +//! Defines [`Exclusive`]. + +use core::fmt; +use core::future::Future; +use core::pin::Pin; +use core::task::{Context, Poll}; + +/// `Exclusive` provides only _mutable_ access, also referred to as _exclusive_ +/// access to the underlying value. It provides no _immutable_, or _shared_ +/// access to the underlying value. +/// +/// While this may seem not very useful, it allows `Exclusive` to _unconditionally_ +/// implement [`Sync`]. Indeed, the safety requirements of `Sync` state that for `Exclusive` +/// to be `Sync`, it must be sound to _share_ across threads, that is, it must be sound +/// for `&Exclusive` to cross thread boundaries. By design, a `&Exclusive` has no API +/// whatsoever, making it useless, thus harmless, thus memory safe. +/// +/// Certain constructs like [`Future`]s can only be used with _exclusive_ access, +/// and are often `Send` but not `Sync`, so `Exclusive` can be used as hint to the +/// rust compiler that something is `Sync` in practice. +/// +/// ## Examples +/// Using a non-`Sync` future prevents the wrapping struct from being `Sync` +/// ```compile_fail +/// use core::cell::Cell; +/// +/// async fn other() {} +/// fn assert_sync(t: T) {} +/// struct State { +/// future: F +/// } +/// +/// assert_sync(State { +/// future: async { +/// let cell = Cell::new(1); +/// let cell_ref = &cell; +/// other().await; +/// let value = cell_ref.get(); +/// } +/// }); +/// ``` +/// +/// `Exclusive` ensures the struct is `Sync` without stripping the future of its +/// functionality. +/// ``` +/// #![feature(exclusive_wrapper)] +/// use core::cell::Cell; +/// use core::sync::Exclusive; +/// +/// async fn other() {} +/// fn assert_sync(t: T) {} +/// struct State { +/// future: Exclusive +/// } +/// +/// assert_sync(State { +/// future: Exclusive::new(async { +/// let cell = Cell::new(1); +/// let cell_ref = &cell; +/// other().await; +/// let value = cell_ref.get(); +/// }) +/// }); +/// ``` +/// +/// ## Parallels with a mutex +/// In some sense, `Exclusive` can be thought of as a _compile-time_ version of +/// a mutex, as the borrow-checker guarantees that only one `&mut` can exist +/// for any value. This is a parallel with the fact that +/// `&` and `&mut` references together can be thought of as a _compile-time_ +/// version of a read-write lock. +/// +/// +/// [`Sync`]: core::marker::Sync +#[unstable(feature = "exclusive_wrapper", issue = "none")] +#[doc(alias = "SyncWrapper")] +#[doc(alias = "SyncCell")] +#[doc(alias = "Unique")] +// `Exclusive` can't have `PartialOrd`, `Clone`, etc. impls as they would +// use `&` access to the inner value, violating the `Sync` impl's safety +// requirements. +#[derive(Default)] +#[repr(transparent)] +pub struct Exclusive { + inner: T, +} + +// See `Exclusive`'s docs for justification. +#[unstable(feature = "exclusive_wrapper", issue = "none")] +unsafe impl Sync for Exclusive {} + +#[unstable(feature = "exclusive_wrapper", issue = "none")] +impl fmt::Debug for Exclusive { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + f.debug_struct("Exclusive").finish_non_exhaustive() + } +} + +impl Exclusive { + /// Wrap a value in an `Exclusive` + #[unstable(feature = "exclusive_wrapper", issue = "none")] + #[must_use] + pub const fn new(t: T) -> Self { + Self { inner: t } + } + + /// Unwrap the value contained in the `Exclusive` + #[unstable(feature = "exclusive_wrapper", issue = "none")] + #[must_use] + pub const fn into_inner(self) -> T { + self.inner + } +} + +impl Exclusive { + /// Get exclusive access to the underlying value. + #[unstable(feature = "exclusive_wrapper", issue = "none")] + #[must_use] + pub const fn get_mut(&mut self) -> &mut T { + &mut self.inner + } + + /// Get pinned exclusive access to the underlying value. + /// + /// `Exclusive` is considered to _structurally pin_ the underlying + /// value, which means _unpinned_ `Exclusive`s can produce _unpinned_ + /// access to the underlying value, but _pinned_ `Exclusive`s only + /// produce _pinned_ access to the underlying value. + #[unstable(feature = "exclusive_wrapper", issue = "none")] + #[must_use] + pub const fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut T> { + // SAFETY: `Exclusive` can only produce `&mut T` if itself is unpinned + // `Pin::map_unchecked_mut` is not const, so we do this conversion manually + unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().inner) } + } + + /// Build a _mutable_ references to an `Exclusive` from + /// a _mutable_ reference to a `T`. This allows you to skip + /// building an `Exclusive` with [`Exclusive::new`]. + #[unstable(feature = "exclusive_wrapper", issue = "none")] + #[must_use] + pub const fn from_mut(r: &'_ mut T) -> &'_ mut Exclusive { + // SAFETY: repr is ≥ C, so refs have the same layout; and `Exclusive` properties are `&mut`-agnostic + unsafe { &mut *(r as *mut T as *mut Exclusive) } + } + + /// Build a _pinned mutable_ references to an `Exclusive` from + /// a _pinned mutable_ reference to a `T`. This allows you to skip + /// building an `Exclusive` with [`Exclusive::new`]. + #[unstable(feature = "exclusive_wrapper", issue = "none")] + #[must_use] + pub const fn from_pin_mut(r: Pin<&'_ mut T>) -> Pin<&'_ mut Exclusive> { + // SAFETY: `Exclusive` can only produce `&mut T` if itself is unpinned + // `Pin::map_unchecked_mut` is not const, so we do this conversion manually + unsafe { Pin::new_unchecked(Self::from_mut(r.get_unchecked_mut())) } + } +} + +#[unstable(feature = "exclusive_wrapper", issue = "none")] +impl From for Exclusive { + fn from(t: T) -> Self { + Self::new(t) + } +} + +#[unstable(feature = "exclusive_wrapper", issue = "none")] +impl Future for Exclusive { + type Output = T::Output; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + self.get_pin_mut().poll(cx) + } +} diff --git a/library/core/src/sync/mod.rs b/library/core/src/sync/mod.rs index b635bae0a47b4..88d87e8ac40ee 100644 --- a/library/core/src/sync/mod.rs +++ b/library/core/src/sync/mod.rs @@ -3,3 +3,6 @@ #![stable(feature = "rust1", since = "1.0.0")] pub mod atomic; +mod exclusive; +#[unstable(feature = "exclusive_wrapper", issue = "none")] +pub use exclusive::Exclusive; diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 71ea5f1a1f01e..f22264c2bf1e6 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -269,6 +269,7 @@ #![feature(duration_checked_float)] #![feature(duration_constants)] #![feature(exact_size_is_empty)] +#![feature(exclusive_wrapper)] #![feature(extend_one)] #![feature(float_minimum_maximum)] #![feature(hasher_prefixfree_extras)] diff --git a/library/std/src/sync/mod.rs b/library/std/src/sync/mod.rs index 87d01daeafc4c..c70dcf7f66c51 100644 --- a/library/std/src/sync/mod.rs +++ b/library/std/src/sync/mod.rs @@ -155,6 +155,8 @@ pub use alloc_crate::sync::{Arc, Weak}; #[stable(feature = "rust1", since = "1.0.0")] pub use core::sync::atomic; +#[unstable(feature = "exclusive_wrapper", issue = "none")] +pub use core::sync::Exclusive; #[stable(feature = "rust1", since = "1.0.0")] pub use self::barrier::{Barrier, BarrierWaitResult}; From 6ef203388483688b0a4598efbc63ee7295bff975 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Wed, 18 May 2022 03:51:52 +0100 Subject: [PATCH 0027/1135] Fix FFI-unwind unsoundness with mixed panic mode --- compiler/rustc_interface/src/passes.rs | 1 + compiler/rustc_lint_defs/src/builtin.rs | 40 +++++ compiler/rustc_metadata/src/creader.rs | 2 +- .../rustc_metadata/src/dependency_format.rs | 11 +- compiler/rustc_metadata/src/rmeta/decoder.rs | 2 +- compiler/rustc_metadata/src/rmeta/encoder.rs | 2 +- compiler/rustc_metadata/src/rmeta/mod.rs | 2 +- compiler/rustc_middle/src/query/mod.rs | 11 +- .../src/ffi_unwind_calls.rs | 148 ++++++++++++++++++ compiler/rustc_mir_transform/src/lib.rs | 5 + library/std/src/lib.rs | 2 + 11 files changed, 212 insertions(+), 14 deletions(-) create mode 100644 compiler/rustc_mir_transform/src/ffi_unwind_calls.rs diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 3c867e308c40e..ef9c7859dbb7a 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -944,6 +944,7 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> { if !tcx.sess.opts.debugging_opts.thir_unsafeck { rustc_mir_transform::check_unsafety::check_unsafety(tcx, def_id); } + tcx.ensure().has_ffi_unwind_calls(def_id); if tcx.hir().body_const_context(def_id).is_some() { tcx.ensure() diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index a067534b18938..48d89a785c117 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -3230,6 +3230,7 @@ declare_lint_pass! { UNEXPECTED_CFGS, DEPRECATED_WHERE_CLAUSE_LOCATION, TEST_UNSTABLE_LINT, + FFI_UNWIND_CALLS, ] } @@ -3895,3 +3896,42 @@ declare_lint! { "this unstable lint is only for testing", @feature_gate = sym::test_unstable_lint; } + +declare_lint! { + /// The `ffi_unwind_calls` lint detects calls to foreign functions or function pointers with + /// `C-unwind` or other FFI-unwind ABIs. + /// + /// ### Example + /// + /// ```rust,ignore (need FFI) + /// #![feature(ffi_unwind_calls)] + /// #![feature(c_unwind)] + /// + /// # mod impl { + /// # #[no_mangle] + /// # pub fn "C-unwind" fn foo() {} + /// # } + /// + /// extern "C-unwind" { + /// fn foo(); + /// } + /// + /// fn bar() { + /// unsafe { foo(); } + /// let ptr: unsafe extern "C-unwind" fn() = foo; + /// unsafe { ptr(); } + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// For crates containing such calls, if they are compiled with `-C panic=unwind` then the + /// produced library cannot be linked with crates compiled with `-C panic=abort`. For crates + /// that desire this ability it is therefore necessary to avoid such calls. + pub FFI_UNWIND_CALLS, + Allow, + "call to foreign functions or function pointers with FFI-unwind ABI", + @feature_gate = sym::c_unwind; +} diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index 947d563ae3cd1..3f6d1f050056d 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -744,7 +744,7 @@ impl<'a> CrateLoader<'a> { if !data.is_panic_runtime() { self.sess.err(&format!("the crate `{}` is not a panic runtime", name)); } - if data.panic_strategy() != desired_strategy { + if data.panic_strategy() != Some(desired_strategy) { self.sess.err(&format!( "the crate `{}` does not have the panic \ strategy `{}`", diff --git a/compiler/rustc_metadata/src/dependency_format.rs b/compiler/rustc_metadata/src/dependency_format.rs index 245b2076ebca9..349ff08124cf6 100644 --- a/compiler/rustc_metadata/src/dependency_format.rs +++ b/compiler/rustc_metadata/src/dependency_format.rs @@ -60,7 +60,6 @@ use rustc_middle::ty::TyCtxt; use rustc_session::config::CrateType; use rustc_session::cstore::CrateDepKind; use rustc_session::cstore::LinkagePreference::{self, RequireDynamic, RequireStatic}; -use rustc_target::spec::PanicStrategy; pub(crate) fn calculate(tcx: TyCtxt<'_>) -> Dependencies { tcx.sess @@ -367,7 +366,7 @@ fn verify_ok(tcx: TyCtxt<'_>, list: &[Linkage]) { prev_name, cur_name )); } - panic_runtime = Some((cnum, tcx.panic_strategy(cnum))); + panic_runtime = Some((cnum, tcx.panic_strategy(cnum).unwrap())); } } @@ -397,18 +396,14 @@ fn verify_ok(tcx: TyCtxt<'_>, list: &[Linkage]) { if let Linkage::NotLinked = *linkage { continue; } - if desired_strategy == PanicStrategy::Abort { - continue; - } let cnum = CrateNum::new(i + 1); if tcx.is_compiler_builtins(cnum) { continue; } - let found_strategy = tcx.panic_strategy(cnum); - if desired_strategy != found_strategy { + if let Some(found_strategy) = tcx.panic_strategy(cnum) && desired_strategy != found_strategy { sess.err(&format!( - "the crate `{}` is compiled with the \ + "the crate `{}` requires \ panic strategy `{}` which is \ incompatible with this crate's \ strategy of `{}`", diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 03ac82b467b8e..658c51bf62043 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -1759,7 +1759,7 @@ impl CrateMetadata { self.dep_kind.with_lock(|dep_kind| *dep_kind = f(*dep_kind)) } - pub(crate) fn panic_strategy(&self) -> PanicStrategy { + pub(crate) fn panic_strategy(&self) -> Option { self.root.panic_strategy } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 8867e008e42fa..cf685a7bd6217 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -665,7 +665,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { triple: tcx.sess.opts.target_triple.clone(), hash: tcx.crate_hash(LOCAL_CRATE), stable_crate_id: tcx.def_path_hash(LOCAL_CRATE.as_def_id()).stable_crate_id(), - panic_strategy: tcx.sess.panic_strategy(), + panic_strategy: tcx.required_panic_strategy(()), panic_in_drop_strategy: tcx.sess.opts.debugging_opts.panic_in_drop, edition: tcx.sess.edition(), has_global_allocator: tcx.has_global_allocator(LOCAL_CRATE), diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 04f0847f5cccc..60510e535b244 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -217,7 +217,7 @@ pub(crate) struct CrateRoot { extra_filename: String, hash: Svh, stable_crate_id: StableCrateId, - panic_strategy: PanicStrategy, + panic_strategy: Option, panic_in_drop_strategy: PanicStrategy, edition: Edition, has_global_allocator: bool, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 5b48f164016f7..5d0d11a1b784a 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1365,9 +1365,16 @@ rustc_queries! { desc { "query a crate is `#![profiler_runtime]`" } separate_provide_extern } - query panic_strategy(_: CrateNum) -> PanicStrategy { + query has_ffi_unwind_calls(key: LocalDefId) -> bool { + desc { |tcx| "check if `{}` contains FFI-unwind calls", tcx.def_path_str(key.to_def_id()) } + cache_on_disk_if { true } + } + query required_panic_strategy(_: ()) -> Option { + desc { "compute the required panic strategy for the current crate" } + } + query panic_strategy(_: CrateNum) -> Option { fatal_cycle - desc { "query a crate's configured panic strategy" } + desc { "query a crate's required panic strategy" } separate_provide_extern } query panic_in_drop_strategy(_: CrateNum) -> PanicStrategy { diff --git a/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs b/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs new file mode 100644 index 0000000000000..e6f8ef5759a11 --- /dev/null +++ b/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs @@ -0,0 +1,148 @@ +use rustc_hir::def::DefKind; +use rustc_hir::def_id::LocalDefId; +use rustc_middle::mir::*; +use rustc_middle::ty::layout; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::{self, TyCtxt}; +use rustc_session::lint::builtin::FFI_UNWIND_CALLS; +use rustc_target::spec::abi::Abi; +use rustc_target::spec::PanicStrategy; + +fn abi_can_unwind(abi: Abi) -> bool { + use Abi::*; + match abi { + C { unwind } + | System { unwind } + | Cdecl { unwind } + | Stdcall { unwind } + | Fastcall { unwind } + | Vectorcall { unwind } + | Thiscall { unwind } + | Aapcs { unwind } + | Win64 { unwind } + | SysV64 { unwind } => unwind, + PtxKernel + | Msp430Interrupt + | X86Interrupt + | AmdGpuKernel + | EfiApi + | AvrInterrupt + | AvrNonBlockingInterrupt + | CCmseNonSecureCall + | Wasm + | RustIntrinsic + | PlatformIntrinsic + | Unadjusted => false, + Rust | RustCall | RustCold => true, + } +} + +// Check if the body of this def_id can possibly leak a foreign unwind into Rust code. +fn has_ffi_unwind_calls(tcx: TyCtxt<'_>, local_def_id: LocalDefId) -> bool { + debug!("has_ffi_unwind_calls({local_def_id:?})"); + + // Only perform check on functions because constants cannot call FFI functions. + let def_id = local_def_id.to_def_id(); + let kind = tcx.def_kind(def_id); + let is_function = match kind { + DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(..) => true, + _ => tcx.is_closure(def_id), + }; + if !is_function { + return false; + } + + let body = &*tcx.mir_built(ty::WithOptConstParam::unknown(local_def_id)).borrow(); + + let body_ty = tcx.type_of(def_id); + let body_abi = match body_ty.kind() { + ty::FnDef(..) => body_ty.fn_sig(tcx).abi(), + ty::Closure(..) => Abi::RustCall, + ty::Generator(..) => Abi::Rust, + _ => span_bug!(body.span, "unexpected body ty: {:?}", body_ty), + }; + let body_can_unwind = layout::fn_can_unwind(tcx, Some(def_id), body_abi); + + // Foreign unwinds cannot leak past functions that themselves cannot unwind. + if !body_can_unwind { + return false; + } + + let mut tainted = false; + + for block in body.basic_blocks() { + if block.is_cleanup { + continue; + } + let Some(terminator) = &block.terminator else { continue }; + let TerminatorKind::Call { func, .. } = &terminator.kind else { continue }; + + let ty = func.ty(body, tcx); + let sig = ty.fn_sig(tcx); + + // Rust calls cannot themselves create foreign unwinds. + if let Abi::Rust | Abi::RustCall | Abi::RustCold = sig.abi() { + continue; + }; + + let fn_def_id = match ty.kind() { + ty::FnPtr(_) => None, + &ty::FnDef(def_id, _) => { + // Rust calls cannot themselves create foreign unwinds. + if !tcx.is_foreign_item(def_id) { + continue; + } + Some(def_id) + } + _ => bug!("invalid callee of type {:?}", ty), + }; + + if layout::fn_can_unwind(tcx, fn_def_id, sig.abi()) && abi_can_unwind(sig.abi()) { + // We have detected a call that can possibly leak foreign unwind. + // + // Because the function body itself can unwind, we are not aborting this function call + // upon unwind, so this call can possibly leak foreign unwind into Rust code if the + // panic runtime linked is panic-abort. + + let lint_root = body.source_scopes[terminator.source_info.scope] + .local_data + .as_ref() + .assert_crate_local() + .lint_root; + let span = terminator.source_info.span; + + tcx.struct_span_lint_hir(FFI_UNWIND_CALLS, lint_root, span, |lint| { + let msg = match fn_def_id { + Some(_) => "call to foreign function with FFI-unwind ABI", + None => "call to function pointer with FFI-unwind ABI", + }; + let mut db = lint.build(msg); + db.span_label(span, msg); + db.emit(); + }); + + tainted = true; + } + } + + tainted +} + +fn required_panic_strategy(tcx: TyCtxt<'_>, (): ()) -> Option { + if tcx.sess.panic_strategy() == PanicStrategy::Abort { + return Some(PanicStrategy::Abort); + } + + for def_id in tcx.hir().body_owners() { + if tcx.has_ffi_unwind_calls(def_id) { + return Some(PanicStrategy::Unwind); + } + } + + // This crate can be linked with either runtime. + None +} + +pub(crate) fn provide(providers: &mut Providers) { + *providers = Providers { has_ffi_unwind_calls, required_panic_strategy, ..*providers }; +} diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 0e57c3c697999..e4591c3f23e71 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -57,6 +57,7 @@ mod dest_prop; pub mod dump_mir; mod early_otherwise_branch; mod elaborate_drops; +mod ffi_unwind_calls; mod function_item_references; mod generator; mod inline; @@ -96,6 +97,7 @@ pub fn provide(providers: &mut Providers) { check_unsafety::provide(providers); check_packed_ref::provide(providers); coverage::query::provide(providers); + ffi_unwind_calls::provide(providers); shim::provide(providers); *providers = Providers { mir_keys, @@ -221,6 +223,9 @@ fn mir_const<'tcx>( } } + // has_ffi_unwind_calls query uses the raw mir, so make sure it is run. + tcx.ensure().has_ffi_unwind_calls(def.did); + let mut body = tcx.mir_built(def).steal(); rustc_middle::mir::dump_mir(tcx, None, "mir_map", &0, &body, |_, _| Ok(())); diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index b1c68ec43bc99..fc2ff3635133c 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -210,6 +210,8 @@ #![allow(unused_lifetimes)] // Tell the compiler to link to either panic_abort or panic_unwind #![needs_panic_runtime] +// Ensure that std can be linked against panic_abort despite compiled with `-C panic=unwind` +#![cfg_attr(not(bootstrap), deny(ffi_unwind_calls))] // std may use features in a platform-specific way #![allow(unused_features)] #![cfg_attr(test, feature(internal_output_capture, print_internals, update_panic_count))] From 77fd0cc566b4eca108b3580312f1e298fc9af8df Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Thu, 19 May 2022 17:38:54 +0100 Subject: [PATCH 0028/1135] Handle panic runtime specially --- compiler/rustc_metadata/src/dependency_format.rs | 13 +++++++++---- .../rustc_mir_transform/src/ffi_unwind_calls.rs | 6 +++++- .../ui/panic-runtime/transitive-link-a-bunch.stderr | 6 ++---- src/test/ui/panic-runtime/want-unwind-got-abort.rs | 2 +- .../ui/panic-runtime/want-unwind-got-abort.stderr | 4 +--- .../ui/panic-runtime/want-unwind-got-abort2.stderr | 6 ++---- 6 files changed, 20 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_metadata/src/dependency_format.rs b/compiler/rustc_metadata/src/dependency_format.rs index 349ff08124cf6..e22c903bf336e 100644 --- a/compiler/rustc_metadata/src/dependency_format.rs +++ b/compiler/rustc_metadata/src/dependency_format.rs @@ -366,14 +366,19 @@ fn verify_ok(tcx: TyCtxt<'_>, list: &[Linkage]) { prev_name, cur_name )); } - panic_runtime = Some((cnum, tcx.panic_strategy(cnum).unwrap())); + panic_runtime = Some(( + cnum, + tcx.panic_strategy(cnum).unwrap_or_else(|| { + bug!("cannot determine panic strategy of a panic runtime"); + }), + )); } } // If we found a panic runtime, then we know by this point that it's the // only one, but we perform validation here that all the panic strategy // compilation modes for the whole DAG are valid. - if let Some((cnum, found_strategy)) = panic_runtime { + if let Some((runtime_cnum, found_strategy)) = panic_runtime { let desired_strategy = sess.panic_strategy(); // First up, validate that our selected panic runtime is indeed exactly @@ -383,7 +388,7 @@ fn verify_ok(tcx: TyCtxt<'_>, list: &[Linkage]) { "the linked panic runtime `{}` is \ not compiled with this crate's \ panic strategy `{}`", - tcx.crate_name(cnum), + tcx.crate_name(runtime_cnum), desired_strategy.desc() )); } @@ -397,7 +402,7 @@ fn verify_ok(tcx: TyCtxt<'_>, list: &[Linkage]) { continue; } let cnum = CrateNum::new(i + 1); - if tcx.is_compiler_builtins(cnum) { + if cnum == runtime_cnum || tcx.is_compiler_builtins(cnum) { continue; } diff --git a/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs b/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs index e6f8ef5759a11..78809b105359d 100644 --- a/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs +++ b/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs @@ -1,5 +1,5 @@ use rustc_hir::def::DefKind; -use rustc_hir::def_id::LocalDefId; +use rustc_hir::def_id::{LocalDefId, LOCAL_CRATE}; use rustc_middle::mir::*; use rustc_middle::ty::layout; use rustc_middle::ty::query::Providers; @@ -129,6 +129,10 @@ fn has_ffi_unwind_calls(tcx: TyCtxt<'_>, local_def_id: LocalDefId) -> bool { } fn required_panic_strategy(tcx: TyCtxt<'_>, (): ()) -> Option { + if tcx.is_panic_runtime(LOCAL_CRATE) { + return Some(tcx.sess.panic_strategy()); + } + if tcx.sess.panic_strategy() == PanicStrategy::Abort { return Some(PanicStrategy::Abort); } diff --git a/src/test/ui/panic-runtime/transitive-link-a-bunch.stderr b/src/test/ui/panic-runtime/transitive-link-a-bunch.stderr index 4af754c81f97c..7f4a8ed290ecf 100644 --- a/src/test/ui/panic-runtime/transitive-link-a-bunch.stderr +++ b/src/test/ui/panic-runtime/transitive-link-a-bunch.stderr @@ -2,9 +2,7 @@ error: cannot link together two panic runtimes: panic_runtime_unwind and panic_r error: the linked panic runtime `panic_runtime_abort` is not compiled with this crate's panic strategy `unwind` -error: the crate `wants_panic_runtime_abort` is compiled with the panic strategy `abort` which is incompatible with this crate's strategy of `unwind` +error: the crate `wants_panic_runtime_abort` requires panic strategy `abort` which is incompatible with this crate's strategy of `unwind` -error: the crate `panic_runtime_abort` is compiled with the panic strategy `abort` which is incompatible with this crate's strategy of `unwind` - -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors diff --git a/src/test/ui/panic-runtime/want-unwind-got-abort.rs b/src/test/ui/panic-runtime/want-unwind-got-abort.rs index c48caaf079077..23bfea6af15c1 100644 --- a/src/test/ui/panic-runtime/want-unwind-got-abort.rs +++ b/src/test/ui/panic-runtime/want-unwind-got-abort.rs @@ -1,6 +1,6 @@ // build-fail // needs-unwind -// error-pattern:is incompatible with this crate's strategy of `unwind` +// error-pattern:is not compiled with this crate's panic strategy `unwind` // aux-build:panic-runtime-abort.rs // aux-build:panic-runtime-lang-items.rs // ignore-wasm32-bare compiled with panic=abort by default diff --git a/src/test/ui/panic-runtime/want-unwind-got-abort.stderr b/src/test/ui/panic-runtime/want-unwind-got-abort.stderr index d4fd2cca81fdc..d306ce6c5ea28 100644 --- a/src/test/ui/panic-runtime/want-unwind-got-abort.stderr +++ b/src/test/ui/panic-runtime/want-unwind-got-abort.stderr @@ -1,6 +1,4 @@ error: the linked panic runtime `panic_runtime_abort` is not compiled with this crate's panic strategy `unwind` -error: the crate `panic_runtime_abort` is compiled with the panic strategy `abort` which is incompatible with this crate's strategy of `unwind` - -error: aborting due to 2 previous errors +error: aborting due to previous error diff --git a/src/test/ui/panic-runtime/want-unwind-got-abort2.stderr b/src/test/ui/panic-runtime/want-unwind-got-abort2.stderr index 364a27a24eb70..014437b7f1b66 100644 --- a/src/test/ui/panic-runtime/want-unwind-got-abort2.stderr +++ b/src/test/ui/panic-runtime/want-unwind-got-abort2.stderr @@ -1,8 +1,6 @@ error: the linked panic runtime `panic_runtime_abort` is not compiled with this crate's panic strategy `unwind` -error: the crate `wants_panic_runtime_abort` is compiled with the panic strategy `abort` which is incompatible with this crate's strategy of `unwind` +error: the crate `wants_panic_runtime_abort` requires panic strategy `abort` which is incompatible with this crate's strategy of `unwind` -error: the crate `panic_runtime_abort` is compiled with the panic strategy `abort` which is incompatible with this crate's strategy of `unwind` - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors From bc5afd9e347c79d7e0c910eb4f452410579ca4b6 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Thu, 19 May 2022 23:37:26 +0100 Subject: [PATCH 0029/1135] Ensure ffi_unwind_calls lint is gated behind c_unwind --- .../ui/unwind-abis/feature-gate-c-unwind.rs | 4 +++ .../unwind-abis/feature-gate-c-unwind.stderr | 25 +++++++++++++++++-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/test/ui/unwind-abis/feature-gate-c-unwind.rs b/src/test/ui/unwind-abis/feature-gate-c-unwind.rs index f02a368d4e097..ba72f74f20ce6 100644 --- a/src/test/ui/unwind-abis/feature-gate-c-unwind.rs +++ b/src/test/ui/unwind-abis/feature-gate-c-unwind.rs @@ -1,6 +1,10 @@ // Test that the "C-unwind" ABI is feature-gated, and cannot be used when the // `c_unwind` feature gate is not used. +#![allow(ffi_unwind_calls)] +//~^ WARNING unknown lint: `ffi_unwind_calls` +//~| WARNING unknown lint: `ffi_unwind_calls` + extern "C-unwind" fn f() {} //~^ ERROR C-unwind ABI is experimental and subject to change [E0658] diff --git a/src/test/ui/unwind-abis/feature-gate-c-unwind.stderr b/src/test/ui/unwind-abis/feature-gate-c-unwind.stderr index f4c785a235f67..a67f46cd2e3b6 100644 --- a/src/test/ui/unwind-abis/feature-gate-c-unwind.stderr +++ b/src/test/ui/unwind-abis/feature-gate-c-unwind.stderr @@ -1,5 +1,16 @@ +warning: unknown lint: `ffi_unwind_calls` + --> $DIR/feature-gate-c-unwind.rs:4:1 + | +LL | #![allow(ffi_unwind_calls)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(unknown_lints)]` on by default + = note: the `ffi_unwind_calls` lint is unstable + = note: see issue #74990 for more information + = help: add `#![feature(c_unwind)]` to the crate attributes to enable + error[E0658]: C-unwind ABI is experimental and subject to change - --> $DIR/feature-gate-c-unwind.rs:4:8 + --> $DIR/feature-gate-c-unwind.rs:8:8 | LL | extern "C-unwind" fn f() {} | ^^^^^^^^^^ @@ -7,6 +18,16 @@ LL | extern "C-unwind" fn f() {} = note: see issue #74990 for more information = help: add `#![feature(c_unwind)]` to the crate attributes to enable -error: aborting due to previous error +warning: unknown lint: `ffi_unwind_calls` + --> $DIR/feature-gate-c-unwind.rs:4:1 + | +LL | #![allow(ffi_unwind_calls)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the `ffi_unwind_calls` lint is unstable + = note: see issue #74990 for more information + = help: add `#![feature(c_unwind)]` to the crate attributes to enable + +error: aborting due to previous error; 2 warnings emitted For more information about this error, try `rustc --explain E0658`. From 1750a2f723a796b9c98b223df6013f3b1b0254df Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Fri, 20 May 2022 21:52:00 +0100 Subject: [PATCH 0030/1135] Add tests for mixed panic mode restriction and lints --- .../ui/panic-runtime/auxiliary/needs-abort.rs | 5 ++++ .../panic-runtime/auxiliary/needs-unwind.rs | 13 ++++++++++ .../ui/panic-runtime/need-abort-got-unwind.rs | 9 +++++++ .../need-abort-got-unwind.stderr | 4 +++ .../ui/panic-runtime/need-unwind-got-abort.rs | 8 ++++++ .../need-unwind-got-abort.stderr | 6 +++++ .../ui/unwind-abis/ffi-unwind-calls-lint.rs | 25 +++++++++++++++++++ .../unwind-abis/ffi-unwind-calls-lint.stderr | 20 +++++++++++++++ 8 files changed, 90 insertions(+) create mode 100644 src/test/ui/panic-runtime/auxiliary/needs-abort.rs create mode 100644 src/test/ui/panic-runtime/auxiliary/needs-unwind.rs create mode 100644 src/test/ui/panic-runtime/need-abort-got-unwind.rs create mode 100644 src/test/ui/panic-runtime/need-abort-got-unwind.stderr create mode 100644 src/test/ui/panic-runtime/need-unwind-got-abort.rs create mode 100644 src/test/ui/panic-runtime/need-unwind-got-abort.stderr create mode 100644 src/test/ui/unwind-abis/ffi-unwind-calls-lint.rs create mode 100644 src/test/ui/unwind-abis/ffi-unwind-calls-lint.stderr diff --git a/src/test/ui/panic-runtime/auxiliary/needs-abort.rs b/src/test/ui/panic-runtime/auxiliary/needs-abort.rs new file mode 100644 index 0000000000000..8fad49b5e9d32 --- /dev/null +++ b/src/test/ui/panic-runtime/auxiliary/needs-abort.rs @@ -0,0 +1,5 @@ +// compile-flags:-C panic=abort +// no-prefer-dynamic + +#![crate_type = "rlib"] +#![no_std] diff --git a/src/test/ui/panic-runtime/auxiliary/needs-unwind.rs b/src/test/ui/panic-runtime/auxiliary/needs-unwind.rs new file mode 100644 index 0000000000000..d555b531986b0 --- /dev/null +++ b/src/test/ui/panic-runtime/auxiliary/needs-unwind.rs @@ -0,0 +1,13 @@ +// compile-flags:-C panic=unwind +// no-prefer-dynamic + +#![crate_type = "rlib"] +#![no_std] +#![feature(c_unwind)] + +extern "C-unwind" fn foo() {} + +fn bar() { + let ptr: extern "C-unwind" fn() = foo; + ptr(); +} diff --git a/src/test/ui/panic-runtime/need-abort-got-unwind.rs b/src/test/ui/panic-runtime/need-abort-got-unwind.rs new file mode 100644 index 0000000000000..c72fb96e357f0 --- /dev/null +++ b/src/test/ui/panic-runtime/need-abort-got-unwind.rs @@ -0,0 +1,9 @@ +// build-fail +// needs-unwind +// error-pattern:is incompatible with this crate's strategy of `unwind` +// aux-build:needs-abort.rs +// ignore-wasm32-bare compiled with panic=abort by default + +extern crate needs_abort; + +fn main() {} diff --git a/src/test/ui/panic-runtime/need-abort-got-unwind.stderr b/src/test/ui/panic-runtime/need-abort-got-unwind.stderr new file mode 100644 index 0000000000000..d29c7875fd0ff --- /dev/null +++ b/src/test/ui/panic-runtime/need-abort-got-unwind.stderr @@ -0,0 +1,4 @@ +error: the crate `needs_abort` requires panic strategy `abort` which is incompatible with this crate's strategy of `unwind` + +error: aborting due to previous error + diff --git a/src/test/ui/panic-runtime/need-unwind-got-abort.rs b/src/test/ui/panic-runtime/need-unwind-got-abort.rs new file mode 100644 index 0000000000000..3bcc0aa39ac96 --- /dev/null +++ b/src/test/ui/panic-runtime/need-unwind-got-abort.rs @@ -0,0 +1,8 @@ +// build-fail +// error-pattern:is incompatible with this crate's strategy of `abort` +// aux-build:needs-unwind.rs +// compile-flags:-C panic=abort + +extern crate needs_unwind; + +fn main() {} diff --git a/src/test/ui/panic-runtime/need-unwind-got-abort.stderr b/src/test/ui/panic-runtime/need-unwind-got-abort.stderr new file mode 100644 index 0000000000000..a53b7ffe57485 --- /dev/null +++ b/src/test/ui/panic-runtime/need-unwind-got-abort.stderr @@ -0,0 +1,6 @@ +error: the linked panic runtime `panic_unwind` is not compiled with this crate's panic strategy `abort` + +error: the crate `needs_unwind` requires panic strategy `unwind` which is incompatible with this crate's strategy of `abort` + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/unwind-abis/ffi-unwind-calls-lint.rs b/src/test/ui/unwind-abis/ffi-unwind-calls-lint.rs new file mode 100644 index 0000000000000..67c20e9eac54e --- /dev/null +++ b/src/test/ui/unwind-abis/ffi-unwind-calls-lint.rs @@ -0,0 +1,25 @@ +// build-pass + +#![feature(c_unwind)] +#![warn(ffi_unwind_calls)] + +mod foo { + #[no_mangle] + pub extern "C-unwind" fn foo() {} +} + +extern "C-unwind" { + fn foo(); +} + +fn main() { + // Call to Rust function is fine. + foo::foo(); + // Call to foreign function should warn. + unsafe { foo(); } + //~^ WARNING call to foreign function with FFI-unwind ABI + let ptr: extern "C-unwind" fn() = foo::foo; + // Call to function pointer should also warn. + ptr(); + //~^ WARNING call to function pointer with FFI-unwind ABI +} diff --git a/src/test/ui/unwind-abis/ffi-unwind-calls-lint.stderr b/src/test/ui/unwind-abis/ffi-unwind-calls-lint.stderr new file mode 100644 index 0000000000000..cf8a7782e35ee --- /dev/null +++ b/src/test/ui/unwind-abis/ffi-unwind-calls-lint.stderr @@ -0,0 +1,20 @@ +warning: call to foreign function with FFI-unwind ABI + --> $DIR/ffi-unwind-calls-lint.rs:19:14 + | +LL | unsafe { foo(); } + | ^^^^^ call to foreign function with FFI-unwind ABI + | +note: the lint level is defined here + --> $DIR/ffi-unwind-calls-lint.rs:4:9 + | +LL | #![warn(ffi_unwind_calls)] + | ^^^^^^^^^^^^^^^^ + +warning: call to function pointer with FFI-unwind ABI + --> $DIR/ffi-unwind-calls-lint.rs:23:5 + | +LL | ptr(); + | ^^^^^ call to function pointer with FFI-unwind ABI + +warning: 2 warnings emitted + From 9e6c044ee6860d8b97324c75cf3dfd6f47e2488e Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Mon, 23 May 2022 21:30:38 +0100 Subject: [PATCH 0031/1135] Use is_fn_like instead of matching on DefKind --- compiler/rustc_mir_transform/src/abort_unwinding_calls.rs | 7 +------ compiler/rustc_mir_transform/src/ffi_unwind_calls.rs | 7 +------ 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs b/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs index 11980382ffdb2..2c8389e532dd7 100644 --- a/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs +++ b/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs @@ -1,6 +1,5 @@ use crate::MirPass; use rustc_ast::InlineAsmOptions; -use rustc_hir::def::DefKind; use rustc_middle::mir::*; use rustc_middle::ty::layout; use rustc_middle::ty::{self, TyCtxt}; @@ -31,11 +30,7 @@ impl<'tcx> MirPass<'tcx> for AbortUnwindingCalls { // We don't simplify the MIR of constants at this time because that // namely results in a cyclic query when we call `tcx.type_of` below. - let is_function = match kind { - DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(..) => true, - _ => tcx.is_closure(def_id), - }; - if !is_function { + if !kind.is_fn_like() { return; } diff --git a/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs b/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs index 78809b105359d..b8ef68f608cc3 100644 --- a/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs +++ b/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs @@ -1,4 +1,3 @@ -use rustc_hir::def::DefKind; use rustc_hir::def_id::{LocalDefId, LOCAL_CRATE}; use rustc_middle::mir::*; use rustc_middle::ty::layout; @@ -44,11 +43,7 @@ fn has_ffi_unwind_calls(tcx: TyCtxt<'_>, local_def_id: LocalDefId) -> bool { // Only perform check on functions because constants cannot call FFI functions. let def_id = local_def_id.to_def_id(); let kind = tcx.def_kind(def_id); - let is_function = match kind { - DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(..) => true, - _ => tcx.is_closure(def_id), - }; - if !is_function { + if !kind.is_fn_like() { return false; } From 14d155a3dc42b35856e45fd9f4212ac0c432cd10 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Wed, 8 Jun 2022 21:32:17 +0100 Subject: [PATCH 0032/1135] Rename `panic_strategy` query to `required_panic_strategy` --- compiler/rustc_metadata/src/creader.rs | 2 +- compiler/rustc_metadata/src/dependency_format.rs | 4 ++-- compiler/rustc_metadata/src/rmeta/decoder.rs | 4 ++-- compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs | 2 +- compiler/rustc_metadata/src/rmeta/encoder.rs | 2 +- compiler/rustc_metadata/src/rmeta/mod.rs | 2 +- compiler/rustc_middle/src/query/mod.rs | 5 +---- compiler/rustc_mir_transform/src/ffi_unwind_calls.rs | 6 ++++-- 8 files changed, 13 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index 3f6d1f050056d..907324f0e4fc4 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -744,7 +744,7 @@ impl<'a> CrateLoader<'a> { if !data.is_panic_runtime() { self.sess.err(&format!("the crate `{}` is not a panic runtime", name)); } - if data.panic_strategy() != Some(desired_strategy) { + if data.required_panic_strategy() != Some(desired_strategy) { self.sess.err(&format!( "the crate `{}` does not have the panic \ strategy `{}`", diff --git a/compiler/rustc_metadata/src/dependency_format.rs b/compiler/rustc_metadata/src/dependency_format.rs index e22c903bf336e..770d164894a73 100644 --- a/compiler/rustc_metadata/src/dependency_format.rs +++ b/compiler/rustc_metadata/src/dependency_format.rs @@ -368,7 +368,7 @@ fn verify_ok(tcx: TyCtxt<'_>, list: &[Linkage]) { } panic_runtime = Some(( cnum, - tcx.panic_strategy(cnum).unwrap_or_else(|| { + tcx.required_panic_strategy(cnum).unwrap_or_else(|| { bug!("cannot determine panic strategy of a panic runtime"); }), )); @@ -406,7 +406,7 @@ fn verify_ok(tcx: TyCtxt<'_>, list: &[Linkage]) { continue; } - if let Some(found_strategy) = tcx.panic_strategy(cnum) && desired_strategy != found_strategy { + if let Some(found_strategy) = tcx.required_panic_strategy(cnum) && desired_strategy != found_strategy { sess.err(&format!( "the crate `{}` requires \ panic strategy `{}` which is \ diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 658c51bf62043..b8efca19fb28b 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -1759,8 +1759,8 @@ impl CrateMetadata { self.dep_kind.with_lock(|dep_kind| *dep_kind = f(*dep_kind)) } - pub(crate) fn panic_strategy(&self) -> Option { - self.root.panic_strategy + pub(crate) fn required_panic_strategy(&self) -> Option { + self.root.required_panic_strategy } pub(crate) fn needs_panic_runtime(&self) -> bool { diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index e3581a7607fb8..1237ac4ec4780 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -246,7 +246,7 @@ provide! { <'tcx> tcx, def_id, other, cdata, has_global_allocator => { cdata.root.has_global_allocator } has_panic_handler => { cdata.root.has_panic_handler } is_profiler_runtime => { cdata.root.profiler_runtime } - panic_strategy => { cdata.root.panic_strategy } + required_panic_strategy => { cdata.root.required_panic_strategy } panic_in_drop_strategy => { cdata.root.panic_in_drop_strategy } extern_crate => { let r = *cdata.extern_crate.lock(); diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index cf685a7bd6217..12cc5a72aa736 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -665,7 +665,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { triple: tcx.sess.opts.target_triple.clone(), hash: tcx.crate_hash(LOCAL_CRATE), stable_crate_id: tcx.def_path_hash(LOCAL_CRATE.as_def_id()).stable_crate_id(), - panic_strategy: tcx.required_panic_strategy(()), + required_panic_strategy: tcx.required_panic_strategy(LOCAL_CRATE), panic_in_drop_strategy: tcx.sess.opts.debugging_opts.panic_in_drop, edition: tcx.sess.edition(), has_global_allocator: tcx.has_global_allocator(LOCAL_CRATE), diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 60510e535b244..900d9b9842631 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -217,7 +217,7 @@ pub(crate) struct CrateRoot { extra_filename: String, hash: Svh, stable_crate_id: StableCrateId, - panic_strategy: Option, + required_panic_strategy: Option, panic_in_drop_strategy: PanicStrategy, edition: Edition, has_global_allocator: bool, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 5d0d11a1b784a..bcf7497b832b4 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1369,10 +1369,7 @@ rustc_queries! { desc { |tcx| "check if `{}` contains FFI-unwind calls", tcx.def_path_str(key.to_def_id()) } cache_on_disk_if { true } } - query required_panic_strategy(_: ()) -> Option { - desc { "compute the required panic strategy for the current crate" } - } - query panic_strategy(_: CrateNum) -> Option { + query required_panic_strategy(_: CrateNum) -> Option { fatal_cycle desc { "query a crate's required panic strategy" } separate_provide_extern diff --git a/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs b/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs index b8ef68f608cc3..d09d2a0b26346 100644 --- a/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs +++ b/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs @@ -1,4 +1,4 @@ -use rustc_hir::def_id::{LocalDefId, LOCAL_CRATE}; +use rustc_hir::def_id::{CrateNum, LocalDefId, LOCAL_CRATE}; use rustc_middle::mir::*; use rustc_middle::ty::layout; use rustc_middle::ty::query::Providers; @@ -123,7 +123,9 @@ fn has_ffi_unwind_calls(tcx: TyCtxt<'_>, local_def_id: LocalDefId) -> bool { tainted } -fn required_panic_strategy(tcx: TyCtxt<'_>, (): ()) -> Option { +fn required_panic_strategy(tcx: TyCtxt<'_>, cnum: CrateNum) -> Option { + assert_eq!(cnum, LOCAL_CRATE); + if tcx.is_panic_runtime(LOCAL_CRATE) { return Some(tcx.sess.panic_strategy()); } From 5823d7b56346247d6def30c946a49e47e7fe9848 Mon Sep 17 00:00:00 2001 From: Iago-lito Date: Mon, 30 May 2022 14:01:24 +0200 Subject: [PATCH 0033/1135] Partial stabilization of "nonzero_unchecked_ops". --- library/core/src/num/nonzero.rs | 52 ++++++++++++++++----------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index 70969edd6eaf7..5a7e8ef82c87a 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -316,7 +316,6 @@ macro_rules! nonzero_unsigned_operations { /// # Examples /// /// ``` - /// #![feature(nonzero_ops)] #[doc = concat!("# use std::num::", stringify!($Ty), ";")] /// /// # fn main() { test().unwrap(); } @@ -331,7 +330,8 @@ macro_rules! nonzero_unsigned_operations { /// # Some(()) /// # } /// ``` - #[unstable(feature = "nonzero_ops", issue = "84186")] + #[stable(feature = "nonzero_checked_ops", since = "1.63.0")] + #[rustc_const_unstable(feature = "const_nonzero_checked_ops", issue = "97547")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -351,7 +351,6 @@ macro_rules! nonzero_unsigned_operations { /// # Examples /// /// ``` - /// #![feature(nonzero_ops)] #[doc = concat!("# use std::num::", stringify!($Ty), ";")] /// /// # fn main() { test().unwrap(); } @@ -366,7 +365,8 @@ macro_rules! nonzero_unsigned_operations { /// # Some(()) /// # } /// ``` - #[unstable(feature = "nonzero_ops", issue = "84186")] + #[stable(feature = "nonzero_checked_ops", since = "1.63.0")] + #[rustc_const_unstable(feature = "const_nonzero_checked_ops", issue = "97547")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -415,7 +415,6 @@ macro_rules! nonzero_unsigned_operations { /// # Examples /// /// ``` - /// #![feature(nonzero_ops)] #[doc = concat!("# use std::num::", stringify!($Ty), ";")] /// /// # fn main() { test().unwrap(); } @@ -432,7 +431,8 @@ macro_rules! nonzero_unsigned_operations { /// # Some(()) /// # } /// ``` - #[unstable(feature = "nonzero_ops", issue = "84186")] + #[stable(feature = "nonzero_checked_ops", since = "1.63.0")] + #[rustc_const_unstable(feature = "const_nonzero_checked_ops", issue = "97547")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -521,7 +521,6 @@ macro_rules! nonzero_signed_operations { /// # Example /// /// ``` - /// #![feature(nonzero_ops)] #[doc = concat!("# use std::num::", stringify!($Ty), ";")] /// /// # fn main() { test().unwrap(); } @@ -534,7 +533,8 @@ macro_rules! nonzero_signed_operations { /// # Some(()) /// # } /// ``` - #[unstable(feature = "nonzero_ops", issue = "84186")] + #[stable(feature = "nonzero_checked_ops", since = "1.63.0")] + #[rustc_const_unstable(feature = "const_nonzero_checked_ops", issue = "97547")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -551,7 +551,6 @@ macro_rules! nonzero_signed_operations { /// # Example /// /// ``` - /// #![feature(nonzero_ops)] #[doc = concat!("# use std::num::", stringify!($Ty), ";")] /// /// # fn main() { test().unwrap(); } @@ -566,7 +565,8 @@ macro_rules! nonzero_signed_operations { /// # Some(()) /// # } /// ``` - #[unstable(feature = "nonzero_ops", issue = "84186")] + #[stable(feature = "nonzero_checked_ops", since = "1.63.0")] + #[rustc_const_unstable(feature = "const_nonzero_checked_ops", issue = "97547")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -586,7 +586,6 @@ macro_rules! nonzero_signed_operations { /// # Example /// /// ``` - /// #![feature(nonzero_ops)] #[doc = concat!("# use std::num::", stringify!($Ty), ";")] /// /// # fn main() { test().unwrap(); } @@ -602,7 +601,8 @@ macro_rules! nonzero_signed_operations { /// # Some(()) /// # } /// ``` - #[unstable(feature = "nonzero_ops", issue = "84186")] + #[stable(feature = "nonzero_checked_ops", since = "1.63.0")] + #[rustc_const_unstable(feature = "const_nonzero_checked_ops", issue = "97547")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -621,7 +621,6 @@ macro_rules! nonzero_signed_operations { /// # Example /// /// ``` - /// #![feature(nonzero_ops)] #[doc = concat!("# use std::num::", stringify!($Ty), ";")] /// /// # fn main() { test().unwrap(); } @@ -642,7 +641,8 @@ macro_rules! nonzero_signed_operations { /// # Some(()) /// # } /// ``` - #[unstable(feature = "nonzero_ops", issue = "84186")] + #[stable(feature = "nonzero_checked_ops", since = "1.63.0")] + #[rustc_const_unstable(feature = "const_nonzero_checked_ops", issue = "97547")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -657,7 +657,6 @@ macro_rules! nonzero_signed_operations { /// # Example /// /// ``` - /// #![feature(nonzero_ops)] #[doc = concat!("# use std::num::", stringify!($Ty), ";")] /// /// # fn main() { test().unwrap(); } @@ -677,7 +676,8 @@ macro_rules! nonzero_signed_operations { /// # Some(()) /// # } /// ``` - #[unstable(feature = "nonzero_ops", issue = "84186")] + #[stable(feature = "nonzero_checked_ops", since = "1.63.0")] + #[rustc_const_unstable(feature = "const_nonzero_checked_ops", issue = "97547")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -692,7 +692,6 @@ macro_rules! nonzero_signed_operations { /// # Example /// /// ``` - /// #![feature(nonzero_ops)] #[doc = concat!("# use std::num::", stringify!($Ty), ";")] #[doc = concat!("# use std::num::", stringify!($Uty), ";")] /// @@ -712,7 +711,8 @@ macro_rules! nonzero_signed_operations { /// # Some(()) /// # } /// ``` - #[unstable(feature = "nonzero_ops", issue = "84186")] + #[stable(feature = "nonzero_checked_ops", since = "1.63.0")] + #[rustc_const_unstable(feature = "const_nonzero_checked_ops", issue = "97547")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -746,7 +746,6 @@ macro_rules! nonzero_unsigned_signed_operations { /// # Examples /// /// ``` - /// #![feature(nonzero_ops)] #[doc = concat!("# use std::num::", stringify!($Ty), ";")] /// /// # fn main() { test().unwrap(); } @@ -761,7 +760,8 @@ macro_rules! nonzero_unsigned_signed_operations { /// # Some(()) /// # } /// ``` - #[unstable(feature = "nonzero_ops", issue = "84186")] + #[stable(feature = "nonzero_checked_ops", since = "1.63.0")] + #[rustc_const_unstable(feature = "const_nonzero_checked_ops", issue = "97547")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -782,7 +782,6 @@ macro_rules! nonzero_unsigned_signed_operations { /// # Examples /// /// ``` - /// #![feature(nonzero_ops)] #[doc = concat!("# use std::num::", stringify!($Ty), ";")] /// /// # fn main() { test().unwrap(); } @@ -797,7 +796,8 @@ macro_rules! nonzero_unsigned_signed_operations { /// # Some(()) /// # } /// ``` - #[unstable(feature = "nonzero_ops", issue = "84186")] + #[stable(feature = "nonzero_checked_ops", since = "1.63.0")] + #[rustc_const_unstable(feature = "const_nonzero_checked_ops", issue = "97547")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -855,7 +855,6 @@ macro_rules! nonzero_unsigned_signed_operations { /// # Examples /// /// ``` - /// #![feature(nonzero_ops)] #[doc = concat!("# use std::num::", stringify!($Ty), ";")] /// /// # fn main() { test().unwrap(); } @@ -870,7 +869,8 @@ macro_rules! nonzero_unsigned_signed_operations { /// # Some(()) /// # } /// ``` - #[unstable(feature = "nonzero_ops", issue = "84186")] + #[stable(feature = "nonzero_checked_ops", since = "1.63.0")] + #[rustc_const_unstable(feature = "const_nonzero_checked_ops", issue = "97547")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -899,7 +899,6 @@ macro_rules! nonzero_unsigned_signed_operations { /// # Examples /// /// ``` - /// #![feature(nonzero_ops)] #[doc = concat!("# use std::num::", stringify!($Ty), ";")] /// /// # fn main() { test().unwrap(); } @@ -914,7 +913,8 @@ macro_rules! nonzero_unsigned_signed_operations { /// # Some(()) /// # } /// ``` - #[unstable(feature = "nonzero_ops", issue = "84186")] + #[stable(feature = "nonzero_checked_ops", since = "1.63.0")] + #[rustc_const_unstable(feature = "const_nonzero_checked_ops", issue = "97547")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] From 69e8e7e73b7e56e518a2552097578d590043a30e Mon Sep 17 00:00:00 2001 From: Iago-lito Date: Thu, 9 Jun 2022 09:17:06 +0200 Subject: [PATCH 0034/1135] Stabilize NonZero* checked operations constness. --- library/core/src/num/nonzero.rs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index 5a7e8ef82c87a..5e95edaee19ff 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -331,7 +331,7 @@ macro_rules! nonzero_unsigned_operations { /// # } /// ``` #[stable(feature = "nonzero_checked_ops", since = "1.63.0")] - #[rustc_const_unstable(feature = "const_nonzero_checked_ops", issue = "97547")] + #[rustc_const_stable(feature = "const_nonzero_checked_ops", since = "1.63.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -366,7 +366,7 @@ macro_rules! nonzero_unsigned_operations { /// # } /// ``` #[stable(feature = "nonzero_checked_ops", since = "1.63.0")] - #[rustc_const_unstable(feature = "const_nonzero_checked_ops", issue = "97547")] + #[rustc_const_stable(feature = "const_nonzero_checked_ops", since = "1.63.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -432,7 +432,7 @@ macro_rules! nonzero_unsigned_operations { /// # } /// ``` #[stable(feature = "nonzero_checked_ops", since = "1.63.0")] - #[rustc_const_unstable(feature = "const_nonzero_checked_ops", issue = "97547")] + #[rustc_const_stable(feature = "const_nonzero_checked_ops", since = "1.63.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -534,7 +534,7 @@ macro_rules! nonzero_signed_operations { /// # } /// ``` #[stable(feature = "nonzero_checked_ops", since = "1.63.0")] - #[rustc_const_unstable(feature = "const_nonzero_checked_ops", issue = "97547")] + #[rustc_const_stable(feature = "const_nonzero_checked_ops", since = "1.63.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -566,7 +566,7 @@ macro_rules! nonzero_signed_operations { /// # } /// ``` #[stable(feature = "nonzero_checked_ops", since = "1.63.0")] - #[rustc_const_unstable(feature = "const_nonzero_checked_ops", issue = "97547")] + #[rustc_const_stable(feature = "const_nonzero_checked_ops", since = "1.63.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -602,7 +602,7 @@ macro_rules! nonzero_signed_operations { /// # } /// ``` #[stable(feature = "nonzero_checked_ops", since = "1.63.0")] - #[rustc_const_unstable(feature = "const_nonzero_checked_ops", issue = "97547")] + #[rustc_const_stable(feature = "const_nonzero_checked_ops", since = "1.63.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -642,7 +642,7 @@ macro_rules! nonzero_signed_operations { /// # } /// ``` #[stable(feature = "nonzero_checked_ops", since = "1.63.0")] - #[rustc_const_unstable(feature = "const_nonzero_checked_ops", issue = "97547")] + #[rustc_const_stable(feature = "const_nonzero_checked_ops", since = "1.63.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -677,7 +677,7 @@ macro_rules! nonzero_signed_operations { /// # } /// ``` #[stable(feature = "nonzero_checked_ops", since = "1.63.0")] - #[rustc_const_unstable(feature = "const_nonzero_checked_ops", issue = "97547")] + #[rustc_const_stable(feature = "const_nonzero_checked_ops", since = "1.63.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -712,7 +712,7 @@ macro_rules! nonzero_signed_operations { /// # } /// ``` #[stable(feature = "nonzero_checked_ops", since = "1.63.0")] - #[rustc_const_unstable(feature = "const_nonzero_checked_ops", issue = "97547")] + #[rustc_const_stable(feature = "const_nonzero_checked_ops", since = "1.63.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -761,7 +761,7 @@ macro_rules! nonzero_unsigned_signed_operations { /// # } /// ``` #[stable(feature = "nonzero_checked_ops", since = "1.63.0")] - #[rustc_const_unstable(feature = "const_nonzero_checked_ops", issue = "97547")] + #[rustc_const_stable(feature = "const_nonzero_checked_ops", since = "1.63.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -797,7 +797,7 @@ macro_rules! nonzero_unsigned_signed_operations { /// # } /// ``` #[stable(feature = "nonzero_checked_ops", since = "1.63.0")] - #[rustc_const_unstable(feature = "const_nonzero_checked_ops", issue = "97547")] + #[rustc_const_stable(feature = "const_nonzero_checked_ops", since = "1.63.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -870,7 +870,7 @@ macro_rules! nonzero_unsigned_signed_operations { /// # } /// ``` #[stable(feature = "nonzero_checked_ops", since = "1.63.0")] - #[rustc_const_unstable(feature = "const_nonzero_checked_ops", issue = "97547")] + #[rustc_const_stable(feature = "const_nonzero_checked_ops", since = "1.63.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -914,7 +914,7 @@ macro_rules! nonzero_unsigned_signed_operations { /// # } /// ``` #[stable(feature = "nonzero_checked_ops", since = "1.63.0")] - #[rustc_const_unstable(feature = "const_nonzero_checked_ops", issue = "97547")] + #[rustc_const_stable(feature = "const_nonzero_checked_ops", since = "1.63.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] From 89f41839e4942a051305c62c0ea78e18d9b401f7 Mon Sep 17 00:00:00 2001 From: Tobias Bucher Date: Thu, 9 Jun 2022 14:27:01 +0200 Subject: [PATCH 0035/1135] Implement `fmt::Write` for `OsString` This allows to format into an `OsString` without unnecessary allocations. E.g. ``` let mut temp_filename = path.into_os_string(); write!(&mut temp_filename, ".tmp.{}", process::id()); ``` --- library/std/src/ffi/os_str.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs index 247bdd954589c..8ce4c2a601722 100644 --- a/library/std/src/ffi/os_str.rs +++ b/library/std/src/ffi/os_str.rs @@ -615,6 +615,14 @@ impl Hash for OsString { } } +#[stable(feature = "os_string_fmt_write", since = "1.63.0")] +impl fmt::Write for OsString { + fn write_str(&mut self, s: &str) -> fmt::Result { + self.push(s); + Ok(()) + } +} + impl OsStr { /// Coerces into an `OsStr` slice. /// From 0503bc0149d310cf8d07bbb793687eda5ac2ce72 Mon Sep 17 00:00:00 2001 From: Aron Parker Date: Thu, 9 Jun 2022 15:29:58 +0200 Subject: [PATCH 0036/1135] Implement ExitCodeExt for Windows --- library/std/src/os/windows/process.rs | 19 +++++++++++++++++++ library/std/src/process.rs | 16 ++++++++++++++++ library/std/src/sys/windows/process.rs | 6 ++++++ 3 files changed, 41 insertions(+) diff --git a/library/std/src/os/windows/process.rs b/library/std/src/os/windows/process.rs index 1c7e361c2a4a8..179c6e78807b4 100644 --- a/library/std/src/os/windows/process.rs +++ b/library/std/src/os/windows/process.rs @@ -194,3 +194,22 @@ impl ChildExt for process::Child { self.handle.main_thread_handle() } } + +/// Windows-specific extensions to [`process::ExitCode`]. +/// +/// This trait is sealed: it cannot be implemented outside the standard library. +/// This is so that future additional methods are not breaking changes. +#[stable(feature = "windows_process_exit_code_from", since = "1.63.0")] +pub trait ExitCodeExt: Sealed { + /// Creates a new `ExitStatus` from the raw underlying `u32` return value of + /// a process. + #[stable(feature = "windows_process_exit_code_from", since = "1.63.0")] + fn from_raw(raw: u32) -> Self; +} + +#[stable(feature = "windows_process_exit_code_from", since = "1.63.0")] +impl ExitCodeExt for process::ExitCode { + fn from_raw(raw: u32) -> Self { + process::ExitCode::from_inner(From::from(raw)) + } +} diff --git a/library/std/src/process.rs b/library/std/src/process.rs index 1def9fe097202..903ad01a20071 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -1708,6 +1708,10 @@ impl crate::error::Error for ExitStatusError {} #[stable(feature = "process_exitcode", since = "1.61.0")] pub struct ExitCode(imp::ExitCode); +/// Allows extension traits within `std`. +#[unstable(feature = "sealed", issue = "none")] +impl crate::sealed::Sealed for ExitCode {} + #[stable(feature = "process_exitcode", since = "1.61.0")] impl ExitCode { /// The canonical `ExitCode` for successful termination on this platform. @@ -1798,6 +1802,18 @@ impl From for ExitCode { } } +impl AsInner for ExitCode { + fn as_inner(&self) -> &imp::ExitCode { + &self.0 + } +} + +impl FromInner for ExitCode { + fn from_inner(s: imp::ExitCode) -> ExitCode { + ExitCode(s) + } +} + impl Child { /// Forces the child process to exit. If the child has already exited, an [`InvalidInput`] /// error is returned. diff --git a/library/std/src/sys/windows/process.rs b/library/std/src/sys/windows/process.rs index 9fd399f4ba1d3..02d5af4719ae8 100644 --- a/library/std/src/sys/windows/process.rs +++ b/library/std/src/sys/windows/process.rs @@ -707,6 +707,12 @@ impl From for ExitCode { } } +impl From for ExitCode { + fn from(code: u32) -> Self { + ExitCode(c::DWORD::from(code)) + } +} + fn zeroed_startupinfo() -> c::STARTUPINFO { c::STARTUPINFO { cb: 0, From ce774e377881671b968d193440d99884e84039b6 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Wed, 8 Jun 2022 22:42:05 +0100 Subject: [PATCH 0037/1135] Add a explanation about required panic strategy computation --- .../src/ffi_unwind_calls.rs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs b/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs index d09d2a0b26346..7728fdaffb0dc 100644 --- a/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs +++ b/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs @@ -136,6 +136,27 @@ fn required_panic_strategy(tcx: TyCtxt<'_>, cnum: CrateNum) -> Option Date: Fri, 10 Jun 2022 14:21:49 +0200 Subject: [PATCH 0038/1135] Fix copy paste error --- library/std/src/os/windows/process.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/os/windows/process.rs b/library/std/src/os/windows/process.rs index 179c6e78807b4..027a901c08c5c 100644 --- a/library/std/src/os/windows/process.rs +++ b/library/std/src/os/windows/process.rs @@ -201,7 +201,7 @@ impl ChildExt for process::Child { /// This is so that future additional methods are not breaking changes. #[stable(feature = "windows_process_exit_code_from", since = "1.63.0")] pub trait ExitCodeExt: Sealed { - /// Creates a new `ExitStatus` from the raw underlying `u32` return value of + /// Creates a new `ExitCode` from the raw underlying `u32` return value of /// a process. #[stable(feature = "windows_process_exit_code_from", since = "1.63.0")] fn from_raw(raw: u32) -> Self; From eb783d9632e054f318ac35613408a3d13774d67f Mon Sep 17 00:00:00 2001 From: Aron Parker Date: Fri, 10 Jun 2022 14:33:19 +0200 Subject: [PATCH 0039/1135] Incorporate warning for potential exit code ambiguities --- library/std/src/os/windows/process.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/library/std/src/os/windows/process.rs b/library/std/src/os/windows/process.rs index 027a901c08c5c..2da1b159d723f 100644 --- a/library/std/src/os/windows/process.rs +++ b/library/std/src/os/windows/process.rs @@ -203,6 +203,10 @@ impl ChildExt for process::Child { pub trait ExitCodeExt: Sealed { /// Creates a new `ExitCode` from the raw underlying `u32` return value of /// a process. + /// + /// The exit code should not be 259, as this conflicts with the `STILL_ACTIVE` + /// macro returned from the `GetExitCodeProcess` function to signal that the + /// process has yet to run to completion. #[stable(feature = "windows_process_exit_code_from", since = "1.63.0")] fn from_raw(raw: u32) -> Self; } From b13af732f74bb84a5af522c4e2f5b51950747f0d Mon Sep 17 00:00:00 2001 From: Aron Parker Date: Fri, 10 Jun 2022 14:55:13 +0200 Subject: [PATCH 0040/1135] Make "windows_process_exit_code_from" unstable --- library/std/src/os/windows/process.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/std/src/os/windows/process.rs b/library/std/src/os/windows/process.rs index 2da1b159d723f..164da2a49f721 100644 --- a/library/std/src/os/windows/process.rs +++ b/library/std/src/os/windows/process.rs @@ -199,7 +199,7 @@ impl ChildExt for process::Child { /// /// This trait is sealed: it cannot be implemented outside the standard library. /// This is so that future additional methods are not breaking changes. -#[stable(feature = "windows_process_exit_code_from", since = "1.63.0")] +#[unstable(feature = "windows_process_exit_code_from", issue = "none")] pub trait ExitCodeExt: Sealed { /// Creates a new `ExitCode` from the raw underlying `u32` return value of /// a process. @@ -207,11 +207,11 @@ pub trait ExitCodeExt: Sealed { /// The exit code should not be 259, as this conflicts with the `STILL_ACTIVE` /// macro returned from the `GetExitCodeProcess` function to signal that the /// process has yet to run to completion. - #[stable(feature = "windows_process_exit_code_from", since = "1.63.0")] + #[unstable(feature = "windows_process_exit_code_from", issue = "none")] fn from_raw(raw: u32) -> Self; } -#[stable(feature = "windows_process_exit_code_from", since = "1.63.0")] +#[unstable(feature = "windows_process_exit_code_from", issue = "none")] impl ExitCodeExt for process::ExitCode { fn from_raw(raw: u32) -> Self { process::ExitCode::from_inner(From::from(raw)) From 6d6c63ee23c627aa9299cffa738f40189126c08a Mon Sep 17 00:00:00 2001 From: Micha White Date: Thu, 9 Jun 2022 16:25:48 -0400 Subject: [PATCH 0041/1135] Lint single_match with Options, Results, and Cows --- clippy_lints/src/matches/single_match.rs | 53 ++++++++++++++---------- tests/ui/single_match.stderr | 11 ++++- tests/ui/single_match_else.stderr | 42 ++++++++++++++++++- 3 files changed, 82 insertions(+), 24 deletions(-) diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index 0c4cb45d147ca..b54d4e8a0ee95 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -140,6 +140,23 @@ fn check_opt_like<'a>( ty: Ty<'a>, els: Option<&Expr<'_>>, ) { + // We want to suggest to exclude an arm that contains only wildcards or forms the exhaustive + // match with the second branch, without enum variants in matches. + if !contains_only_wilds(arms[1].pat) && !form_exhaustive_matches(cx, ty, arms[0].pat, arms[1].pat) { + return; + } + + let mut paths_and_types = Vec::new(); + if !collect_pat_paths(&mut paths_and_types, cx, arms[1].pat, ty) { + return; + } + + if paths_and_types.iter().all(|info| in_candidate_enum(cx, info)) { + report_single_pattern(cx, ex, arms, expr, els); + } +} + +fn in_candidate_enum<'a>(cx: &LateContext<'a>, path_info: &(String, Ty<'_>)) -> bool { // list of candidate `Enum`s we know will never get any more members let candidates = &[ (&paths::COW, "Borrowed"), @@ -151,29 +168,13 @@ fn check_opt_like<'a>( (&paths::RESULT, "Ok"), ]; - // We want to suggest to exclude an arm that contains only wildcards or forms the exhaustive - // match with the second branch, without enum variants in matches. - if !contains_only_wilds(arms[1].pat) && !form_exhaustive_matches(arms[0].pat, arms[1].pat) { - return; - } - - let mut paths_and_types = Vec::new(); - if !collect_pat_paths(&mut paths_and_types, cx, arms[1].pat, ty) { - return; - } - - let in_candidate_enum = |path_info: &(String, Ty<'_>)| -> bool { - let (path, ty) = path_info; - for &(ty_path, pat_path) in candidates { - if path == pat_path && match_type(cx, *ty, ty_path) { - return true; - } + let (path, ty) = path_info; + for &(ty_path, pat_path) in candidates { + if path == pat_path && match_type(cx, *ty, ty_path) { + return true; } - false - }; - if paths_and_types.iter().all(in_candidate_enum) { - report_single_pattern(cx, ex, arms, expr, els); } + false } /// Collects paths and their types from the given patterns. Returns true if the given pattern could @@ -218,7 +219,7 @@ fn contains_only_wilds(pat: &Pat<'_>) -> bool { /// Returns true if the given patterns forms only exhaustive matches that don't contain enum /// patterns without a wildcard. -fn form_exhaustive_matches(left: &Pat<'_>, right: &Pat<'_>) -> bool { +fn form_exhaustive_matches<'a>(cx: &LateContext<'a>, ty: Ty<'a>, left: &Pat<'_>, right: &Pat<'_>) -> bool { match (&left.kind, &right.kind) { (PatKind::Wild, _) | (_, PatKind::Wild) => true, (PatKind::Tuple(left_in, left_pos), PatKind::Tuple(right_in, right_pos)) => { @@ -264,6 +265,14 @@ fn form_exhaustive_matches(left: &Pat<'_>, right: &Pat<'_>) -> bool { } true }, + (PatKind::TupleStruct(..), PatKind::Path(_) | PatKind::TupleStruct(..)) => { + let mut paths_and_types = Vec::new(); + if !collect_pat_paths(&mut paths_and_types, cx, right, ty) { + return false; + } + + paths_and_types.iter().all(|info| in_candidate_enum(cx, info)) + }, _ => false, } } diff --git a/tests/ui/single_match.stderr b/tests/ui/single_match.stderr index 318faf2571758..4d2b9ec5f903a 100644 --- a/tests/ui/single_match.stderr +++ b/tests/ui/single_match.stderr @@ -38,6 +38,15 @@ LL | | _ => {}, LL | | }; | |_____^ help: try this: `if let (2..=3, 7..=9) = z { dummy() }` +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> $DIR/single_match.rs:54:5 + | +LL | / match x { +LL | | Some(y) => dummy(), +LL | | None => (), +LL | | }; + | |_____^ help: try this: `if let Some(y) = x { dummy() }` + error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` --> $DIR/single_match.rs:59:5 | @@ -146,5 +155,5 @@ LL | | (..) => {}, LL | | } | |_____^ help: try this: `if let (.., Some(E::V), _) = (Some(42), Some(E::V), Some(42)) {}` -error: aborting due to 15 previous errors +error: aborting due to 16 previous errors diff --git a/tests/ui/single_match_else.stderr b/tests/ui/single_match_else.stderr index 7756c6f204e67..dc603578fded1 100644 --- a/tests/ui/single_match_else.stderr +++ b/tests/ui/single_match_else.stderr @@ -20,5 +20,45 @@ LL + None LL ~ }; | -error: aborting due to previous error +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> $DIR/single_match_else.rs:84:5 + | +LL | / match Some(1) { +LL | | Some(a) => println!("${:?}", a), +LL | | None => { +LL | | println!("else block"); +LL | | return +LL | | }, +LL | | } + | |_____^ + | +help: try this + | +LL ~ if let Some(a) = Some(1) { println!("${:?}", a) } else { +LL + println!("else block"); +LL + return +LL + } + | + +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> $DIR/single_match_else.rs:93:5 + | +LL | / match Some(1) { +LL | | Some(a) => println!("${:?}", a), +LL | | None => { +LL | | println!("else block"); +LL | | return; +LL | | }, +LL | | } + | |_____^ + | +help: try this + | +LL ~ if let Some(a) = Some(1) { println!("${:?}", a) } else { +LL + println!("else block"); +LL + return; +LL + } + | + +error: aborting due to 3 previous errors From 2967127de2b99957cb03a3af741e797710eae534 Mon Sep 17 00:00:00 2001 From: Micha White Date: Thu, 9 Jun 2022 17:44:28 -0400 Subject: [PATCH 0042/1135] Refactored the candidate type checking --- clippy_lints/src/matches/single_match.rs | 48 +++++------------------- 1 file changed, 9 insertions(+), 39 deletions(-) diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index b54d4e8a0ee95..7d1ff2087290e 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -151,26 +151,17 @@ fn check_opt_like<'a>( return; } - if paths_and_types.iter().all(|info| in_candidate_enum(cx, info)) { + if paths_and_types.iter().all(|ty| in_candidate_enum(cx, *ty)) { report_single_pattern(cx, ex, arms, expr, els); } } -fn in_candidate_enum<'a>(cx: &LateContext<'a>, path_info: &(String, Ty<'_>)) -> bool { +fn in_candidate_enum<'a>(cx: &LateContext<'a>, ty: Ty<'_>) -> bool { // list of candidate `Enum`s we know will never get any more members - let candidates = &[ - (&paths::COW, "Borrowed"), - (&paths::COW, "Cow::Borrowed"), - (&paths::COW, "Cow::Owned"), - (&paths::COW, "Owned"), - (&paths::OPTION, "None"), - (&paths::RESULT, "Err"), - (&paths::RESULT, "Ok"), - ]; + let candidates = [&paths::COW, &paths::OPTION, &paths::RESULT]; - let (path, ty) = path_info; - for &(ty_path, pat_path) in candidates { - if path == pat_path && match_type(cx, *ty, ty_path) { + for candidate_ty in candidates { + if match_type(cx, ty, candidate_ty) { return true; } } @@ -179,29 +170,15 @@ fn in_candidate_enum<'a>(cx: &LateContext<'a>, path_info: &(String, Ty<'_>)) -> /// Collects paths and their types from the given patterns. Returns true if the given pattern could /// be simplified, false otherwise. -fn collect_pat_paths<'a>(acc: &mut Vec<(String, Ty<'a>)>, cx: &LateContext<'a>, pat: &Pat<'_>, ty: Ty<'a>) -> bool { +fn collect_pat_paths<'a>(acc: &mut Vec>, cx: &LateContext<'a>, pat: &Pat<'_>, ty: Ty<'a>) -> bool { match pat.kind { PatKind::Wild => true, PatKind::Tuple(inner, _) => inner.iter().all(|p| { let p_ty = cx.typeck_results().pat_ty(p); collect_pat_paths(acc, cx, p, p_ty) }), - PatKind::TupleStruct(ref path, ..) => { - let path = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| { - s.print_qpath(path, false); - }); - acc.push((path, ty)); - true - }, - PatKind::Binding(BindingAnnotation::Unannotated, .., ident, None) => { - acc.push((ident.to_string(), ty)); - true - }, - PatKind::Path(ref path) => { - let path = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| { - s.print_qpath(path, false); - }); - acc.push((path, ty)); + PatKind::TupleStruct(..) | PatKind::Binding(BindingAnnotation::Unannotated, .., None) | PatKind::Path(_) => { + acc.push(ty); true }, _ => false, @@ -265,14 +242,7 @@ fn form_exhaustive_matches<'a>(cx: &LateContext<'a>, ty: Ty<'a>, left: &Pat<'_>, } true }, - (PatKind::TupleStruct(..), PatKind::Path(_) | PatKind::TupleStruct(..)) => { - let mut paths_and_types = Vec::new(); - if !collect_pat_paths(&mut paths_and_types, cx, right, ty) { - return false; - } - - paths_and_types.iter().all(|info| in_candidate_enum(cx, info)) - }, + (PatKind::TupleStruct(..), PatKind::Path(_) | PatKind::TupleStruct(..)) => in_candidate_enum(cx, ty), _ => false, } } From b46f1c4a7fa5c0705cc65953e9887c4449aa8cba Mon Sep 17 00:00:00 2001 From: Micha White Date: Thu, 9 Jun 2022 20:01:22 -0400 Subject: [PATCH 0043/1135] Don't trigger if a binding is in the else pattern --- clippy_lints/src/matches/single_match.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index 7d1ff2087290e..e67ef2a2c3aa4 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -242,7 +242,10 @@ fn form_exhaustive_matches<'a>(cx: &LateContext<'a>, ty: Ty<'a>, left: &Pat<'_>, } true }, - (PatKind::TupleStruct(..), PatKind::Path(_) | PatKind::TupleStruct(..)) => in_candidate_enum(cx, ty), + (PatKind::TupleStruct(..), PatKind::Path(_)) => in_candidate_enum(cx, ty), + (PatKind::TupleStruct(..), PatKind::TupleStruct(_, inner, _)) => { + in_candidate_enum(cx, ty) && inner.iter().all(contains_only_wilds) + }, _ => false, } } From b0c20302b7a647ae362071d72b382b1bd393e4a5 Mon Sep 17 00:00:00 2001 From: Micha White Date: Thu, 9 Jun 2022 21:16:39 -0400 Subject: [PATCH 0044/1135] Some refactoring --- clippy_lints/src/matches/single_match.rs | 37 ++++++++++-------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index e67ef2a2c3aa4..813469be2f755 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -140,22 +140,20 @@ fn check_opt_like<'a>( ty: Ty<'a>, els: Option<&Expr<'_>>, ) { - // We want to suggest to exclude an arm that contains only wildcards or forms the exhaustive - // match with the second branch, without enum variants in matches. - if !contains_only_wilds(arms[1].pat) && !form_exhaustive_matches(cx, ty, arms[0].pat, arms[1].pat) { - return; + // We don't want to lint if the second arm contains an enum which could + // have more variants in the future. + if form_exhaustive_matches(cx, ty, arms[0].pat, arms[1].pat) { + report_single_pattern(cx, ex, arms, expr, els); } +} +fn pat_in_candidate_enum<'a>(cx: &LateContext<'a>, ty: Ty<'a>, pat: &Pat<'_>) -> bool { let mut paths_and_types = Vec::new(); - if !collect_pat_paths(&mut paths_and_types, cx, arms[1].pat, ty) { - return; - } - - if paths_and_types.iter().all(|ty| in_candidate_enum(cx, *ty)) { - report_single_pattern(cx, ex, arms, expr, els); - } + collect_pat_paths(&mut paths_and_types, cx, pat, ty); + paths_and_types.iter().all(|ty| in_candidate_enum(cx, *ty)) } +/// Returns `true` if the given type is an enum we know won't be expanded in the future fn in_candidate_enum<'a>(cx: &LateContext<'a>, ty: Ty<'_>) -> bool { // list of candidate `Enum`s we know will never get any more members let candidates = [&paths::COW, &paths::OPTION, &paths::RESULT]; @@ -168,20 +166,17 @@ fn in_candidate_enum<'a>(cx: &LateContext<'a>, ty: Ty<'_>) -> bool { false } -/// Collects paths and their types from the given patterns. Returns true if the given pattern could -/// be simplified, false otherwise. -fn collect_pat_paths<'a>(acc: &mut Vec>, cx: &LateContext<'a>, pat: &Pat<'_>, ty: Ty<'a>) -> bool { +/// Collects paths and their types from the given patterns +fn collect_pat_paths<'a>(acc: &mut Vec>, cx: &LateContext<'a>, pat: &Pat<'_>, ty: Ty<'a>) { match pat.kind { - PatKind::Wild => true, - PatKind::Tuple(inner, _) => inner.iter().all(|p| { + PatKind::Tuple(inner, _) => inner.iter().for_each(|p| { let p_ty = cx.typeck_results().pat_ty(p); - collect_pat_paths(acc, cx, p, p_ty) + collect_pat_paths(acc, cx, p, p_ty); }), PatKind::TupleStruct(..) | PatKind::Binding(BindingAnnotation::Unannotated, .., None) | PatKind::Path(_) => { acc.push(ty); - true }, - _ => false, + _ => {}, } } @@ -242,9 +237,9 @@ fn form_exhaustive_matches<'a>(cx: &LateContext<'a>, ty: Ty<'a>, left: &Pat<'_>, } true }, - (PatKind::TupleStruct(..), PatKind::Path(_)) => in_candidate_enum(cx, ty), + (PatKind::TupleStruct(..), PatKind::Path(_)) => pat_in_candidate_enum(cx, ty, right), (PatKind::TupleStruct(..), PatKind::TupleStruct(_, inner, _)) => { - in_candidate_enum(cx, ty) && inner.iter().all(contains_only_wilds) + pat_in_candidate_enum(cx, ty, right) && inner.iter().all(contains_only_wilds) }, _ => false, } From 01c75e4b98c3fe4cb1e6340bfd3148086ba05879 Mon Sep 17 00:00:00 2001 From: Micha White Date: Thu, 9 Jun 2022 21:39:13 -0400 Subject: [PATCH 0045/1135] Added tests for Cow and Result --- tests/ui/single_match_else.rs | 19 ++++++++++++++ tests/ui/single_match_else.stderr | 42 ++++++++++++++++++++++++++++++- 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/tests/ui/single_match_else.rs b/tests/ui/single_match_else.rs index 82387f3d80b77..70d6febb71f9d 100644 --- a/tests/ui/single_match_else.rs +++ b/tests/ui/single_match_else.rs @@ -97,4 +97,23 @@ fn main() { return; }, } + + // lint here + use std::convert::Infallible; + match Result::::Ok(1) { + Ok(a) => println!("${:?}", a), + Err(_) => { + println!("else block"); + return; + } + } + + use std::borrow::Cow; + match Cow::from("moo") { + Cow::Owned(a) => println!("${:?}", a), + Cow::Borrowed(_) => { + println!("else block"); + return; + } + } } diff --git a/tests/ui/single_match_else.stderr b/tests/ui/single_match_else.stderr index dc603578fded1..38fd9c6a6782a 100644 --- a/tests/ui/single_match_else.stderr +++ b/tests/ui/single_match_else.stderr @@ -60,5 +60,45 @@ LL + return; LL + } | -error: aborting due to 3 previous errors +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> $DIR/single_match_else.rs:103:5 + | +LL | / match Result::::Ok(1) { +LL | | Ok(a) => println!("${:?}", a), +LL | | Err(_) => { +LL | | println!("else block"); +LL | | return; +LL | | } +LL | | } + | |_____^ + | +help: try this + | +LL ~ if let Ok(a) = Result::::Ok(1) { println!("${:?}", a) } else { +LL + println!("else block"); +LL + return; +LL + } + | + +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> $DIR/single_match_else.rs:112:5 + | +LL | / match Cow::from("moo") { +LL | | Cow::Owned(a) => println!("${:?}", a), +LL | | Cow::Borrowed(_) => { +LL | | println!("else block"); +LL | | return; +LL | | } +LL | | } + | |_____^ + | +help: try this + | +LL ~ if let Cow::Owned(a) = Cow::from("moo") { println!("${:?}", a) } else { +LL + println!("else block"); +LL + return; +LL + } + | + +error: aborting due to 5 previous errors From f3dc7ae370a67988e48a573e332247717ce9bee4 Mon Sep 17 00:00:00 2001 From: Micha White Date: Thu, 9 Jun 2022 21:46:13 -0400 Subject: [PATCH 0046/1135] Fixed up the documentation for a couple functions --- clippy_lints/src/matches/single_match.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index 813469be2f755..888ace687bb3b 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -147,6 +147,7 @@ fn check_opt_like<'a>( } } +/// Returns `true` if all of the types in the pattern are candidate enums fn pat_in_candidate_enum<'a>(cx: &LateContext<'a>, ty: Ty<'a>, pat: &Pat<'_>) -> bool { let mut paths_and_types = Vec::new(); collect_pat_paths(&mut paths_and_types, cx, pat, ty); @@ -166,7 +167,7 @@ fn in_candidate_enum<'a>(cx: &LateContext<'a>, ty: Ty<'_>) -> bool { false } -/// Collects paths and their types from the given patterns +/// Collects types from the given pattern fn collect_pat_paths<'a>(acc: &mut Vec>, cx: &LateContext<'a>, pat: &Pat<'_>, ty: Ty<'a>) { match pat.kind { PatKind::Tuple(inner, _) => inner.iter().for_each(|p| { From ded2bb54642507c7a037a46bce31078026078672 Mon Sep 17 00:00:00 2001 From: Micha White Date: Thu, 9 Jun 2022 21:47:24 -0400 Subject: [PATCH 0047/1135] Slight documentation change --- clippy_lints/src/matches/single_match.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index 888ace687bb3b..92091a0c3395f 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -147,7 +147,8 @@ fn check_opt_like<'a>( } } -/// Returns `true` if all of the types in the pattern are candidate enums +/// Returns `true` if all of the types in the pattern are enums which we know +/// won't be expanded in the future fn pat_in_candidate_enum<'a>(cx: &LateContext<'a>, ty: Ty<'a>, pat: &Pat<'_>) -> bool { let mut paths_and_types = Vec::new(); collect_pat_paths(&mut paths_and_types, cx, pat, ty); From 1a5e2d8c92ce1341fcf1632be981f78a3a8d635b Mon Sep 17 00:00:00 2001 From: Pietro Albini Date: Mon, 6 Jun 2022 10:35:29 +0200 Subject: [PATCH 0048/1135] account for simulated remap-debuginfo when resolving remapped paths --- compiler/rustc_metadata/src/rmeta/decoder.rs | 38 +++++++++++--------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 186031d4586aa..3b6ed1d834518 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -41,7 +41,7 @@ use std::io; use std::iter::TrustedLen; use std::mem; use std::num::NonZeroUsize; -use std::path::Path; +use std::path::PathBuf; use tracing::debug; pub(super) use cstore_impl::provide; @@ -1472,20 +1472,26 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { // // NOTE: if you update this, you might need to also update bootstrap's code for generating // the `rust-src` component in `Src::run` in `src/bootstrap/dist.rs`. - let virtual_rust_source_base_dir = option_env!("CFG_VIRTUAL_RUST_SOURCE_BASE_DIR") - .map(Path::new) - .filter(|_| { - // Only spend time on further checks if we have what to translate *to*. - sess.opts.real_rust_source_base_dir.is_some() - // Some tests need the translation to be always skipped. - && sess.opts.debugging_opts.translate_remapped_path_to_local_path - }) - .filter(|virtual_dir| { - // Don't translate away `/rustc/$hash` if we're still remapping to it, - // since that means we're still building `std`/`rustc` that need it, - // and we don't want the real path to leak into codegen/debuginfo. - !sess.opts.remap_path_prefix.iter().any(|(_from, to)| to == virtual_dir) - }); + let virtual_rust_source_base_dir = [ + option_env!("CFG_VIRTUAL_RUST_SOURCE_BASE_DIR").map(PathBuf::from), + sess.opts.debugging_opts.simulate_remapped_rust_src_base.clone(), + ] + .into_iter() + .filter(|_| { + // Only spend time on further checks if we have what to translate *to*. + sess.opts.real_rust_source_base_dir.is_some() + // Some tests need the translation to be always skipped. + && sess.opts.debugging_opts.translate_remapped_path_to_local_path + }) + .flatten() + .filter(|virtual_dir| { + // Don't translate away `/rustc/$hash` if we're still remapping to it, + // since that means we're still building `std`/`rustc` that need it, + // and we don't want the real path to leak into codegen/debuginfo. + !sess.opts.remap_path_prefix.iter().any(|(_from, to)| to == virtual_dir) + }) + .collect::>(); + let try_to_translate_virtual_to_real = |name: &mut rustc_span::FileName| { debug!( "try_to_translate_virtual_to_real(name={:?}): \ @@ -1493,7 +1499,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { name, virtual_rust_source_base_dir, sess.opts.real_rust_source_base_dir, ); - if let Some(virtual_dir) = virtual_rust_source_base_dir { + for virtual_dir in &virtual_rust_source_base_dir { if let Some(real_dir) = &sess.opts.real_rust_source_base_dir { if let rustc_span::FileName::Real(old_name) = name { if let rustc_span::RealFileName::Remapped { local_path: _, virtual_name } = From 03999c23944e5d06cab3ddc6919c5f98b494aad0 Mon Sep 17 00:00:00 2001 From: Jane Lusby Date: Mon, 13 Jun 2022 14:10:25 -0700 Subject: [PATCH 0049/1135] Add provider API to error trait --- library/std/src/error.rs | 116 ++++++++++++++++++++++++++++++++++++++- library/std/src/lib.rs | 1 + 2 files changed, 116 insertions(+), 1 deletion(-) diff --git a/library/std/src/error.rs b/library/std/src/error.rs index 87f213b160830..3d7d8191770a5 100644 --- a/library/std/src/error.rs +++ b/library/std/src/error.rs @@ -156,7 +156,7 @@ use core::array; use core::convert::Infallible; use crate::alloc::{AllocError, LayoutError}; -use crate::any::TypeId; +use crate::any::{Demand, Provider, TypeId}; use crate::backtrace::Backtrace; use crate::borrow::Cow; use crate::cell; @@ -295,6 +295,84 @@ pub trait Error: Debug + Display { fn cause(&self) -> Option<&dyn Error> { self.source() } + + /// Provides type based access to context intended for error reports. + /// + /// Used in conjunction with [`context`] and [`context_ref`] to extract + /// references to member variables from `dyn Error` trait objects. + /// + /// # Example + /// + /// ```rust + /// #![feature(provide_any)] + /// #![feature(error_in_core)] + /// use core::fmt; + /// use core::any::Demand; + /// + /// #[derive(Debug)] + /// struct MyBacktrace { + /// // ... + /// } + /// + /// impl MyBacktrace { + /// fn new() -> MyBacktrace { + /// // ... + /// # MyBacktrace {} + /// } + /// } + /// + /// #[derive(Debug)] + /// struct SourceError { + /// // ... + /// } + /// + /// impl fmt::Display for SourceError { + /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// write!(f, "Example Source Error") + /// } + /// } + /// + /// impl std::error::Error for SourceError {} + /// + /// #[derive(Debug)] + /// struct Error { + /// source: SourceError, + /// backtrace: MyBacktrace, + /// } + /// + /// impl fmt::Display for Error { + /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// write!(f, "Example Error") + /// } + /// } + /// + /// impl std::error::Error for Error { + /// fn provide<'a>(&'a self, req: &mut Demand<'a>) { + /// req + /// .provide_ref::(&self.backtrace) + /// .provide_ref::(&self.source); + /// } + /// } + /// + /// fn main() { + /// let backtrace = MyBacktrace::new(); + /// let source = SourceError {}; + /// let error = Error { source, backtrace }; + /// let dyn_error = &error as &dyn std::error::Error; + /// let backtrace_ref = dyn_error.request_ref::().unwrap(); + /// + /// assert!(core::ptr::eq(&error.backtrace, backtrace_ref)); + /// } + /// ``` + #[unstable(feature = "generic_member_access", issue = "none")] + fn provide<'a>(&'a self, _req: &mut Demand<'a>) {} +} + +#[unstable(feature = "generic_member_access", issue = "none")] +impl Provider for dyn Error + 'static { + fn provide<'a>(&'a self, req: &mut Demand<'a>) { + self.provide(req) + } } mod private { @@ -831,6 +909,18 @@ impl dyn Error + 'static { None } } + + /// Request a reference to context of type `T`. + #[unstable(feature = "generic_member_access", issue = "none")] + pub fn request_ref(&self) -> Option<&T> { + core::any::request_ref(self) + } + + /// Request a value to context of type `T`. + #[unstable(feature = "generic_member_access", issue = "none")] + pub fn request_value(&self) -> Option { + core::any::request_value(self) + } } impl dyn Error + 'static + Send { @@ -854,6 +944,18 @@ impl dyn Error + 'static + Send { pub fn downcast_mut(&mut self) -> Option<&mut T> { ::downcast_mut::(self) } + + /// Request a reference to context of type `T`. + #[unstable(feature = "generic_member_access", issue = "none")] + pub fn request_ref(&self) -> Option<&T> { + ::request_ref(self) + } + + /// Request a value to context of type `T`. + #[unstable(feature = "generic_member_access", issue = "none")] + pub fn request_value(&self) -> Option { + ::request_value(self) + } } impl dyn Error + 'static + Send + Sync { @@ -877,6 +979,18 @@ impl dyn Error + 'static + Send + Sync { pub fn downcast_mut(&mut self) -> Option<&mut T> { ::downcast_mut::(self) } + + /// Request a reference to context of type `T`. + #[unstable(feature = "generic_member_access", issue = "none")] + pub fn request_ref(&self) -> Option<&T> { + ::request_ref(self) + } + + /// Request a value to context of type `T`. + #[unstable(feature = "generic_member_access", issue = "none")] + pub fn request_value(&self) -> Option { + ::request_value(self) + } } impl dyn Error { diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 7da9f248c877a..c46752cc6f956 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -284,6 +284,7 @@ #![feature(panic_internals)] #![feature(portable_simd)] #![feature(prelude_2024)] +#![feature(provide_any)] #![feature(ptr_as_uninit)] #![feature(raw_os_nonzero)] #![feature(slice_internals)] From fb2d2e53fde0c74029aad4c10750fdb07a973263 Mon Sep 17 00:00:00 2001 From: Jane Lusby Date: Mon, 13 Jun 2022 14:15:05 -0700 Subject: [PATCH 0050/1135] remove outdated references --- library/std/src/error.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/library/std/src/error.rs b/library/std/src/error.rs index 3d7d8191770a5..e5cc008096d27 100644 --- a/library/std/src/error.rs +++ b/library/std/src/error.rs @@ -298,14 +298,13 @@ pub trait Error: Debug + Display { /// Provides type based access to context intended for error reports. /// - /// Used in conjunction with [`context`] and [`context_ref`] to extract + /// Used in conjunction with [`provide_value`] and [`provide_ref`] to extract /// references to member variables from `dyn Error` trait objects. /// /// # Example /// /// ```rust /// #![feature(provide_any)] - /// #![feature(error_in_core)] /// use core::fmt; /// use core::any::Demand; /// From e3839ccc83dfbdf5fc6bddc1a9a435e78379fec7 Mon Sep 17 00:00:00 2001 From: Jane Lusby Date: Mon, 13 Jun 2022 14:47:51 -0700 Subject: [PATCH 0051/1135] fix broken doc comment --- library/std/src/error.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/library/std/src/error.rs b/library/std/src/error.rs index e5cc008096d27..6ad04e3f922ca 100644 --- a/library/std/src/error.rs +++ b/library/std/src/error.rs @@ -298,13 +298,14 @@ pub trait Error: Debug + Display { /// Provides type based access to context intended for error reports. /// - /// Used in conjunction with [`provide_value`] and [`provide_ref`] to extract + /// Used in conjunction with [`Demand::provide_value`] and [`Demand::provide_ref`] to extract /// references to member variables from `dyn Error` trait objects. /// /// # Example /// /// ```rust /// #![feature(provide_any)] + /// #![feature(error_generic_member_access)] /// use core::fmt; /// use core::any::Demand; /// @@ -363,11 +364,11 @@ pub trait Error: Debug + Display { /// assert!(core::ptr::eq(&error.backtrace, backtrace_ref)); /// } /// ``` - #[unstable(feature = "generic_member_access", issue = "none")] + #[unstable(feature = "error_generic_member_access", issue = "none")] fn provide<'a>(&'a self, _req: &mut Demand<'a>) {} } -#[unstable(feature = "generic_member_access", issue = "none")] +#[unstable(feature = "error_generic_member_access", issue = "none")] impl Provider for dyn Error + 'static { fn provide<'a>(&'a self, req: &mut Demand<'a>) { self.provide(req) @@ -910,13 +911,13 @@ impl dyn Error + 'static { } /// Request a reference to context of type `T`. - #[unstable(feature = "generic_member_access", issue = "none")] + #[unstable(feature = "error_generic_member_access", issue = "none")] pub fn request_ref(&self) -> Option<&T> { core::any::request_ref(self) } /// Request a value to context of type `T`. - #[unstable(feature = "generic_member_access", issue = "none")] + #[unstable(feature = "error_generic_member_access", issue = "none")] pub fn request_value(&self) -> Option { core::any::request_value(self) } @@ -945,13 +946,13 @@ impl dyn Error + 'static + Send { } /// Request a reference to context of type `T`. - #[unstable(feature = "generic_member_access", issue = "none")] + #[unstable(feature = "error_generic_member_access", issue = "none")] pub fn request_ref(&self) -> Option<&T> { ::request_ref(self) } /// Request a value to context of type `T`. - #[unstable(feature = "generic_member_access", issue = "none")] + #[unstable(feature = "error_generic_member_access", issue = "none")] pub fn request_value(&self) -> Option { ::request_value(self) } @@ -980,13 +981,13 @@ impl dyn Error + 'static + Send + Sync { } /// Request a reference to context of type `T`. - #[unstable(feature = "generic_member_access", issue = "none")] + #[unstable(feature = "error_generic_member_access", issue = "none")] pub fn request_ref(&self) -> Option<&T> { ::request_ref(self) } /// Request a value to context of type `T`. - #[unstable(feature = "generic_member_access", issue = "none")] + #[unstable(feature = "error_generic_member_access", issue = "none")] pub fn request_value(&self) -> Option { ::request_value(self) } From ab3a2a024fb58d5ad3e893f1d5694468b187b2d3 Mon Sep 17 00:00:00 2001 From: Raoul Strackx Date: Tue, 29 Mar 2022 15:31:42 +0200 Subject: [PATCH 0052/1135] Unify copying data from enclave to userspace --- library/std/src/sys/sgx/abi/usercalls/alloc.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/library/std/src/sys/sgx/abi/usercalls/alloc.rs b/library/std/src/sys/sgx/abi/usercalls/alloc.rs index 3792a3820a534..9a64b7e4b150c 100644 --- a/library/std/src/sys/sgx/abi/usercalls/alloc.rs +++ b/library/std/src/sys/sgx/abi/usercalls/alloc.rs @@ -225,13 +225,9 @@ where /// Copies `val` into freshly allocated space in user memory. pub fn new_from_enclave(val: &T) -> Self { unsafe { - let ret = Self::new_uninit_bytes(mem::size_of_val(val)); - ptr::copy( - val as *const T as *const u8, - ret.0.as_ptr() as *mut u8, - mem::size_of_val(val), - ); - ret + let mut user = Self::new_uninit_bytes(mem::size_of_val(val)); + user.copy_from_enclave(val); + user } } From 531752f39ab662a73e7ab580bf8a06c6bfeef486 Mon Sep 17 00:00:00 2001 From: Raoul Strackx Date: Mon, 28 Mar 2022 14:34:16 +0200 Subject: [PATCH 0053/1135] Mitigate MMIO stale data vulnerabilities Intel Security Advisory: https://www.intel.com/content/www/us/en/security-center/advisory/intel-sa-00615.html --- .../std/src/sys/sgx/abi/usercalls/alloc.rs | 100 +++++++++++++++++- 1 file changed, 98 insertions(+), 2 deletions(-) diff --git a/library/std/src/sys/sgx/abi/usercalls/alloc.rs b/library/std/src/sys/sgx/abi/usercalls/alloc.rs index 9a64b7e4b150c..b37e9b257edb4 100644 --- a/library/std/src/sys/sgx/abi/usercalls/alloc.rs +++ b/library/std/src/sys/sgx/abi/usercalls/alloc.rs @@ -1,13 +1,15 @@ #![allow(unused)] +use crate::arch::asm; use crate::cell::UnsafeCell; +use crate::convert::TryInto; use crate::mem; use crate::ops::{CoerceUnsized, Deref, DerefMut, Index, IndexMut}; use crate::ptr::{self, NonNull}; use crate::slice; use crate::slice::SliceIndex; -use super::super::mem::is_user_range; +use super::super::mem::{is_enclave_range, is_user_range}; use fortanix_sgx_abi::*; /// A type that can be safely read from or written to userspace. @@ -300,6 +302,100 @@ where } } +/// Copies `len` bytes of data from enclave pointer `src` to userspace `dst` +/// +/// This function mitigates stale data vulnerabilities +/// https://www.intel.com/content/www/us/en/security-center/advisory/intel-sa-00615.html +/// +/// # Panics +/// This function panics if: +/// +/// * The `src` pointer is null +/// * The `dst` pointer is null +/// * The `src` memory range is not in enclave memory +/// * The `dst` memory range is not in user memory +unsafe fn copy_to_userspace(src: *const u8, dst: *mut u8, len: usize) { + unsafe fn copy_bytewise_to_userspace(src: *const u8, dst: *mut u8, len: usize) { + unsafe { + let seg_sel: u16 = 0; + for off in 0..len { + asm!(" + mov %ds, ({seg_sel}) + verw ({seg_sel}) + movb {val}, ({dst}) + mfence + lfence + ", + val = in(reg_byte) *src.offset(off as isize), + dst = in(reg) dst.offset(off as isize), + seg_sel = in(reg) &seg_sel, + options(nostack, att_syntax) + ); + } + } + } + + unsafe fn copy_aligned_quadwords_to_userspace(src: *const u8, dst: *mut u8, len: usize) { + unsafe { + asm!( + "rep movsq (%rsi), (%rdi)", + inout("rcx") len / 8 => _, + inout("rdi") dst => _, + inout("rsi") src => _, + options(att_syntax, nostack, preserves_flags) + ); + } + } + assert!(!src.is_null()); + assert!(!dst.is_null()); + assert!(is_enclave_range(src, len)); + assert!(is_user_range(dst, len)); + assert!(len < isize::MAX as usize); + assert!(!(src as usize).overflowing_add(len).1); + assert!(!(dst as usize).overflowing_add(len).1); + + if len < 8 { + // Can't align on 8 byte boundary: copy safely byte per byte + unsafe { + copy_bytewise_to_userspace(src, dst, len); + } + } else if len % 8 == 0 && dst as usize % 8 == 0 { + // Copying 8-byte aligned quadwords: copy quad word per quad word + unsafe { + copy_aligned_quadwords_to_userspace(src, dst, len); + } + } else { + // Split copies into three parts: + // +--------+ + // | small0 | Chunk smaller than 8 bytes + // +--------+ + // | big | Chunk 8-byte aligned, and size a multiple of 8 bytes + // +--------+ + // | small1 | Chunk smaller than 8 bytes + // +--------+ + + unsafe { + // Copy small0 + let small0_size = (8 - dst as usize % 8) as u8; + let small0_src = src; + let small0_dst = dst; + copy_bytewise_to_userspace(small0_src as _, small0_dst, small0_size as _); + + // Copy big + let small1_size = ((len - small0_size as usize) % 8) as u8; + let big_size = len - small0_size as usize - small1_size as usize; + let big_src = src.offset(small0_size as _); + let big_dst = dst.offset(small0_size as _); + copy_aligned_quadwords_to_userspace(big_src as _, big_dst, big_size); + + // Copy small1 + let small1_src = src.offset(big_size as isize + small0_size as isize); + let small1_dst = dst.offset(big_size as isize + small0_size as isize); + copy_bytewise_to_userspace(small1_src, small1_dst, small1_size as _); + } + } +} + #[unstable(feature = "sgx_platform", issue = "56975")] impl UserRef where @@ -348,7 +444,7 @@ where pub fn copy_from_enclave(&mut self, val: &T) { unsafe { assert_eq!(mem::size_of_val(val), mem::size_of_val(&*self.0.get())); - ptr::copy( + copy_to_userspace( val as *const T as *const u8, self.0.get() as *mut T as *mut u8, mem::size_of_val(val), From 6f7d1937e20edf00ea2b5d7c2c8ce2dab2830452 Mon Sep 17 00:00:00 2001 From: Raoul Strackx Date: Wed, 23 Mar 2022 10:03:00 +0100 Subject: [PATCH 0054/1135] Ensure userspace allocation is 8-byte aligned --- library/std/src/sys/sgx/abi/usercalls/alloc.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/library/std/src/sys/sgx/abi/usercalls/alloc.rs b/library/std/src/sys/sgx/abi/usercalls/alloc.rs index b37e9b257edb4..4c1f279857f9c 100644 --- a/library/std/src/sys/sgx/abi/usercalls/alloc.rs +++ b/library/std/src/sys/sgx/abi/usercalls/alloc.rs @@ -2,6 +2,7 @@ use crate::arch::asm; use crate::cell::UnsafeCell; +use crate::cmp; use crate::convert::TryInto; use crate::mem; use crate::ops::{CoerceUnsized, Deref, DerefMut, Index, IndexMut}; @@ -212,7 +213,9 @@ where unsafe { // Mustn't call alloc with size 0. let ptr = if size > 0 { - rtunwrap!(Ok, super::alloc(size, T::align_of())) as _ + // `copy_to_userspace` is more efficient when data is 8-byte aligned + let alignment = cmp::max(T::align_of(), 8); + rtunwrap!(Ok, super::alloc(size, alignment)) as _ } else { T::align_of() as _ // dangling pointer ok for size 0 }; From caff72361f9a3d9938032be703295ef7a0c0dd5d Mon Sep 17 00:00:00 2001 From: joboet Date: Tue, 7 Jun 2022 11:06:19 +0200 Subject: [PATCH 0055/1135] std: relax memory orderings in `Parker` Co-authored-by: Tomoaki Kawada --- .../std/src/sys_common/thread_parker/wait_flag.rs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/library/std/src/sys_common/thread_parker/wait_flag.rs b/library/std/src/sys_common/thread_parker/wait_flag.rs index 8db12693ef734..6561c186655a5 100644 --- a/library/std/src/sys_common/thread_parker/wait_flag.rs +++ b/library/std/src/sys_common/thread_parker/wait_flag.rs @@ -25,7 +25,7 @@ use crate::pin::Pin; use crate::sync::atomic::AtomicI8; -use crate::sync::atomic::Ordering::{Relaxed, SeqCst}; +use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; use crate::sys::wait_flag::WaitFlag; use crate::time::Duration; @@ -47,7 +47,7 @@ impl Parker { // This implementation doesn't require `unsafe` and `Pin`, but other implementations do. pub unsafe fn park(self: Pin<&Self>) { - match self.state.fetch_sub(1, SeqCst) { + match self.state.fetch_sub(1, Acquire) { // NOTIFIED => EMPTY NOTIFIED => return, // EMPTY => PARKED @@ -59,7 +59,7 @@ impl Parker { loop { self.wait_flag.wait(); - match self.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, Relaxed) { + match self.state.compare_exchange(NOTIFIED, EMPTY, Acquire, Relaxed) { Ok(_) => return, Err(PARKED) => (), Err(_) => panic!("inconsistent park state"), @@ -69,7 +69,7 @@ impl Parker { // This implementation doesn't require `unsafe` and `Pin`, but other implementations do. pub unsafe fn park_timeout(self: Pin<&Self>, dur: Duration) { - match self.state.fetch_sub(1, SeqCst) { + match self.state.fetch_sub(1, Acquire) { NOTIFIED => return, EMPTY => (), _ => panic!("inconsistent park state"), @@ -83,9 +83,8 @@ impl Parker { // is protected against this by looping until the token is actually given, but // here we cannot easily tell. - // Use `swap` to provide acquire ordering (not strictly necessary, but all other - // implementations do). - match self.state.swap(EMPTY, SeqCst) { + // Use `swap` to provide acquire ordering. + match self.state.swap(EMPTY, Acquire) { NOTIFIED => (), PARKED => (), _ => panic!("inconsistent park state"), @@ -94,7 +93,7 @@ impl Parker { // This implementation doesn't require `Pin`, but other implementations do. pub fn unpark(self: Pin<&Self>) { - let state = self.state.swap(NOTIFIED, SeqCst); + let state = self.state.swap(NOTIFIED, Release); if state == PARKED { self.wait_flag.raise(); From a27aaceee91c1b3a7b4f9b070054290e8530e1bd Mon Sep 17 00:00:00 2001 From: Raoul Strackx Date: Tue, 22 Mar 2022 17:34:44 +0100 Subject: [PATCH 0056/1135] Test `copy_to_userspace` function --- .../std/src/sys/sgx/abi/usercalls/alloc.rs | 2 +- library/std/src/sys/sgx/abi/usercalls/mod.rs | 2 ++ .../std/src/sys/sgx/abi/usercalls/tests.rs | 30 +++++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 library/std/src/sys/sgx/abi/usercalls/tests.rs diff --git a/library/std/src/sys/sgx/abi/usercalls/alloc.rs b/library/std/src/sys/sgx/abi/usercalls/alloc.rs index 4c1f279857f9c..4ac27e85f8ba8 100644 --- a/library/std/src/sys/sgx/abi/usercalls/alloc.rs +++ b/library/std/src/sys/sgx/abi/usercalls/alloc.rs @@ -317,7 +317,7 @@ where /// * The `dst` pointer is null /// * The `src` memory range is not in enclave memory /// * The `dst` memory range is not in user memory -unsafe fn copy_to_userspace(src: *const u8, dst: *mut u8, len: usize) { +pub(crate) unsafe fn copy_to_userspace(src: *const u8, dst: *mut u8, len: usize) { unsafe fn copy_bytewise_to_userspace(src: *const u8, dst: *mut u8, len: usize) { unsafe { let seg_sel: u16 = 0; diff --git a/library/std/src/sys/sgx/abi/usercalls/mod.rs b/library/std/src/sys/sgx/abi/usercalls/mod.rs index 2f99abba77667..79d1db5e1c50d 100644 --- a/library/std/src/sys/sgx/abi/usercalls/mod.rs +++ b/library/std/src/sys/sgx/abi/usercalls/mod.rs @@ -6,6 +6,8 @@ use crate::time::{Duration, Instant}; pub(crate) mod alloc; #[macro_use] pub(crate) mod raw; +#[cfg(test)] +mod tests; use self::raw::*; diff --git a/library/std/src/sys/sgx/abi/usercalls/tests.rs b/library/std/src/sys/sgx/abi/usercalls/tests.rs new file mode 100644 index 0000000000000..cbf7d7d54f7a2 --- /dev/null +++ b/library/std/src/sys/sgx/abi/usercalls/tests.rs @@ -0,0 +1,30 @@ +use super::alloc::copy_to_userspace; +use super::alloc::User; + +#[test] +fn test_copy_function() { + let mut src = [0u8; 100]; + let mut dst = User::<[u8]>::uninitialized(100); + + for i in 0..src.len() { + src[i] = i as _; + } + + for size in 0..48 { + // For all possible alignment + for offset in 0..8 { + // overwrite complete dst + dst.copy_from_enclave(&[0u8; 100]); + + // Copy src[0..size] to dst + offset + unsafe { copy_to_userspace(src.as_ptr(), dst.as_mut_ptr().offset(offset), size) }; + + // Verify copy + for byte in 0..size { + unsafe { + assert_eq!(*dst.as_ptr().offset(offset + byte as isize), src[byte as usize]); + } + } + } + } +} From 9306e9a4df2e6f8ed577fff3c82006c532ca51d2 Mon Sep 17 00:00:00 2001 From: Dany Marcoux Date: Wed, 18 May 2022 20:28:11 +0200 Subject: [PATCH 0057/1135] Ignore bodies containing `todo!()` in `clippy::if_same_then_else` --- clippy_lints/src/copies.rs | 18 +++++++++++++++++- tests/ui/if_same_then_else.rs | 3 +++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index 1deff9684a140..6f92caf739173 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -12,7 +12,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::hygiene::walk_chain; use rustc_span::source_map::SourceMap; -use rustc_span::{BytePos, Span, Symbol}; +use rustc_span::{sym, BytePos, Span, Symbol}; use std::borrow::Cow; declare_clippy_lint! { @@ -365,6 +365,21 @@ fn eq_stmts( .all(|b| get_stmt(b).map_or(false, |s| eq.eq_stmt(s, stmt))) } +fn block_contains_todo_macro(cx: &LateContext<'_>, block: &Block<'_>) -> bool { + dbg!(block); + if let Some(macro_def_id) = block.span.ctxt().outer_expn_data().macro_def_id { + dbg!(macro_def_id); + if let Some(diagnostic_name) = cx.tcx.get_diagnostic_name(macro_def_id) { + dbg!(diagnostic_name); + diagnostic_name == sym::todo_macro + } else { + false + } + } else { + false + } +} + fn scan_block_for_eq(cx: &LateContext<'_>, _conds: &[&Expr<'_>], block: &Block<'_>, blocks: &[&Block<'_>]) -> BlockEq { let mut eq = SpanlessEq::new(cx); let mut eq = eq.inter_expr(); @@ -398,6 +413,7 @@ fn scan_block_for_eq(cx: &LateContext<'_>, _conds: &[&Expr<'_>], block: &Block<' moved_locals, }; } + let end_search_start = block.stmts[start_end_eq..] .iter() .rev() diff --git a/tests/ui/if_same_then_else.rs b/tests/ui/if_same_then_else.rs index ef9567455008b..767185c207b3f 100644 --- a/tests/ui/if_same_then_else.rs +++ b/tests/ui/if_same_then_else.rs @@ -126,6 +126,9 @@ fn if_same_then_else() { _ => 4, }; } + + // Issue #8836 + if true { todo!() } else { todo!() } } // Issue #2423. This was causing an ICE. From d72294491c5d28449c49b884b620cc24b3cf010f Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Thu, 16 Jun 2022 11:23:31 +0200 Subject: [PATCH 0058/1135] Leak pthreax_mutex_t when it's dropped while locked. --- .../std/src/sys/unix/locks/pthread_mutex.rs | 20 ++++++++++++++++++- library/std/src/sys_common/lazy_box.rs | 19 +++++++++++++++--- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/library/std/src/sys/unix/locks/pthread_mutex.rs b/library/std/src/sys/unix/locks/pthread_mutex.rs index 916e898d8906e..98afee69ba622 100644 --- a/library/std/src/sys/unix/locks/pthread_mutex.rs +++ b/library/std/src/sys/unix/locks/pthread_mutex.rs @@ -1,5 +1,5 @@ use crate::cell::UnsafeCell; -use crate::mem::MaybeUninit; +use crate::mem::{forget, MaybeUninit}; use crate::sys::cvt_nz; use crate::sys_common::lazy_box::{LazyBox, LazyInit}; @@ -23,6 +23,24 @@ impl LazyInit for Mutex { unsafe { mutex.init() }; mutex } + + fn destroy(mutex: Box) { + // We're not allowed to pthread_mutex_destroy a locked mutex, + // so check first if it's unlocked. + if unsafe { mutex.try_lock() } { + unsafe { mutex.unlock() }; + drop(mutex); + } else { + // The mutex is locked. This happens if a MutexGuard is leaked. + // In this case, we just leak the Mutex too. + forget(mutex); + } + } + + fn cancel_init(_: Box) { + // In this case, we can just drop it without any checks, + // since it cannot have been locked yet. + } } impl Mutex { diff --git a/library/std/src/sys_common/lazy_box.rs b/library/std/src/sys_common/lazy_box.rs index 647c13d243724..63c3316bdeb28 100644 --- a/library/std/src/sys_common/lazy_box.rs +++ b/library/std/src/sys_common/lazy_box.rs @@ -21,8 +21,21 @@ pub(crate) trait LazyInit { /// /// It might be called more than once per LazyBox, as multiple threads /// might race to initialize it concurrently, each constructing and initializing - /// their own box. (All but one of them will be destroyed right after.) + /// their own box. All but one of them will be passed to `cancel_init` right after. fn init() -> Box; + + /// Any surplus boxes from `init()` that lost the initialization race + /// are passed to this function for disposal. + /// + /// The default implementation calls destroy(). + fn cancel_init(x: Box) { + Self::destroy(x); + } + + /// This is called to destroy a used box. + /// + /// The default implementation just drops it. + fn destroy(_: Box) {} } impl LazyBox { @@ -45,7 +58,7 @@ impl LazyBox { Err(ptr) => { // Lost the race to another thread. // Drop the box we created, and use the one from the other thread instead. - drop(unsafe { Box::from_raw(new_ptr) }); + T::cancel_init(unsafe { Box::from_raw(new_ptr) }); ptr } } @@ -71,7 +84,7 @@ impl Drop for LazyBox { fn drop(&mut self) { let ptr = *self.ptr.get_mut(); if !ptr.is_null() { - drop(unsafe { Box::from_raw(ptr) }); + T::destroy(unsafe { Box::from_raw(ptr) }); } } } From 7cb4cef123a53534aaaa3956d4edf079d8482c6b Mon Sep 17 00:00:00 2001 From: kyoto7250 <50972773+kyoto7250@users.noreply.github.com> Date: Thu, 16 Jun 2022 21:29:43 +0900 Subject: [PATCH 0059/1135] feat(fix): ignore todo! and unimplemented! in if_same_then_else --- clippy_lints/src/copies.rs | 51 ++++++++++++++++-------- tests/ui/if_same_then_else.rs | 64 +++++++++++++++++++++++++++++-- tests/ui/if_same_then_else.stderr | 20 +++++----- 3 files changed, 106 insertions(+), 29 deletions(-) diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index 6f92caf739173..a771656c20f25 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then}; +use clippy_utils::macros::macro_backtrace; use clippy_utils::source::{first_line_of_span, indent_of, reindent_multiline, snippet, snippet_opt}; use clippy_utils::{ eq_expr_value, get_enclosing_block, hash_expr, hash_stmt, if_sequence, is_else_clause, is_lint_allowed, @@ -7,14 +8,16 @@ use clippy_utils::{ use core::iter; use rustc_errors::Applicability; use rustc_hir::intravisit; -use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, Stmt, StmtKind}; +use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, QPath, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::hygiene::walk_chain; use rustc_span::source_map::SourceMap; -use rustc_span::{sym, BytePos, Span, Symbol}; +use rustc_span::{BytePos, Span, Symbol}; use std::borrow::Cow; +const ACCEPTABLE_MACRO: [&str; 2] = ["todo", "unimplemented"]; + declare_clippy_lint! { /// ### What it does /// Checks for consecutive `if`s with the same condition. @@ -195,7 +198,12 @@ fn lint_if_same_then_else(cx: &LateContext<'_>, conds: &[&Expr<'_>], blocks: &[& .array_windows::<2>() .enumerate() .fold(true, |all_eq, (i, &[lhs, rhs])| { - if eq.eq_block(lhs, rhs) && !contains_let(conds[i]) && conds.get(i + 1).map_or(true, |e| !contains_let(e)) { + if eq.eq_block(lhs, rhs) + && !contains_acceptable_macro(cx, lhs) + && !contains_acceptable_macro(cx, rhs) + && !contains_let(conds[i]) + && conds.get(i + 1).map_or(true, |e| !contains_let(e)) + { span_lint_and_note( cx, IF_SAME_THEN_ELSE, @@ -365,19 +373,33 @@ fn eq_stmts( .all(|b| get_stmt(b).map_or(false, |s| eq.eq_stmt(s, stmt))) } -fn block_contains_todo_macro(cx: &LateContext<'_>, block: &Block<'_>) -> bool { - dbg!(block); - if let Some(macro_def_id) = block.span.ctxt().outer_expn_data().macro_def_id { - dbg!(macro_def_id); - if let Some(diagnostic_name) = cx.tcx.get_diagnostic_name(macro_def_id) { - dbg!(diagnostic_name); - diagnostic_name == sym::todo_macro - } else { - false +fn contains_acceptable_macro(cx: &LateContext<'_>, block: &Block<'_>) -> bool { + for stmt in block.stmts { + match stmt.kind { + StmtKind::Semi(semi_expr) if acceptable_macro(cx, semi_expr) => return true, + _ => {}, } - } else { - false } + + if let Some(block_expr) = block.expr + && acceptable_macro(cx, block_expr) + { + return true + } + + false +} + +fn acceptable_macro(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + if let ExprKind::Call(call_expr, _) = expr.kind + && let ExprKind::Path(QPath::Resolved(None, path)) = call_expr.kind + && macro_backtrace(path.span).any(|macro_call| { + ACCEPTABLE_MACRO.contains(&cx.tcx.item_name(macro_call.def_id).as_str()) + }) { + return true; + } + + false } fn scan_block_for_eq(cx: &LateContext<'_>, _conds: &[&Expr<'_>], block: &Block<'_>, blocks: &[&Block<'_>]) -> BlockEq { @@ -413,7 +435,6 @@ fn scan_block_for_eq(cx: &LateContext<'_>, _conds: &[&Expr<'_>], block: &Block<' moved_locals, }; } - let end_search_start = block.stmts[start_end_eq..] .iter() .rev() diff --git a/tests/ui/if_same_then_else.rs b/tests/ui/if_same_then_else.rs index 767185c207b3f..2598c2ab426d3 100644 --- a/tests/ui/if_same_then_else.rs +++ b/tests/ui/if_same_then_else.rs @@ -6,7 +6,9 @@ clippy::no_effect, clippy::unused_unit, clippy::zero_divided_by_zero, - clippy::branches_sharing_code + clippy::branches_sharing_code, + dead_code, + unreachable_code )] struct Foo { @@ -126,9 +128,6 @@ fn if_same_then_else() { _ => 4, }; } - - // Issue #8836 - if true { todo!() } else { todo!() } } // Issue #2423. This was causing an ICE. @@ -158,4 +157,61 @@ mod issue_5698 { } } +mod issue_8836 { + fn do_not_lint() { + if true { + todo!() + } else { + todo!() + } + if true { + todo!(); + } else { + todo!(); + } + if true { + unimplemented!() + } else { + unimplemented!() + } + if true { + unimplemented!(); + } else { + unimplemented!(); + } + + if true { + println!("FOO"); + todo!(); + } else { + println!("FOO"); + todo!(); + } + + if true { + println!("FOO"); + unimplemented!(); + } else { + println!("FOO"); + unimplemented!(); + } + + if true { + println!("FOO"); + todo!() + } else { + println!("FOO"); + todo!() + } + + if true { + println!("FOO"); + unimplemented!() + } else { + println!("FOO"); + unimplemented!() + } + } +} + fn main() {} diff --git a/tests/ui/if_same_then_else.stderr b/tests/ui/if_same_then_else.stderr index 2f38052fc209e..2cdf442486a30 100644 --- a/tests/ui/if_same_then_else.stderr +++ b/tests/ui/if_same_then_else.stderr @@ -1,5 +1,5 @@ error: this `if` has identical blocks - --> $DIR/if_same_then_else.rs:21:13 + --> $DIR/if_same_then_else.rs:23:13 | LL | if true { | _____________^ @@ -13,7 +13,7 @@ LL | | } else { | = note: `-D clippy::if-same-then-else` implied by `-D warnings` note: same as this - --> $DIR/if_same_then_else.rs:29:12 + --> $DIR/if_same_then_else.rs:31:12 | LL | } else { | ____________^ @@ -26,7 +26,7 @@ LL | | } | |_____^ error: this `if` has identical blocks - --> $DIR/if_same_then_else.rs:65:21 + --> $DIR/if_same_then_else.rs:67:21 | LL | let _ = if true { | _____________________^ @@ -35,7 +35,7 @@ LL | | } else { | |_____^ | note: same as this - --> $DIR/if_same_then_else.rs:67:12 + --> $DIR/if_same_then_else.rs:69:12 | LL | } else { | ____________^ @@ -45,7 +45,7 @@ LL | | }; | |_____^ error: this `if` has identical blocks - --> $DIR/if_same_then_else.rs:72:21 + --> $DIR/if_same_then_else.rs:74:21 | LL | let _ = if true { | _____________________^ @@ -54,7 +54,7 @@ LL | | } else { | |_____^ | note: same as this - --> $DIR/if_same_then_else.rs:74:12 + --> $DIR/if_same_then_else.rs:76:12 | LL | } else { | ____________^ @@ -64,7 +64,7 @@ LL | | }; | |_____^ error: this `if` has identical blocks - --> $DIR/if_same_then_else.rs:88:21 + --> $DIR/if_same_then_else.rs:90:21 | LL | let _ = if true { | _____________________^ @@ -73,7 +73,7 @@ LL | | } else { | |_____^ | note: same as this - --> $DIR/if_same_then_else.rs:90:12 + --> $DIR/if_same_then_else.rs:92:12 | LL | } else { | ____________^ @@ -83,7 +83,7 @@ LL | | }; | |_____^ error: this `if` has identical blocks - --> $DIR/if_same_then_else.rs:95:13 + --> $DIR/if_same_then_else.rs:97:13 | LL | if true { | _____________^ @@ -96,7 +96,7 @@ LL | | } else { | |_____^ | note: same as this - --> $DIR/if_same_then_else.rs:102:12 + --> $DIR/if_same_then_else.rs:104:12 | LL | } else { | ____________^ From 922ff84baf2889709c89fcd2704ba097c4b4fd35 Mon Sep 17 00:00:00 2001 From: klensy Date: Wed, 15 Jun 2022 14:15:54 +0300 Subject: [PATCH 0060/1135] bless clippy tests --- tests/ui/eprint_with_newline.stderr | 16 ++++++++-------- tests/ui/manual_split_once.stderr | 20 ++++++++++---------- tests/ui/map_unwrap_or.stderr | 8 ++++---- tests/ui/needless_late_init.stderr | 4 ++-- tests/ui/print_literal.stderr | 22 +++++++++++----------- tests/ui/print_with_newline.stderr | 16 ++++++++-------- tests/ui/unnecessary_iter_cloned.stderr | 4 ++-- tests/ui/unnecessary_to_owned.stderr | 2 +- tests/ui/write_literal.stderr | 22 +++++++++++----------- tests/ui/write_literal_2.stderr | 10 +++++----- tests/ui/write_with_newline.stderr | 16 ++++++++-------- 11 files changed, 70 insertions(+), 70 deletions(-) diff --git a/tests/ui/eprint_with_newline.stderr b/tests/ui/eprint_with_newline.stderr index 090dae3733d91..f137787bff0c7 100644 --- a/tests/ui/eprint_with_newline.stderr +++ b/tests/ui/eprint_with_newline.stderr @@ -9,7 +9,7 @@ help: use `eprintln!` instead | LL - eprint!("Hello/n"); LL + eprintln!("Hello"); - | + | error: using `eprint!()` with a format string that ends in a single newline --> $DIR/eprint_with_newline.rs:6:5 @@ -21,7 +21,7 @@ help: use `eprintln!` instead | LL - eprint!("Hello {}/n", "world"); LL + eprintln!("Hello {}", "world"); - | + | error: using `eprint!()` with a format string that ends in a single newline --> $DIR/eprint_with_newline.rs:7:5 @@ -33,7 +33,7 @@ help: use `eprintln!` instead | LL - eprint!("Hello {} {}/n", "world", "#2"); LL + eprintln!("Hello {} {}", "world", "#2"); - | + | error: using `eprint!()` with a format string that ends in a single newline --> $DIR/eprint_with_newline.rs:8:5 @@ -45,7 +45,7 @@ help: use `eprintln!` instead | LL - eprint!("{}/n", 1265); LL + eprintln!("{}", 1265); - | + | error: using `eprint!()` with a format string that ends in a single newline --> $DIR/eprint_with_newline.rs:9:5 @@ -57,7 +57,7 @@ help: use `eprintln!` instead | LL - eprint!("/n"); LL + eprintln!(); - | + | error: using `eprint!()` with a format string that ends in a single newline --> $DIR/eprint_with_newline.rs:28:5 @@ -69,7 +69,7 @@ help: use `eprintln!` instead | LL - eprint!("//n"); // should fail LL + eprintln!("/"); // should fail - | + | error: using `eprint!()` with a format string that ends in a single newline --> $DIR/eprint_with_newline.rs:35:5 @@ -111,7 +111,7 @@ help: use `eprintln!` instead | LL - eprint!("/r/n"); //~ ERROR LL + eprintln!("/r"); //~ ERROR - | + | error: using `eprint!()` with a format string that ends in a single newline --> $DIR/eprint_with_newline.rs:48:5 @@ -123,7 +123,7 @@ help: use `eprintln!` instead | LL - eprint!("foo/rbar/n") // ~ ERROR LL + eprintln!("foo/rbar") // ~ ERROR - | + | error: aborting due to 10 previous errors diff --git a/tests/ui/manual_split_once.stderr b/tests/ui/manual_split_once.stderr index 2563a6904b77c..2696694680ad7 100644 --- a/tests/ui/manual_split_once.stderr +++ b/tests/ui/manual_split_once.stderr @@ -96,12 +96,12 @@ help: remove the `iter` usages | LL - let l = iter.next().unwrap(); LL + - | + | help: remove the `iter` usages | LL - let r = iter.next().unwrap(); LL + - | + | error: manual implementation of `split_once` --> $DIR/manual_split_once.rs:49:5 @@ -121,12 +121,12 @@ help: remove the `iter` usages | LL - let l = iter.next()?; LL + - | + | help: remove the `iter` usages | LL - let r = iter.next()?; LL + - | + | error: manual implementation of `rsplit_once` --> $DIR/manual_split_once.rs:53:5 @@ -146,12 +146,12 @@ help: remove the `iter` usages | LL - let r = iter.next().unwrap(); LL + - | + | help: remove the `iter` usages | LL - let l = iter.next().unwrap(); LL + - | + | error: manual implementation of `rsplit_once` --> $DIR/manual_split_once.rs:57:5 @@ -171,12 +171,12 @@ help: remove the `iter` usages | LL - let r = iter.next()?; LL + - | + | help: remove the `iter` usages | LL - let l = iter.next()?; LL + - | + | error: manual implementation of `split_once` --> $DIR/manual_split_once.rs:142:13 @@ -202,12 +202,12 @@ help: remove the `iter` usages | LL - let a = iter.next().unwrap(); LL + - | + | help: remove the `iter` usages | LL - let b = iter.next().unwrap(); LL + - | + | error: aborting due to 19 previous errors diff --git a/tests/ui/map_unwrap_or.stderr b/tests/ui/map_unwrap_or.stderr index 954000b8b76d2..abc9c1ece327a 100644 --- a/tests/ui/map_unwrap_or.stderr +++ b/tests/ui/map_unwrap_or.stderr @@ -12,7 +12,7 @@ help: use `map_or(, )` instead | LL - let _ = opt.map(|x| x + 1) LL + let _ = opt.map_or(0, |x| x + 1); - | + | error: called `map().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead --> $DIR/map_unwrap_or.rs:20:13 @@ -59,7 +59,7 @@ help: use `and_then()` instead | LL - let _ = opt.map(|x| Some(x + 1)).unwrap_or(None); LL + let _ = opt.and_then(|x| Some(x + 1)); - | + | error: called `map().unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then()` instead --> $DIR/map_unwrap_or.rs:31:13 @@ -92,7 +92,7 @@ help: use `and_then()` instead | LL - .map(|x| Some(x + 1)) LL + .and_then(|x| Some(x + 1)); - | + | error: called `map().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead --> $DIR/map_unwrap_or.rs:46:13 @@ -104,7 +104,7 @@ help: use `map_or(, )` instead | LL - let _ = Some("prefix").map(|p| format!("{}.", p)).unwrap_or(id); LL + let _ = Some("prefix").map_or(id, |p| format!("{}.", p)); - | + | error: called `map().unwrap_or_else()` on an `Option` value. This can be done more directly by calling `map_or_else(, )` instead --> $DIR/map_unwrap_or.rs:50:13 diff --git a/tests/ui/needless_late_init.stderr b/tests/ui/needless_late_init.stderr index f320b5b9cbb37..313cdbbeba183 100644 --- a/tests/ui/needless_late_init.stderr +++ b/tests/ui/needless_late_init.stderr @@ -164,7 +164,7 @@ help: remove the assignments from the `match` arms | LL - 1 => f = "three", LL + 1 => "three", - | + | error: unneeded late initialization --> $DIR/needless_late_init.rs:76:5 @@ -180,7 +180,7 @@ help: remove the assignments from the branches | LL - g = 5; LL + 5 - | + | help: add a semicolon after the `if` expression | LL | }; diff --git a/tests/ui/print_literal.stderr b/tests/ui/print_literal.stderr index a10cac04411cc..72aae0756033a 100644 --- a/tests/ui/print_literal.stderr +++ b/tests/ui/print_literal.stderr @@ -9,7 +9,7 @@ help: try this | LL - print!("Hello {}", "world"); LL + print!("Hello world"); - | + | error: literal with an empty format string --> $DIR/print_literal.rs:26:36 @@ -21,7 +21,7 @@ help: try this | LL - println!("Hello {} {}", world, "world"); LL + println!("Hello {} world", world); - | + | error: literal with an empty format string --> $DIR/print_literal.rs:27:26 @@ -33,7 +33,7 @@ help: try this | LL - println!("Hello {}", "world"); LL + println!("Hello world"); - | + | error: literal with an empty format string --> $DIR/print_literal.rs:32:25 @@ -45,7 +45,7 @@ help: try this | LL - println!("{0} {1}", "hello", "world"); LL + println!("hello {1}", "world"); - | + | error: literal with an empty format string --> $DIR/print_literal.rs:32:34 @@ -57,7 +57,7 @@ help: try this | LL - println!("{0} {1}", "hello", "world"); LL + println!("{0} world", "hello"); - | + | error: literal with an empty format string --> $DIR/print_literal.rs:33:25 @@ -69,7 +69,7 @@ help: try this | LL - println!("{1} {0}", "hello", "world"); LL + println!("{1} hello", "world"); - | + | error: literal with an empty format string --> $DIR/print_literal.rs:33:34 @@ -81,7 +81,7 @@ help: try this | LL - println!("{1} {0}", "hello", "world"); LL + println!("world {0}", "hello"); - | + | error: literal with an empty format string --> $DIR/print_literal.rs:36:29 @@ -93,7 +93,7 @@ help: try this | LL - println!("{foo} {bar}", foo = "hello", bar = "world"); LL + println!("hello {bar}", bar = "world"); - | + | error: literal with an empty format string --> $DIR/print_literal.rs:36:44 @@ -105,7 +105,7 @@ help: try this | LL - println!("{foo} {bar}", foo = "hello", bar = "world"); LL + println!("{foo} world", foo = "hello"); - | + | error: literal with an empty format string --> $DIR/print_literal.rs:37:29 @@ -117,7 +117,7 @@ help: try this | LL - println!("{bar} {foo}", foo = "hello", bar = "world"); LL + println!("{bar} hello", bar = "world"); - | + | error: literal with an empty format string --> $DIR/print_literal.rs:37:44 @@ -129,7 +129,7 @@ help: try this | LL - println!("{bar} {foo}", foo = "hello", bar = "world"); LL + println!("world {foo}", foo = "hello"); - | + | error: aborting due to 11 previous errors diff --git a/tests/ui/print_with_newline.stderr b/tests/ui/print_with_newline.stderr index d409bee30ece3..edbaa1cdf979f 100644 --- a/tests/ui/print_with_newline.stderr +++ b/tests/ui/print_with_newline.stderr @@ -9,7 +9,7 @@ help: use `println!` instead | LL - print!("Hello/n"); LL + println!("Hello"); - | + | error: using `print!()` with a format string that ends in a single newline --> $DIR/print_with_newline.rs:9:5 @@ -21,7 +21,7 @@ help: use `println!` instead | LL - print!("Hello {}/n", "world"); LL + println!("Hello {}", "world"); - | + | error: using `print!()` with a format string that ends in a single newline --> $DIR/print_with_newline.rs:10:5 @@ -33,7 +33,7 @@ help: use `println!` instead | LL - print!("Hello {} {}/n", "world", "#2"); LL + println!("Hello {} {}", "world", "#2"); - | + | error: using `print!()` with a format string that ends in a single newline --> $DIR/print_with_newline.rs:11:5 @@ -45,7 +45,7 @@ help: use `println!` instead | LL - print!("{}/n", 1265); LL + println!("{}", 1265); - | + | error: using `print!()` with a format string that ends in a single newline --> $DIR/print_with_newline.rs:12:5 @@ -57,7 +57,7 @@ help: use `println!` instead | LL - print!("/n"); LL + println!(); - | + | error: using `print!()` with a format string that ends in a single newline --> $DIR/print_with_newline.rs:31:5 @@ -69,7 +69,7 @@ help: use `println!` instead | LL - print!("//n"); // should fail LL + println!("/"); // should fail - | + | error: using `print!()` with a format string that ends in a single newline --> $DIR/print_with_newline.rs:38:5 @@ -111,7 +111,7 @@ help: use `println!` instead | LL - print!("/r/n"); //~ ERROR LL + println!("/r"); //~ ERROR - | + | error: using `print!()` with a format string that ends in a single newline --> $DIR/print_with_newline.rs:51:5 @@ -123,7 +123,7 @@ help: use `println!` instead | LL - print!("foo/rbar/n") // ~ ERROR LL + println!("foo/rbar") // ~ ERROR - | + | error: aborting due to 10 previous errors diff --git a/tests/ui/unnecessary_iter_cloned.stderr b/tests/ui/unnecessary_iter_cloned.stderr index e44379f8aa047..8f151e620a25e 100644 --- a/tests/ui/unnecessary_iter_cloned.stderr +++ b/tests/ui/unnecessary_iter_cloned.stderr @@ -13,7 +13,7 @@ help: remove this `&` | LL - let other = match get_file_path(&t) { LL + let other = match get_file_path(t) { - | + | error: unnecessary use of `copied` --> $DIR/unnecessary_iter_cloned.rs:46:22 @@ -29,7 +29,7 @@ help: remove this `&` | LL - let other = match get_file_path(&t) { LL + let other = match get_file_path(t) { - | + | error: aborting due to 2 previous errors diff --git a/tests/ui/unnecessary_to_owned.stderr b/tests/ui/unnecessary_to_owned.stderr index af7e7b41fb004..243b4599dba42 100644 --- a/tests/ui/unnecessary_to_owned.stderr +++ b/tests/ui/unnecessary_to_owned.stderr @@ -489,7 +489,7 @@ help: remove this `&` | LL - let path = match get_file_path(&t) { LL + let path = match get_file_path(t) { - | + | error: unnecessary use of `to_vec` --> $DIR/unnecessary_to_owned.rs:221:14 diff --git a/tests/ui/write_literal.stderr b/tests/ui/write_literal.stderr index 593e9493ec590..3c5ec91d3e0fb 100644 --- a/tests/ui/write_literal.stderr +++ b/tests/ui/write_literal.stderr @@ -9,7 +9,7 @@ help: try this | LL - write!(v, "Hello {}", "world"); LL + write!(v, "Hello world"); - | + | error: literal with an empty format string --> $DIR/write_literal.rs:31:39 @@ -21,7 +21,7 @@ help: try this | LL - writeln!(v, "Hello {} {}", world, "world"); LL + writeln!(v, "Hello {} world", world); - | + | error: literal with an empty format string --> $DIR/write_literal.rs:32:29 @@ -33,7 +33,7 @@ help: try this | LL - writeln!(v, "Hello {}", "world"); LL + writeln!(v, "Hello world"); - | + | error: literal with an empty format string --> $DIR/write_literal.rs:37:28 @@ -45,7 +45,7 @@ help: try this | LL - writeln!(v, "{0} {1}", "hello", "world"); LL + writeln!(v, "hello {1}", "world"); - | + | error: literal with an empty format string --> $DIR/write_literal.rs:37:37 @@ -57,7 +57,7 @@ help: try this | LL - writeln!(v, "{0} {1}", "hello", "world"); LL + writeln!(v, "{0} world", "hello"); - | + | error: literal with an empty format string --> $DIR/write_literal.rs:38:28 @@ -69,7 +69,7 @@ help: try this | LL - writeln!(v, "{1} {0}", "hello", "world"); LL + writeln!(v, "{1} hello", "world"); - | + | error: literal with an empty format string --> $DIR/write_literal.rs:38:37 @@ -81,7 +81,7 @@ help: try this | LL - writeln!(v, "{1} {0}", "hello", "world"); LL + writeln!(v, "world {0}", "hello"); - | + | error: literal with an empty format string --> $DIR/write_literal.rs:41:32 @@ -93,7 +93,7 @@ help: try this | LL - writeln!(v, "{foo} {bar}", foo = "hello", bar = "world"); LL + writeln!(v, "hello {bar}", bar = "world"); - | + | error: literal with an empty format string --> $DIR/write_literal.rs:41:47 @@ -105,7 +105,7 @@ help: try this | LL - writeln!(v, "{foo} {bar}", foo = "hello", bar = "world"); LL + writeln!(v, "{foo} world", foo = "hello"); - | + | error: literal with an empty format string --> $DIR/write_literal.rs:42:32 @@ -117,7 +117,7 @@ help: try this | LL - writeln!(v, "{bar} {foo}", foo = "hello", bar = "world"); LL + writeln!(v, "{bar} hello", bar = "world"); - | + | error: literal with an empty format string --> $DIR/write_literal.rs:42:47 @@ -129,7 +129,7 @@ help: try this | LL - writeln!(v, "{bar} {foo}", foo = "hello", bar = "world"); LL + writeln!(v, "world {foo}", foo = "hello"); - | + | error: aborting due to 11 previous errors diff --git a/tests/ui/write_literal_2.stderr b/tests/ui/write_literal_2.stderr index fc40fbfa9e239..9ff297069c402 100644 --- a/tests/ui/write_literal_2.stderr +++ b/tests/ui/write_literal_2.stderr @@ -9,7 +9,7 @@ help: try this | LL - writeln!(v, "{}", "{hello}"); LL + writeln!(v, "{{hello}}"); - | + | error: literal with an empty format string --> $DIR/write_literal_2.rs:10:24 @@ -21,7 +21,7 @@ help: try this | LL - writeln!(v, r"{}", r"{hello}"); LL + writeln!(v, r"{{hello}}"); - | + | error: literal with an empty format string --> $DIR/write_literal_2.rs:11:23 @@ -33,7 +33,7 @@ help: try this | LL - writeln!(v, "{}", '/''); LL + writeln!(v, "'"); - | + | error: literal with an empty format string --> $DIR/write_literal_2.rs:12:23 @@ -45,7 +45,7 @@ help: try this | LL - writeln!(v, "{}", '"'); LL + writeln!(v, "/""); - | + | error: literal with an empty format string --> $DIR/write_literal_2.rs:14:24 @@ -57,7 +57,7 @@ help: try this | LL - writeln!(v, r"{}", '/''); LL + writeln!(v, r"'"); - | + | error: literal with an empty format string --> $DIR/write_literal_2.rs:18:9 diff --git a/tests/ui/write_with_newline.stderr b/tests/ui/write_with_newline.stderr index 3314a2a6e2420..5f55431be0bd9 100644 --- a/tests/ui/write_with_newline.stderr +++ b/tests/ui/write_with_newline.stderr @@ -9,7 +9,7 @@ help: use `writeln!()` instead | LL - write!(v, "Hello/n"); LL + writeln!(v, "Hello"); - | + | error: using `write!()` with a format string that ends in a single newline --> $DIR/write_with_newline.rs:14:5 @@ -21,7 +21,7 @@ help: use `writeln!()` instead | LL - write!(v, "Hello {}/n", "world"); LL + writeln!(v, "Hello {}", "world"); - | + | error: using `write!()` with a format string that ends in a single newline --> $DIR/write_with_newline.rs:15:5 @@ -33,7 +33,7 @@ help: use `writeln!()` instead | LL - write!(v, "Hello {} {}/n", "world", "#2"); LL + writeln!(v, "Hello {} {}", "world", "#2"); - | + | error: using `write!()` with a format string that ends in a single newline --> $DIR/write_with_newline.rs:16:5 @@ -45,7 +45,7 @@ help: use `writeln!()` instead | LL - write!(v, "{}/n", 1265); LL + writeln!(v, "{}", 1265); - | + | error: using `write!()` with a format string that ends in a single newline --> $DIR/write_with_newline.rs:17:5 @@ -57,7 +57,7 @@ help: use `writeln!()` instead | LL - write!(v, "/n"); LL + writeln!(v); - | + | error: using `write!()` with a format string that ends in a single newline --> $DIR/write_with_newline.rs:36:5 @@ -69,7 +69,7 @@ help: use `writeln!()` instead | LL - write!(v, "//n"); // should fail LL + writeln!(v, "/"); // should fail - | + | error: using `write!()` with a format string that ends in a single newline --> $DIR/write_with_newline.rs:43:5 @@ -115,7 +115,7 @@ help: use `writeln!()` instead | LL - write!(v, "/r/n"); //~ ERROR LL + writeln!(v, "/r"); //~ ERROR - | + | error: using `write!()` with a format string that ends in a single newline --> $DIR/write_with_newline.rs:58:5 @@ -127,7 +127,7 @@ help: use `writeln!()` instead | LL - write!(v, "foo/rbar/n"); LL + writeln!(v, "foo/rbar"); - | + | error: aborting due to 10 previous errors From 4d889935443b7d5cd3c33b432c920807b1387340 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Thu, 16 Jun 2022 18:00:32 +0400 Subject: [PATCH 0061/1135] bless clippy ui tests --- tests/ui/bind_instead_of_map_multipart.stderr | 18 ++++++++ .../shared_at_top_and_bottom.stderr | 3 +- tests/ui/entry.stderr | 41 ++++++++++++++++--- tests/ui/entry_with_else.stderr | 17 ++++++-- tests/ui/let_unit.stderr | 3 +- tests/ui/manual_async_fn.stderr | 9 +++- tests/ui/needless_for_each_unfixable.stderr | 3 +- tests/ui/new_without_default.stderr | 3 +- tests/ui/ptr_arg.stderr | 3 +- tests/ui/significant_drop_in_scrutinee.stderr | 9 +++- tests/ui/unit_arg.stderr | 8 +++- tests/ui/unnecessary_wraps.stderr | 6 ++- 12 files changed, 102 insertions(+), 21 deletions(-) diff --git a/tests/ui/bind_instead_of_map_multipart.stderr b/tests/ui/bind_instead_of_map_multipart.stderr index f822b6f49fa35..0152a93feee42 100644 --- a/tests/ui/bind_instead_of_map_multipart.stderr +++ b/tests/ui/bind_instead_of_map_multipart.stderr @@ -56,7 +56,25 @@ LL | if s == "43" { LL ~ return 43; LL | } LL | s == "42" +LL | } { +LL ~ return 45; +LL | } +LL | match s.len() { +LL ~ 10 => 2, +LL | 20 => { ... +LL | if foo() { +LL ~ return 20; +LL | } +LL | println!("foo"); +LL ~ 3 +LL | }; +LL | } +LL ~ 20 +LL | }, +LL ~ 40 => 30, +LL ~ _ => 1, + | error: using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)` --> $DIR/bind_instead_of_map_multipart.rs:61:13 diff --git a/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr b/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr index 1db2343d3fe97..37fe2f76f4a02 100644 --- a/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr +++ b/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr @@ -98,7 +98,8 @@ LL + id: e_id, LL + name: "Player 1".to_string(), LL + some_data: vec![0x12, 0x34, 0x56, 0x78, 0x90], LL + }; - ... +LL + process_data(pack); + | error: all if blocks contain the same code at the start and the end. Here at the start --> $DIR/shared_at_top_and_bottom.rs:94:5 diff --git a/tests/ui/entry.stderr b/tests/ui/entry.stderr index 1076500498d32..2ef9966525cef 100644 --- a/tests/ui/entry.stderr +++ b/tests/ui/entry.stderr @@ -28,7 +28,8 @@ LL + v LL + } else { LL + v2 LL + } - ... +LL + }); + | error: usage of `contains_key` followed by `insert` on a `HashMap` --> $DIR/entry.rs:38:5 @@ -50,7 +51,8 @@ LL + v LL + } else { LL + v2 LL + } - ... +LL + }); + | error: usage of `contains_key` followed by `insert` on a `HashMap` --> $DIR/entry.rs:47:5 @@ -72,7 +74,9 @@ LL + e.insert(v); LL + } else { LL + e.insert(v2); LL + return; - ... +LL + } +LL + } + | error: usage of `contains_key` followed by `insert` on a `HashMap` --> $DIR/entry.rs:57:5 @@ -111,7 +115,11 @@ LL + 1 if true => { LL + v LL + }, LL + _ => { - ... +LL + v2 +LL + }, +LL + } +LL + }); + | error: usage of `contains_key` followed by `insert` on a `HashMap` --> $DIR/entry.rs:75:5 @@ -133,7 +141,9 @@ LL + 0 => foo(), LL + _ => { LL + e.insert(v2); LL + }, - ... +LL + }; +LL + } + | error: usage of `contains_key` followed by `insert` on a `HashMap` --> $DIR/entry.rs:85:5 @@ -155,7 +165,26 @@ LL + match 0 { LL + 0 if false => { LL + v LL + }, - ... +LL + 1 => { +LL + foo(); +LL + v +LL + }, +LL + 2 | 3 => { +LL + for _ in 0..2 { +LL + foo(); +LL + } +LL + if true { +LL + v +LL + } else { +LL + v2 +LL + } +LL + }, +LL + _ => { +LL + v2 +LL + }, +LL + } +LL + }); + | error: usage of `contains_key` followed by `insert` on a `HashMap` --> $DIR/entry.rs:119:5 diff --git a/tests/ui/entry_with_else.stderr b/tests/ui/entry_with_else.stderr index 7279efc595959..e0f6671b460ed 100644 --- a/tests/ui/entry_with_else.stderr +++ b/tests/ui/entry_with_else.stderr @@ -17,7 +17,9 @@ LL + e.insert(v); LL + } LL + std::collections::hash_map::Entry::Occupied(mut e) => { LL + e.insert(v2); - ... +LL + } +LL + } + | error: usage of `contains_key` followed by `insert` on a `HashMap` --> $DIR/entry_with_else.rs:22:5 @@ -37,7 +39,9 @@ LL + e.insert(v); LL + } LL + std::collections::hash_map::Entry::Vacant(e) => { LL + e.insert(v2); - ... +LL + } +LL + } + | error: usage of `contains_key` followed by `insert` on a `HashMap` --> $DIR/entry_with_else.rs:28:5 @@ -95,7 +99,9 @@ LL + e.insert(v); LL + } LL + std::collections::hash_map::Entry::Occupied(mut e) => { LL + e.insert(v2); - ... +LL + } +LL + } + | error: usage of `contains_key` followed by `insert` on a `HashMap` --> $DIR/entry_with_else.rs:46:5 @@ -115,7 +121,10 @@ LL + if true { Some(e.insert(v)) } else { Some(e.insert(v2)) } LL + } LL + std::collections::hash_map::Entry::Vacant(e) => { LL + e.insert(v); - ... +LL + None +LL + } +LL ~ }; + | error: usage of `contains_key` followed by `insert` on a `HashMap` --> $DIR/entry_with_else.rs:52:5 diff --git a/tests/ui/let_unit.stderr b/tests/ui/let_unit.stderr index 13ec11a6d33e9..45bf67acdb736 100644 --- a/tests/ui/let_unit.stderr +++ b/tests/ui/let_unit.stderr @@ -32,7 +32,8 @@ LL + .map(|i| i * 2) LL + .filter(|i| i % 2 == 0) LL + .map(|_| ()) LL + .next() - ... +LL + .unwrap(); + | error: this let-binding has unit value --> $DIR/let_unit.rs:80:5 diff --git a/tests/ui/manual_async_fn.stderr b/tests/ui/manual_async_fn.stderr index 7435f46074c81..0a903ed6fd436 100644 --- a/tests/ui/manual_async_fn.stderr +++ b/tests/ui/manual_async_fn.stderr @@ -122,7 +122,14 @@ LL + let a = 42; LL + let b = 21; LL + if a < b { LL + let c = 21; - ... +LL + let d = 42; +LL + if c < d { +LL + let _ = 42; +LL + } +LL + } +LL + 42 +LL + } + | error: this function can be simplified using the `async fn` syntax --> $DIR/manual_async_fn.rs:92:1 diff --git a/tests/ui/needless_for_each_unfixable.stderr b/tests/ui/needless_for_each_unfixable.stderr index f607e0a430e24..7893ff31a6fdb 100644 --- a/tests/ui/needless_for_each_unfixable.stderr +++ b/tests/ui/needless_for_each_unfixable.stderr @@ -19,7 +19,8 @@ LL + return; LL + } else { LL + println!("{}", v); LL + } - ... +LL + } + | help: ...and replace `return` with `continue` | LL | continue; diff --git a/tests/ui/new_without_default.stderr b/tests/ui/new_without_default.stderr index 19572dfe8b075..212a69ab94e65 100644 --- a/tests/ui/new_without_default.stderr +++ b/tests/ui/new_without_default.stderr @@ -117,7 +117,8 @@ LL + Self::new() LL + } LL + } LL + - ... +LL ~ impl Foo { + | error: aborting due to 7 previous errors diff --git a/tests/ui/ptr_arg.stderr b/tests/ui/ptr_arg.stderr index a9613daadde10..7ec4a566ff34b 100644 --- a/tests/ui/ptr_arg.stderr +++ b/tests/ui/ptr_arg.stderr @@ -56,7 +56,8 @@ LL | let f = e.clone(); // OK LL | let g = x; LL ~ let h = g.to_owned(); LL | let i = (e).clone(); - ... +LL ~ x.to_owned() + | error: writing `&String` instead of `&str` involves a new object where a slice will do --> $DIR/ptr_arg.rs:57:18 diff --git a/tests/ui/significant_drop_in_scrutinee.stderr b/tests/ui/significant_drop_in_scrutinee.stderr index 303f3c1df033c..5ce9520441637 100644 --- a/tests/ui/significant_drop_in_scrutinee.stderr +++ b/tests/ui/significant_drop_in_scrutinee.stderr @@ -188,7 +188,9 @@ LL + _ => mutex2.lock().unwrap(), LL + } LL + .s LL + .len() - ... +LL + > 1; +LL ~ match value + | error: temporary with significant drop in match scrutinee --> $DIR/significant_drop_in_scrutinee.rs:397:11 @@ -211,7 +213,10 @@ LL + } else { LL + mutex2.lock().unwrap() LL + } LL + .s - ... +LL + .len() +LL + > 1; +LL ~ match value + | error: temporary with significant drop in match scrutinee --> $DIR/significant_drop_in_scrutinee.rs:451:11 diff --git a/tests/ui/unit_arg.stderr b/tests/ui/unit_arg.stderr index 394dee29dc96e..11cfe66a30e85 100644 --- a/tests/ui/unit_arg.stderr +++ b/tests/ui/unit_arg.stderr @@ -137,7 +137,13 @@ LL + foo(1); LL + }; LL + { LL + foo(2); - ... +LL + foo(3); +LL + }; +LL + taking_multiple_units( +LL + (), +LL + (), +LL ~ ); + | error: passing a unit value to a function --> $DIR/unit_arg.rs:85:13 diff --git a/tests/ui/unnecessary_wraps.stderr b/tests/ui/unnecessary_wraps.stderr index 8e31db3950247..a6a0b22cf689f 100644 --- a/tests/ui/unnecessary_wraps.stderr +++ b/tests/ui/unnecessary_wraps.stderr @@ -23,7 +23,8 @@ LL | if a { LL | Some(-1); LL ~ 2 LL | } else { - ... +LL ~ return 1337; + | error: this function's return value is unnecessarily wrapped by `Option` --> $DIR/unnecessary_wraps.rs:21:1 @@ -122,7 +123,8 @@ LL | if a { LL | Some(()); LL ~ LL | } else { - ... +LL ~ return ; + | error: this function's return value is unnecessary --> $DIR/unnecessary_wraps.rs:117:1 From f8f9d01c2ad0dff565bdd60feeb4cbd09dada8cd Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 16 Jun 2022 17:39:06 +0200 Subject: [PATCH 0062/1135] Merge commit 'd7b5cbf065b88830ca519adcb73fad4c0d24b1c7' into clippyup --- .github/workflows/clippy_bors.yml | 19 + .github/workflows/remark.yml | 9 + .gitignore | 3 + CHANGELOG.md | 2 + CONTRIBUTING.md | 168 +---- Cargo.toml | 2 +- book/README.md | 4 + book/book.toml | 28 + book/src/README.md | 34 + book/src/SUMMARY.md | 23 + book/src/configuration.md | 92 +++ book/src/continuous_integration/README.md | 18 + .../continuous_integration/github_actions.md | 21 + book/src/continuous_integration/travis.md | 20 + book/src/development/README.md | 43 ++ {doc => book/src/development}/adding_lints.md | 326 +++++---- {doc => book/src/development}/basics.md | 72 +- .../common_tools_writing_lints.md | 131 ++-- book/src/development/infrastructure/README.md | 19 + .../development/infrastructure}/backport.md | 0 book/src/development/infrastructure/book.md | 42 ++ .../infrastructure}/changelog_update.md | 39 +- .../development/infrastructure}/release.md | 31 +- book/src/development/infrastructure/sync.md | 123 ++++ book/src/development/proposals/README.md | 11 + .../development/proposals}/roadmap-2021.md | 0 book/src/installation.md | 24 + book/src/lints.md | 105 +++ book/src/usage.md | 151 ++++ clippy_dev/src/lint.rs | 2 +- clippy_dev/src/main.rs | 212 +++--- clippy_dev/src/new_lint.rs | 7 +- clippy_dev/src/serve.rs | 2 +- clippy_dev/src/update_lints.rs | 10 + clippy_lints/Cargo.toml | 4 +- clippy_lints/src/assertions_on_constants.rs | 3 - clippy_lints/src/async_yields_async.rs | 6 +- clippy_lints/src/attrs.rs | 4 +- clippy_lints/src/await_holding_invalid.rs | 2 - clippy_lints/src/bool_assert_comparison.rs | 5 +- clippy_lints/src/borrow_deref_ref.rs | 13 +- clippy_lints/src/casts/mod.rs | 36 +- clippy_lints/src/checked_conversions.rs | 15 +- clippy_lints/src/collapsible_if.rs | 12 +- clippy_lints/src/comparison_chain.rs | 1 - clippy_lints/src/copies.rs | 675 ++++++++---------- clippy_lints/src/create_dir.rs | 6 +- clippy_lints/src/dbg_macro.rs | 5 +- clippy_lints/src/default.rs | 11 +- clippy_lints/src/default_numeric_fallback.rs | 15 +- clippy_lints/src/dereference.rs | 61 +- clippy_lints/src/derivable_impls.rs | 4 +- clippy_lints/src/derive.rs | 97 +-- clippy_lints/src/doc.rs | 6 +- clippy_lints/src/enum_variants.rs | 3 +- clippy_lints/src/eq_op.rs | 10 +- clippy_lints/src/eta_reduction.rs | 8 +- clippy_lints/src/excessive_bools.rs | 3 +- clippy_lints/src/float_literal.rs | 5 +- clippy_lints/src/functions/mod.rs | 5 +- clippy_lints/src/get_first.rs | 3 +- clippy_lints/src/implicit_saturating_sub.rs | 16 +- clippy_lints/src/index_refutable_slice.rs | 2 +- clippy_lints/src/indexing_slicing.rs | 43 +- clippy_lints/src/infinite_iter.rs | 1 + clippy_lints/src/inherent_to_string.rs | 12 +- clippy_lints/src/int_plus_one.rs | 3 +- clippy_lints/src/integer_division.rs | 5 +- clippy_lints/src/items_after_statements.rs | 3 +- clippy_lints/src/large_const_arrays.rs | 5 +- clippy_lints/src/large_enum_variant.rs | 4 +- clippy_lints/src/large_include_file.rs | 3 +- clippy_lints/src/let_underscore.rs | 24 +- clippy_lints/src/lib.register_all.rs | 2 + clippy_lints/src/lib.register_correctness.rs | 1 + clippy_lints/src/lib.register_lints.rs | 2 + clippy_lints/src/lib.register_style.rs | 1 + clippy_lints/src/lib.rs | 11 +- clippy_lints/src/lifetimes.rs | 12 +- clippy_lints/src/literal_representation.rs | 29 +- .../src/loops/for_loops_over_fallibles.rs | 40 +- clippy_lints/src/loops/mod.rs | 34 +- clippy_lints/src/loops/never_loop.rs | 12 +- clippy_lints/src/matches/match_same_arms.rs | 11 +- clippy_lints/src/matches/mod.rs | 68 +- clippy_lints/src/methods/filter_map.rs | 27 +- .../src/methods/iter_overeager_cloned.rs | 97 ++- clippy_lints/src/methods/mod.rs | 230 +++--- clippy_lints/src/misc.rs | 30 +- clippy_lints/src/misc_early/mod.rs | 71 +- .../src/mixed_read_write_in_expression.rs | 6 +- clippy_lints/src/mut_reference.rs | 15 +- clippy_lints/src/mutex_atomic.rs | 11 +- clippy_lints/src/needless_bool.rs | 11 +- clippy_lints/src/needless_borrowed_ref.rs | 9 +- clippy_lints/src/needless_late_init.rs | 16 +- .../src/needless_parens_on_range_literals.rs | 87 +++ clippy_lints/src/needless_update.rs | 7 +- clippy_lints/src/neg_cmp_op_on_partial_ord.rs | 14 +- clippy_lints/src/neg_multiply.rs | 9 +- clippy_lints/src/non_copy_const.rs | 14 +- clippy_lints/src/octal_escapes.rs | 7 +- clippy_lints/src/only_used_in_recursion.rs | 2 +- clippy_lints/src/pass_by_ref_or_value.rs | 12 +- clippy_lints/src/ptr.rs | 22 +- clippy_lints/src/ranges.rs | 45 +- clippy_lints/src/read_zero_byte_vec.rs | 142 ++++ clippy_lints/src/redundant_closure_call.rs | 11 +- clippy_lints/src/redundant_slicing.rs | 2 +- clippy_lints/src/reference.rs | 5 +- clippy_lints/src/return_self_not_must_use.rs | 14 +- clippy_lints/src/same_name_method.rs | 18 +- clippy_lints/src/shadow.rs | 15 +- .../src/single_char_lifetime_names.rs | 2 +- .../src/slow_vector_initialization.rs | 7 +- clippy_lints/src/strings.rs | 16 +- clippy_lints/src/trait_bounds.rs | 3 +- .../src/transmute/useless_transmute.rs | 7 +- clippy_lints/src/unicode.rs | 3 +- clippy_lints/src/unused_async.rs | 5 +- clippy_lints/src/useless_conversion.rs | 5 +- clippy_lints/src/utils/conf.rs | 74 +- clippy_lints/src/utils/internal_lints.rs | 26 +- .../internal_lints/metadata_collector.rs | 232 +++++- clippy_lints/src/vec.rs | 8 +- clippy_lints/src/wildcard_imports.rs | 14 +- clippy_lints/src/write.rs | 27 +- clippy_lints/src/zero_div_zero.rs | 5 +- clippy_utils/src/consts.rs | 4 +- clippy_utils/src/hir_utils.rs | 14 +- clippy_utils/src/lib.rs | 4 +- clippy_utils/src/ty.rs | 4 +- lintcheck/src/config.rs | 64 +- rust-toolchain | 2 +- tests/dogfood.rs | 4 +- tests/ui-toml/bad_toml/conf_bad_toml.rs | 2 - tests/ui-toml/bad_toml_type/conf_bad_type.rs | 3 - .../blacklisted_names.rs | 10 + .../blacklisted_names.stderr | 16 + .../blacklisted_names_append/clippy.toml | 1 + .../blacklisted_names.rs | 10 + .../blacklisted_names.stderr | 10 + .../blacklisted_names_replace/clippy.toml | 1 + .../conf_deprecated_key.rs | 3 - .../doc_valid_idents_append/clippy.toml | 1 + .../doc_valid_idents_append/doc_markdown.rs | 12 + .../doc_markdown.stderr | 14 + .../doc_valid_idents_replace/clippy.toml | 1 + .../doc_valid_idents_replace/doc_markdown.rs | 12 + .../doc_markdown.stderr | 36 + .../conf_no_false_negatives.rs | 2 - .../toml_unknown_key/conf_unknown_key.rs | 2 - tests/ui/almost_complete_letter_range.fixed | 1 + tests/ui/almost_complete_letter_range.rs | 1 + tests/ui/almost_complete_letter_range.stderr | 24 +- tests/ui/async_yields_async.fixed | 13 +- tests/ui/async_yields_async.rs | 13 +- .../branches_sharing_code/false_positives.rs | 15 +- .../shared_at_bottom.stderr | 28 +- .../shared_at_top.stderr | 18 +- .../shared_at_top_and_bottom.stderr | 48 +- tests/ui/checked_conversions.fixed | 5 + tests/ui/checked_conversions.rs | 5 + tests/ui/default_numeric_fallback_i32.fixed | 6 + tests/ui/default_numeric_fallback_i32.rs | 6 + tests/ui/default_numeric_fallback_i32.stderr | 50 +- tests/ui/derive_partial_eq_without_eq.fixed | 60 +- tests/ui/derive_partial_eq_without_eq.rs | 56 +- tests/ui/derive_partial_eq_without_eq.stderr | 26 +- tests/ui/eta.fixed | 12 + tests/ui/eta.rs | 12 + tests/ui/for_loops_over_fallibles.rs | 17 +- tests/ui/for_loops_over_fallibles.stderr | 40 +- tests/ui/iter_overeager_cloned.fixed | 6 +- tests/ui/iter_overeager_cloned.rs | 3 + tests/ui/iter_overeager_cloned.stderr | 42 +- tests/ui/manual_filter_map.fixed | 50 ++ tests/ui/manual_filter_map.rs | 59 ++ tests/ui/manual_filter_map.stderr | 74 +- tests/ui/manual_find_map.fixed | 50 ++ tests/ui/manual_find_map.rs | 59 ++ tests/ui/manual_find_map.stderr | 74 +- tests/ui/manual_map_option.fixed | 1 + tests/ui/manual_map_option.rs | 1 + tests/ui/manual_map_option.stderr | 42 +- tests/ui/needless_borrow.fixed | 9 + tests/ui/needless_borrow.rs | 9 + tests/ui/needless_borrow.stderr | 32 +- .../needless_parens_on_range_literals.fixed | 14 + tests/ui/needless_parens_on_range_literals.rs | 14 + .../needless_parens_on_range_literals.stderr | 40 ++ tests/ui/never_loop.rs | 17 + tests/ui/read_zero_byte_vec.rs | 87 +++ tests/ui/read_zero_byte_vec.stderr | 64 ++ tests/ui/ref_binding_to_reference.rs | 10 + tests/ui/ref_binding_to_reference.stderr | 14 +- tests/ui/same_name_method.rs | 16 + tests/ui/same_name_method.stderr | 20 +- util/gh-pages/index.html | 20 + 199 files changed, 4140 insertions(+), 1913 deletions(-) create mode 100644 book/README.md create mode 100644 book/book.toml create mode 100644 book/src/README.md create mode 100644 book/src/SUMMARY.md create mode 100644 book/src/configuration.md create mode 100644 book/src/continuous_integration/README.md create mode 100644 book/src/continuous_integration/github_actions.md create mode 100644 book/src/continuous_integration/travis.md create mode 100644 book/src/development/README.md rename {doc => book/src/development}/adding_lints.md (73%) rename {doc => book/src/development}/basics.md (75%) rename {doc => book/src/development}/common_tools_writing_lints.md (76%) create mode 100644 book/src/development/infrastructure/README.md rename {doc => book/src/development/infrastructure}/backport.md (100%) create mode 100644 book/src/development/infrastructure/book.md rename {doc => book/src/development/infrastructure}/changelog_update.md (78%) rename {doc => book/src/development/infrastructure}/release.md (85%) create mode 100644 book/src/development/infrastructure/sync.md create mode 100644 book/src/development/proposals/README.md rename {doc => book/src/development/proposals}/roadmap-2021.md (100%) create mode 100644 book/src/installation.md create mode 100644 book/src/lints.md create mode 100644 book/src/usage.md create mode 100644 clippy_lints/src/needless_parens_on_range_literals.rs create mode 100644 clippy_lints/src/read_zero_byte_vec.rs create mode 100644 tests/ui-toml/blacklisted_names_append/blacklisted_names.rs create mode 100644 tests/ui-toml/blacklisted_names_append/blacklisted_names.stderr create mode 100644 tests/ui-toml/blacklisted_names_append/clippy.toml create mode 100644 tests/ui-toml/blacklisted_names_replace/blacklisted_names.rs create mode 100644 tests/ui-toml/blacklisted_names_replace/blacklisted_names.stderr create mode 100644 tests/ui-toml/blacklisted_names_replace/clippy.toml create mode 100644 tests/ui-toml/doc_valid_idents_append/clippy.toml create mode 100644 tests/ui-toml/doc_valid_idents_append/doc_markdown.rs create mode 100644 tests/ui-toml/doc_valid_idents_append/doc_markdown.stderr create mode 100644 tests/ui-toml/doc_valid_idents_replace/clippy.toml create mode 100644 tests/ui-toml/doc_valid_idents_replace/doc_markdown.rs create mode 100644 tests/ui-toml/doc_valid_idents_replace/doc_markdown.stderr create mode 100644 tests/ui/needless_parens_on_range_literals.fixed create mode 100644 tests/ui/needless_parens_on_range_literals.rs create mode 100644 tests/ui/needless_parens_on_range_literals.stderr create mode 100644 tests/ui/read_zero_byte_vec.rs create mode 100644 tests/ui/read_zero_byte_vec.stderr diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index 9b3fd3ddfeb38..b8ea424ef3483 100644 --- a/.github/workflows/clippy_bors.yml +++ b/.github/workflows/clippy_bors.yml @@ -143,6 +143,25 @@ jobs: env: OS: ${{ runner.os }} + metadata_collection: + needs: base + runs-on: ubuntu-latest + + steps: + # Setup + - uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master + with: + github_token: "${{ secrets.github_token }}" + + - name: Checkout + uses: actions/checkout@v3.0.2 + + - name: Install toolchain + run: rustup show active-toolchain + + - name: Test metadata collection + run: cargo collect-metadata + integration_build: needs: changelog runs-on: ubuntu-latest diff --git a/.github/workflows/remark.yml b/.github/workflows/remark.yml index a179bfa726172..ff471207b65a3 100644 --- a/.github/workflows/remark.yml +++ b/.github/workflows/remark.yml @@ -26,10 +26,19 @@ jobs: - name: Install remark run: npm install remark-cli remark-lint remark-lint-maximum-line-length remark-preset-lint-recommended remark-gfm + - name: Install mdbook + run: | + mkdir mdbook + curl -Lf https://github.com/rust-lang/mdBook/releases/download/v0.4.18/mdbook-v0.4.18-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=./mdbook + echo `pwd`/mdbook >> $GITHUB_PATH + # Run - name: Check *.md files run: git ls-files -z '*.md' | xargs -0 -n 1 -I {} ./node_modules/.bin/remark {} -u lint -f > /dev/null + - name: Build mdbook + run: mdbook build book + # These jobs doesn't actually test anything, but they're only used to tell # bors the build completed, as there is no practical way to detect when a # workflow is successful listening to webhooks only. diff --git a/.gitignore b/.gitignore index 3e50c45a9b63e..503ae3c509039 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,6 @@ helper.txt *.iml .vscode .idea + +# mdbook generated output +/book/book diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ef338b819d4d..6aaf12ed9324a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3605,6 +3605,7 @@ Released 2018-09-13 [`needless_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_match [`needless_option_as_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_as_deref [`needless_option_take`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_take +[`needless_parens_on_range_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_parens_on_range_literals [`needless_pass_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_value [`needless_question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_question_mark [`needless_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_range_loop @@ -3677,6 +3678,7 @@ Released 2018-09-13 [`rc_buffer`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_buffer [`rc_clone_in_vec_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_clone_in_vec_init [`rc_mutex`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_mutex +[`read_zero_byte_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#read_zero_byte_vec [`recursive_format_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#recursive_format_impl [`redundant_allocation`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation [`redundant_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_clone diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6ab2bd59137fa..e81e7ceedcb50 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -13,19 +13,14 @@ anything, feel free to ask questions on issues or visit the `#clippy` on [Zulip] All contributors are expected to follow the [Rust Code of Conduct]. - [Contributing to Clippy](#contributing-to-clippy) - - [Getting started](#getting-started) - - [High level approach](#high-level-approach) - - [Finding something to fix/improve](#finding-something-to-fiximprove) + - [The Clippy book](#the-clippy-book) + - [High level approach](#high-level-approach) + - [Finding something to fix/improve](#finding-something-to-fiximprove) - [Writing code](#writing-code) - [Getting code-completion for rustc internals to work](#getting-code-completion-for-rustc-internals-to-work) - [IntelliJ Rust](#intellij-rust) - [Rust Analyzer](#rust-analyzer) - [How Clippy works](#how-clippy-works) - - [Syncing changes between Clippy and `rust-lang/rust`](#syncing-changes-between-clippy-and-rust-langrust) - - [Patching git-subtree to work with big repos](#patching-git-subtree-to-work-with-big-repos) - - [Performing the sync from `rust-lang/rust` to Clippy](#performing-the-sync-from-rust-langrust-to-clippy) - - [Performing the sync from Clippy to `rust-lang/rust`](#performing-the-sync-from-clippy-to-rust-langrust) - - [Defining remotes](#defining-remotes) - [Issue and PR triage](#issue-and-pr-triage) - [Bors and Homu](#bors-and-homu) - [Contributions](#contributions) @@ -33,24 +28,29 @@ All contributors are expected to follow the [Rust Code of Conduct]. [Zulip]: https://rust-lang.zulipchat.com/#narrow/stream/clippy [Rust Code of Conduct]: https://www.rust-lang.org/policies/code-of-conduct -## Getting started +## The Clippy book -**Note: If this is your first time contributing to Clippy, you should -first read the [Basics docs](doc/basics.md).** +If you're new to Clippy and don't know where to start the [Clippy book] includes +a developer guide and is a good place to start your journey. -### High level approach + +[Clippy book]: book/src + +## High level approach 1. Find something to fix/improve 2. Change code (likely some file in `clippy_lints/src/`) -3. Follow the instructions in the [Basics docs](doc/basics.md) to get set up +3. Follow the instructions in the [Basics docs](book/src/development/basics.md) + to get set up 4. Run `cargo test` in the root directory and wiggle code until it passes 5. Open a PR (also can be done after 2. if you run into problems) -### Finding something to fix/improve +## Finding something to fix/improve -All issues on Clippy are mentored, if you want help simply ask @Manishearth, @flip1995, @phansch -or @llogiq directly by mentioning them in the issue or over on [Zulip]. This list may be out of date. -All currently active mentors can be found [here](https://github.com/rust-lang/highfive/blob/master/highfive/configs/rust-lang/rust-clippy.json#L3) +All issues on Clippy are mentored, if you want help simply ask someone from the +Clippy team directly by mentioning them in the issue or over on [Zulip]. All +currently active team members can be found +[here](https://github.com/rust-lang/highfive/blob/master/highfive/configs/rust-lang/rust-clippy.json#L3) Some issues are easier than others. The [`good-first-issue`] label can be used to find the easy issues. You can use `@rustbot claim` to assign the issue to yourself. @@ -91,20 +91,6 @@ an AST expression). `match_def_path()` in Clippy's `utils` module can also be us [let chains]: https://github.com/rust-lang/rust/pull/94927 [nest-less]: https://github.com/rust-lang/rust-clippy/blob/5e4f0922911536f80d9591180fa604229ac13939/clippy_lints/src/bit_mask.rs#L133-L159 -## Writing code - -Have a look at the [docs for writing lints][adding_lints] for more details. - -If you want to add a new lint or change existing ones apart from bugfixing, it's -also a good idea to give the [stability guarantees][rfc_stability] and -[lint categories][rfc_lint_cats] sections of the [Clippy 1.0 RFC][clippy_rfc] a -quick read. - -[adding_lints]: https://github.com/rust-lang/rust-clippy/blob/master/doc/adding_lints.md -[clippy_rfc]: https://github.com/rust-lang/rfcs/blob/master/text/2476-clippy-uno.md -[rfc_stability]: https://github.com/rust-lang/rfcs/blob/master/text/2476-clippy-uno.md#stability-guarantees -[rfc_lint_cats]: https://github.com/rust-lang/rfcs/blob/master/text/2476-clippy-uno.md#lint-audit-and-categories - ## Getting code-completion for rustc internals to work ### IntelliJ Rust @@ -205,126 +191,6 @@ That's why the `else_if_without_else` example uses the `register_early_pass` fun [early_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html [late_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html -## Syncing changes between Clippy and [`rust-lang/rust`] - -Clippy currently gets built with a pinned nightly version. - -In the `rust-lang/rust` repository, where rustc resides, there's a copy of Clippy -that compiler hackers modify from time to time to adapt to changes in the unstable -API of the compiler. - -We need to sync these changes back to this repository periodically, and the changes -made to this repository in the meantime also need to be synced to the `rust-lang/rust` repository. - -To avoid flooding the `rust-lang/rust` PR queue, this two-way sync process is done -in a bi-weekly basis if there's no urgent changes. This is done starting on the day of -the Rust stable release and then every other week. That way we guarantee that we keep -this repo up to date with the latest compiler API, and every feature in Clippy is available -for 2 weeks in nightly, before it can get to beta. For reference, the first sync -following this cadence was performed the 2020-08-27. - -This process is described in detail in the following sections. For general information -about `subtree`s in the Rust repository see [Rust's `CONTRIBUTING.md`][subtree]. - -### Patching git-subtree to work with big repos - -Currently, there's a bug in `git-subtree` that prevents it from working properly -with the [`rust-lang/rust`] repo. There's an open PR to fix that, but it's stale. -Before continuing with the following steps, we need to manually apply that fix to -our local copy of `git-subtree`. - -You can get the patched version of `git-subtree` from [here][gitgitgadget-pr]. -Put this file under `/usr/lib/git-core` (taking a backup of the previous file) -and make sure it has the proper permissions: - -```bash -sudo cp --backup /path/to/patched/git-subtree.sh /usr/lib/git-core/git-subtree -sudo chmod --reference=/usr/lib/git-core/git-subtree~ /usr/lib/git-core/git-subtree -sudo chown --reference=/usr/lib/git-core/git-subtree~ /usr/lib/git-core/git-subtree -``` - -_Note:_ The first time running `git subtree push` a cache has to be built. This -involves going through the complete Clippy history once. For this you have to -increase the stack limit though, which you can do with `ulimit -s 60000`. -Make sure to run the `ulimit` command from the same session you call git subtree. - -_Note:_ If you are a Debian user, `dash` is the shell used by default for scripts instead of `sh`. -This shell has a hardcoded recursion limit set to 1000. In order to make this process work, -you need to force the script to run `bash` instead. You can do this by editing the first -line of the `git-subtree` script and changing `sh` to `bash`. - -### Performing the sync from [`rust-lang/rust`] to Clippy - -Here is a TL;DR version of the sync process (all of the following commands have -to be run inside the `rust` directory): - -1. Clone the [`rust-lang/rust`] repository or make sure it is up to date. -2. Checkout the commit from the latest available nightly. You can get it using `rustup check`. -3. Sync the changes to the rust-copy of Clippy to your Clippy fork: - ```bash - # Make sure to change `your-github-name` to your github name in the following command. Also be - # sure to either use a net-new branch, e.g. `sync-from-rust`, or delete the branch beforehand - # because changes cannot be fast forwarded - git subtree push -P src/tools/clippy git@github.com:your-github-name/rust-clippy sync-from-rust - ``` - - _Note:_ This will directly push to the remote repository. You can also push - to your local copy by replacing the remote address with `/path/to/rust-clippy` - directory. - - _Note:_ Most of the time you have to create a merge commit in the - `rust-clippy` repo (this has to be done in the Clippy repo, not in the - rust-copy of Clippy): - ```bash - git fetch origin && git fetch upstream - git checkout sync-from-rust - git merge upstream/master - ``` -4. Open a PR to `rust-lang/rust-clippy` and wait for it to get merged (to - accelerate the process ping the `@rust-lang/clippy` team in your PR and/or - ~~annoy~~ ask them in the [Zulip] stream.) - -### Performing the sync from Clippy to [`rust-lang/rust`] - -All of the following commands have to be run inside the `rust` directory. - -1. Make sure Clippy itself is up-to-date by following the steps outlined in the previous -section if necessary. - -2. Sync the `rust-lang/rust-clippy` master to the rust-copy of Clippy: - ```bash - git checkout -b sync-from-clippy - git subtree pull -P src/tools/clippy https://github.com/rust-lang/rust-clippy master - ``` -3. Open a PR to [`rust-lang/rust`] - -### Defining remotes - -You may want to define remotes, so you don't have to type out the remote -addresses on every sync. You can do this with the following commands (these -commands still have to be run inside the `rust` directory): - -```bash -# Set clippy-upstream remote for pulls -$ git remote add clippy-upstream https://github.com/rust-lang/rust-clippy -# Make sure to not push to the upstream repo -$ git remote set-url --push clippy-upstream DISABLED -# Set clippy-origin remote to your fork for pushes -$ git remote add clippy-origin git@github.com:your-github-name/rust-clippy -# Set a local remote -$ git remote add clippy-local /path/to/rust-clippy -``` - -You can then sync with the remote names from above, e.g.: - -```bash -$ git subtree push -P src/tools/clippy clippy-local sync-from-rust -``` - -[gitgitgadget-pr]: https://github.com/gitgitgadget/git/pull/493 -[subtree]: https://rustc-dev-guide.rust-lang.org/contributing.html#external-dependencies-subtree -[`rust-lang/rust`]: https://github.com/rust-lang/rust - ## Issue and PR triage Clippy is following the [Rust triage procedure][triage] for issues and pull diff --git a/Cargo.toml b/Cargo.toml index 3c8b758d53dca..e4060ce29a7b5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,7 +58,7 @@ rustc_tools_util = { version = "0.2", path = "rustc_tools_util" } [features] deny-warnings = ["clippy_lints/deny-warnings"] integration = ["tempfile"] -internal = ["clippy_lints/internal"] +internal = ["clippy_lints/internal", "tempfile"] [package.metadata.rust-analyzer] # This package uses #[feature(rustc_private)] diff --git a/book/README.md b/book/README.md new file mode 100644 index 0000000000000..b652194d0d13b --- /dev/null +++ b/book/README.md @@ -0,0 +1,4 @@ +# Clippy Book + +This is the source for the Clippy Book. See the +[book](src/infrastructure/book.md) for more information. diff --git a/book/book.toml b/book/book.toml new file mode 100644 index 0000000000000..93b6641f7e1e7 --- /dev/null +++ b/book/book.toml @@ -0,0 +1,28 @@ +[book] +authors = ["The Rust Clippy Developers"] +language = "en" +multilingual = false +src = "src" +title = "Clippy Documentation" + +[rust] +edition = "2018" + +[output.html] +edit-url-template = "https://github.com/rust-lang/rust-clippy/edit/master/book/{path}" +git-repository-url = "https://github.com/rust-lang/rust-clippy/tree/master/book" +mathjax-support = true +site-url = "/rust-clippy/" + +[output.html.playground] +editable = true +line-numbers = true + +[output.html.search] +boost-hierarchy = 2 +boost-paragraph = 1 +boost-title = 2 +expand = true +heading-split-level = 2 +limit-results = 20 +use-boolean-and = true diff --git a/book/src/README.md b/book/src/README.md new file mode 100644 index 0000000000000..de1f70d7e9640 --- /dev/null +++ b/book/src/README.md @@ -0,0 +1,34 @@ +# Clippy + +[![Clippy Test](https://github.com/rust-lang/rust-clippy/workflows/Clippy%20Test/badge.svg?branch=auto&event=push)](https://github.com/rust-lang/rust-clippy/actions?query=workflow%3A%22Clippy+Test%22+event%3Apush+branch%3Aauto) +[![License: MIT OR Apache-2.0](https://img.shields.io/crates/l/clippy.svg)](#license) + +A collection of lints to catch common mistakes and improve your +[Rust](https://github.com/rust-lang/rust) code. + +[There are over 500 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) + +Lints are divided into categories, each with a default [lint +level](https://doc.rust-lang.org/rustc/lints/levels.html). You can choose how +much Clippy is supposed to ~~annoy~~ help you by changing the lint level by +category. + +| Category | Description | Default level | +| --------------------- | ----------------------------------------------------------------------------------- | ------------- | +| `clippy::all` | all lints that are on by default (correctness, suspicious, style, complexity, perf) | **warn/deny** | +| `clippy::correctness` | code that is outright wrong or useless | **deny** | +| `clippy::suspicious` | code that is most likely wrong or useless | **warn** | +| `clippy::complexity` | code that does something simple but in a complex way | **warn** | +| `clippy::perf` | code that can be written to run faster | **warn** | +| `clippy::style` | code that should be written in a more idiomatic way | **warn** | +| `clippy::pedantic` | lints which are rather strict or might have false positives | allow | +| `clippy::nursery` | new lints that are still under development | allow | +| `clippy::cargo` | lints for the cargo manifest | allow | | allow | + +More to come, please [file an +issue](https://github.com/rust-lang/rust-clippy/issues) if you have ideas! + +The [lint list](https://rust-lang.github.io/rust-clippy/master/index.html) also +contains "restriction lints", which are for things which are usually not +considered "bad", but may be useful to turn on in specific cases. These should +be used very selectively, if at all. diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md new file mode 100644 index 0000000000000..0b945faf9b78e --- /dev/null +++ b/book/src/SUMMARY.md @@ -0,0 +1,23 @@ +# Summary + +[Introduction](README.md) + +- [Installation](installation.md) +- [Usage](usage.md) +- [Configuration](configuration.md) +- [Clippy's Lints](lints.md) +- [Continuous Integration](continuous_integration/README.md) + - [GitHub Actions](continuous_integration/github_actions.md) + - [Travis CI](continuous_integration/travis.md) +- [Development](development/README.md) + - [Basics](development/basics.md) + - [Adding Lints](development/adding_lints.md) + - [Common Tools](development/common_tools_writing_lints.md) + - [Infrastructure](development/infrastructure/README.md) + - [Syncing changes between Clippy and rust-lang/rust](development/infrastructure/sync.md) + - [Backporting Changes](development/infrastructure/backport.md) + - [Updating the Changelog](development/infrastructure/changelog_update.md) + - [Release a New Version](development/infrastructure/release.md) + - [The Clippy Book](development/infrastructure/book.md) + - [Proposals](development/proposals/README.md) + - [Roadmap 2021](development/proposals/roadmap-2021.md) diff --git a/book/src/configuration.md b/book/src/configuration.md new file mode 100644 index 0000000000000..6e295ac3181dd --- /dev/null +++ b/book/src/configuration.md @@ -0,0 +1,92 @@ +# Configuring Clippy + +> **Note:** The configuration file is unstable and may be deprecated in the future. + +Some lints can be configured in a TOML file named `clippy.toml` or `.clippy.toml`. It contains a +basic `variable = value` mapping eg. + +```toml +avoid-breaking-exported-api = false +blacklisted-names = ["toto", "tata", "titi"] +cognitive-complexity-threshold = 30 +``` + +See the [list of lints](https://rust-lang.github.io/rust-clippy/master/index.html) for more information about which +lints can be configured and the meaning of the variables. + +To deactivate the "for further information visit *lint-link*" message you can define the `CLIPPY_DISABLE_DOCS_LINKS` +environment variable. + +### Allowing/denying lints + +You can add options to your code to `allow`/`warn`/`deny` Clippy lints: + +* the whole set of `Warn` lints using the `clippy` lint group (`#![deny(clippy::all)]`) + +* all lints using both the `clippy` and `clippy::pedantic` lint groups (`#![deny(clippy::all)]`, + `#![deny(clippy::pedantic)]`). Note that `clippy::pedantic` contains some very aggressive lints prone to false + positives. + +* only some lints (`#![deny(clippy::single_match, clippy::box_vec)]`, etc.) + +* `allow`/`warn`/`deny` can be limited to a single function or module using `#[allow(...)]`, etc. + +Note: `allow` means to suppress the lint for your code. With `warn` the lint will only emit a warning, while with `deny` +the lint will emit an error, when triggering for your code. An error causes clippy to exit with an error code, so is +useful in scripts like CI/CD. + +If you do not want to include your lint levels in your code, you can globally enable/disable lints by passing extra +flags to Clippy during the run: + +To allow `lint_name`, run + +```terminal +cargo clippy -- -A clippy::lint_name +``` + +And to warn on `lint_name`, run + +```terminal +cargo clippy -- -W clippy::lint_name +``` + +This also works with lint groups. For example you can run Clippy with warnings for all lints enabled: + +```terminal +cargo clippy -- -W clippy::pedantic +``` + +If you care only about a single lint, you can allow all others and then explicitly warn on the lint(s) you are +interested in: + +```terminal +cargo clippy -- -A clippy::all -W clippy::useless_format -W clippy::... +``` + +### Specifying the minimum supported Rust version + +Projects that intend to support old versions of Rust can disable lints pertaining to newer features by specifying the +minimum supported Rust version (MSRV) in the clippy configuration file. + +```toml +msrv = "1.30.0" +``` + +The MSRV can also be specified as an inner attribute, like below. + +```rust +#![feature(custom_inner_attributes)] +#![clippy::msrv = "1.30.0"] + +fn main() { + ... +} +``` + +You can also omit the patch version when specifying the MSRV, so `msrv = 1.30` +is equivalent to `msrv = 1.30.0`. + +Note: `custom_inner_attributes` is an unstable feature so it has to be enabled explicitly. + +Lints that recognize this configuration option can be +found [here](https://rust-lang.github.io/rust-clippy/master/index.html#msrv) diff --git a/book/src/continuous_integration/README.md b/book/src/continuous_integration/README.md new file mode 100644 index 0000000000000..e5c3673bde451 --- /dev/null +++ b/book/src/continuous_integration/README.md @@ -0,0 +1,18 @@ +# Continuous Integration + +It is recommended to run Clippy on CI with `-Dwarnings`, so that Clippy lints +prevent CI from passing. To enforce errors on warnings on all `cargo` commands +not just `cargo clippy`, you can set the env var `RUSTFLAGS="-Dwarnings"`. + +We recommend to use Clippy from the same toolchain, that you use for compiling +your crate for maximum compatibility. E.g. if your crate is compiled with the +`stable` toolchain, you should also use `stable` Clippy. + +> _Note:_ New Clippy lints are first added to the `nightly` toolchain. If you +> want to help with improving Clippy and have CI resources left, please consider +> adding a `nightly` Clippy check to your CI and report problems like false +> positives back to us. With that we can fix bugs early, before they can get to +> stable. + +This chapter will give an overview on how to use Clippy on different popular CI +providers. diff --git a/book/src/continuous_integration/github_actions.md b/book/src/continuous_integration/github_actions.md new file mode 100644 index 0000000000000..42a43ef138016 --- /dev/null +++ b/book/src/continuous_integration/github_actions.md @@ -0,0 +1,21 @@ +# GitHub Actions + +On the GitHub hosted runners, Clippy from the latest stable Rust version comes +pre-installed. So all you have to do is to run `cargo clippy`. + +```yml +on: push +name: Clippy check + +# Make sure CI fails on all warnings, including Clippy lints +env: + RUSTFLAGS: "-Dwarnings" + +jobs: + clippy_check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Run Clippy + run: cargo clippy --all-targets --all-features +``` diff --git a/book/src/continuous_integration/travis.md b/book/src/continuous_integration/travis.md new file mode 100644 index 0000000000000..85b9ed53daeaf --- /dev/null +++ b/book/src/continuous_integration/travis.md @@ -0,0 +1,20 @@ +# Travis CI + +You can add Clippy to Travis CI in the same way you use it locally: + +```yml +language: rust +rust: + - stable + - beta +before_script: + - rustup component add clippy +script: + - cargo clippy + # if you want the build job to fail when encountering warnings, use + - cargo clippy -- -D warnings + # in order to also check tests and non-default crate features, use + - cargo clippy --all-targets --all-features -- -D warnings + - cargo test + # etc. +``` diff --git a/book/src/development/README.md b/book/src/development/README.md new file mode 100644 index 0000000000000..5cf7201cffad7 --- /dev/null +++ b/book/src/development/README.md @@ -0,0 +1,43 @@ +# Clippy Development + +Hello fellow Rustacean! If you made it here, you're probably interested in +making Clippy better by contributing to it. In that case, welcome to the +project! + +> _Note:_ If you're just interested in using Clippy, there's nothing to see from +> this point onward and you should return to one of the earlier chapters. + +## Getting started + +If this is your first time contributing to Clippy, you should first read the +[Basics docs](basics.md). This will explain the basics on how to get the source +code and how to compile and test the code. + +## Writing code + +If you have done the basic setup, it's time to start hacking. + +The [Adding lints](adding_lints.md) chapter is a walk through on how to add a +new lint to Clippy. This is also interesting if you just want to fix a lint, +because it also covers how to test lints and gives an overview of the bigger +picture. + +If you want to add a new lint or change existing ones apart from bugfixing, it's +also a good idea to give the [stability guarantees][rfc_stability] and +[lint categories][rfc_lint_cats] sections of the [Clippy 1.0 RFC][clippy_rfc] a +quick read. The lint categories are also described [earlier in this +book](../lints.md). + +> _Note:_ Some higher level things about contributing to Clippy are still +> covered in the [`CONTRIBUTING.md`] document. Some of those will be moved to +> the book over time, like: +> - Finding something to fix +> - IDE setup +> - High level overview on how Clippy works +> - Triage procedure +> - Bors and Homu + +[clippy_rfc]: https://github.com/rust-lang/rfcs/blob/master/text/2476-clippy-uno.md +[rfc_stability]: https://github.com/rust-lang/rfcs/blob/master/text/2476-clippy-uno.md#stability-guarantees +[rfc_lint_cats]: https://github.com/rust-lang/rfcs/blob/master/text/2476-clippy-uno.md#lint-audit-and-categories +[`CONTRIBUTING.md`]: https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md diff --git a/doc/adding_lints.md b/book/src/development/adding_lints.md similarity index 73% rename from doc/adding_lints.md rename to book/src/development/adding_lints.md index 3e0b1c5c4f782..3da07fcb96863 100644 --- a/doc/adding_lints.md +++ b/book/src/development/adding_lints.md @@ -45,9 +45,9 @@ take a look at our [lint naming guidelines][lint_naming]. To get started on this lint you can run `cargo dev new_lint --name=foo_functions --pass=early --category=pedantic` (category will default to nursery if not provided). This command will create two files: `tests/ui/foo_functions.rs` and -`clippy_lints/src/foo_functions.rs`, as well as -[registering the lint](#lint-registration). For cargo lints, two project -hierarchies (fail/pass) will be created by default under `tests/ui-cargo`. +`clippy_lints/src/foo_functions.rs`, as well as [registering the +lint](#lint-registration). For cargo lints, two project hierarchies (fail/pass) +will be created by default under `tests/ui-cargo`. Next, we'll open up these files and add our lint! @@ -58,8 +58,8 @@ Let's write some tests first that we can execute while we iterate on our lint. Clippy uses UI tests for testing. UI tests check that the output of Clippy is exactly as expected. Each test is just a plain Rust file that contains the code we want to check. The output of Clippy is compared against a `.stderr` file. -Note that you don't have to create this file yourself, we'll get to -generating the `.stderr` files further down. +Note that you don't have to create this file yourself, we'll get to generating +the `.stderr` files further down. We start by opening the test file created at `tests/ui/foo_functions.rs`. @@ -96,61 +96,57 @@ fn main() { } ``` -Now we can run the test with `TESTNAME=foo_functions cargo uitest`, -currently this test is meaningless though. +Now we can run the test with `TESTNAME=foo_functions cargo uitest`, currently +this test is meaningless though. -While we are working on implementing our lint, we can keep running the UI -test. That allows us to check if the output is turning into what we want. +While we are working on implementing our lint, we can keep running the UI test. +That allows us to check if the output is turning into what we want. -Once we are satisfied with the output, we need to run -`cargo dev bless` to update the `.stderr` file for our lint. -Please note that, we should run `TESTNAME=foo_functions cargo uitest` -every time before running `cargo dev bless`. -Running `TESTNAME=foo_functions cargo uitest` should pass then. When we commit -our lint, we need to commit the generated `.stderr` files, too. In general, you -should only commit files changed by `cargo dev bless` for the +Once we are satisfied with the output, we need to run `cargo dev bless` to +update the `.stderr` file for our lint. Please note that, we should run +`TESTNAME=foo_functions cargo uitest` every time before running `cargo dev +bless`. Running `TESTNAME=foo_functions cargo uitest` should pass then. When we +commit our lint, we need to commit the generated `.stderr` files, too. In +general, you should only commit files changed by `cargo dev bless` for the specific lint you are creating/editing. Note that if the generated files are empty, they should be removed. -Note that you can run multiple test files by specifying a comma separated list: -`TESTNAME=foo_functions,test2,test3`. +> _Note:_ you can run multiple test files by specifying a comma separated list: +> `TESTNAME=foo_functions,test2,test3`. ### Cargo lints -For cargo lints, the process of testing differs in that we are interested in -the `Cargo.toml` manifest file. We also need a minimal crate associated -with that manifest. +For cargo lints, the process of testing differs in that we are interested in the +`Cargo.toml` manifest file. We also need a minimal crate associated with that +manifest. -If our new lint is named e.g. `foo_categories`, after running `cargo dev new_lint` -we will find by default two new crates, each with its manifest file: +If our new lint is named e.g. `foo_categories`, after running `cargo dev +new_lint` we will find by default two new crates, each with its manifest file: -* `tests/ui-cargo/foo_categories/fail/Cargo.toml`: this file should cause the new lint to raise an error. -* `tests/ui-cargo/foo_categories/pass/Cargo.toml`: this file should not trigger the lint. +* `tests/ui-cargo/foo_categories/fail/Cargo.toml`: this file should cause the + new lint to raise an error. +* `tests/ui-cargo/foo_categories/pass/Cargo.toml`: this file should not trigger + the lint. -If you need more cases, you can copy one of those crates (under `foo_categories`) and rename it. +If you need more cases, you can copy one of those crates (under +`foo_categories`) and rename it. -The process of generating the `.stderr` file is the same, and prepending the `TESTNAME` -variable to `cargo uitest` works too. +The process of generating the `.stderr` file is the same, and prepending the +`TESTNAME` variable to `cargo uitest` works too. ## Rustfix tests -If the lint you are working on is making use of structured suggestions, the -test file should include a `// run-rustfix` comment at the top. This will +If the lint you are working on is making use of structured suggestions, the test +file should include a `// run-rustfix` comment at the top. This will additionally run [rustfix] for that test. Rustfix will apply the suggestions -from the lint to the code of the test file and compare that to the contents of -a `.fixed` file. +from the lint to the code of the test file and compare that to the contents of a +`.fixed` file. -Use `cargo dev bless` to automatically generate the -`.fixed` file after running the tests. +Use `cargo dev bless` to automatically generate the `.fixed` file after running +the tests. [rustfix]: https://github.com/rust-lang/rustfix -## Edition 2018 tests - -Some features require the 2018 edition to work (e.g. `async_await`), but -compile-test tests run on the 2015 edition by default. To change this behavior -add `// edition:2018` at the top of the test file (note that it's space-sensitive). - ## Testing manually Manually testing against an example file can be useful if you have added some @@ -166,9 +162,9 @@ implementing our lint now. ## Lint declaration -Let's start by opening the new file created in the `clippy_lints` crate -at `clippy_lints/src/foo_functions.rs`. That's the crate where all the -lint code is. This file has already imported some initial things we will need: +Let's start by opening the new file created in the `clippy_lints` crate at +`clippy_lints/src/foo_functions.rs`. That's the crate where all the lint code +is. This file has already imported some initial things we will need: ```rust use rustc_lint::{EarlyLintPass, EarlyContext}; @@ -178,7 +174,8 @@ use rustc_ast::ast::*; The next step is to update the lint declaration. Lints are declared using the [`declare_clippy_lint!`][declare_clippy_lint] macro, and we just need to update -the auto-generated lint declaration to have a real description, something like this: +the auto-generated lint declaration to have a real description, something like +this: ```rust declare_clippy_lint! { @@ -198,24 +195,25 @@ declare_clippy_lint! { ``` * The section of lines prefixed with `///` constitutes the lint documentation - section. This is the default documentation style and will be displayed - [like this][example_lint_page]. To render and open this documentation locally - in a browser, run `cargo dev serve`. -* The `#[clippy::version]` attribute will be rendered as part of the lint documentation. - The value should be set to the current Rust version that the lint is developed in, - it can be retrieved by running `rustc -vV` in the rust-clippy directory. The version - is listed under *release*. (Use the version without the `-nightly`) suffix. -* `FOO_FUNCTIONS` is the name of our lint. Be sure to follow the - [lint naming guidelines][lint_naming] here when naming your lint. - In short, the name should state the thing that is being checked for and - read well when used with `allow`/`warn`/`deny`. -* `pedantic` sets the lint level to `Allow`. - The exact mapping can be found [here][category_level_mapping] + section. This is the default documentation style and will be displayed [like + this][example_lint_page]. To render and open this documentation locally in a + browser, run `cargo dev serve`. +* The `#[clippy::version]` attribute will be rendered as part of the lint + documentation. The value should be set to the current Rust version that the + lint is developed in, it can be retrieved by running `rustc -vV` in the + rust-clippy directory. The version is listed under *release*. (Use the version + without the `-nightly`) suffix. +* `FOO_FUNCTIONS` is the name of our lint. Be sure to follow the [lint naming + guidelines][lint_naming] here when naming your lint. In short, the name should + state the thing that is being checked for and read well when used with + `allow`/`warn`/`deny`. +* `pedantic` sets the lint level to `Allow`. The exact mapping can be found + [here][category_level_mapping] * The last part should be a text that explains what exactly is wrong with the code -The rest of this file contains an empty implementation for our lint pass, -which in this case is `EarlyLintPass` and should look like this: +The rest of this file contains an empty implementation for our lint pass, which +in this case is `EarlyLintPass` and should look like this: ```rust // clippy_lints/src/foo_functions.rs @@ -324,9 +322,9 @@ impl EarlyLintPass for FooFunctions { Running our UI test should now produce output that contains the lint message. According to [the rustc-dev-guide], the text should be matter of fact and avoid -capitalization and periods, unless multiple sentences are needed. -When code or an identifier must appear in a message or label, it should be -surrounded with single grave accents \`. +capitalization and periods, unless multiple sentences are needed. When code or +an identifier must appear in a message or label, it should be surrounded with +single grave accents \`. [check_fn]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html#method.check_fn [diagnostics]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_utils/src/diagnostics.rs @@ -382,8 +380,8 @@ fn is_foo_fn(fn_kind: FnKind<'_>) -> bool { ``` Now we should also run the full test suite with `cargo test`. At this point -running `cargo test` should produce the expected output. Remember to run -`cargo dev bless` to update the `.stderr` file. +running `cargo test` should produce the expected output. Remember to run `cargo +dev bless` to update the `.stderr` file. `cargo test` (as opposed to `cargo uitest`) will also ensure that our lint implementation is not violating any Clippy lints itself. @@ -397,13 +395,16 @@ pass. ## Specifying the lint's minimum supported Rust version (MSRV) -Sometimes a lint makes suggestions that require a certain version of Rust. For example, the `manual_strip` lint suggests -using `str::strip_prefix` and `str::strip_suffix` which is only available after Rust 1.45. In such cases, you need to -ensure that the MSRV configured for the project is >= the MSRV of the required Rust feature. If multiple features are -required, just use the one with a lower MSRV. +Sometimes a lint makes suggestions that require a certain version of Rust. For +example, the `manual_strip` lint suggests using `str::strip_prefix` and +`str::strip_suffix` which is only available after Rust 1.45. In such cases, you +need to ensure that the MSRV configured for the project is >= the MSRV of the +required Rust feature. If multiple features are required, just use the one with +a lower MSRV. -First, add an MSRV alias for the required feature in [`clippy_utils::msrvs`](/clippy_utils/src/msrvs.rs). This can be -accessed later as `msrvs::STR_STRIP_PREFIX`, for example. +First, add an MSRV alias for the required feature in +[`clippy_utils::msrvs`](/clippy_utils/src/msrvs.rs). This can be accessed later +as `msrvs::STR_STRIP_PREFIX`, for example. ```rust msrv_aliases! { @@ -412,8 +413,9 @@ msrv_aliases! { } ``` -In order to access the project-configured MSRV, you need to have an `msrv` field in the LintPass struct, and a -constructor to initialize the field. The `msrv` value is passed to the constructor in `clippy_lints/lib.rs`. +In order to access the project-configured MSRV, you need to have an `msrv` field +in the LintPass struct, and a constructor to initialize the field. The `msrv` +value is passed to the constructor in `clippy_lints/lib.rs`. ```rust pub struct ManualStrip { @@ -472,11 +474,10 @@ If you have trouble implementing your lint, there is also the internal `author` lint to generate Clippy code that detects the offending pattern. It does not work for all of the Rust syntax, but can give a good starting point. -The quickest way to use it, is the -[Rust playground: play.rust-lang.org][author_example]. -Put the code you want to lint into the editor and add the `#[clippy::author]` -attribute above the item. Then run Clippy via `Tools -> Clippy` and you should -see the generated code in the output below. +The quickest way to use it, is the [Rust playground: +play.rust-lang.org][author_example]. Put the code you want to lint into the +editor and add the `#[clippy::author]` attribute above the item. Then run Clippy +via `Tools -> Clippy` and you should see the generated code in the output below. [Here][author_example] is an example on the playground. @@ -487,13 +488,15 @@ you are implementing your lint. ## Print HIR lint -To implement a lint, it's helpful to first understand the internal representation -that rustc uses. Clippy has the `#[clippy::dump]` attribute that prints the -[_High-Level Intermediate Representation (HIR)_] of the item, statement, or -expression that the attribute is attached to. To attach the attribute to expressions -you often need to enable `#![feature(stmt_expr_attributes)]`. +To implement a lint, it's helpful to first understand the internal +representation that rustc uses. Clippy has the `#[clippy::dump]` attribute that +prints the [_High-Level Intermediate Representation (HIR)_] of the item, +statement, or expression that the attribute is attached to. To attach the +attribute to expressions you often need to enable +`#![feature(stmt_expr_attributes)]`. -[Here][print_hir_example] you can find an example, just select _Tools_ and run _Clippy_. +[Here][print_hir_example] you can find an example, just select _Tools_ and run +_Clippy_. [_High-Level Intermediate Representation (HIR)_]: https://rustc-dev-guide.rust-lang.org/hir.html [print_hir_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=daf14db3a7f39ca467cd1b86c34b9afb @@ -518,7 +521,7 @@ declare_clippy_lint! { /// ```rust,ignore /// // A short example of code that triggers the lint /// ``` - /// + /// /// Use instead: /// ```rust,ignore /// // A short example of improved code that doesn't trigger the lint @@ -537,9 +540,9 @@ list][lint_list]. ## Running rustfmt -[Rustfmt] is a tool for formatting Rust code according to style guidelines. -Your code has to be formatted by `rustfmt` before a PR can be merged. -Clippy uses nightly `rustfmt` in the CI. +[Rustfmt] is a tool for formatting Rust code according to style guidelines. Your +code has to be formatted by `rustfmt` before a PR can be merged. Clippy uses +nightly `rustfmt` in the CI. It can be installed via `rustup`: @@ -575,94 +578,105 @@ Before submitting your PR make sure you followed all of the basic requirements: ## Adding configuration to a lint -Clippy supports the configuration of lints values using a `clippy.toml` file in the workspace -directory. Adding a configuration to a lint can be useful for thresholds or to constrain some -behavior that can be seen as a false positive for some users. Adding a configuration is done -in the following steps: +Clippy supports the configuration of lints values using a `clippy.toml` file in +the workspace directory. Adding a configuration to a lint can be useful for +thresholds or to constrain some behavior that can be seen as a false positive +for some users. Adding a configuration is done in the following steps: -1. Adding a new configuration entry to [clippy_lints::utils::conf](/clippy_lints/src/utils/conf.rs) - like this: - ```rust - /// Lint: LINT_NAME. - /// - /// - (configuration_ident: Type = DefaultValue), - ``` - The doc comment is automatically added to the documentation of the listed lints. The default - value will be formatted using the `Debug` implementation of the type. -2. Adding the configuration value to the lint impl struct: - 1. This first requires the definition of a lint impl struct. Lint impl structs are usually - generated with the `declare_lint_pass!` macro. This struct needs to be defined manually - to add some kind of metadata to it: - ```rust - // Generated struct definition - declare_lint_pass!(StructName => [ - LINT_NAME - ]); - - // New manual definition struct - #[derive(Copy, Clone)] - pub struct StructName {} - - impl_lint_pass!(StructName => [ - LINT_NAME - ]); - ``` - - 2. Next add the configuration value and a corresponding creation method like this: - ```rust - #[derive(Copy, Clone)] - pub struct StructName { - configuration_ident: Type, - } +1. Adding a new configuration entry to + [clippy_lints::utils::conf](/clippy_lints/src/utils/conf.rs) like this: - // ... + ```rust + /// Lint: LINT_NAME. + /// + /// + (configuration_ident: Type = DefaultValue), + ``` - impl StructName { - pub fn new(configuration_ident: Type) -> Self { - Self { - configuration_ident, - } - } - } - ``` + The doc comment is automatically added to the documentation of the listed + lints. The default value will be formatted using the `Debug` implementation + of the type. +2. Adding the configuration value to the lint impl struct: + 1. This first requires the definition of a lint impl struct. Lint impl + structs are usually generated with the `declare_lint_pass!` macro. This + struct needs to be defined manually to add some kind of metadata to it: + ```rust + // Generated struct definition + declare_lint_pass!(StructName => [ + LINT_NAME + ]); + + // New manual definition struct + #[derive(Copy, Clone)] + pub struct StructName {} + + impl_lint_pass!(StructName => [ + LINT_NAME + ]); + ``` + + 2. Next add the configuration value and a corresponding creation method like + this: + ```rust + #[derive(Copy, Clone)] + pub struct StructName { + configuration_ident: Type, + } + + // ... + + impl StructName { + pub fn new(configuration_ident: Type) -> Self { + Self { + configuration_ident, + } + } + } + ``` 3. Passing the configuration value to the lint impl struct: - First find the struct construction in the [clippy_lints lib file](/clippy_lints/src/lib.rs). - The configuration value is now cloned or copied into a local value that is then passed to the - impl struct like this: - ```rust - // Default generated registration: - store.register_*_pass(|| box module::StructName); + First find the struct construction in the [clippy_lints lib + file](/clippy_lints/src/lib.rs). The configuration value is now cloned or + copied into a local value that is then passed to the impl struct like this: + + ```rust + // Default generated registration: + store.register_*_pass(|| box module::StructName); - // New registration with configuration value - let configuration_ident = conf.configuration_ident.clone(); - store.register_*_pass(move || box module::StructName::new(configuration_ident)); - ``` + // New registration with configuration value + let configuration_ident = conf.configuration_ident.clone(); + store.register_*_pass(move || box module::StructName::new(configuration_ident)); + ``` - Congratulations the work is almost done. The configuration value can now be accessed - in the linting code via `self.configuration_ident`. + Congratulations the work is almost done. The configuration value can now be + accessed in the linting code via `self.configuration_ident`. 4. Adding tests: - 1. The default configured value can be tested like any normal lint in [`tests/ui`](/tests/ui). - 2. The configuration itself will be tested separately in [`tests/ui-toml`](/tests/ui-toml). - Simply add a new subfolder with a fitting name. This folder contains a `clippy.toml` file - with the configuration value and a rust file that should be linted by Clippy. The test can - otherwise be written as usual. + 1. The default configured value can be tested like any normal lint in + [`tests/ui`](/tests/ui). + 2. The configuration itself will be tested separately in + [`tests/ui-toml`](/tests/ui-toml). Simply add a new subfolder with a + fitting name. This folder contains a `clippy.toml` file with the + configuration value and a rust file that should be linted by Clippy. The + test can otherwise be written as usual. ## Cheat Sheet Here are some pointers to things you are likely going to need for every lint: * [Clippy utils][utils] - Various helper functions. Maybe the function you need - is already in here ([`is_type_diagnostic_item`], [`implements_trait`], [`snippet`], etc) + is already in here ([`is_type_diagnostic_item`], [`implements_trait`], + [`snippet`], etc) * [Clippy diagnostics][diagnostics] * [Let chains][let-chains] -* [`from_expansion`][from_expansion] and [`in_external_macro`][in_external_macro] +* [`from_expansion`][from_expansion] and + [`in_external_macro`][in_external_macro] * [`Span`][span] * [`Applicability`][applicability] -* [Common tools for writing lints](common_tools_writing_lints.md) helps with common operations -* [The rustc-dev-guide][rustc-dev-guide] explains a lot of internal compiler concepts +* [Common tools for writing lints](common_tools_writing_lints.md) helps with + common operations +* [The rustc-dev-guide][rustc-dev-guide] explains a lot of internal compiler + concepts * [The nightly rustc docs][nightly_docs] which has been linked to throughout this guide diff --git a/doc/basics.md b/book/src/development/basics.md similarity index 75% rename from doc/basics.md rename to book/src/development/basics.md index 57a90a924ec3c..78c429ea01322 100644 --- a/doc/basics.md +++ b/book/src/development/basics.md @@ -1,8 +1,8 @@ # Basics for hacking on Clippy This document explains the basics for hacking on Clippy. Besides others, this -includes how to build and test Clippy. For a more in depth description on -the codebase take a look at [Adding Lints] or [Common Tools]. +includes how to build and test Clippy. For a more in depth description on the +codebase take a look at [Adding Lints] or [Common Tools]. [Adding Lints]: https://github.com/rust-lang/rust-clippy/blob/master/doc/adding_lints.md [Common Tools]: https://github.com/rust-lang/rust-clippy/blob/master/doc/common_tools_writing_lints.md @@ -62,8 +62,8 @@ TESTNAME="test_" cargo uitest cargo test --test dogfood ``` -If the output of a [UI test] differs from the expected output, you can update the -reference file with: +If the output of a [UI test] differs from the expected output, you can update +the reference file with: ```bash cargo dev bless @@ -72,8 +72,8 @@ cargo dev bless For example, this is necessary, if you fix a typo in an error message of a lint or if you modify a test file to add a test case. -_Note:_ This command may update more files than you intended. In that case only -commit the files you wanted to update. +> _Note:_ This command may update more files than you intended. In that case +> only commit the files you wanted to update. [UI test]: https://rustc-dev-guide.rust-lang.org/tests/adding.html#guide-to-the-ui-tests @@ -96,22 +96,26 @@ cargo dev setup git-hook # (experimental) Setup Clippy to work with IntelliJ-Rust cargo dev setup intellij ``` -More about intellij command usage and reasons [here](../CONTRIBUTING.md#intellij-rust) + +More about intellij command usage and reasons +[here](../CONTRIBUTING.md#intellij-rust) ## lintcheck -`cargo lintcheck` will build and run clippy on a fixed set of crates and generate a log of the results. -You can `git diff` the updated log against its previous version and -see what impact your lint made on a small set of crates. -If you add a new lint, please audit the resulting warnings and make sure -there are no false positives and that the suggestions are valid. + +`cargo lintcheck` will build and run clippy on a fixed set of crates and +generate a log of the results. You can `git diff` the updated log against its +previous version and see what impact your lint made on a small set of crates. +If you add a new lint, please audit the resulting warnings and make sure there +are no false positives and that the suggestions are valid. Refer to the tools [README] for more details. [README]: https://github.com/rust-lang/rust-clippy/blob/master/lintcheck/README.md + ## PR -We follow a rustc no merge-commit policy. -See . +We follow a rustc no merge-commit policy. See +. ## Common Abbreviations @@ -126,27 +130,34 @@ See . | HIR | High-Level Intermediate Representation | | TCX | Type context | -This is a concise list of abbreviations that can come up during Clippy development. An extensive -general list can be found in the [rustc-dev-guide glossary][glossary]. Always feel free to ask if -an abbreviation or meaning is unclear to you. +This is a concise list of abbreviations that can come up during Clippy +development. An extensive general list can be found in the [rustc-dev-guide +glossary][glossary]. Always feel free to ask if an abbreviation or meaning is +unclear to you. ## Install from source -If you are hacking on Clippy and want to install it from source, do the following: +If you are hacking on Clippy and want to install it from source, do the +following: -First, take note of the toolchain [override](https://rust-lang.github.io/rustup/overrides.html) in `/rust-toolchain`. -We will use this override to install Clippy into the right toolchain. +First, take note of the toolchain +[override](https://rust-lang.github.io/rustup/overrides.html) in +`/rust-toolchain`. We will use this override to install Clippy into the right +toolchain. -> Tip: You can view the active toolchain for the current directory with `rustup show active-toolchain`. +> Tip: You can view the active toolchain for the current directory with `rustup +> show active-toolchain`. -From the Clippy project root, run the following command to build the Clippy binaries and copy them into the -toolchain directory. This will override the currently installed Clippy component. +From the Clippy project root, run the following command to build the Clippy +binaries and copy them into the toolchain directory. This will override the +currently installed Clippy component. ```terminal cargo build --release --bin cargo-clippy --bin clippy-driver -Zunstable-options --out-dir "$(rustc --print=sysroot)/bin" ``` -Now you may run `cargo clippy` in any project, using the toolchain where you just installed Clippy. +Now you may run `cargo clippy` in any project, using the toolchain where you +just installed Clippy. ```terminal cd my-project @@ -159,16 +170,19 @@ cargo +nightly-2021-07-01 clippy clippy-driver +nightly-2021-07-01 ``` -If you need to restore the default Clippy installation, run the following (from the Clippy project root). +If you need to restore the default Clippy installation, run the following (from +the Clippy project root). ```terminal rustup component remove clippy rustup component add clippy ``` -> **DO NOT** install using `cargo install --path . --force` since this will overwrite rustup -> [proxies](https://rust-lang.github.io/rustup/concepts/proxies.html). That is, `~/.cargo/bin/cargo-clippy` and -> `~/.cargo/bin/clippy-driver` should be hard or soft links to `~/.cargo/bin/rustup`. You can repair these by running -> `rustup update`. +> **DO NOT** install using `cargo install --path . --force` since this will +> overwrite rustup +> [proxies](https://rust-lang.github.io/rustup/concepts/proxies.html). That is, +> `~/.cargo/bin/cargo-clippy` and `~/.cargo/bin/clippy-driver` should be hard or +> soft links to `~/.cargo/bin/rustup`. You can repair these by running `rustup +> update`. [glossary]: https://rustc-dev-guide.rust-lang.org/appendix/glossary.html diff --git a/doc/common_tools_writing_lints.md b/book/src/development/common_tools_writing_lints.md similarity index 76% rename from doc/common_tools_writing_lints.md rename to book/src/development/common_tools_writing_lints.md index 1d1aee0da2cc7..e1ed89262f677 100644 --- a/doc/common_tools_writing_lints.md +++ b/book/src/development/common_tools_writing_lints.md @@ -18,15 +18,17 @@ Useful Rustc dev guide links: ## Retrieving the type of an expression -Sometimes you may want to retrieve the type `Ty` of an expression `Expr`, for example to answer following questions: +Sometimes you may want to retrieve the type `Ty` of an expression `Expr`, for +example to answer following questions: - which type does this expression correspond to (using its [`TyKind`][TyKind])? - is it a sized type? - is it a primitive type? - does it implement a trait? -This operation is performed using the [`expr_ty()`][expr_ty] method from the [`TypeckResults`][TypeckResults] struct, -that gives you access to the underlying structure [`Ty`][Ty]. +This operation is performed using the [`expr_ty()`][expr_ty] method from the +[`TypeckResults`][TypeckResults] struct, that gives you access to the underlying +structure [`Ty`][Ty]. Example of use: ```rust @@ -43,8 +45,8 @@ impl LateLintPass<'_> for MyStructLint { } ``` -Similarly in [`TypeckResults`][TypeckResults] methods, you have the [`pat_ty()`][pat_ty] method -to retrieve a type from a pattern. +Similarly in [`TypeckResults`][TypeckResults] methods, you have the +[`pat_ty()`][pat_ty] method to retrieve a type from a pattern. Two noticeable items here: - `cx` is the lint context [`LateContext`][LateContext]. The two most useful @@ -52,12 +54,13 @@ Two noticeable items here: `LateContext::typeck_results`, allowing us to jump to type definitions and other compilation stages such as HIR. - `typeck_results`'s return value is [`TypeckResults`][TypeckResults] and is - created by type checking step, it includes useful information such as types - of expressions, ways to resolve methods and so on. + created by type checking step, it includes useful information such as types of + expressions, ways to resolve methods and so on. ## Checking if an expr is calling a specific method -Starting with an `expr`, you can check whether it is calling a specific method `some_method`: +Starting with an `expr`, you can check whether it is calling a specific method +`some_method`: ```rust impl<'tcx> LateLintPass<'tcx> for MyStructLint { @@ -77,8 +80,9 @@ impl<'tcx> LateLintPass<'tcx> for MyStructLint { ## Checking for a specific type -There are three ways to check if an expression type is a specific type we want to check for. -All of these methods only check for the base type, generic arguments have to be checked separately. +There are three ways to check if an expression type is a specific type we want +to check for. All of these methods only check for the base type, generic +arguments have to be checked separately. ```rust use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; @@ -115,7 +119,8 @@ Prefer using diagnostic items and lang items where possible. ## Checking if a type implements a specific trait -There are three ways to do this, depending on if the target trait has a diagnostic item, lang item or neither. +There are three ways to do this, depending on if the target trait has a +diagnostic item, lang item or neither. ```rust use clippy_utils::{implements_trait, is_trait_method, match_trait_method, paths}; @@ -151,8 +156,9 @@ impl LateLintPass<'_> for MyStructLint { > Prefer using diagnostic and lang items, if the target trait has one. -We access lang items through the type context `tcx`. `tcx` is of type [`TyCtxt`][TyCtxt] and is defined in the `rustc_middle` crate. -A list of defined paths for Clippy can be found in [paths.rs][paths] +We access lang items through the type context `tcx`. `tcx` is of type +[`TyCtxt`][TyCtxt] and is defined in the `rustc_middle` crate. A list of defined +paths for Clippy can be found in [paths.rs][paths] ## Checking if a type defines a specific method @@ -182,14 +188,15 @@ impl<'tcx> LateLintPass<'tcx> for MyTypeImpl { ## Dealing with macros and expansions Keep in mind that macros are already expanded and desugaring is already applied -to the code representation that you are working with in Clippy. This unfortunately causes a lot of -false positives because macro expansions are "invisible" unless you actively check for them. -Generally speaking, code with macro expansions should just be ignored by Clippy because that code can be -dynamic in ways that are difficult or impossible to see. -Use the following functions to deal with macros: +to the code representation that you are working with in Clippy. This +unfortunately causes a lot of false positives because macro expansions are +"invisible" unless you actively check for them. Generally speaking, code with +macro expansions should just be ignored by Clippy because that code can be +dynamic in ways that are difficult or impossible to see. Use the following +functions to deal with macros: -- `span.from_expansion()`: detects if a span is from macro expansion or desugaring. - Checking this is a common first step in a lint. +- `span.from_expansion()`: detects if a span is from macro expansion or + desugaring. Checking this is a common first step in a lint. ```rust if expr.span.from_expansion() { @@ -198,45 +205,51 @@ Use the following functions to deal with macros: } ``` -- `span.ctxt()`: the span's context represents whether it is from expansion, and if so, which macro call expanded it. - It is sometimes useful to check if the context of two spans are equal. - - ```rust - // expands to `1 + 0`, but don't lint - 1 + mac!() - ``` - ```rust - if left.span.ctxt() != right.span.ctxt() { - // the coder most likely cannot modify this expression - return; - } - ``` - Note: Code that is not from expansion is in the "root" context. So any spans where `from_expansion` returns `true` can - be assumed to have the same context. And so just using `span.from_expansion()` is often good enough. - - -- `in_external_macro(span)`: detect if the given span is from a macro defined in a foreign crate. - If you want the lint to work with macro-generated code, this is the next line of defense to avoid macros - not defined in the current crate. It doesn't make sense to lint code that the coder can't change. - - You may want to use it for example to not start linting in macros from other crates - - ```rust - #[macro_use] - extern crate a_crate_with_macros; - - // `foo` is defined in `a_crate_with_macros` - foo!("bar"); - - // if we lint the `match` of `foo` call and test its span - assert_eq!(in_external_macro(cx.sess(), match_span), true); - ``` - -- `span.ctxt()`: the span's context represents whether it is from expansion, and if so, what expanded it - -One thing `SpanContext` is useful for is to check if two spans are in the same context. For example, -in `a == b`, `a` and `b` have the same context. In a `macro_rules!` with `a == $b`, `$b` is expanded to some -expression with a different context from `a`. +- `span.ctxt()`: the span's context represents whether it is from expansion, and + if so, which macro call expanded it. It is sometimes useful to check if the + context of two spans are equal. + + ```rust + // expands to `1 + 0`, but don't lint + 1 + mac!() + ``` + ```rust + if left.span.ctxt() != right.span.ctxt() { + // the coder most likely cannot modify this expression + return; + } + ``` + > Note: Code that is not from expansion is in the "root" context. So any spans + > where `from_expansion` returns `true` can be assumed to have the same + > context. And so just using `span.from_expansion()` is often good enough. + + +- `in_external_macro(span)`: detect if the given span is from a macro defined in + a foreign crate. If you want the lint to work with macro-generated code, this + is the next line of defense to avoid macros not defined in the current crate. + It doesn't make sense to lint code that the coder can't change. + + You may want to use it for example to not start linting in macros from other + crates + + ```rust + #[macro_use] + extern crate a_crate_with_macros; + + // `foo` is defined in `a_crate_with_macros` + foo!("bar"); + + // if we lint the `match` of `foo` call and test its span + assert_eq!(in_external_macro(cx.sess(), match_span), true); + ``` + +- `span.ctxt()`: the span's context represents whether it is from expansion, and + if so, what expanded it + + One thing `SpanContext` is useful for is to check if two spans are in the same + context. For example, in `a == b`, `a` and `b` have the same context. In a + `macro_rules!` with `a == $b`, `$b` is expanded to some expression with a + different context from `a`. ```rust macro_rules! m { diff --git a/book/src/development/infrastructure/README.md b/book/src/development/infrastructure/README.md new file mode 100644 index 0000000000000..3b2a253999629 --- /dev/null +++ b/book/src/development/infrastructure/README.md @@ -0,0 +1,19 @@ +# Infrastructure + +In order to deploy Clippy over `rustup`, some infrastructure is necessary. This +chapter describes the different parts of the Clippy infrastructure that need to +be maintained to make this possible. + +The most important part is the sync between the `rust-lang/rust` repository and +the Clippy repository that takes place every two weeks. This process is +described in the [Syncing changes between Clippy and `rust-lang/rust`](sync.md) +section. + +A new Clippy release is done together with every Rust release, so every six +weeks. The release process is described in the [Release a new Clippy +Version](release.md) section. During a release cycle a changelog entry for the +next release has to be written. The format of that and how to do that is +documented in the [Changelog Update](changelog_update.md) section. + +> _Note:_ The Clippy CI should also be described in this chapter, but for now is +> left as a TODO. diff --git a/doc/backport.md b/book/src/development/infrastructure/backport.md similarity index 100% rename from doc/backport.md rename to book/src/development/infrastructure/backport.md diff --git a/book/src/development/infrastructure/book.md b/book/src/development/infrastructure/book.md new file mode 100644 index 0000000000000..b62314c6735a2 --- /dev/null +++ b/book/src/development/infrastructure/book.md @@ -0,0 +1,42 @@ +# The Clippy Book + +This document explains how to make additions and changes to the Clippy book, the +guide to Clippy that you're reading right now. The Clippy book is formatted with +[Markdown](https://www.markdownguide.org) and generated by +[mdbook](https://github.com/rust-lang/mdBook). + +- [Get mdbook](#get-mdbook) +- [Make changes](#make-changes) + +## Get mdbook + +While not strictly necessary since the book source is simply Markdown text +files, having mdbook locally will allow you to build, test and serve the book +locally to view changes before you commit them to the repository. You likely +already have `cargo` installed, so the easiest option is to simply: + +```shell +cargo install mdbook +``` + +See the mdbook [installation](https://github.com/rust-lang/mdBook#installation) +instructions for other options. + +## Make changes + +The book's +[src](https://github.com/joshrotenberg/rust-clippy/tree/clippy_guide/book/src) +directory contains all of the markdown files used to generate the book. If you +want to see your changes in real time, you can use the mdbook `serve` command to +run a web server locally that will automatically update changes as they are +made. From the top level of your `rust-clippy` directory: + +```shell +mdbook serve book --open +``` + +Then navigate to `http://localhost:3000` to see the generated book. While the +server is running, changes you make will automatically be updated. + +For more information, see the mdbook +[guide](https://rust-lang.github.io/mdBook/). diff --git a/doc/changelog_update.md b/book/src/development/infrastructure/changelog_update.md similarity index 78% rename from doc/changelog_update.md rename to book/src/development/infrastructure/changelog_update.md index 0cbad2c09249c..e560f4c6a3e51 100644 --- a/doc/changelog_update.md +++ b/book/src/development/infrastructure/changelog_update.md @@ -1,6 +1,6 @@ # Changelog Update -If you want to help with updating the [changelog][changelog], you're in the right place. +If you want to help with updating the [changelog], you're in the right place. ## When to update @@ -11,8 +11,8 @@ Rust release. For that purpose, the changelog is ideally updated during the week before an upcoming stable release. You can find the release dates on the [Rust Forge][forge]. -Most of the time we only need to update the changelog for minor Rust releases. It's -been very rare that Clippy changes were included in a patch release. +Most of the time we only need to update the changelog for minor Rust releases. +It's been very rare that Clippy changes were included in a patch release. ## Changelog update walkthrough @@ -24,10 +24,12 @@ be found in the `tools` directory of the Rust repository. Depending on the current time and what exactly you want to update, the following bullet points might be helpful: -* When writing the release notes for the **upcoming stable release** you need to check - out the Clippy commit of the current Rust `beta` branch. [Link][rust_beta_tools] -* When writing the release notes for the **upcoming beta release**, you need to check - out the Clippy commit of the current Rust `master`. [Link][rust_master_tools] +* When writing the release notes for the **upcoming stable release** you need to + check out the Clippy commit of the current Rust `beta` branch. + [Link][rust_beta_tools] +* When writing the release notes for the **upcoming beta release**, you need to + check out the Clippy commit of the current Rust `master`. + [Link][rust_master_tools] * When writing the (forgotten) release notes for a **past stable release**, you need to check out the Rust release tag of the stable release. [Link][rust_stable_tools] @@ -35,7 +37,8 @@ bullet points might be helpful: Usually you want to write the changelog of the **upcoming stable release**. Make sure though, that `beta` was already branched in the Rust repository. -To find the commit hash, issue the following command when in a `rust-lang/rust` checkout: +To find the commit hash, issue the following command when in a `rust-lang/rust` +checkout: ``` git log --oneline -- src/tools/clippy/ | grep -o "Merge commit '[a-f0-9]*' into .*" | head -1 | sed -e "s/Merge commit '\([a-f0-9]*\)' into .*/\1/g" ``` @@ -44,7 +47,9 @@ git log --oneline -- src/tools/clippy/ | grep -o "Merge commit '[a-f0-9]*' into Once you've got the correct commit range, run - util/fetch_prs_between.sh commit1 commit2 > changes.txt +``` +util/fetch_prs_between.sh commit1 commit2 > changes.txt +``` and open that file in your editor of choice. @@ -54,14 +59,14 @@ already correct in the current changelog. ### 3. Authoring the final changelog The above script should have dumped all the relevant PRs to the file you -specified. It should have filtered out most of the irrelevant PRs -already, but it's a good idea to do a manual cleanup pass where you look for -more irrelevant PRs. If you're not sure about some PRs, just leave them in for -the review and ask for feedback. - -With the PRs filtered, you can start to take each PR and move the -`changelog: ` content to `CHANGELOG.md`. Adapt the wording as you see fit but -try to keep it somewhat coherent. +specified. It should have filtered out most of the irrelevant PRs already, but +it's a good idea to do a manual cleanup pass where you look for more irrelevant +PRs. If you're not sure about some PRs, just leave them in for the review and +ask for feedback. + +With the PRs filtered, you can start to take each PR and move the `changelog: ` +content to `CHANGELOG.md`. Adapt the wording as you see fit but try to keep it +somewhat coherent. The order should roughly be: diff --git a/doc/release.md b/book/src/development/infrastructure/release.md similarity index 85% rename from doc/release.md rename to book/src/development/infrastructure/release.md index c4f8f98938428..0572281803e71 100644 --- a/doc/release.md +++ b/book/src/development/infrastructure/release.md @@ -1,7 +1,7 @@ # Release a new Clippy Version -_NOTE: This document is probably only relevant to you, if you're a member of the -Clippy team._ +> _NOTE:_ This document is probably only relevant to you, if you're a member of +> the Clippy team. Clippy is released together with stable Rust releases. The dates for these releases can be found at the [Rust Forge]. This document explains the necessary @@ -13,12 +13,11 @@ steps to create a Clippy release. 4. [Tag the stable commit](#tag-the-stable-commit) 5. [Update `CHANGELOG.md`](#update-changelogmd) -_NOTE: This document is for stable Rust releases, not for point releases. For -point releases, step 1. and 2. should be enough._ +> _NOTE:_ This document is for stable Rust releases, not for point releases. For +> point releases, step 1. and 2. should be enough. [Rust Forge]: https://forge.rust-lang.org/ - ## Remerge the `beta` branch This step is only necessary, if since the last release something was backported @@ -29,7 +28,7 @@ tree of the Clippy repository. To find out if this step is necessary run ```bash -# Assumes that the local master branch is up-to-date +# Assumes that the local master branch of rust-lang/rust-clippy is up-to-date $ git fetch upstream $ git branch master --contains upstream/beta ``` @@ -45,9 +44,8 @@ $ git push origin backport_remerge # This can be pushed to your fork ``` After this, open a PR to the master branch. In this PR, the commit hash of the -`HEAD` of the `beta` branch must exists. In addition to that, no files should -be changed by this PR. - +`HEAD` of the `beta` branch must exists. In addition to that, no files should be +changed by this PR. ## Update the `beta` branch @@ -58,7 +56,8 @@ determined. ```bash # Assuming the current directory corresponds to the Rust repository -$ git checkout beta +$ git fetch upstream +$ git checkout upstream/beta $ BETA_SHA=$(git log --oneline -- src/tools/clippy/ | grep -o "Merge commit '[a-f0-9]*' into .*" | head -1 | sed -e "s/Merge commit '\([a-f0-9]*\)' into .*/\1/g") ``` @@ -72,7 +71,6 @@ $ git reset --hard $BETA_SHA $ git push upstream beta ``` - ## Find the Clippy commit The first step is to tag the Clippy commit, that is included in the stable Rust @@ -85,7 +83,6 @@ $ git checkout 1.XX.0 # XX should be exchanged with the corresponding version $ SHA=$(git log --oneline -- src/tools/clippy/ | grep -o "Merge commit '[a-f0-9]*' into .*" | head -1 | sed -e "s/Merge commit '\([a-f0-9]*\)' into .*/\1/g") ``` - ## Tag the stable commit After finding the Clippy commit, it can be tagged with the release number. @@ -112,10 +109,10 @@ tag. Updating the stable branch from here is as easy as: $ git push upstream rust-1.XX.0:stable # `upstream` is the `rust-lang/rust-clippy` remote ``` -_NOTE: Usually there are no stable backports for Clippy, so this update should -be possible without force pushing or anything like this. If there should have -happened a stable backport, make sure to re-merge those changes just as with the -`beta` branch._ +> _NOTE:_ Usually there are no stable backports for Clippy, so this update +> should be possible without force pushing or anything like this. If there +> should have happened a stable backport, make sure to re-merge those changes +> just as with the `beta` branch. ## Update `CHANGELOG.md` @@ -142,4 +139,4 @@ the following parts: Current stable, released 20YY-MM-DD -> Released 20YY-MM-DD ``` -[how to update the changelog]: https://github.com/rust-lang/rust-clippy/blob/master/doc/changelog_update.md +[how to update the changelog]: changelog_update.md diff --git a/book/src/development/infrastructure/sync.md b/book/src/development/infrastructure/sync.md new file mode 100644 index 0000000000000..5a0f7409a2e4c --- /dev/null +++ b/book/src/development/infrastructure/sync.md @@ -0,0 +1,123 @@ +# Syncing changes between Clippy and [`rust-lang/rust`] + +Clippy currently gets built with a pinned nightly version. + +In the `rust-lang/rust` repository, where rustc resides, there's a copy of +Clippy that compiler hackers modify from time to time to adapt to changes in the +unstable API of the compiler. + +We need to sync these changes back to this repository periodically, and the +changes made to this repository in the meantime also need to be synced to the +`rust-lang/rust` repository. + +To avoid flooding the `rust-lang/rust` PR queue, this two-way sync process is +done in a bi-weekly basis if there's no urgent changes. This is done starting on +the day of the Rust stable release and then every other week. That way we +guarantee that we keep this repo up to date with the latest compiler API, and +every feature in Clippy is available for 2 weeks in nightly, before it can get +to beta. For reference, the first sync following this cadence was performed the +2020-08-27. + +This process is described in detail in the following sections. For general +information about `subtree`s in the Rust repository see [Rust's +`CONTRIBUTING.md`][subtree]. + +## Patching git-subtree to work with big repos + +Currently, there's a bug in `git-subtree` that prevents it from working properly +with the [`rust-lang/rust`] repo. There's an open PR to fix that, but it's +stale. Before continuing with the following steps, we need to manually apply +that fix to our local copy of `git-subtree`. + +You can get the patched version of `git-subtree` from [here][gitgitgadget-pr]. +Put this file under `/usr/lib/git-core` (making a backup of the previous file) +and make sure it has the proper permissions: + +```bash +sudo cp --backup /path/to/patched/git-subtree.sh /usr/lib/git-core/git-subtree +sudo chmod --reference=/usr/lib/git-core/git-subtree~ /usr/lib/git-core/git-subtree +sudo chown --reference=/usr/lib/git-core/git-subtree~ /usr/lib/git-core/git-subtree +``` + +> _Note:_ The first time running `git subtree push` a cache has to be built. +> This involves going through the complete Clippy history once. For this you +> have to increase the stack limit though, which you can do with `ulimit -s +> 60000`. Make sure to run the `ulimit` command from the same session you call +> git subtree. + +> _Note:_ If you are a Debian user, `dash` is the shell used by default for +> scripts instead of `sh`. This shell has a hardcoded recursion limit set to +> 1000. In order to make this process work, you need to force the script to run +> `bash` instead. You can do this by editing the first line of the `git-subtree` +> script and changing `sh` to `bash`. + +## Defining remotes + +You may want to define remotes, so you don't have to type out the remote +addresses on every sync. You can do this with the following commands (these +commands still have to be run inside the `rust` directory): + +```bash +# Set clippy-upstream remote for pulls +$ git remote add clippy-upstream https://github.com/rust-lang/rust-clippy +# Make sure to not push to the upstream repo +$ git remote set-url --push clippy-upstream DISABLED +# Set a local remote +$ git remote add clippy-local /path/to/rust-clippy +``` + +> Note: The following sections assume that you have set those remotes with the +> above remote names. + +## Performing the sync from [`rust-lang/rust`] to Clippy + +Here is a TL;DR version of the sync process (all of the following commands have +to be run inside the `rust` directory): + +1. Clone the [`rust-lang/rust`] repository or make sure it is up to date. +2. Checkout the commit from the latest available nightly. You can get it using + `rustup check`. +3. Sync the changes to the rust-copy of Clippy to your Clippy fork: + ```bash + # Make sure to change `your-github-name` to your github name in the following command. Also be + # sure to either use a net-new branch, e.g. `sync-from-rust`, or delete the branch beforehand + # because changes cannot be fast forwarded and you have to run this command again. + git subtree push -P src/tools/clippy clippy-local sync-from-rust + ``` + + > _Note:_ Most of the time you have to create a merge commit in the + > `rust-clippy` repo (this has to be done in the Clippy repo, not in the + > rust-copy of Clippy): + ```bash + git fetch upstream # assuming upstream is the rust-lang/rust remote + git checkout sync-from-rust + git merge upstream/master --no-ff + ``` + > Note: This is one of the few instances where a merge commit is allowed in + > a PR. +4. Bump the nightly version in the Clippy repository by changing the date in the + rust-toolchain file to the current date and committing it with the message: + ```bash + git commit -m "Bump nightly version -> YYYY-MM-DD" + ``` +5. Open a PR to `rust-lang/rust-clippy` and wait for it to get merged (to + accelerate the process ping the `@rust-lang/clippy` team in your PR and/or + ask them in the [Zulip] stream.) + +[Zulip]: https://rust-lang.zulipchat.com/#narrow/stream/clippy + +## Performing the sync from Clippy to [`rust-lang/rust`] + +All of the following commands have to be run inside the `rust` directory. + +1. Make sure you have checked out the latest `master` of `rust-lang/rust`. +2. Sync the `rust-lang/rust-clippy` master to the rust-copy of Clippy: + ```bash + git checkout -b sync-from-clippy + git subtree pull -P src/tools/clippy clippy-upstream master + ``` +3. Open a PR to [`rust-lang/rust`] + +[gitgitgadget-pr]: https://github.com/gitgitgadget/git/pull/493 +[subtree]: https://rustc-dev-guide.rust-lang.org/contributing.html#external-dependencies-subtree +[`rust-lang/rust`]: https://github.com/rust-lang/rust diff --git a/book/src/development/proposals/README.md b/book/src/development/proposals/README.md new file mode 100644 index 0000000000000..78fe34ebf8faf --- /dev/null +++ b/book/src/development/proposals/README.md @@ -0,0 +1,11 @@ +# Proposals + +This chapter is about accepted proposals for changes that should be worked on in +or around Clippy in the long run. + +Besides adding more and more lints and improve the lints that Clippy already +has, Clippy is also interested in making the experience of its users, developers +and maintainers better over time. Projects that address bigger picture things +like this usually take more time and it is useful to have a proposal for those +first. This is the place where such proposals are collected, so that we can +refer to them when working on them. diff --git a/doc/roadmap-2021.md b/book/src/development/proposals/roadmap-2021.md similarity index 100% rename from doc/roadmap-2021.md rename to book/src/development/proposals/roadmap-2021.md diff --git a/book/src/installation.md b/book/src/installation.md new file mode 100644 index 0000000000000..b2a28d0be622f --- /dev/null +++ b/book/src/installation.md @@ -0,0 +1,24 @@ +# Installation + +If you're using `rustup` to install and manage you're Rust toolchains, Clippy is +usually **already installed**. In that case you can skip this chapter and go to +the [Usage] chapter. + +> Note: If you used the `minimal` profile when installing a Rust toolchain, +> Clippy is not automatically installed. + +## Using Rustup + +If Clippy was not installed for a toolchain, it can be installed with + +``` +$ rustup component add clippy [--toolchain=] +``` + +## From Source + +Take a look at the [Basics] chapter in the Clippy developer guide to find step +by step instructions on how to build and install Clippy from source. + +[Basics]: development/basics.md#install-from-source +[Usage]: usage.md diff --git a/book/src/lints.md b/book/src/lints.md new file mode 100644 index 0000000000000..35e30960b56c6 --- /dev/null +++ b/book/src/lints.md @@ -0,0 +1,105 @@ +# Clippy's Lints + +Clippy offers a bunch of additional lints, to help its users write more correct +and idiomatic Rust code. A full list of all lints, that can be filtered by +category, lint level or keywords, can be found in the [Clippy lint +documentation]. + +This chapter will give an overview of the different lint categories, which kind +of lints they offer and recommended actions when you should see a lint out of +that category. For examples, see the [Clippy lint documentation] and filter by +category. + +The different lint groups were defined in the [Clippy 1.0 RFC]. + +## Correctness + +The `clippy::correctness` group is the only lint group in Clippy which lints are +deny-by-default and abort the compilation when triggered. This is for good +reason: If you see a `correctness` lint, it means that your code is outright +wrong or useless and you should try to fix it. + +Lints in this category are carefully picked and should be free of false +positives. So just `#[allow]`ing those lints is not recommended. + +## Suspicious + +The `clippy::suspicious` group is similar to the correctness lints in that it +contains lints that trigger on code that is really _sus_ and should be fixed. As +opposed to correctness lints, it might be possible that the linted code is +intentionally written like it is. + +It is still recommended to fix code that is linted by lints out of this group +instead of `#[allow]`ing the lint. In case you intentionally have written code +that offends the lint you should specifically and locally `#[allow]` the lint +and add give a reason why the code is correct as written. + +## Complexity + +The `clippy::complexity` group offers lints that give you suggestions on how to +simplify your code. It mostly focuses on code that can be written in a shorter +and more readable way, while preserving the semantics. + +If you should see a complexity lint, it usually means that you can remove or +replace some code and it is recommended to do so. However, if you need the more +complex code for some expressiveness reason, it is recommended to allow +complexity lints on a case-by-case basis. + +## Perf + +The `clippy::perf` group gives you suggestions on how you can increase the +performance of your code. Those lints are mostly about code that the compiler +can't trivially optimize, but has to be written in a slightly different way to +make the optimizer's job easier. + +Perf lints are usually easy to apply and it is recommended to do so. + +## Style + +The `clippy::style` group is mostly about writing idiomatic code. Because style +is subjective, this lint group is the most opinionated warn-by-default group in +Clippy. + +If you see a style lint, applying the suggestion usually makes your code more +readable and idiomatic. But because we know that this is opinionated, feel free +to sprinkle `#[allow]`s for style lints in your code or `#![allow]` a style lint +on your whole crate if you disagree with the suggested style completely. + +## Pedantic + +The `clippy::pedantic` group makes Clippy even more _pedantic_. You can enable +the whole group with `#![warn(clippy::pedantic)]` in the `lib.rs`/`main.rs` of +your crate. This lint group is for Clippy power users that want an in depth +check of their code. + +> _Note:_ Instead of enabling the whole group (like Clippy itself does), you may +> want to cherry-pick lints out of the pedantic group. + +If you enable this group, expect to also use `#[allow]` attributes generously +throughout your code. Lints in this group are designed to be pedantic and false +positives sometimes are intentional in order to prevent false negatives. + +## Restriction + +The `clippy::restriction` group contains lints that will _restrict_ you from +using certain parts of the Rust language. It is **not** recommended to enable +the whole group, but rather cherry-pick lints that are useful for your code base +and your use case. + +> _Note:_ Clippy will produce a warning if it finds a +> `#![warn(clippy::restriction)]` attribute in your code! + +Lints from this group will restrict you in some way. If you enable a restriction +lint for your crate it is recommended to also fix code that this lint triggers +on. However, those lints are really strict by design and you might want to +`#[allow]` them in some special cases, with a comment justifying that. + +## Cargo + +The `clippy::cargo` group gives you suggestions on how to improve your +`Cargo.toml` file. This might be especially interesting if you want to publish +your crate and are not sure if you have all useful information in your +`Cargo.toml`. + +[Clippy lint documentation]: https://rust-lang.github.io/rust-clippy/ +[Clippy 1.0 RFC]: https://github.com/rust-lang/rfcs/blob/master/text/2476-clippy-uno.md#lint-audit-and-categories diff --git a/book/src/usage.md b/book/src/usage.md new file mode 100644 index 0000000000000..337680aa3139b --- /dev/null +++ b/book/src/usage.md @@ -0,0 +1,151 @@ +# Usage + +This chapter describes how to use Clippy to get the most out of it. Clippy can +be used as a `cargo` subcommand or, like `rustc`, directly with the +`clippy-driver` binary. + +> _Note:_ This chapter assumes that you have Clippy installed already. If you're +> not sure, take a look at the [Installation] chapter. + +## Cargo subcommand + +The easiest and most common way to run Clippy is through `cargo`. To do that, +just run + +```bash +cargo clippy +``` + +### Lint configuration + +The above command will run the default set of lints, which are included in the +lint group `clippy::all`. You might want to use even more lints or you might not +agree with every Clippy lint, and for that there are ways to configure lint +levels. + +> _Note:_ Clippy is meant to be used with a generous sprinkling of +> `#[allow(..)]`s through your code. So if you disagree with a lint, don't feel +> bad disabling them for parts of your code or the whole project. + +#### Command line + +You can configure lint levels on the command line by adding +`-A/W/D clippy::lint_name` like this: + +```bash +cargo clippy -- -Aclippy::style -Wclippy::double_neg -Dclippy::perf +``` + +For [CI] all warnings can be elevated to errors which will inturn fail +the build and cause Clippy to exit with a code other than `0`. + +``` +cargo clippy -- -Dwarnings +``` + +> _Note:_ Adding `-D warnings` will cause your build to fail if **any** warnings +> are found in your code. That includes warnings found by rustc (e.g. +> `dead_code`, etc.). + +For more information on configuring lint levels, see the [rustc documentation]. + +[rustc documentation]: https://doc.rust-lang.org/rustc/lints/levels.html#configuring-warning-levels + +#### Even more lints + +Clippy has lint groups which are allow-by-default. This means, that you will +have to enable the lints in those groups manually. + +For a full list of all lints with their description and examples, please refere +to [Clippy's lint list]. The two most important allow-by-default groups are +described below: + +[Clippy's lint list]: https://rust-lang.github.io/rust-clippy/master/index.html + +##### `clippy::pedantic` + +The first group is the `pedantic` group. This group contains really opinionated +lints, that may have some intentional false positives in order to prevent false +negatives. So while this group is ready to be used in production, you can expect +to sprinkle multiple `#[allow(..)]`s in your code. If you find any false +positives, you're still welcome to report them to us for future improvements. + +> FYI: Clippy uses the whole group to lint itself. + +##### `clippy::restriction` + +The second group is the `restriction` group. This group contains lints that +"restrict" the language in some way. For example the `clippy::unwrap` lint from +this group won't allow you to use `.unwrap()` in your code. You may want to look +through the lints in this group and enable the ones that fit your need. + +> _Note:_ You shouldn't enable the whole lint group, but cherry-pick lints from +> this group. Some lints in this group will even contradict other Clippy lints! + +#### Too many lints + +The most opinionated warn-by-default group of Clippy is the `clippy::style` +group. Some people prefer to disable this group completely and then cherry-pick +some lints they like from this group. The same is of course possible with every +other of Clippy's lint groups. + +> _Note:_ We try to keep the warn-by-default groups free from false positives +> (FP). If you find that a lint wrongly triggers, please report it in an issue +> (if there isn't an issue for that FP already) + +#### Source Code + +You can configure lint levels in source code the same way you can configure +`rustc` lints: + +```rust +#![allow(clippy::style)] + +#[warn(clippy::double_neg)] +fn main() { + let x = 1; + let y = --x; + // ^^ warning: double negation +} +``` + +### Automatically applying Clippy suggestions + +Clippy can automatically apply some lint suggestions, just like the compiler. + +```terminal +cargo clippy --fix +``` + +### Workspaces + +All the usual workspace options should work with Clippy. For example the +following command will run Clippy on the `example` crate in your workspace: + +```terminal +cargo clippy -p example +``` + +As with `cargo check`, this includes dependencies that are members of the +workspace, like path dependencies. If you want to run Clippy **only** on the +given crate, use the `--no-deps` option like this: + +```terminal +cargo clippy -p example -- --no-deps +``` + +## Using Clippy without `cargo`: `clippy-driver` + +Clippy can also be used in projects that do not use cargo. To do so, run +`clippy-driver` with the same arguments you use for `rustc`. For example: + +```terminal +clippy-driver --edition 2018 -Cpanic=abort foo.rs +``` + +> _Note:_ `clippy-driver` is designed for running Clippy and should not be used +> as a general replacement for `rustc`. `clippy-driver` may produce artifacts +> that are not optimized as expected, for example. + +[Installation]: installation.md +[CI]: continuous_integration diff --git a/clippy_dev/src/lint.rs b/clippy_dev/src/lint.rs index 9e463aa741c75..71005449b4ddf 100644 --- a/clippy_dev/src/lint.rs +++ b/clippy_dev/src/lint.rs @@ -13,7 +13,7 @@ fn exit_if_err(status: io::Result) { } } -pub fn run<'a>(path: &str, args: impl Iterator) { +pub fn run<'a>(path: &str, args: impl Iterator) { let is_file = match fs::metadata(path) { Ok(metadata) => metadata.is_file(), Err(e) => { diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index ee535b1d3be8b..2c27a0bcaf95e 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -2,7 +2,7 @@ // warn on lints, that are included in `rust-lang/rust`s bootstrap #![warn(rust_2018_idioms, unused_lifetimes)] -use clap::{Arg, ArgMatches, Command}; +use clap::{Arg, ArgAction, ArgMatches, Command, PossibleValue}; use clippy_dev::{bless, fmt, lint, new_lint, serve, setup, update_lints}; use indoc::indoc; fn main() { @@ -10,15 +10,15 @@ fn main() { match matches.subcommand() { Some(("bless", matches)) => { - bless::bless(matches.is_present("ignore-timestamp")); + bless::bless(matches.contains_id("ignore-timestamp")); }, Some(("fmt", matches)) => { - fmt::run(matches.is_present("check"), matches.is_present("verbose")); + fmt::run(matches.contains_id("check"), matches.contains_id("verbose")); }, Some(("update_lints", matches)) => { - if matches.is_present("print-only") { + if matches.contains_id("print-only") { update_lints::print_lints(); - } else if matches.is_present("check") { + } else if matches.contains_id("check") { update_lints::update(update_lints::UpdateMode::Check); } else { update_lints::update(update_lints::UpdateMode::Change); @@ -26,10 +26,10 @@ fn main() { }, Some(("new_lint", matches)) => { match new_lint::create( - matches.value_of("pass"), - matches.value_of("name"), - matches.value_of("category"), - matches.is_present("msrv"), + matches.get_one::("pass"), + matches.get_one::("name"), + matches.get_one::("category"), + matches.contains_id("msrv"), ) { Ok(_) => update_lints::update(update_lints::UpdateMode::Change), Err(e) => eprintln!("Unable to create lint: {}", e), @@ -37,28 +37,28 @@ fn main() { }, Some(("setup", sub_command)) => match sub_command.subcommand() { Some(("intellij", matches)) => { - if matches.is_present("remove") { + if matches.contains_id("remove") { setup::intellij::remove_rustc_src(); } else { setup::intellij::setup_rustc_src( matches - .value_of("rustc-repo-path") + .get_one::("rustc-repo-path") .expect("this field is mandatory and therefore always valid"), ); } }, Some(("git-hook", matches)) => { - if matches.is_present("remove") { + if matches.contains_id("remove") { setup::git_hook::remove_hook(); } else { - setup::git_hook::install_hook(matches.is_present("force-override")); + setup::git_hook::install_hook(matches.contains_id("force-override")); } }, Some(("vscode-tasks", matches)) => { - if matches.is_present("remove") { + if matches.contains_id("remove") { setup::vscode::remove_tasks(); } else { - setup::vscode::install_tasks(matches.is_present("force-override")); + setup::vscode::install_tasks(matches.contains_id("force-override")); } }, _ => {}, @@ -70,19 +70,19 @@ fn main() { _ => {}, }, Some(("serve", matches)) => { - let port = matches.value_of("port").unwrap().parse().unwrap(); - let lint = matches.value_of("lint"); + let port = *matches.get_one::("port").unwrap(); + let lint = matches.get_one::("lint"); serve::run(port, lint); }, Some(("lint", matches)) => { - let path = matches.value_of("path").unwrap(); - let args = matches.values_of("args").into_iter().flatten(); + let path = matches.get_one::("path").unwrap(); + let args = matches.get_many::("args").into_iter().flatten(); lint::run(path, args); }, Some(("rename_lint", matches)) => { - let old_name = matches.value_of("old_name").unwrap(); - let new_name = matches.value_of("new_name").unwrap_or(old_name); - let uplift = matches.is_present("uplift"); + let old_name = matches.get_one::("old_name").unwrap(); + let new_name = matches.get_one::("new_name").unwrap_or(old_name); + let uplift = matches.contains_id("uplift"); update_lints::rename(old_name, new_name, uplift); }, _ => {}, @@ -92,98 +92,86 @@ fn main() { fn get_clap_config() -> ArgMatches { Command::new("Clippy developer tooling") .arg_required_else_help(true) - .subcommand( + .subcommands([ Command::new("bless").about("bless the test output changes").arg( Arg::new("ignore-timestamp") .long("ignore-timestamp") .help("Include files updated before clippy was built"), ), - ) - .subcommand( Command::new("fmt") .about("Run rustfmt on all projects and tests") - .arg(Arg::new("check").long("check").help("Use the rustfmt --check option")) - .arg(Arg::new("verbose").short('v').long("verbose").help("Echo commands run")), - ) - .subcommand( + .args([ + Arg::new("check").long("check").help("Use the rustfmt --check option"), + Arg::new("verbose").short('v').long("verbose").help("Echo commands run"), + ]), Command::new("update_lints") .about("Updates lint registration and information from the source code") .long_about( "Makes sure that:\n \ - * the lint count in README.md is correct\n \ - * the changelog contains markdown link references at the bottom\n \ - * all lint groups include the correct lints\n \ - * lint modules in `clippy_lints/*` are visible in `src/lib.rs` via `pub mod`\n \ - * all lints are registered in the lint store", + * the lint count in README.md is correct\n \ + * the changelog contains markdown link references at the bottom\n \ + * all lint groups include the correct lints\n \ + * lint modules in `clippy_lints/*` are visible in `src/lib.rs` via `pub mod`\n \ + * all lints are registered in the lint store", ) - .arg(Arg::new("print-only").long("print-only").help( - "Print a table of lints to STDOUT. \ - This does not include deprecated and internal lints. \ - (Does not modify any files)", - )) - .arg( + .args([ + Arg::new("print-only").long("print-only").help( + "Print a table of lints to STDOUT. \ + This does not include deprecated and internal lints. \ + (Does not modify any files)", + ), Arg::new("check") .long("check") .help("Checks that `cargo dev update_lints` has been run. Used on CI."), - ), - ) - .subcommand( + ]), Command::new("new_lint") .about("Create new lint and run `cargo dev update_lints`") - .arg( + .args([ Arg::new("pass") .short('p') .long("pass") .help("Specify whether the lint runs during the early or late pass") .takes_value(true) - .possible_values(&["early", "late"]) + .value_parser([PossibleValue::new("early"), PossibleValue::new("late")]) .required(true), - ) - .arg( Arg::new("name") .short('n') .long("name") .help("Name of the new lint in snake case, ex: fn_too_long") .takes_value(true) .required(true), - ) - .arg( Arg::new("category") .short('c') .long("category") .help("What category the lint belongs to") .default_value("nursery") - .possible_values(&[ - "style", - "correctness", - "suspicious", - "complexity", - "perf", - "pedantic", - "restriction", - "cargo", - "nursery", - "internal", - "internal_warn", + .value_parser([ + PossibleValue::new("style"), + PossibleValue::new("correctness"), + PossibleValue::new("suspicious"), + PossibleValue::new("complexity"), + PossibleValue::new("perf"), + PossibleValue::new("pedantic"), + PossibleValue::new("restriction"), + PossibleValue::new("cargo"), + PossibleValue::new("nursery"), + PossibleValue::new("internal"), + PossibleValue::new("internal_warn"), ]) .takes_value(true), - ) - .arg(Arg::new("msrv").long("msrv").help("Add MSRV config code to the lint")), - ) - .subcommand( + Arg::new("msrv").long("msrv").help("Add MSRV config code to the lint"), + ]), Command::new("setup") .about("Support for setting up your personal development environment") .arg_required_else_help(true) - .subcommand( + .subcommands([ Command::new("intellij") .about("Alter dependencies so Intellij Rust can find rustc internals") - .arg( + .args([ Arg::new("remove") .long("remove") .help("Remove the dependencies added with 'cargo dev setup intellij'") .required(false), - ) - .arg( Arg::new("rustc-repo-path") .long("repo-path") .short('r') @@ -192,67 +180,53 @@ fn get_clap_config() -> ArgMatches { .value_name("path") .conflicts_with("remove") .required(true), - ), - ) - .subcommand( + ]), Command::new("git-hook") .about("Add a pre-commit git hook that formats your code to make it look pretty") - .arg( + .args([ Arg::new("remove") .long("remove") .help("Remove the pre-commit hook added with 'cargo dev setup git-hook'") .required(false), - ) - .arg( Arg::new("force-override") .long("force-override") .short('f') .help("Forces the override of an existing git pre-commit hook") .required(false), - ), - ) - .subcommand( + ]), Command::new("vscode-tasks") .about("Add several tasks to vscode for formatting, validation and testing") - .arg( + .args([ Arg::new("remove") .long("remove") .help("Remove the tasks added with 'cargo dev setup vscode-tasks'") .required(false), - ) - .arg( Arg::new("force-override") .long("force-override") .short('f') .help("Forces the override of existing vscode tasks") .required(false), - ), - ), - ) - .subcommand( + ]), + ]), Command::new("remove") .about("Support for undoing changes done by the setup command") .arg_required_else_help(true) - .subcommand(Command::new("git-hook").about("Remove any existing pre-commit git hook")) - .subcommand(Command::new("vscode-tasks").about("Remove any existing vscode tasks")) - .subcommand( + .subcommands([ + Command::new("git-hook").about("Remove any existing pre-commit git hook"), + Command::new("vscode-tasks").about("Remove any existing vscode tasks"), Command::new("intellij").about("Removes rustc source paths added via `cargo dev setup intellij`"), - ), - ) - .subcommand( + ]), Command::new("serve") .about("Launch a local 'ALL the Clippy Lints' website in a browser") - .arg( + .args([ Arg::new("port") .long("port") .short('p') .help("Local port for the http server") .default_value("8000") - .validator_os(serve::validate_port), - ) - .arg(Arg::new("lint").help("Which lint's page to load initially (optional)")), - ) - .subcommand( + .value_parser(clap::value_parser!(u16)), + Arg::new("lint").help("Which lint's page to load initially (optional)"), + ]), Command::new("lint") .about("Manually run clippy on a file or package") .after_help(indoc! {" @@ -271,37 +245,27 @@ fn get_clap_config() -> ArgMatches { cargo dev lint file.rs -- -W clippy::pedantic cargo dev lint ~/my-project -- -- -W clippy::pedantic "}) - .arg( + .args([ Arg::new("path") .required(true) .help("The path to a file or package directory to lint"), - ) - .arg( Arg::new("args") - .multiple_occurrences(true) + .action(ArgAction::Append) .help("Pass extra arguments to cargo/clippy-driver"), - ), - ) - .subcommand( - Command::new("rename_lint") - .about("Renames the given lint") - .arg( - Arg::new("old_name") - .index(1) - .required(true) - .help("The name of the lint to rename"), - ) - .arg( - Arg::new("new_name") - .index(2) - .required_unless_present("uplift") - .help("The new name of the lint"), - ) - .arg( - Arg::new("uplift") - .long("uplift") - .help("This lint will be uplifted into rustc"), - ), - ) + ]), + Command::new("rename_lint").about("Renames the given lint").args([ + Arg::new("old_name") + .index(1) + .required(true) + .help("The name of the lint to rename"), + Arg::new("new_name") + .index(2) + .required_unless_present("uplift") + .help("The new name of the lint"), + Arg::new("uplift") + .long("uplift") + .help("This lint will be uplifted into rustc"), + ]), + ]) .get_matches() } diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index 07d196387887c..748d73c080126 100644 --- a/clippy_dev/src/new_lint.rs +++ b/clippy_dev/src/new_lint.rs @@ -34,7 +34,12 @@ impl Context for io::Result { /// # Errors /// /// This function errors out if the files couldn't be created or written to. -pub fn create(pass: Option<&str>, lint_name: Option<&str>, category: Option<&str>, msrv: bool) -> io::Result<()> { +pub fn create( + pass: Option<&String>, + lint_name: Option<&String>, + category: Option<&String>, + msrv: bool, +) -> io::Result<()> { let lint = LintData { pass: pass.expect("`pass` argument is validated by clap"), name: lint_name.expect("`name` argument is validated by clap"), diff --git a/clippy_dev/src/serve.rs b/clippy_dev/src/serve.rs index d55b1a354d003..f15f24da94671 100644 --- a/clippy_dev/src/serve.rs +++ b/clippy_dev/src/serve.rs @@ -8,7 +8,7 @@ use std::time::{Duration, SystemTime}; /// # Panics /// /// Panics if the python commands could not be spawned -pub fn run(port: u16, lint: Option<&str>) -> ! { +pub fn run(port: u16, lint: Option<&String>) -> ! { let mut url = Some(match lint { None => format!("http://localhost:{}", port), Some(lint) => format!("http://localhost:{}/#{}", port, lint), diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index 5024e63bfa738..1bbd9a45b619e 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -58,6 +58,16 @@ fn generate_lint_files( }, ); + replace_region_in_file( + update_mode, + Path::new("book/src/README.md"), + "[There are over ", + " lints included in this crate!]", + |res| { + write!(res, "{}", round_to_fifty(usable_lints.len())).unwrap(); + }, + ); + replace_region_in_file( update_mode, Path::new("CHANGELOG.md"), diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 0a3f04da35705..4d5bf47833fb8 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -10,6 +10,7 @@ edition = "2021" [dependencies] cargo_metadata = "0.14" +clippy_dev = { path = "../clippy_dev", optional = true } clippy_utils = { path = "../clippy_utils" } if_chain = "1.0" itertools = "0.10.1" @@ -18,6 +19,7 @@ quine-mc_cluskey = "0.2" regex-syntax = "0.6" serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0", optional = true } +tempfile = { version = "3.2", optional = true } toml = "0.5" unicode-normalization = "0.1" unicode-script = { version = "0.5", default-features = false } @@ -30,7 +32,7 @@ url = { version = "2.2", features = ["serde"] } [features] deny-warnings = ["clippy_utils/deny-warnings"] # build clippy with internal lints enabled, off by default -internal = ["clippy_utils/internal", "serde_json"] +internal = ["clippy_utils/internal", "serde_json", "tempfile", "clippy_dev"] [package.metadata.rust-analyzer] # This crate uses #[feature(rustc_private)] diff --git a/clippy_lints/src/assertions_on_constants.rs b/clippy_lints/src/assertions_on_constants.rs index c82837746bd5d..2705ffffdcbff 100644 --- a/clippy_lints/src/assertions_on_constants.rs +++ b/clippy_lints/src/assertions_on_constants.rs @@ -14,9 +14,6 @@ declare_clippy_lint! { /// Will be optimized out by the compiler or should probably be replaced by a /// `panic!()` or `unreachable!()` /// - /// ### Known problems - /// None - /// /// ### Example /// ```rust,ignore /// assert!(false) diff --git a/clippy_lints/src/async_yields_async.rs b/clippy_lints/src/async_yields_async.rs index 0619490e73c43..27c2896e1e5ce 100644 --- a/clippy_lints/src/async_yields_async.rs +++ b/clippy_lints/src/async_yields_async.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::source::snippet; use clippy_utils::ty::implements_trait; use rustc_errors::Applicability; @@ -24,6 +24,7 @@ declare_clippy_lint! { /// }; /// } /// ``` + /// /// Use instead: /// ```rust /// async fn foo() {} @@ -63,9 +64,10 @@ impl<'tcx> LateLintPass<'tcx> for AsyncYieldsAsync { _ => None, }; if let Some(return_expr_span) = return_expr_span { - span_lint_and_then( + span_lint_hir_and_then( cx, ASYNC_YIELDS_ASYNC, + body.value.hir_id, return_expr_span, "an async construct yields a type which is itself awaitable", |db| { diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 770cb6a3d7b8b..ed12ad9c36790 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -340,7 +340,7 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { for lint in lint_list { match item.kind { ItemKind::Use(..) => { - if is_word(lint, sym!(unused_imports)) + if is_word(lint, sym::unused_imports) || is_word(lint, sym::deprecated) || is_word(lint, sym!(unreachable_pub)) || is_word(lint, sym!(unused)) @@ -355,7 +355,7 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { } }, ItemKind::ExternCrate(..) => { - if is_word(lint, sym!(unused_imports)) && skip_unused_imports { + if is_word(lint, sym::unused_imports) && skip_unused_imports { return; } if is_word(lint, sym!(unused_extern_crates)) { diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index 5b7c4591504e1..eee5f90d17885 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -140,8 +140,6 @@ declare_clippy_lint! { /// from a memory access perspective but will cause bugs at runtime if they /// are held in such a way. /// - /// ### Known problems - /// /// ### Example /// /// ```toml diff --git a/clippy_lints/src/bool_assert_comparison.rs b/clippy_lints/src/bool_assert_comparison.rs index c50e214be288d..95abe8aa59fbe 100644 --- a/clippy_lints/src/bool_assert_comparison.rs +++ b/clippy_lints/src/bool_assert_comparison.rs @@ -17,11 +17,12 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad /// assert_eq!("a".is_empty(), false); /// assert_ne!("a".is_empty(), true); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust /// assert!(!"a".is_empty()); /// ``` #[clippy::version = "1.53.0"] diff --git a/clippy_lints/src/borrow_deref_ref.rs b/clippy_lints/src/borrow_deref_ref.rs index ec2f31cf67374..1582ec9ee5ce6 100644 --- a/clippy_lints/src/borrow_deref_ref.rs +++ b/clippy_lints/src/borrow_deref_ref.rs @@ -18,7 +18,7 @@ declare_clippy_lint! { /// Dereferencing and then borrowing a reference value has no effect in most cases. /// /// ### Known problems - /// false negative on such code: + /// False negative on such code: /// ``` /// let x = &12; /// let addr_x = &x as *const _ as usize; @@ -29,17 +29,20 @@ declare_clippy_lint! { /// /// ### Example /// ```rust + /// fn foo(_x: &str) {} + /// /// let s = &String::new(); /// - /// // Bad /// let a: &String = &* s; /// foo(&*s); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # fn foo(_x: &str) {} + /// # let s = &String::new(); /// let a: &String = s; /// foo(&**s); - /// - /// fn foo(_: &str){ } /// ``` #[clippy::version = "1.59.0"] pub BORROW_DEREF_REF, diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index daf3b7b4ce4fe..02c2f30a4dd6a 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -219,13 +219,14 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad /// fn fun() -> i32 { 1 } - /// let a = fun as i64; + /// let _ = fun as i64; + /// ``` /// - /// // Good - /// fn fun2() -> i32 { 1 } - /// let a = fun2 as usize; + /// Use instead: + /// ```rust + /// # fn fun() -> i32 { 1 } + /// let _ = fun as usize; /// ``` #[clippy::version = "pre 1.29.0"] pub FN_TO_NUMERIC_CAST, @@ -245,17 +246,19 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad /// fn fn1() -> i16 { /// 1 /// }; /// let _ = fn1 as i32; + /// ``` /// - /// // Better: Cast to usize first, then comment with the reason for the truncation - /// fn fn2() -> i16 { + /// Use instead: + /// ```rust + /// // Cast to usize first, then comment with the reason for the truncation + /// fn fn1() -> i16 { /// 1 /// }; - /// let fn_ptr = fn2 as usize; + /// let fn_ptr = fn1 as usize; /// let fn_ptr_truncated = fn_ptr as i32; /// ``` #[clippy::version = "pre 1.29.0"] @@ -277,19 +280,24 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad: fn1 is cast as `usize` + /// // fn1 is cast as `usize` /// fn fn1() -> u16 { /// 1 /// }; /// let _ = fn1 as usize; + /// ``` /// - /// // Good: maybe you intended to call the function? + /// Use instead: + /// ```rust + /// // maybe you intended to call the function? /// fn fn2() -> u16 { /// 1 /// }; /// let _ = fn2() as usize; /// - /// // Good: maybe you intended to cast it to a function type? + /// // or + /// + /// // maybe you intended to cast it to a function type? /// fn fn3() -> u16 { /// 1 /// } @@ -406,7 +414,7 @@ declare_clippy_lint! { /// enum E { X = 256 }; /// let _ = E::X as u8; /// ``` - #[clippy::version = "1.60.0"] + #[clippy::version = "1.61.0"] pub CAST_ENUM_TRUNCATION, suspicious, "casts from an enum type to an integral type which will truncate the value" @@ -451,7 +459,7 @@ declare_clippy_lint! { /// println!("{:?}", &*new_ptr); /// } /// ``` - #[clippy::version = "1.60.0"] + #[clippy::version = "1.61.0"] pub CAST_SLICE_DIFFERENT_SIZES, correctness, "casting using `as` between raw pointers to slices of types with different sizes" diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs index 1010340c71213..17fc81951f95b 100644 --- a/clippy_lints/src/checked_conversions.rs +++ b/clippy_lints/src/checked_conversions.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{meets_msrv, msrvs, SpanlessEq}; +use clippy_utils::{in_constant, meets_msrv, msrvs, SpanlessEq}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -22,18 +22,14 @@ declare_clippy_lint! { /// ### Example /// ```rust /// # let foo: u32 = 5; - /// # let _ = - /// foo <= i32::MAX as u32 - /// # ; + /// foo <= i32::MAX as u32; /// ``` /// - /// Could be written: - /// + /// Use instead: /// ```rust /// # let foo = 1; - /// # let _ = - /// i32::try_from(foo).is_ok() - /// # ; + /// # #[allow(unused)] + /// i32::try_from(foo).is_ok(); /// ``` #[clippy::version = "1.37.0"] pub CHECKED_CONVERSIONS, @@ -61,6 +57,7 @@ impl<'tcx> LateLintPass<'tcx> for CheckedConversions { } let result = if_chain! { + if !in_constant(cx, item.hir_id); if !in_external_macro(cx.sess(), item.span); if let ExprKind::Binary(op, left, right) = &item.kind; diff --git a/clippy_lints/src/collapsible_if.rs b/clippy_lints/src/collapsible_if.rs index 3eceb848822e9..90430b71a0ef9 100644 --- a/clippy_lints/src/collapsible_if.rs +++ b/clippy_lints/src/collapsible_if.rs @@ -32,20 +32,20 @@ declare_clippy_lint! { /// makes code look more complex than it really is. /// /// ### Example - /// ```rust,ignore + /// ```rust + /// # let (x, y) = (true, true); /// if x { /// if y { - /// … + /// // … /// } /// } - /// /// ``` /// /// Use instead: - /// - /// ```rust,ignore + /// ```rust + /// # let (x, y) = (true, true); /// if x && y { - /// … + /// // … /// } /// ``` #[clippy::version = "pre 1.29.0"] diff --git a/clippy_lints/src/comparison_chain.rs b/clippy_lints/src/comparison_chain.rs index 913e081af3bda..a05b41eb3ab52 100644 --- a/clippy_lints/src/comparison_chain.rs +++ b/clippy_lints/src/comparison_chain.rs @@ -35,7 +35,6 @@ declare_clippy_lint! { /// ``` /// /// Use instead: - /// /// ```rust,ignore /// use std::cmp::Ordering; /// # fn a() {} diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index 1e9a115301100..1deff9684a140 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -1,18 +1,18 @@ 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, snippet_opt}; use clippy_utils::{ - both, count_eq, eq_expr_value, get_enclosing_block, get_parent_expr, if_sequence, is_else_clause, is_lint_allowed, - search_same, ContainsName, SpanlessEq, SpanlessHash, + eq_expr_value, get_enclosing_block, hash_expr, hash_stmt, if_sequence, is_else_clause, is_lint_allowed, + search_same, ContainsName, HirEqInterExpr, SpanlessEq, }; -use if_chain::if_chain; -use rustc_data_structures::fx::FxHashSet; -use rustc_errors::{Applicability, Diagnostic}; -use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{Block, Expr, ExprKind, HirId}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::hir::nested_filter; +use core::iter; +use rustc_errors::Applicability; +use rustc_hir::intravisit; +use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, Stmt, StmtKind}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::{source_map::Span, symbol::Symbol, BytePos}; +use rustc_span::hygiene::walk_chain; +use rustc_span::source_map::SourceMap; +use rustc_span::{BytePos, Span, Symbol}; use std::borrow::Cow; declare_clippy_lint! { @@ -165,243 +165,315 @@ declare_lint_pass!(CopyAndPaste => [ impl<'tcx> LateLintPass<'tcx> for CopyAndPaste { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if !expr.span.from_expansion() { - if let ExprKind::If(_, _, _) = expr.kind { - // skip ifs directly in else, it will be checked in the parent if - if let Some(&Expr { - kind: ExprKind::If(_, _, Some(else_expr)), - .. - }) = get_parent_expr(cx, expr) - { - if else_expr.hir_id == expr.hir_id { - return; - } - } - - let (conds, blocks) = if_sequence(expr); - // Conditions - lint_same_cond(cx, &conds); - lint_same_fns_in_if_cond(cx, &conds); - // Block duplication - lint_same_then_else(cx, &conds, &blocks, conds.len() == blocks.len(), expr); + if !expr.span.from_expansion() && matches!(expr.kind, ExprKind::If(..)) && !is_else_clause(cx.tcx, expr) { + let (conds, blocks) = if_sequence(expr); + lint_same_cond(cx, &conds); + lint_same_fns_in_if_cond(cx, &conds); + let all_same = + !is_lint_allowed(cx, IF_SAME_THEN_ELSE, expr.hir_id) && lint_if_same_then_else(cx, &conds, &blocks); + if !all_same && conds.len() != blocks.len() { + lint_branches_sharing_code(cx, &conds, &blocks, expr); } } } } -/// Implementation of `BRANCHES_SHARING_CODE` and `IF_SAME_THEN_ELSE` if the blocks are equal. -fn lint_same_then_else<'tcx>( +/// Checks if the given expression is a let chain. +fn contains_let(e: &Expr<'_>) -> bool { + match e.kind { + ExprKind::Let(..) => true, + ExprKind::Binary(op, lhs, rhs) if op.node == BinOpKind::And => { + matches!(lhs.kind, ExprKind::Let(..)) || contains_let(rhs) + }, + _ => false, + } +} + +fn lint_if_same_then_else(cx: &LateContext<'_>, conds: &[&Expr<'_>], blocks: &[&Block<'_>]) -> bool { + let mut eq = SpanlessEq::new(cx); + blocks + .array_windows::<2>() + .enumerate() + .fold(true, |all_eq, (i, &[lhs, rhs])| { + if eq.eq_block(lhs, rhs) && !contains_let(conds[i]) && conds.get(i + 1).map_or(true, |e| !contains_let(e)) { + span_lint_and_note( + cx, + IF_SAME_THEN_ELSE, + lhs.span, + "this `if` has identical blocks", + Some(rhs.span), + "same as this", + ); + all_eq + } else { + false + } + }) +} + +fn lint_branches_sharing_code<'tcx>( cx: &LateContext<'tcx>, conds: &[&'tcx Expr<'_>], blocks: &[&Block<'tcx>], - has_conditional_else: bool, expr: &'tcx Expr<'_>, ) { // We only lint ifs with multiple blocks - if blocks.len() < 2 || is_else_clause(cx.tcx, expr) { - return; - } - - // Check if each block has shared code - let has_expr = blocks[0].expr.is_some(); - - let (start_eq, mut end_eq, expr_eq) = if let Some(block_eq) = scan_block_for_eq(cx, conds, blocks) { - (block_eq.start_eq, block_eq.end_eq, block_eq.expr_eq) - } else { + let &[first_block, ref blocks @ ..] = blocks else { return; }; - - // BRANCHES_SHARING_CODE prerequisites - if has_conditional_else || (start_eq == 0 && end_eq == 0 && (has_expr && !expr_eq)) { + let &[.., last_block] = blocks else { return; - } - - // Only the start is the same - if start_eq != 0 && end_eq == 0 && (!has_expr || !expr_eq) { - let block = blocks[0]; - let start_stmts = block.stmts.split_at(start_eq).0; - - let mut start_walker = UsedValueFinderVisitor::new(cx); - for stmt in start_stmts { - intravisit::walk_stmt(&mut start_walker, stmt); - } + }; - emit_branches_sharing_code_lint( - cx, - start_eq, - 0, - false, - check_for_warn_of_moved_symbol(cx, &start_walker.def_symbols, expr), - blocks, - expr, - ); - } else if end_eq != 0 || (has_expr && expr_eq) { - let block = blocks[blocks.len() - 1]; - let (start_stmts, block_stmts) = block.stmts.split_at(start_eq); - let (block_stmts, end_stmts) = block_stmts.split_at(block_stmts.len() - end_eq); + let res = scan_block_for_eq(cx, conds, first_block, blocks); + let sm = cx.tcx.sess.source_map(); + let start_suggestion = res.start_span(first_block, sm).map(|span| { + let first_line_span = first_line_of_span(cx, expr.span); + let replace_span = first_line_span.with_hi(span.hi()); + let cond_span = first_line_span.until(first_block.span); + let cond_snippet = reindent_multiline(snippet(cx, cond_span, "_"), false, None); + let cond_indent = indent_of(cx, cond_span); + let moved_snippet = reindent_multiline(snippet(cx, span, "_"), true, None); + let suggestion = moved_snippet.to_string() + "\n" + &cond_snippet + "{"; + let suggestion = reindent_multiline(Cow::Borrowed(&suggestion), true, cond_indent); + (replace_span, suggestion.to_string()) + }); + let end_suggestion = res.end_span(last_block, sm).map(|span| { + let moved_snipped = reindent_multiline(snippet(cx, span, "_"), true, None); + let indent = indent_of(cx, expr.span.shrink_to_hi()); + let suggestion = "}\n".to_string() + &moved_snipped; + let suggestion = reindent_multiline(Cow::Borrowed(&suggestion), true, indent); - // Scan start - let mut start_walker = UsedValueFinderVisitor::new(cx); - for stmt in start_stmts { - intravisit::walk_stmt(&mut start_walker, stmt); + let span = span.with_hi(last_block.span.hi()); + // Improve formatting if the inner block has indention (i.e. normal Rust formatting) + let test_span = Span::new(span.lo() - BytePos(4), span.lo(), span.ctxt(), span.parent()); + let span = if snippet_opt(cx, test_span).map_or(false, |snip| snip == " ") { + span.with_lo(test_span.lo()) + } else { + span + }; + (span, suggestion.to_string()) + }); + + let (span, msg, end_span) = match (&start_suggestion, &end_suggestion) { + (&Some((span, _)), &Some((end_span, _))) => ( + span, + "all if blocks contain the same code at both the start and the end", + Some(end_span), + ), + (&Some((span, _)), None) => (span, "all if blocks contain the same code at the start", None), + (None, &Some((span, _))) => (span, "all if blocks contain the same code at the end", None), + (None, None) => return, + }; + span_lint_and_then(cx, BRANCHES_SHARING_CODE, span, msg, |diag| { + if let Some(span) = end_span { + diag.span_note(span, "this code is shared at the end"); } - let mut moved_syms = start_walker.def_symbols; - - // Scan block - let mut block_walker = UsedValueFinderVisitor::new(cx); - for stmt in block_stmts { - intravisit::walk_stmt(&mut block_walker, stmt); + if let Some((span, sugg)) = start_suggestion { + diag.span_suggestion( + span, + "consider moving these statements before the if", + sugg, + Applicability::Unspecified, + ); } - let mut block_defs = block_walker.defs; - - // Scan moved stmts - let mut moved_start: Option = None; - let mut end_walker = UsedValueFinderVisitor::new(cx); - for (index, stmt) in end_stmts.iter().enumerate() { - intravisit::walk_stmt(&mut end_walker, stmt); - - for value in &end_walker.uses { - // Well we can't move this and all prev statements. So reset - if block_defs.contains(value) { - moved_start = Some(index + 1); - end_walker.defs.drain().for_each(|x| { - block_defs.insert(x); - }); - - end_walker.def_symbols.clear(); - } + if let Some((span, sugg)) = end_suggestion { + diag.span_suggestion( + span, + "consider moving these statements after the if", + sugg, + Applicability::Unspecified, + ); + if !cx.typeck_results().expr_ty(expr).is_unit() { + diag.note("the end suggestion probably needs some adjustments to use the expression result correctly"); } - - end_walker.uses.clear(); } - - if let Some(moved_start) = moved_start { - end_eq -= moved_start; + if check_for_warn_of_moved_symbol(cx, &res.moved_locals, expr) { + diag.warn("some moved values might need to be renamed to avoid wrong references"); } + }); +} - let end_linable = block.expr.map_or_else( - || end_eq != 0, - |expr| { - intravisit::walk_expr(&mut end_walker, expr); - end_walker.uses.iter().any(|x| !block_defs.contains(x)) - }, - ); - - if end_linable { - end_walker.def_symbols.drain().for_each(|x| { - moved_syms.insert(x); - }); +struct BlockEq { + /// The end of the range of equal stmts at the start. + start_end_eq: usize, + /// The start of the range of equal stmts at the end. + end_begin_eq: Option, + /// The name and id of every local which can be moved at the beginning and the end. + moved_locals: Vec<(HirId, Symbol)>, +} +impl BlockEq { + fn start_span(&self, b: &Block<'_>, sm: &SourceMap) -> Option { + match &b.stmts[..self.start_end_eq] { + [first, .., last] => Some(sm.stmt_span(first.span, b.span).to(sm.stmt_span(last.span, b.span))), + [s] => Some(sm.stmt_span(s.span, b.span)), + [] => None, } + } - emit_branches_sharing_code_lint( - cx, - start_eq, - end_eq, - end_linable, - check_for_warn_of_moved_symbol(cx, &moved_syms, expr), - blocks, - expr, - ); + fn end_span(&self, b: &Block<'_>, sm: &SourceMap) -> Option { + match (&b.stmts[b.stmts.len() - self.end_begin_eq?..], b.expr) { + ([first, .., last], None) => Some(sm.stmt_span(first.span, b.span).to(sm.stmt_span(last.span, b.span))), + ([first, ..], Some(last)) => Some(sm.stmt_span(first.span, b.span).to(sm.stmt_span(last.span, b.span))), + ([s], None) => Some(sm.stmt_span(s.span, b.span)), + ([], Some(e)) => Some(walk_chain(e.span, b.span.ctxt())), + ([], None) => None, + } } } -struct BlockEqual { - /// The amount statements that are equal from the start - start_eq: usize, - /// The amount statements that are equal from the end - end_eq: usize, - /// An indication if the block expressions are the same. This will also be true if both are - /// `None` - expr_eq: bool, +/// If the statement is a local, checks if the bound names match the expected list of names. +fn eq_binding_names(s: &Stmt<'_>, names: &[(HirId, Symbol)]) -> bool { + if let StmtKind::Local(l) = s.kind { + let mut i = 0usize; + let mut res = true; + l.pat.each_binding_or_first(&mut |_, _, _, name| { + if names.get(i).map_or(false, |&(_, n)| n == name.name) { + i += 1; + } else { + res = false; + } + }); + res && i == names.len() + } else { + false + } } -/// This function can also trigger the `IF_SAME_THEN_ELSE` in which case it'll return `None` to -/// abort any further processing and avoid duplicate lint triggers. -fn scan_block_for_eq(cx: &LateContext<'_>, conds: &[&Expr<'_>], blocks: &[&Block<'_>]) -> Option { - let mut start_eq = usize::MAX; - let mut end_eq = usize::MAX; - let mut expr_eq = true; - let mut iter = blocks.windows(2).enumerate(); - while let Some((i, &[block0, block1])) = iter.next() { - let l_stmts = block0.stmts; - let r_stmts = block1.stmts; - - // `SpanlessEq` now keeps track of the locals and is therefore context sensitive clippy#6752. - // The comparison therefore needs to be done in a way that builds the correct context. - let mut evaluator = SpanlessEq::new(cx); - let mut evaluator = evaluator.inter_expr(); - - let current_start_eq = count_eq(&mut l_stmts.iter(), &mut r_stmts.iter(), |l, r| evaluator.eq_stmt(l, r)); +/// Checks if the given statement should be considered equal to the statement in the same position +/// for each block. +fn eq_stmts( + stmt: &Stmt<'_>, + blocks: &[&Block<'_>], + get_stmt: impl for<'a> Fn(&'a Block<'a>) -> Option<&'a Stmt<'a>>, + eq: &mut HirEqInterExpr<'_, '_, '_>, + moved_bindings: &mut Vec<(HirId, Symbol)>, +) -> bool { + (if let StmtKind::Local(l) = stmt.kind { + let old_count = moved_bindings.len(); + l.pat.each_binding_or_first(&mut |_, id, _, name| { + moved_bindings.push((id, name.name)); + }); + let new_bindings = &moved_bindings[old_count..]; + blocks + .iter() + .all(|b| get_stmt(b).map_or(false, |s| eq_binding_names(s, new_bindings))) + } else { + true + }) && blocks + .iter() + .all(|b| get_stmt(b).map_or(false, |s| eq.eq_stmt(s, stmt))) +} - let current_end_eq = { - // We skip the middle statements which can't be equal - let end_comparison_count = l_stmts.len().min(r_stmts.len()) - current_start_eq; - let it1 = l_stmts.iter().skip(l_stmts.len() - end_comparison_count); - let it2 = r_stmts.iter().skip(r_stmts.len() - end_comparison_count); - it1.zip(it2) - .fold(0, |acc, (l, r)| if evaluator.eq_stmt(l, r) { acc + 1 } else { 0 }) +fn scan_block_for_eq(cx: &LateContext<'_>, _conds: &[&Expr<'_>], block: &Block<'_>, blocks: &[&Block<'_>]) -> BlockEq { + let mut eq = SpanlessEq::new(cx); + let mut eq = eq.inter_expr(); + let mut moved_locals = Vec::new(); + + let start_end_eq = block + .stmts + .iter() + .enumerate() + .find(|&(i, stmt)| !eq_stmts(stmt, blocks, |b| b.stmts.get(i), &mut eq, &mut moved_locals)) + .map_or(block.stmts.len(), |(i, _)| i); + + // Walk backwards through the final expression/statements so long as their hashes are equal. Note + // `SpanlessHash` treats all local references as equal allowing locals declared earlier in the block + // to match those in other blocks. e.g. If each block ends with the following the hash value will be + // the same even though each `x` binding will have a different `HirId`: + // let x = foo(); + // x + 50 + let expr_hash_eq = if let Some(e) = block.expr { + let hash = hash_expr(cx, e); + blocks + .iter() + .all(|b| b.expr.map_or(false, |e| hash_expr(cx, e) == hash)) + } else { + blocks.iter().all(|b| b.expr.is_none()) + }; + if !expr_hash_eq { + return BlockEq { + start_end_eq, + end_begin_eq: None, + moved_locals, }; - let block_expr_eq = both(&block0.expr, &block1.expr, |l, r| evaluator.eq_expr(l, r)); - - // IF_SAME_THEN_ELSE - if_chain! { - if block_expr_eq; - if l_stmts.len() == r_stmts.len(); - if l_stmts.len() == current_start_eq; - // `conds` may have one last item than `blocks`. - // Any `i` from `blocks.windows(2)` will exist in `conds`, but `i+1` may not exist on the last iteration. - if !matches!(conds[i].kind, ExprKind::Let(..)); - if !matches!(conds.get(i + 1).map(|e| &e.kind), Some(ExprKind::Let(..))); - if !is_lint_allowed(cx, IF_SAME_THEN_ELSE, block0.hir_id); - if !is_lint_allowed(cx, IF_SAME_THEN_ELSE, block1.hir_id); - then { - span_lint_and_note( - cx, - IF_SAME_THEN_ELSE, - block0.span, - "this `if` has identical blocks", - Some(block1.span), - "same as this", - ); - - return None; + } + let end_search_start = block.stmts[start_end_eq..] + .iter() + .rev() + .enumerate() + .find(|&(offset, stmt)| { + let hash = hash_stmt(cx, stmt); + blocks.iter().any(|b| { + b.stmts + // the bounds check will catch the underflow + .get(b.stmts.len().wrapping_sub(offset + 1)) + .map_or(true, |s| hash != hash_stmt(cx, s)) + }) + }) + .map_or(block.stmts.len() - start_end_eq, |(i, _)| i); + + let moved_locals_at_start = moved_locals.len(); + let mut i = end_search_start; + let end_begin_eq = block.stmts[block.stmts.len() - end_search_start..] + .iter() + .zip(iter::repeat_with(move || { + let x = i; + i -= 1; + x + })) + .fold(end_search_start, |init, (stmt, offset)| { + if eq_stmts( + stmt, + blocks, + |b| b.stmts.get(b.stmts.len() - offset), + &mut eq, + &mut moved_locals, + ) { + init + } else { + // Clear out all locals seen at the end so far. None of them can be moved. + let stmts = &blocks[0].stmts; + for stmt in &stmts[stmts.len() - init..=stmts.len() - offset] { + if let StmtKind::Local(l) = stmt.kind { + l.pat.each_binding_or_first(&mut |_, id, _, _| { + eq.locals.remove(&id); + }); + } + } + moved_locals.truncate(moved_locals_at_start); + offset - 1 + } + }); + if let Some(e) = block.expr { + for block in blocks { + if block.expr.map_or(false, |expr| !eq.eq_expr(expr, e)) { + moved_locals.truncate(moved_locals_at_start); + return BlockEq { + start_end_eq, + end_begin_eq: None, + moved_locals, + }; } } - - start_eq = start_eq.min(current_start_eq); - end_eq = end_eq.min(current_end_eq); - expr_eq &= block_expr_eq; - } - - if !expr_eq { - end_eq = 0; } - // Check if the regions are overlapping. Set `end_eq` to prevent the overlap - let min_block_size = blocks.iter().map(|x| x.stmts.len()).min().unwrap(); - if (start_eq + end_eq) > min_block_size { - end_eq = min_block_size - start_eq; + BlockEq { + start_end_eq, + end_begin_eq: Some(end_begin_eq), + moved_locals, } - - Some(BlockEqual { - start_eq, - end_eq, - expr_eq, - }) } -fn check_for_warn_of_moved_symbol(cx: &LateContext<'_>, symbols: &FxHashSet, if_expr: &Expr<'_>) -> bool { +fn check_for_warn_of_moved_symbol(cx: &LateContext<'_>, symbols: &[(HirId, Symbol)], if_expr: &Expr<'_>) -> bool { get_enclosing_block(cx, if_expr.hir_id).map_or(false, |block| { let ignore_span = block.span.shrink_to_lo().to(if_expr.span); symbols .iter() - .filter(|sym| !sym.as_str().starts_with('_')) - .any(move |sym| { - let mut walker = ContainsName { - name: *sym, - result: false, - }; + .filter(|&&(_, name)| !name.as_str().starts_with('_')) + .any(|&(_, name)| { + let mut walker = ContainsName { name, result: false }; // Scan block block @@ -419,194 +491,9 @@ fn check_for_warn_of_moved_symbol(cx: &LateContext<'_>, symbols: &FxHashSet, - start_stmts: usize, - end_stmts: usize, - lint_end: bool, - warn_about_moved_symbol: bool, - blocks: &[&Block<'_>], - if_expr: &Expr<'_>, -) { - if start_stmts == 0 && !lint_end { - return; - } - - // (help, span, suggestion) - let mut suggestions: Vec<(&str, Span, String)> = vec![]; - let mut add_expr_note = false; - - // Construct suggestions - let sm = cx.sess().source_map(); - if start_stmts > 0 { - let block = blocks[0]; - let span_start = first_line_of_span(cx, if_expr.span).shrink_to_lo(); - let span_end = sm.stmt_span(block.stmts[start_stmts - 1].span, block.span); - - let cond_span = first_line_of_span(cx, if_expr.span).until(block.span); - let cond_snippet = reindent_multiline(snippet(cx, cond_span, "_"), false, None); - let cond_indent = indent_of(cx, cond_span); - let moved_span = block.stmts[0].span.source_callsite().to(span_end); - let moved_snippet = reindent_multiline(snippet(cx, moved_span, "_"), true, None); - let suggestion = moved_snippet.to_string() + "\n" + &cond_snippet + "{"; - let suggestion = reindent_multiline(Cow::Borrowed(&suggestion), true, cond_indent); - - let span = span_start.to(span_end); - suggestions.push(("start", span, suggestion.to_string())); - } - - if lint_end { - let block = blocks[blocks.len() - 1]; - let span_end = block.span.shrink_to_hi(); - - let moved_start = if end_stmts == 0 && block.expr.is_some() { - block.expr.unwrap().span.source_callsite() - } else { - sm.stmt_span(block.stmts[block.stmts.len() - end_stmts].span, block.span) - }; - let moved_end = block.expr.map_or_else( - || sm.stmt_span(block.stmts[block.stmts.len() - 1].span, block.span), - |expr| expr.span.source_callsite(), - ); - - let moved_span = moved_start.to(moved_end); - let moved_snipped = reindent_multiline(snippet(cx, moved_span, "_"), true, None); - let indent = indent_of(cx, if_expr.span.shrink_to_hi()); - let suggestion = "}\n".to_string() + &moved_snipped; - let suggestion = reindent_multiline(Cow::Borrowed(&suggestion), true, indent); - - let mut span = moved_start.to(span_end); - // Improve formatting if the inner block has indention (i.e. normal Rust formatting) - let test_span = Span::new(span.lo() - BytePos(4), span.lo(), span.ctxt(), span.parent()); - if snippet_opt(cx, test_span) - .map(|snip| snip == " ") - .unwrap_or_default() - { - span = span.with_lo(test_span.lo()); - } - - suggestions.push(("end", span, suggestion.to_string())); - add_expr_note = !cx.typeck_results().expr_ty(if_expr).is_unit(); - } - - let add_optional_msgs = |diag: &mut Diagnostic| { - if add_expr_note { - diag.note("The end suggestion probably needs some adjustments to use the expression result correctly"); - } - - if warn_about_moved_symbol { - diag.warn("Some moved values might need to be renamed to avoid wrong references"); - } - }; - - // Emit lint - if suggestions.len() == 1 { - let (place_str, span, sugg) = suggestions.pop().unwrap(); - let msg = format!("all if blocks contain the same code at the {}", place_str); - let help = format!("consider moving the {} statements out like this", place_str); - span_lint_and_then(cx, BRANCHES_SHARING_CODE, span, msg.as_str(), |diag| { - diag.span_suggestion(span, help.as_str(), sugg, Applicability::Unspecified); - - add_optional_msgs(diag); - }); - } else if suggestions.len() == 2 { - let (_, end_span, end_sugg) = suggestions.pop().unwrap(); - let (_, start_span, start_sugg) = suggestions.pop().unwrap(); - span_lint_and_then( - cx, - BRANCHES_SHARING_CODE, - start_span, - "all if blocks contain the same code at the start and the end. Here at the start", - move |diag| { - diag.span_note(end_span, "and here at the end"); - - diag.span_suggestion( - start_span, - "consider moving the start statements out like this", - start_sugg, - Applicability::Unspecified, - ); - - diag.span_suggestion( - end_span, - "and consider moving the end statements out like this", - end_sugg, - Applicability::Unspecified, - ); - - add_optional_msgs(diag); - }, - ); - } -} - -/// This visitor collects `HirId`s and Symbols of defined symbols and `HirId`s of used values. -struct UsedValueFinderVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - - /// The `HirId`s of defined values in the scanned statements - defs: FxHashSet, - - /// The Symbols of the defined symbols in the scanned statements - def_symbols: FxHashSet, - - /// The `HirId`s of the used values - uses: FxHashSet, -} - -impl<'a, 'tcx> UsedValueFinderVisitor<'a, 'tcx> { - fn new(cx: &'a LateContext<'tcx>) -> Self { - UsedValueFinderVisitor { - cx, - defs: FxHashSet::default(), - def_symbols: FxHashSet::default(), - uses: FxHashSet::default(), - } - } -} - -impl<'a, 'tcx> Visitor<'tcx> for UsedValueFinderVisitor<'a, 'tcx> { - type NestedFilter = nested_filter::All; - - fn nested_visit_map(&mut self) -> Self::Map { - self.cx.tcx.hir() - } - - fn visit_local(&mut self, l: &'tcx rustc_hir::Local<'tcx>) { - let local_id = l.pat.hir_id; - self.defs.insert(local_id); - - if let Some(sym) = l.pat.simple_ident() { - self.def_symbols.insert(sym.name); - } - - if let Some(expr) = l.init { - intravisit::walk_expr(self, expr); - } - } - - fn visit_qpath(&mut self, qpath: &'tcx rustc_hir::QPath<'tcx>, id: HirId, _span: rustc_span::Span) { - if let rustc_hir::QPath::Resolved(_, path) = *qpath { - if path.segments.len() == 1 { - if let rustc_hir::def::Res::Local(var) = self.cx.qpath_res(qpath, id) { - self.uses.insert(var); - } - } - } - } -} - /// Implementation of `IFS_SAME_COND`. fn lint_same_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) { - let hash: &dyn Fn(&&Expr<'_>) -> u64 = &|expr| -> u64 { - let mut h = SpanlessHash::new(cx); - h.hash_expr(expr); - h.finish() - }; - - let eq: &dyn Fn(&&Expr<'_>, &&Expr<'_>) -> bool = &|&lhs, &rhs| -> bool { eq_expr_value(cx, lhs, rhs) }; - - for (i, j) in search_same(conds, hash, eq) { + for (i, j) in search_same(conds, |e| hash_expr(cx, e), |lhs, rhs| eq_expr_value(cx, lhs, rhs)) { span_lint_and_note( cx, IFS_SAME_COND, @@ -620,12 +507,6 @@ fn lint_same_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) { /// Implementation of `SAME_FUNCTIONS_IN_IF_CONDITION`. fn lint_same_fns_in_if_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) { - let hash: &dyn Fn(&&Expr<'_>) -> u64 = &|expr| -> u64 { - let mut h = SpanlessHash::new(cx); - h.hash_expr(expr); - h.finish() - }; - let eq: &dyn Fn(&&Expr<'_>, &&Expr<'_>) -> bool = &|&lhs, &rhs| -> bool { // Do not lint if any expr originates from a macro if lhs.span.from_expansion() || rhs.span.from_expansion() { @@ -638,7 +519,7 @@ fn lint_same_fns_in_if_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) { SpanlessEq::new(cx).eq_expr(lhs, rhs) }; - for (i, j) in search_same(conds, hash, eq) { + for (i, j) in search_same(conds, |e| hash_expr(cx, e), eq) { span_lint_and_note( cx, SAME_FUNCTIONS_IN_IF_CONDITION, diff --git a/clippy_lints/src/create_dir.rs b/clippy_lints/src/create_dir.rs index 6bc4054a5abca..18d34370a7b87 100644 --- a/clippy_lints/src/create_dir.rs +++ b/clippy_lints/src/create_dir.rs @@ -15,12 +15,12 @@ declare_clippy_lint! { /// Sometimes `std::fs::create_dir` is mistakenly chosen over `std::fs::create_dir_all`. /// /// ### Example - /// - /// ```rust + /// ```rust,ignore /// std::fs::create_dir("foo"); /// ``` + /// /// Use instead: - /// ```rust + /// ```rust,ignore /// std::fs::create_dir_all("foo"); /// ``` #[clippy::version = "1.48.0"] diff --git a/clippy_lints/src/dbg_macro.rs b/clippy_lints/src/dbg_macro.rs index 17deccf8c3930..fe9f4f9ae3cb9 100644 --- a/clippy_lints/src/dbg_macro.rs +++ b/clippy_lints/src/dbg_macro.rs @@ -18,10 +18,11 @@ declare_clippy_lint! { /// /// ### Example /// ```rust,ignore - /// // Bad /// dbg!(true) + /// ``` /// - /// // Good + /// Use instead: + /// ```rust,ignore /// true /// ``` #[clippy::version = "1.34.0"] diff --git a/clippy_lints/src/default.rs b/clippy_lints/src/default.rs index 243dfd3a46183..d99a1aa296946 100644 --- a/clippy_lints/src/default.rs +++ b/clippy_lints/src/default.rs @@ -18,15 +18,16 @@ declare_clippy_lint! { /// Checks for literal calls to `Default::default()`. /// /// ### Why is this bad? - /// It's more clear to the reader to use the name of the type whose default is - /// being gotten than the generic `Default`. + /// It's easier for the reader if the name of the type is used, rather than the + /// generic `Default`. /// /// ### Example /// ```rust - /// // Bad /// let s: String = Default::default(); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust /// let s = String::default(); /// ``` #[clippy::version = "pre 1.29.0"] @@ -47,13 +48,13 @@ declare_clippy_lint! { /// Assignments to patterns that are of tuple type are not linted. /// /// ### Example - /// Bad: /// ``` /// # #[derive(Default)] /// # struct A { i: i32 } /// let mut a: A = Default::default(); /// a.i = 42; /// ``` + /// /// Use instead: /// ``` /// # #[derive(Default)] diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs index 3d9f9ed41ce18..fb418a3251f58 100644 --- a/clippy_lints/src/default_numeric_fallback.rs +++ b/clippy_lints/src/default_numeric_fallback.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::numeric_literal; use clippy_utils::source::snippet_opt; use if_chain::if_chain; @@ -76,7 +76,7 @@ impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> { } /// Check whether a passed literal has potential to cause fallback or not. - fn check_lit(&self, lit: &Lit, lit_ty: Ty<'tcx>) { + fn check_lit(&self, lit: &Lit, lit_ty: Ty<'tcx>, emit_hir_id: HirId) { if_chain! { if !in_external_macro(self.cx.sess(), lit.span); if let Some(ty_bound) = self.ty_bounds.last(); @@ -101,14 +101,15 @@ impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> { } }; let sugg = numeric_literal::format(&src, Some(suffix), is_float); - span_lint_and_sugg( + span_lint_hir_and_then( self.cx, DEFAULT_NUMERIC_FALLBACK, + emit_hir_id, lit.span, "default numeric fallback might occur", - "consider adding suffix", - sugg, - Applicability::MaybeIncorrect, + |diag| { + diag.span_suggestion(lit.span, "consider adding suffix", sugg, Applicability::MaybeIncorrect); + } ); } } @@ -179,7 +180,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> { ExprKind::Lit(lit) => { let ty = self.cx.typeck_results().expr_ty(expr); - self.check_lit(lit, ty); + self.check_lit(lit, ty, expr.hir_id); return; }, diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 527529965a96f..b47441eff3748 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; use clippy_utils::ty::peel_mid_ty_refs; @@ -30,13 +30,14 @@ declare_clippy_lint! { /// let a: &mut String = &mut String::from("foo"); /// let b: &str = a.deref(); /// ``` - /// Could be written as: + /// + /// Use instead: /// ```rust /// let a: &mut String = &mut String::from("foo"); /// let b = &*a; /// ``` /// - /// This lint excludes + /// This lint excludes: /// ```rust,ignore /// let _ = d.unwrap().deref(); /// ``` @@ -59,11 +60,13 @@ declare_clippy_lint! { /// ```rust /// fn fun(_a: &i32) {} /// - /// // Bad /// let x: &i32 = &&&&&&5; /// fun(&x); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # fn fun(_a: &i32) {} /// let x: &i32 = &5; /// fun(x); /// ``` @@ -82,13 +85,14 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad /// let x = Some(""); /// if let Some(ref x) = x { /// // use `x` here /// } + /// ``` /// - /// // Good + /// Use instead: + /// ```rust /// let x = Some(""); /// if let Some(x) = x { /// // use `&x` here @@ -131,6 +135,7 @@ pub struct Dereferencing { struct StateData { /// Span of the top level expression span: Span, + hir_id: HirId, } enum State { @@ -165,6 +170,8 @@ struct RefPat { app: Applicability, /// All the replacements which need to be made. replacements: Vec<(Span, String)>, + /// The [`HirId`] that the lint should be emitted at. + hir_id: HirId, } impl<'tcx> LateLintPass<'tcx> for Dereferencing { @@ -218,7 +225,10 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing { is_final_ufcs: matches!(expr.kind, ExprKind::Call(..)), target_mut, }, - StateData { span: expr.span }, + StateData { + span: expr.span, + hir_id: expr.hir_id, + }, )); }, RefOp::AddrOf => { @@ -290,7 +300,10 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing { required_precedence, msg, }, - StateData { span: expr.span }, + StateData { + span: expr.span, + hir_id: expr.hir_id, + }, )); } }, @@ -383,6 +396,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing { spans: vec![pat.span], app, replacements: vec![(pat.span, snip.into())], + hir_id: pat.hir_id }), ); } @@ -395,13 +409,15 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing { for pat in self.ref_locals.drain(..).filter_map(|(_, x)| x) { let replacements = pat.replacements; let app = pat.app; - span_lint_and_then( + let lint = if pat.always_deref { + NEEDLESS_BORROW + } else { + REF_BINDING_TO_REFERENCE + }; + span_lint_hir_and_then( cx, - if pat.always_deref { - NEEDLESS_BORROW - } else { - REF_BINDING_TO_REFERENCE - }, + lint, + pat.hir_id, pat.spans, "this pattern creates a reference to a reference", |diag| { @@ -638,19 +654,14 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, state: State, data: S } => { let mut app = Applicability::MachineApplicable; let snip = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app).0; - span_lint_and_sugg( - cx, - NEEDLESS_BORROW, - data.span, - msg, - "change this to", - if required_precedence > expr.precedence().order() && !has_enclosing_paren(&snip) { + span_lint_hir_and_then(cx, NEEDLESS_BORROW, data.hir_id, data.span, msg, |diag| { + let sugg = if required_precedence > expr.precedence().order() && !has_enclosing_paren(&snip) { format!("({})", snip) } else { snip.into() - }, - app, - ); + }; + diag.span_suggestion(data.span, "change this to", sugg, app); + }); }, } } diff --git a/clippy_lints/src/derivable_impls.rs b/clippy_lints/src/derivable_impls.rs index e98691fd5bb0a..4d7f4076d7b51 100644 --- a/clippy_lints/src/derivable_impls.rs +++ b/clippy_lints/src/derivable_impls.rs @@ -30,8 +30,7 @@ declare_clippy_lint! { /// } /// ``` /// - /// Could be written as: - /// + /// Use instead: /// ```rust /// #[derive(Default)] /// struct Foo { @@ -45,7 +44,6 @@ declare_clippy_lint! { /// specialized than what derive will produce. This lint can't detect the manual `impl` /// has exactly equal bounds, and therefore this lint is disabled for types with /// generic parameters. - /// #[clippy::version = "1.57.0"] pub DERIVABLE_IMPLS, complexity, diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 99347ebadc602..28f218a8e344f 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -4,14 +4,19 @@ use clippy_utils::ty::{implements_trait, implements_trait_with_env, is_copy}; use clippy_utils::{is_lint_allowed, match_def_path}; use if_chain::if_chain; 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::{ - self as hir, BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, HirId, Impl, Item, ItemKind, UnsafeSource, Unsafety, + self as hir, BlockCheckMode, BodyId, Constness, Expr, ExprKind, FnDecl, HirId, Impl, Item, ItemKind, UnsafeSource, + Unsafety, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; -use rustc_middle::ty::subst::GenericArg; -use rustc_middle::ty::{self, BoundConstness, ImplPolarity, ParamEnv, PredicateKind, TraitPredicate, TraitRef, Ty}; +use rustc_middle::traits::Reveal; +use rustc_middle::ty::{ + self, Binder, BoundConstness, GenericParamDefKind, ImplPolarity, ParamEnv, PredicateKind, TraitPredicate, TraitRef, + Ty, TyCtxt, Visibility, +}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::sym; @@ -459,50 +464,18 @@ impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> { fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) { if_chain! { if let ty::Adt(adt, substs) = ty.kind(); + if cx.tcx.visibility(adt.did()) == Visibility::Public; if let Some(eq_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Eq); - if let Some(peq_trait_def_id) = cx.tcx.get_diagnostic_item(sym::PartialEq); if let Some(def_id) = trait_ref.trait_def_id(); if cx.tcx.is_diagnostic_item(sym::PartialEq, def_id); - // New `ParamEnv` replacing `T: PartialEq` with `T: Eq` - let param_env = ParamEnv::new( - cx.tcx.mk_predicates(cx.param_env.caller_bounds().iter().map(|p| { - let kind = p.kind(); - match kind.skip_binder() { - PredicateKind::Trait(p) - if p.trait_ref.def_id == peq_trait_def_id - && p.trait_ref.substs.get(0) == p.trait_ref.substs.get(1) - && matches!(p.trait_ref.self_ty().kind(), ty::Param(_)) - && p.constness == BoundConstness::NotConst - && p.polarity == ImplPolarity::Positive => - { - cx.tcx.mk_predicate(kind.rebind(PredicateKind::Trait(TraitPredicate { - trait_ref: TraitRef::new( - eq_trait_def_id, - cx.tcx.mk_substs([GenericArg::from(p.trait_ref.self_ty())].into_iter()), - ), - constness: BoundConstness::NotConst, - polarity: ImplPolarity::Positive, - }))) - }, - _ => p, - } - })), - cx.param_env.reveal(), - cx.param_env.constness(), - ); - if !implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, substs); + let param_env = param_env_for_derived_eq(cx.tcx, adt.did(), eq_trait_def_id); + if !implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, &[]); + // If all of our fields implement `Eq`, we can implement `Eq` too + if adt + .all_fields() + .map(|f| f.ty(cx.tcx, substs)) + .all(|ty| implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, &[])); then { - // If all of our fields implement `Eq`, we can implement `Eq` too - for variant in adt.variants() { - for field in &variant.fields { - let ty = field.ty(cx.tcx, substs); - - if !implements_trait(cx, ty, eq_trait_def_id, substs) { - return; - } - } - } - span_lint_and_sugg( cx, DERIVE_PARTIAL_EQ_WITHOUT_EQ, @@ -515,3 +488,41 @@ fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_r } } } + +/// Creates the `ParamEnv` used for the give type's derived `Eq` impl. +fn param_env_for_derived_eq(tcx: TyCtxt<'_>, did: DefId, eq_trait_id: DefId) -> ParamEnv<'_> { + // Initial map from generic index to param def. + // Vec<(param_def, needs_eq)> + let mut params = tcx + .generics_of(did) + .params + .iter() + .map(|p| (p, matches!(p.kind, GenericParamDefKind::Type { .. }))) + .collect::>(); + + let ty_predicates = tcx.predicates_of(did).predicates; + for (p, _) in ty_predicates { + if let PredicateKind::Trait(p) = p.kind().skip_binder() + && p.trait_ref.def_id == eq_trait_id + && let ty::Param(self_ty) = p.trait_ref.self_ty().kind() + && p.constness == BoundConstness::NotConst + { + // Flag types which already have an `Eq` bound. + params[self_ty.index as usize].1 = false; + } + } + + ParamEnv::new( + tcx.mk_predicates(ty_predicates.iter().map(|&(p, _)| p).chain( + params.iter().filter(|&&(_, needs_eq)| needs_eq).map(|&(param, _)| { + tcx.mk_predicate(Binder::dummy(PredicateKind::Trait(TraitPredicate { + trait_ref: TraitRef::new(eq_trait_id, tcx.mk_substs([tcx.mk_param_from_def(param)].into_iter())), + constness: BoundConstness::NotConst, + polarity: ImplPolarity::Positive, + }))) + }), + )), + Reveal::UserFacing, + Constness::NotConst, + ) +} diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index aaec88f50c771..da111e7378eaf 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -163,7 +163,7 @@ declare_clippy_lint! { /// } /// } /// ``` - #[clippy::version = "1.52.0"] + #[clippy::version = "1.51.0"] pub MISSING_PANICS_DOC, pedantic, "`pub fn` may panic without `# Panics` in doc comment" @@ -178,7 +178,7 @@ declare_clippy_lint! { /// if the `fn main()` is left implicit. /// /// ### Examples - /// ``````rust + /// ```rust /// /// An example of a doctest with a `main()` function /// /// /// /// # Examples @@ -191,7 +191,7 @@ declare_clippy_lint! { /// fn needless_main() { /// unimplemented!(); /// } - /// `````` + /// ``` #[clippy::version = "1.40.0"] pub NEEDLESS_DOCTEST_MAIN, style, diff --git a/clippy_lints/src/enum_variants.rs b/clippy_lints/src/enum_variants.rs index 263a5b573c9cf..23b7510457091 100644 --- a/clippy_lints/src/enum_variants.rs +++ b/clippy_lints/src/enum_variants.rs @@ -60,7 +60,8 @@ declare_clippy_lint! { /// struct BlackForestCake; /// } /// ``` - /// Could be written as: + /// + /// Use instead: /// ```rust /// mod cake { /// struct BlackForest; diff --git a/clippy_lints/src/eq_op.rs b/clippy_lints/src/eq_op.rs index c3176d987c637..2f4c90d07cf66 100644 --- a/clippy_lints/src/eq_op.rs +++ b/clippy_lints/src/eq_op.rs @@ -52,15 +52,13 @@ declare_clippy_lint! { /// ### Why is this bad? /// It is more idiomatic to dereference the other argument. /// - /// ### Known problems - /// None - /// /// ### Example - /// ```ignore - /// // Bad + /// ```rust,ignore /// &x == y + /// ``` /// - /// // Good + /// Use instead: + /// ```rust,ignore /// x == *y /// ``` #[clippy::version = "pre 1.29.0"] diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 197cac86a57d6..a5a763c37d1be 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -34,14 +34,14 @@ declare_clippy_lint! { /// /// ### Example /// ```rust,ignore - /// // Bad /// xs.map(|x| foo(x)) + /// ``` /// - /// // Good + /// Use instead: + /// ```rust,ignore + /// // where `foo(_)` is a plain function that takes the exact argument type of `x`. /// xs.map(foo) /// ``` - /// where `foo(_)` is a plain function that takes the exact argument type of - /// `x`. #[clippy::version = "pre 1.29.0"] pub REDUNDANT_CLOSURE, style, diff --git a/clippy_lints/src/excessive_bools.rs b/clippy_lints/src/excessive_bools.rs index a2af10e2ba5ea..f7a92bc079567 100644 --- a/clippy_lints/src/excessive_bools.rs +++ b/clippy_lints/src/excessive_bools.rs @@ -54,12 +54,11 @@ declare_clippy_lint! { /// API easier to use. /// /// ### Example - /// Bad: /// ```rust,ignore /// fn f(is_round: bool, is_hot: bool) { ... } /// ``` /// - /// Good: + /// Use instead: /// ```rust,ignore /// enum Shape { /// Round, diff --git a/clippy_lints/src/float_literal.rs b/clippy_lints/src/float_literal.rs index f850ea31f4d6e..f2e0798096378 100644 --- a/clippy_lints/src/float_literal.rs +++ b/clippy_lints/src/float_literal.rs @@ -45,10 +45,11 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad /// let _: f32 = 16_777_217.0; // 16_777_216.0 + /// ``` /// - /// // Good + /// Use instead: + /// ```rust /// let _: f32 = 16_777_216.0; /// let _: f64 = 16_777_217.0; /// ``` diff --git a/clippy_lints/src/functions/mod.rs b/clippy_lints/src/functions/mod.rs index ad031cbc09d4d..73261fb8a44c7 100644 --- a/clippy_lints/src/functions/mod.rs +++ b/clippy_lints/src/functions/mod.rs @@ -76,12 +76,13 @@ declare_clippy_lint! { /// /// ### Example /// ```rust,ignore - /// // Bad /// pub fn foo(x: *const u8) { /// println!("{}", unsafe { *x }); /// } + /// ``` /// - /// // Good + /// Use instead: + /// ```rust,ignore /// pub unsafe fn foo(x: *const u8) { /// println!("{}", unsafe { *x }); /// } diff --git a/clippy_lints/src/get_first.rs b/clippy_lints/src/get_first.rs index 0748ab45252ad..529f7babaa5ea 100644 --- a/clippy_lints/src/get_first.rs +++ b/clippy_lints/src/get_first.rs @@ -20,13 +20,12 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad /// let x = vec![2, 3, 5]; /// let first_element = x.get(0); /// ``` + /// /// Use instead: /// ```rust - /// // Good /// let x = vec![2, 3, 5]; /// let first_element = x.first(); /// ``` diff --git a/clippy_lints/src/implicit_saturating_sub.rs b/clippy_lints/src/implicit_saturating_sub.rs index ae4158662d464..46654bc61e0f1 100644 --- a/clippy_lints/src/implicit_saturating_sub.rs +++ b/clippy_lints/src/implicit_saturating_sub.rs @@ -16,17 +16,21 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// let end: u32 = 10; - /// let start: u32 = 5; - /// + /// # let end: u32 = 10; + /// # let start: u32 = 5; /// let mut i: u32 = end - start; /// - /// // Bad /// if i != 0 { /// i -= 1; /// } + /// ``` + /// + /// Use instead: + /// ```rust + /// # let end: u32 = 10; + /// # let start: u32 = 5; + /// let mut i: u32 = end - start; /// - /// // Good /// i = i.saturating_sub(1); /// ``` #[clippy::version = "1.44.0"] @@ -48,7 +52,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub { // Check if the conditional expression is a binary operation if let ExprKind::Binary(ref cond_op, cond_left, cond_right) = cond.kind; - // Ensure that the binary operator is >, != and < + // Ensure that the binary operator is >, !=, or < if BinOpKind::Ne == cond_op.node || BinOpKind::Gt == cond_op.node || BinOpKind::Lt == cond_op.node; // Check if assign operation is done diff --git a/clippy_lints/src/index_refutable_slice.rs b/clippy_lints/src/index_refutable_slice.rs index 9ce5b8e17a9ae..d0c6495e35a72 100644 --- a/clippy_lints/src/index_refutable_slice.rs +++ b/clippy_lints/src/index_refutable_slice.rs @@ -45,7 +45,7 @@ declare_clippy_lint! { /// println!("{}", first); /// } /// ``` - #[clippy::version = "1.58.0"] + #[clippy::version = "1.59.0"] pub INDEX_REFUTABLE_SLICE, nursery, "avoid indexing on slices which could be destructed" diff --git a/clippy_lints/src/indexing_slicing.rs b/clippy_lints/src/indexing_slicing.rs index 4ba7477add82a..4a375752e1d3b 100644 --- a/clippy_lints/src/indexing_slicing.rs +++ b/clippy_lints/src/indexing_slicing.rs @@ -17,19 +17,20 @@ declare_clippy_lint! { /// ### Why is this bad? /// This will always panic at runtime. /// - /// ### Known problems - /// Hopefully none. - /// /// ### Example - /// ```no_run + /// ```rust,no_run /// # #![allow(const_err)] /// let x = [1, 2, 3, 4]; /// - /// // Bad /// x[9]; /// &x[2..9]; + /// ``` + /// + /// Use instead: + /// ```rust + /// # let x = [1, 2, 3, 4]; + /// // Index within bounds /// - /// // Good /// x[0]; /// x[3]; /// ``` @@ -49,42 +50,32 @@ declare_clippy_lint! { /// Indexing and slicing can panic at runtime and there are /// safe alternatives. /// - /// ### Known problems - /// Hopefully none. - /// /// ### Example /// ```rust,no_run /// // Vector /// let x = vec![0; 5]; /// - /// // Bad /// x[2]; /// &x[2..100]; - /// &x[2..]; - /// &x[..100]; - /// - /// // Good - /// x.get(2); - /// x.get(2..100); - /// x.get(2..); - /// x.get(..100); /// /// // Array /// let y = [0, 1, 2, 3]; /// - /// // Bad /// &y[10..100]; /// &y[10..]; - /// &y[..100]; + /// ``` + /// + /// Use instead: + /// ```rust + /// # #![allow(unused)] + /// + /// # let x = vec![0; 5]; + /// # let y = [0, 1, 2, 3]; + /// x.get(2); + /// x.get(2..100); /// - /// // Good - /// &y[2..]; - /// &y[..2]; - /// &y[0..3]; /// y.get(10); /// y.get(10..100); - /// y.get(10..); - /// y.get(..100); /// ``` #[clippy::version = "pre 1.29.0"] pub INDEXING_SLICING, diff --git a/clippy_lints/src/infinite_iter.rs b/clippy_lints/src/infinite_iter.rs index 41e1fc4e3c209..78b5ec8ec1ef4 100644 --- a/clippy_lints/src/infinite_iter.rs +++ b/clippy_lints/src/infinite_iter.rs @@ -41,6 +41,7 @@ declare_clippy_lint! { /// ### Example /// ```rust /// let infinite_iter = 0..; + /// # #[allow(unused)] /// [0..].iter().zip(infinite_iter.take_while(|x| *x > 5)); /// ``` #[clippy::version = "pre 1.29.0"] diff --git a/clippy_lints/src/inherent_to_string.rs b/clippy_lints/src/inherent_to_string.rs index 55c04a1186fc3..39f68a8a1b480 100644 --- a/clippy_lints/src/inherent_to_string.rs +++ b/clippy_lints/src/inherent_to_string.rs @@ -14,12 +14,8 @@ declare_clippy_lint! { /// ### Why is this bad? /// This method is also implicitly defined if a type implements the `Display` trait. As the functionality of `Display` is much more versatile, it should be preferred. /// - /// ### Known problems - /// None - /// /// ### Example /// ```rust - /// // Bad /// pub struct A; /// /// impl A { @@ -29,8 +25,8 @@ declare_clippy_lint! { /// } /// ``` /// + /// Use instead: /// ```rust - /// // Good /// use std::fmt; /// /// pub struct A; @@ -54,12 +50,8 @@ declare_clippy_lint! { /// ### Why is this bad? /// This method is also implicitly defined if a type implements the `Display` trait. The less versatile inherent method will then shadow the implementation introduced by `Display`. /// - /// ### Known problems - /// None - /// /// ### Example /// ```rust - /// // Bad /// use std::fmt; /// /// pub struct A; @@ -77,8 +69,8 @@ declare_clippy_lint! { /// } /// ``` /// + /// Use instead: /// ```rust - /// // Good /// use std::fmt; /// /// pub struct A; diff --git a/clippy_lints/src/int_plus_one.rs b/clippy_lints/src/int_plus_one.rs index 8db7b307ddb75..9a944def3eb22 100644 --- a/clippy_lints/src/int_plus_one.rs +++ b/clippy_lints/src/int_plus_one.rs @@ -21,8 +21,7 @@ declare_clippy_lint! { /// if x >= y + 1 {} /// ``` /// - /// Could be written as: - /// + /// Use instead: /// ```rust /// # let x = 1; /// # let y = 1; diff --git a/clippy_lints/src/integer_division.rs b/clippy_lints/src/integer_division.rs index fa78620567880..3effba5682607 100644 --- a/clippy_lints/src/integer_division.rs +++ b/clippy_lints/src/integer_division.rs @@ -15,11 +15,12 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad /// let x = 3 / 2; /// println!("{}", x); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust /// let x = 3f32 / 2f32; /// println!("{}", x); /// ``` diff --git a/clippy_lints/src/items_after_statements.rs b/clippy_lints/src/items_after_statements.rs index cdefe627efdaa..46d439b4497e1 100644 --- a/clippy_lints/src/items_after_statements.rs +++ b/clippy_lints/src/items_after_statements.rs @@ -17,7 +17,6 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad /// fn foo() { /// println!("cake"); /// } @@ -31,8 +30,8 @@ declare_clippy_lint! { /// } /// ``` /// + /// Use instead: /// ```rust - /// // Good /// fn foo() { /// println!("cake"); /// } diff --git a/clippy_lints/src/large_const_arrays.rs b/clippy_lints/src/large_const_arrays.rs index e10993ba7ddce..289755bfec66f 100644 --- a/clippy_lints/src/large_const_arrays.rs +++ b/clippy_lints/src/large_const_arrays.rs @@ -20,10 +20,11 @@ declare_clippy_lint! { /// /// ### Example /// ```rust,ignore - /// // Bad /// pub const a = [0u32; 1_000_000]; + /// ``` /// - /// // Good + /// Use instead: + /// ```rust.ignore /// pub static a = [0u32; 1_000_000]; /// ``` #[clippy::version = "1.44.0"] diff --git a/clippy_lints/src/large_enum_variant.rs b/clippy_lints/src/large_enum_variant.rs index 63ac092dfaf12..9be057bcf901f 100644 --- a/clippy_lints/src/large_enum_variant.rs +++ b/clippy_lints/src/large_enum_variant.rs @@ -38,12 +38,14 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad /// enum Test { /// A(i32), /// B([i32; 8000]), /// } + /// ``` /// + /// Use instead: + /// ```rust /// // Possibly better /// enum Test2 { /// A(i32), diff --git a/clippy_lints/src/large_include_file.rs b/clippy_lints/src/large_include_file.rs index 8bef13c682dbf..84dd61a1e4b0d 100644 --- a/clippy_lints/src/large_include_file.rs +++ b/clippy_lints/src/large_include_file.rs @@ -22,10 +22,11 @@ declare_clippy_lint! { /// let included_bytes = include_bytes!("very_large_file.txt"); /// ``` /// - /// Instead, you can load the file at runtime: + /// Use instead: /// ```rust,ignore /// use std::fs; /// + /// // You can load the file at runtime /// let string = fs::read_to_string("very_large_file.txt")?; /// let bytes = fs::read("very_large_file.txt")?; /// ``` diff --git a/clippy_lints/src/let_underscore.rs b/clippy_lints/src/let_underscore.rs index cb1ef01f5ba9d..26c540e2223b1 100644 --- a/clippy_lints/src/let_underscore.rs +++ b/clippy_lints/src/let_underscore.rs @@ -45,13 +45,11 @@ declare_clippy_lint! { /// `std::mem::drop` conveys your intention better and is less error-prone. /// /// ### Example - /// - /// Bad: /// ```rust,ignore /// let _ = mutex.lock(); /// ``` /// - /// Good: + /// Use instead: /// ```rust,ignore /// let _lock = mutex.lock(); /// ``` @@ -75,24 +73,20 @@ declare_clippy_lint! { /// better and is less error-prone. /// /// ### Example - /// - /// Bad: - /// ```rust,ignore - /// struct Droppable; - /// impl Drop for Droppable { - /// fn drop(&mut self) {} - /// } + /// ```rust + /// # struct DroppableItem; /// { - /// let _ = Droppable; - /// // ^ dropped here + /// let _ = DroppableItem; + /// // ^ dropped here /// /* more code */ /// } /// ``` /// - /// Good: - /// ```rust,ignore + /// Use instead: + /// ```rust + /// # struct DroppableItem; /// { - /// let _droppable = Droppable; + /// let _droppable = DroppableItem; /// /* more code */ /// // dropped at end of scope /// } diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index d4ec046d0bb08..8a2cfbff953ec 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -242,6 +242,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(needless_bool::NEEDLESS_BOOL), LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE), LintId::of(needless_late_init::NEEDLESS_LATE_INIT), + LintId::of(needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS), LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK), LintId::of(needless_update::NEEDLESS_UPDATE), LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD), @@ -270,6 +271,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(ranges::RANGE_ZIP_WITH_LEN), LintId::of(ranges::REVERSED_EMPTY_RANGES), LintId::of(rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT), + LintId::of(read_zero_byte_vec::READ_ZERO_BYTE_VEC), LintId::of(redundant_clone::REDUNDANT_CLONE), LintId::of(redundant_closure_call::REDUNDANT_CLOSURE_CALL), LintId::of(redundant_field_names::REDUNDANT_FIELD_NAMES), diff --git a/clippy_lints/src/lib.register_correctness.rs b/clippy_lints/src/lib.register_correctness.rs index 50cdd0af92305..92a3a0aabf1ca 100644 --- a/clippy_lints/src/lib.register_correctness.rs +++ b/clippy_lints/src/lib.register_correctness.rs @@ -55,6 +55,7 @@ store.register_group(true, "clippy::correctness", Some("clippy_correctness"), ve LintId::of(ptr::INVALID_NULL_PTR_USAGE), LintId::of(ptr::MUT_FROM_REF), LintId::of(ranges::REVERSED_EMPTY_RANGES), + LintId::of(read_zero_byte_vec::READ_ZERO_BYTE_VEC), LintId::of(regex::INVALID_REGEX), LintId::of(self_assignment::SELF_ASSIGNMENT), LintId::of(serde_api::SERDE_API_MISUSE), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index b927ba3b17c0e..8ad984c68b8ec 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -408,6 +408,7 @@ store.register_lints(&[ needless_continue::NEEDLESS_CONTINUE, needless_for_each::NEEDLESS_FOR_EACH, needless_late_init::NEEDLESS_LATE_INIT, + needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS, needless_pass_by_value::NEEDLESS_PASS_BY_VALUE, needless_question_mark::NEEDLESS_QUESTION_MARK, needless_update::NEEDLESS_UPDATE, @@ -458,6 +459,7 @@ store.register_lints(&[ ranges::RANGE_ZIP_WITH_LEN, ranges::REVERSED_EMPTY_RANGES, rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT, + read_zero_byte_vec::READ_ZERO_BYTE_VEC, redundant_clone::REDUNDANT_CLONE, redundant_closure_call::REDUNDANT_CLOSURE_CALL, redundant_else::REDUNDANT_ELSE, diff --git a/clippy_lints/src/lib.register_style.rs b/clippy_lints/src/lib.register_style.rs index 35575351784a2..b6992ae0ad25f 100644 --- a/clippy_lints/src/lib.register_style.rs +++ b/clippy_lints/src/lib.register_style.rs @@ -91,6 +91,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK), LintId::of(mut_reference::UNNECESSARY_MUT_PASSED), LintId::of(needless_late_init::NEEDLESS_LATE_INIT), + LintId::of(needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS), LintId::of(neg_multiply::NEG_MULTIPLY), LintId::of(new_without_default::NEW_WITHOUT_DEFAULT), LintId::of(non_copy_const::BORROW_INTERIOR_MUTABLE_CONST), diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index ee0416fc0ff5e..84898eae05ac4 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1,5 +1,3 @@ -// error-pattern:cargo-clippy - #![feature(array_windows)] #![feature(binary_heap_into_iter_sorted)] #![feature(box_patterns)] @@ -88,10 +86,11 @@ use rustc_session::Session; /// /// /// /// ### Example /// /// ```rust -/// /// // Bad /// /// Insert a short example of code that triggers the lint +/// /// ``` /// /// -/// /// // Good +/// /// Use instead: +/// /// ```rust /// /// Insert a short example of improved code that doesn't trigger the lint /// /// ``` /// pub LINT_NAME, @@ -315,6 +314,7 @@ mod needless_borrowed_ref; mod needless_continue; mod needless_for_each; mod needless_late_init; +mod needless_parens_on_range_literals; mod needless_pass_by_value; mod needless_question_mark; mod needless_update; @@ -348,6 +348,7 @@ mod pub_use; mod question_mark; mod ranges; mod rc_clone_in_vec_init; +mod read_zero_byte_vec; mod redundant_clone; mod redundant_closure_call; mod redundant_else; @@ -746,6 +747,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| Box::new(collapsible_if::CollapsibleIf)); store.register_early_pass(|| Box::new(items_after_statements::ItemsAfterStatements)); store.register_early_pass(|| Box::new(precedence::Precedence)); + store.register_late_pass(|| Box::new(needless_parens_on_range_literals::NeedlessParensOnRangeLiterals)); store.register_early_pass(|| Box::new(needless_continue::NeedlessContinue)); store.register_early_pass(|| Box::new(redundant_else::RedundantElse)); store.register_late_pass(|| Box::new(create_dir::CreateDir)); @@ -907,6 +909,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(swap_ptr_to_ref::SwapPtrToRef)); store.register_late_pass(|| Box::new(mismatching_type_param_order::TypeParamMismatch)); store.register_late_pass(|| Box::new(as_underscore::AsUnderscore)); + store.register_late_pass(|| Box::new(read_zero_byte_vec::ReadZeroByteVec)); // 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 070c7e591420d..93f5663312f2e 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -36,12 +36,14 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad: unnecessary lifetime annotations + /// // Unnecessary lifetime annotations /// fn in_and_out<'a>(x: &'a u8, y: u8) -> &'a u8 { /// x /// } + /// ``` /// - /// // Good + /// Use instead: + /// ```rust /// fn elided(x: &u8, y: u8) -> &u8 { /// x /// } @@ -65,12 +67,14 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad: unnecessary lifetimes + /// // unnecessary lifetimes /// fn unused_lifetime<'a>(x: u8) { /// // .. /// } + /// ``` /// - /// // Good + /// Use instead: + /// ```rust /// fn no_lifetime(x: u8) { /// // ... /// } diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index 9998712b8527d..fb2104861c87f 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -22,11 +22,16 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad - /// let x: u64 = 61864918973511; + /// # let _: u64 = + /// 61864918973511 + /// # ; + /// ``` /// - /// // Good - /// let x: u64 = 61_864_918_973_511; + /// Use instead: + /// ```rust + /// # let _: u64 = + /// 61_864_918_973_511 + /// # ; /// ``` #[clippy::version = "pre 1.29.0"] pub UNREADABLE_LITERAL, @@ -46,6 +51,7 @@ declare_clippy_lint! { /// - Does not match on `_127` since that is a valid grouping for decimal and octal numbers /// /// ### Example + /// ```ignore /// `2_32` => `2_i32` /// `250_8 => `250_u8` /// ``` @@ -66,11 +72,16 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad - /// let x: u64 = 618_64_9189_73_511; + /// # let _: u64 = + /// 618_64_9189_73_511 + /// # ; + /// ``` /// - /// // Good - /// let x: u64 = 61_864_918_973_511; + /// Use instead: + /// ```rust + /// # let _: u64 = + /// 61_864_918_973_511 + /// # ; /// ``` #[clippy::version = "pre 1.29.0"] pub INCONSISTENT_DIGIT_GROUPING, @@ -125,9 +136,11 @@ declare_clippy_lint! { /// readable than a decimal representation. /// /// ### Example + /// ```text /// `255` => `0xFF` /// `65_535` => `0xFFFF` /// `4_042_322_160` => `0xF0F0_F0F0` + /// ``` #[clippy::version = "pre 1.29.0"] pub DECIMAL_LITERAL_REPRESENTATION, restriction, diff --git a/clippy_lints/src/loops/for_loops_over_fallibles.rs b/clippy_lints/src/loops/for_loops_over_fallibles.rs index 90530ebf0031f..77de90fd7b94a 100644 --- a/clippy_lints/src/loops/for_loops_over_fallibles.rs +++ b/clippy_lints/src/loops/for_loops_over_fallibles.rs @@ -7,9 +7,22 @@ use rustc_lint::LateContext; use rustc_span::symbol::sym; /// Checks for `for` loops over `Option`s and `Result`s. -pub(super) fn check(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>) { +pub(super) fn check(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>, method_name: Option<&str>) { let ty = cx.typeck_results().expr_ty(arg); if is_type_diagnostic_item(cx, ty, sym::Option) { + let help_string = if let Some(method_name) = method_name { + format!( + "consider replacing `for {0} in {1}.{method_name}()` with `if let Some({0}) = {1}`", + snippet(cx, pat.span, "_"), + snippet(cx, arg.span, "_") + ) + } else { + format!( + "consider replacing `for {0} in {1}` with `if let Some({0}) = {1}`", + snippet(cx, pat.span, "_"), + snippet(cx, arg.span, "_") + ) + }; span_lint_and_help( cx, FOR_LOOPS_OVER_FALLIBLES, @@ -20,13 +33,22 @@ pub(super) fn check(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>) { snippet(cx, arg.span, "_") ), None, - &format!( - "consider replacing `for {0} in {1}` with `if let Some({0}) = {1}`", - snippet(cx, pat.span, "_"), - snippet(cx, arg.span, "_") - ), + &help_string, ); } else if is_type_diagnostic_item(cx, ty, sym::Result) { + let help_string = if let Some(method_name) = method_name { + format!( + "consider replacing `for {0} in {1}.{method_name}()` with `if let Ok({0}) = {1}`", + snippet(cx, pat.span, "_"), + snippet(cx, arg.span, "_") + ) + } else { + format!( + "consider replacing `for {0} in {1}` with `if let Ok({0}) = {1}`", + snippet(cx, pat.span, "_"), + snippet(cx, arg.span, "_") + ) + }; span_lint_and_help( cx, FOR_LOOPS_OVER_FALLIBLES, @@ -37,11 +59,7 @@ pub(super) fn check(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>) { snippet(cx, arg.span, "_") ), None, - &format!( - "consider replacing `for {0} in {1}` with `if let Ok({0}) = {1}`", - snippet(cx, pat.span, "_"), - snippet(cx, arg.span, "_") - ), + &help_string, ); } } diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index d61be78895ffc..391de922e1e1a 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -42,7 +42,8 @@ declare_clippy_lint! { /// dst[i + 64] = src[i]; /// } /// ``` - /// Could be written as: + /// + /// Use instead: /// ```rust /// # let src = vec![1]; /// # let mut dst = vec![0; 65]; @@ -70,7 +71,8 @@ declare_clippy_lint! { /// println!("{}", vec[i]); /// } /// ``` - /// Could be written as: + /// + /// Use instead: /// ```rust /// let vec = vec!['a', 'b', 'c']; /// for i in vec { @@ -103,7 +105,8 @@ declare_clippy_lint! { /// // .. /// } /// ``` - /// can be rewritten to + /// + /// Use instead: /// ```rust /// # let y = vec![1]; /// for x in &y { @@ -188,6 +191,10 @@ declare_clippy_lint! { /// for x in &res { /// // .. /// } + /// + /// for x in res.iter() { + /// // .. + /// } /// ``` /// /// Use instead: @@ -282,7 +289,8 @@ declare_clippy_lint! { /// i += 1; /// } /// ``` - /// Could be written as + /// + /// Use instead: /// ```rust /// # let v = vec![1]; /// # fn bar(bar: usize, baz: usize) {} @@ -469,7 +477,7 @@ declare_clippy_lint! { /// /// ### Why is this bad? /// This kind of operation can be expressed more succinctly with - /// `vec![item;SIZE]` or `vec.resize(NEW_SIZE, item)` and using these alternatives may also + /// `vec![item; SIZE]` or `vec.resize(NEW_SIZE, item)` and using these alternatives may also /// have better performance. /// /// ### Example @@ -484,7 +492,8 @@ declare_clippy_lint! { /// vec.push(item2); /// } /// ``` - /// could be written as + /// + /// Use instead: /// ```rust /// let item1 = 2; /// let item2 = 3; @@ -512,7 +521,8 @@ declare_clippy_lint! { /// println!("{}", item); /// } /// ``` - /// could be written as + /// + /// Use instead: /// ```rust /// let item1 = 2; /// let item = &item1; @@ -586,7 +596,7 @@ declare_clippy_lint! { /// std::hint::spin_loop() /// } /// ``` - #[clippy::version = "1.59.0"] + #[clippy::version = "1.61.0"] pub MISSING_SPIN_LOOP, perf, "An empty busy waiting loop" @@ -695,10 +705,14 @@ fn check_for_loop_arg(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>) { let method_name = method.ident.as_str(); // check for looping over x.iter() or x.iter_mut(), could use &x or &mut x match method_name { - "iter" | "iter_mut" => explicit_iter_loop::check(cx, self_arg, arg, method_name), + "iter" | "iter_mut" => { + explicit_iter_loop::check(cx, self_arg, arg, method_name); + for_loops_over_fallibles::check(cx, pat, self_arg, Some(method_name)); + }, "into_iter" => { explicit_iter_loop::check(cx, self_arg, arg, method_name); explicit_into_iter_loop::check(cx, self_arg, arg); + for_loops_over_fallibles::check(cx, pat, self_arg, Some(method_name)); }, "next" => { next_loop_linted = iter_next_loop::check(cx, arg); @@ -708,6 +722,6 @@ fn check_for_loop_arg(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>) { } if !next_loop_linted { - for_loops_over_fallibles::check(cx, pat, arg); + for_loops_over_fallibles::check(cx, pat, arg, None); } } diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index 99d2146693595..32de20f6531fe 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -117,13 +117,20 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { | ExprKind::Type(e, _) | ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) - | ExprKind::Struct(_, _, Some(e)) | ExprKind::Repeat(e, _) | ExprKind::DropTemps(e) => never_loop_expr(e, main_loop_id), ExprKind::Let(let_expr) => never_loop_expr(let_expr.init, main_loop_id), ExprKind::Array(es) | ExprKind::MethodCall(_, es, _) | ExprKind::Tup(es) => { never_loop_expr_all(&mut es.iter(), main_loop_id) }, + ExprKind::Struct(_, fields, base) => { + let fields = never_loop_expr_all(&mut fields.iter().map(|f| f.expr), main_loop_id); + if let Some(base) = base { + combine_both(fields, never_loop_expr(base, main_loop_id)) + } else { + fields + } + }, ExprKind::Call(e, es) => never_loop_expr_all(&mut once(e).chain(es.iter()), main_loop_id), ExprKind::Binary(_, e1, e2) | ExprKind::Assign(e1, e2, _) @@ -180,8 +187,7 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { | InlineAsmOperand::SymStatic { .. } => NeverLoopResult::Otherwise, }) .fold(NeverLoopResult::Otherwise, combine_both), - ExprKind::Struct(_, _, None) - | ExprKind::Yield(_, _) + ExprKind::Yield(_, _) | ExprKind::Closure { .. } | ExprKind::Path(_) | ExprKind::ConstBlock(_) diff --git a/clippy_lints/src/matches/match_same_arms.rs b/clippy_lints/src/matches/match_same_arms.rs index 4f8baf7efb0bb..16fefea552019 100644 --- a/clippy_lints/src/matches/match_same_arms.rs +++ b/clippy_lints/src/matches/match_same_arms.rs @@ -110,14 +110,9 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { arm1.span, "this match arm has an identical body to the `_` wildcard arm", |diag| { - diag.span_suggestion( - arm1.span, - "try removing the arm", - "", - Applicability::MaybeIncorrect, - ) - .help("or try changing either arm body") - .span_note(arm2.span, "`_` wildcard arm here"); + diag.span_suggestion(arm1.span, "try removing the arm", "", Applicability::MaybeIncorrect) + .help("or try changing either arm body") + .span_note(arm2.span, "`_` wildcard arm here"); }, ); } else { diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index d1e42f39e470d..3e765173fb9f3 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -43,13 +43,16 @@ declare_clippy_lint! { /// ```rust /// # fn bar(stool: &str) {} /// # let x = Some("abc"); - /// // Bad /// match x { /// Some(ref foo) => bar(foo), /// _ => (), /// } + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # fn bar(stool: &str) {} + /// # let x = Some("abc"); /// if let Some(ref foo) = x { /// bar(foo); /// } @@ -114,14 +117,15 @@ declare_clippy_lint! { /// /// ### Example /// ```rust,ignore - /// // Bad /// match x { /// &A(ref y) => foo(y), /// &B => bar(), /// _ => frob(&x), /// } + /// ``` /// - /// // Good + /// Use instead: + /// ```rust,ignore /// match *x { /// A(ref y) => foo(y), /// B => bar(), @@ -227,13 +231,16 @@ declare_clippy_lint! { /// ```rust /// let x: Option<()> = None; /// - /// // Bad /// let r: Option<&()> = match x { /// None => None, /// Some(ref v) => Some(v), /// }; + /// ``` + /// + /// Use instead: + /// ```rust + /// let x: Option<()> = None; /// - /// // Good /// let r: Option<&()> = x.as_ref(); /// ``` #[clippy::version = "pre 1.29.0"] @@ -257,13 +264,16 @@ declare_clippy_lint! { /// ```rust /// # enum Foo { A(usize), B(usize) } /// # let x = Foo::B(1); - /// // Bad /// match x { /// Foo::A(_) => {}, /// _ => {}, /// } + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # enum Foo { A(usize), B(usize) } + /// # let x = Foo::B(1); /// match x { /// Foo::A(_) => {}, /// Foo::B(_) => {}, @@ -290,14 +300,17 @@ declare_clippy_lint! { /// ```rust /// # enum Foo { A, B, C } /// # let x = Foo::B; - /// // Bad /// match x { /// Foo::A => {}, /// Foo::B => {}, /// _ => {}, /// } + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # enum Foo { A, B, C } + /// # let x = Foo::B; /// match x { /// Foo::A => {}, /// Foo::B => {}, @@ -320,14 +333,17 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad - /// match "foo" { + /// # let s = "foo"; + /// match s { /// "a" => {}, /// "bar" | _ => {}, /// } + /// ``` /// - /// // Good - /// match "foo" { + /// Use instead: + /// ```rust + /// # let s = "foo"; + /// match s { /// "a" => {}, /// _ => {}, /// } @@ -389,15 +405,17 @@ declare_clippy_lint! { /// ```rust /// # let a = 1; /// # let b = 2; - /// - /// // Bad /// match (a, b) { /// (c, d) => { /// // useless match /// } /// } + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # let a = 1; + /// # let b = 2; /// let (c, d) = (a, b); /// ``` #[clippy::version = "1.43.0"] @@ -419,13 +437,16 @@ declare_clippy_lint! { /// # struct A { a: i32 } /// let a = A { a: 5 }; /// - /// // Bad /// match a { /// A { a: 5, .. } => {}, /// _ => {}, /// } + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # struct A { a: i32 } + /// # let a = A { a: 5 }; /// match a { /// A { a: 5 } => {}, /// _ => {}, @@ -509,7 +530,6 @@ declare_clippy_lint! { /// ```rust /// let x = Some(5); /// - /// // Bad /// let a = match x { /// Some(0) => true, /// _ => false, @@ -520,8 +540,11 @@ declare_clippy_lint! { /// } else { /// false /// }; + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// let x = Some(5); /// let a = matches!(x, Some(0)); /// ``` #[clippy::version = "1.47.0"] @@ -695,19 +718,18 @@ declare_clippy_lint! { /// let arr = vec![0, 1, 2, 3]; /// let idx = 1; /// - /// // Bad /// match arr[idx] { /// 0 => println!("{}", 0), /// 1 => println!("{}", 3), /// _ => {}, /// } /// ``` + /// /// Use instead: /// ```rust, no_run /// let arr = vec![0, 1, 2, 3]; /// let idx = 1; /// - /// // Good /// match arr.get(idx) { /// Some(0) => println!("{}", 0), /// Some(1) => println!("{}", 3), diff --git a/clippy_lints/src/methods/filter_map.rs b/clippy_lints/src/methods/filter_map.rs index 3efccd703a651..58c3e52e138c4 100644 --- a/clippy_lints/src/methods/filter_map.rs +++ b/clippy_lints/src/methods/filter_map.rs @@ -6,7 +6,7 @@ use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::Res; -use rustc_hir::{Expr, ExprKind, PatKind, QPath, UnOp}; +use rustc_hir::{Expr, ExprKind, PatKind, PathSegment, QPath, UnOp}; use rustc_lint::LateContext; use rustc_span::source_map::Span; use rustc_span::symbol::{sym, Symbol}; @@ -155,7 +155,15 @@ pub(super) fn check<'tcx>( } false }; - if SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(filter_arg, map_arg); + + if match map_arg.kind { + ExprKind::MethodCall(method, [original_arg], _) => { + acceptable_methods(method) + && SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(filter_arg, original_arg) + }, + _ => SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(filter_arg, map_arg) + }; + then { let span = filter_span.with_hi(expr.span.hi()); let (filter_name, lint) = if is_find { @@ -171,3 +179,18 @@ pub(super) fn check<'tcx>( } } } + +fn acceptable_methods(method: &PathSegment<'_>) -> bool { + let methods: [Symbol; 8] = [ + sym::clone, + sym::as_ref, + sym!(copied), + sym!(cloned), + sym!(as_deref), + sym!(as_mut), + sym!(as_deref_mut), + sym!(to_owned), + ]; + + methods.contains(&method.ident.name) +} diff --git a/clippy_lints/src/methods/iter_overeager_cloned.rs b/clippy_lints/src/methods/iter_overeager_cloned.rs index 54c9ca435a447..06a39c5997e20 100644 --- a/clippy_lints/src/methods/iter_overeager_cloned.rs +++ b/clippy_lints/src/methods/iter_overeager_cloned.rs @@ -1,70 +1,59 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet; -use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy}; -use itertools::Itertools; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::snippet_opt; +use clippy_utils::ty::{get_associated_type, implements_trait, is_copy}; use rustc_errors::Applicability; -use rustc_hir as hir; +use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_span::sym; -use std::ops::Not; use super::ITER_OVEREAGER_CLONED; use crate::redundant_clone::REDUNDANT_CLONE; -/// lint overeager use of `cloned()` for `Iterator`s pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, - expr: &'tcx hir::Expr<'_>, - recv: &'tcx hir::Expr<'_>, - name: &str, - map_arg: &[hir::Expr<'_>], + expr: &'tcx Expr<'_>, + cloned_call: &'tcx Expr<'_>, + cloned_recv: &'tcx Expr<'_>, + is_count: bool, + needs_into_iter: bool, ) { - // Check if it's iterator and get type associated with `Item`. - let inner_ty = if_chain! { - if let Some(iterator_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator); - let recv_ty = cx.typeck_results().expr_ty(recv); - if implements_trait(cx, recv_ty, iterator_trait_id, &[]); - if let Some(inner_ty) = get_iterator_item_ty(cx, cx.typeck_results().expr_ty_adjusted(recv)); - then { - inner_ty - } else { + let typeck = cx.typeck_results(); + if let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator) + && let Some(method_id) = typeck.type_dependent_def_id(expr.hir_id) + && cx.tcx.trait_of_item(method_id) == Some(iter_id) + && let Some(method_id) = typeck.type_dependent_def_id(cloned_call.hir_id) + && cx.tcx.trait_of_item(method_id) == Some(iter_id) + && let cloned_recv_ty = typeck.expr_ty_adjusted(cloned_recv) + && let Some(iter_assoc_ty) = get_associated_type(cx, cloned_recv_ty, iter_id, "Item") + && matches!(*iter_assoc_ty.kind(), ty::Ref(_, ty, _) if !is_copy(cx, ty)) + { + if needs_into_iter + && let Some(into_iter_id) = cx.tcx.get_diagnostic_item(sym::IntoIterator) + && !implements_trait(cx, iter_assoc_ty, into_iter_id, &[]) + { return; } - }; - - match inner_ty.kind() { - ty::Ref(_, ty, _) if !is_copy(cx, *ty) => {}, - _ => return, - }; - let (lint, preserve_cloned) = match name { - "count" => (REDUNDANT_CLONE, false), - _ => (ITER_OVEREAGER_CLONED, true), - }; - let wildcard_params = map_arg.is_empty().not().then(|| "...").unwrap_or_default(); - let msg = format!( - "called `cloned().{}({})` on an `Iterator`. It may be more efficient to call `{}({}){}` instead", - name, - wildcard_params, - name, - wildcard_params, - preserve_cloned.then(|| ".cloned()").unwrap_or_default(), - ); + let (lint, msg, trailing_clone) = if is_count { + (REDUNDANT_CLONE, "unneeded cloning of iterator items", "") + } else { + (ITER_OVEREAGER_CLONED, "unnecessarily eager cloning of iterator items", ".cloned()") + }; - span_lint_and_sugg( - cx, - lint, - expr.span, - &msg, - "try this", - format!( - "{}.{}({}){}", - snippet(cx, recv.span, ".."), - name, - map_arg.iter().map(|a| snippet(cx, a.span, "..")).join(", "), - preserve_cloned.then(|| ".cloned()").unwrap_or_default(), - ), - Applicability::MachineApplicable, - ); + span_lint_and_then( + cx, + lint, + expr.span, + msg, + |diag| { + let method_span = expr.span.with_lo(cloned_call.span.hi()); + if let Some(mut snip) = snippet_opt(cx, method_span) { + snip.push_str(trailing_clone); + let replace_span = expr.span.with_lo(cloned_recv.span.hi()); + diag.span_suggestion(replace_span, "try this", snip, Applicability::MachineApplicable); + } + } + ); + } } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 7308e74c323e3..9bb7bb7a7aba4 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -124,28 +124,24 @@ declare_clippy_lint! { /// It's often inefficient to clone all elements of an iterator, when eventually, only some /// of them will be consumed. /// + /// ### Known Problems + /// This `lint` removes the side of effect of cloning items in the iterator. + /// A code that relies on that side-effect could fail. + /// /// ### Examples /// ```rust /// # let vec = vec!["string".to_string()]; - /// - /// // Bad /// vec.iter().cloned().take(10); - /// - /// // Good - /// vec.iter().take(10).cloned(); - /// - /// // Bad /// vec.iter().cloned().last(); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # let vec = vec!["string".to_string()]; + /// vec.iter().take(10).cloned(); /// vec.iter().last().cloned(); - /// /// ``` - /// ### Known Problems - /// This `lint` removes the side of effect of cloning items in the iterator. - /// A code that relies on that side-effect could fail. - /// - #[clippy::version = "1.59.0"] + #[clippy::version = "1.60.0"] pub ITER_OVEREAGER_CLONED, perf, "using `cloned()` early with `Iterator::iter()` can lead to some performance inefficiencies" @@ -342,11 +338,12 @@ declare_clippy_lint! { /// ### Example /// ```rust /// # let x = Ok::<_, ()>(()); - /// - /// // Bad /// x.ok().expect("why did I do this again?"); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # let x = Ok::<_, ()>(()); /// x.expect("why did I do this again?"); /// ``` #[clippy::version = "pre 1.29.0"] @@ -390,12 +387,13 @@ declare_clippy_lint! { /// ### Examples /// ```rust /// # let x = Some(1); - /// - /// // Bad /// x.unwrap_or_else(Default::default); /// x.unwrap_or_else(u32::default); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # let x = Some(1); /// x.unwrap_or_default(); /// ``` #[clippy::version = "1.56.0"] @@ -453,11 +451,12 @@ declare_clippy_lint! { /// ### Example /// ```rust /// # let opt = Some(1); - /// - /// // Bad /// opt.map_or(None, |a| Some(a + 1)); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # let opt = Some(1); /// opt.and_then(|a| Some(a + 1)); /// ``` #[clippy::version = "pre 1.29.0"] @@ -475,13 +474,12 @@ declare_clippy_lint! { /// `_.ok()`. /// /// ### Example - /// Bad: /// ```rust /// # let r: Result = Ok(1); /// assert_eq!(Some(1), r.map_or(None, Some)); /// ``` /// - /// Good: + /// Use instead: /// ```rust /// # let r: Result = Ok(1); /// assert_eq!(Some(1), r.ok()); @@ -538,7 +536,8 @@ declare_clippy_lint! { /// # let vec = vec![1]; /// vec.iter().filter(|x| **x == 0).next(); /// ``` - /// Could be written as + /// + /// Use instead: /// ```rust /// # let vec = vec![1]; /// vec.iter().find(|x| **x == 0); @@ -562,7 +561,8 @@ declare_clippy_lint! { /// # let vec = vec![1]; /// vec.iter().skip_while(|x| **x == 0).next(); /// ``` - /// Could be written as + /// + /// Use instead: /// ```rust /// # let vec = vec![1]; /// vec.iter().find(|x| **x != 0); @@ -586,11 +586,14 @@ declare_clippy_lint! { /// let vec = vec![vec![1]]; /// let opt = Some(5); /// - /// // Bad /// vec.iter().map(|x| x.iter()).flatten(); /// opt.map(|x| Some(x * 2)).flatten(); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # let vec = vec![vec![1]]; + /// # let opt = Some(5); /// vec.iter().flat_map(|x| x.iter()); /// opt.and_then(|x| Some(x * 2)); /// ``` @@ -610,15 +613,16 @@ declare_clippy_lint! { /// less performant. /// /// ### Example - /// Bad: /// ```rust + /// # #![allow(unused)] /// (0_i32..10) /// .filter(|n| n.checked_add(1).is_some()) /// .map(|n| n.checked_add(1).unwrap()); /// ``` /// - /// Good: + /// Use instead: /// ```rust + /// # #[allow(unused)] /// (0_i32..10).filter_map(|n| n.checked_add(1)); /// ``` #[clippy::version = "1.51.0"] @@ -637,14 +641,13 @@ declare_clippy_lint! { /// less performant. /// /// ### Example - /// Bad: /// ```rust /// (0_i32..10) /// .find(|n| n.checked_add(1).is_some()) /// .map(|n| n.checked_add(1).unwrap()); /// ``` /// - /// Good: + /// Use instead: /// ```rust /// (0_i32..10).find_map(|n| n.checked_add(1)); /// ``` @@ -712,17 +715,20 @@ declare_clippy_lint! { /// /// ### Example /// ```rust + /// # #![allow(unused)] /// let vec = vec![1]; /// vec.iter().find(|x| **x == 0).is_some(); /// - /// let _ = "hello world".find("world").is_none(); + /// "hello world".find("world").is_none(); /// ``` - /// Could be written as + /// + /// Use instead: /// ```rust /// let vec = vec![1]; /// vec.iter().any(|x| *x == 0); /// - /// let _ = !"hello world".contains("world"); + /// # #[allow(unused)] + /// !"hello world".contains("world"); /// ``` #[clippy::version = "pre 1.29.0"] pub SEARCH_IS_SOME, @@ -744,7 +750,8 @@ declare_clippy_lint! { /// let name = "foo"; /// if name.chars().next() == Some('_') {}; /// ``` - /// Could be written as + /// + /// Use instead: /// ```rust /// let name = "foo"; /// if name.starts_with('_') {}; @@ -899,10 +906,13 @@ declare_clippy_lint! { /// # use std::rc::Rc; /// let x = Rc::new(1); /// - /// // Bad /// x.clone(); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # use std::rc::Rc; + /// # let x = Rc::new(1); /// Rc::clone(&x); /// ``` #[clippy::version = "pre 1.29.0"] @@ -1034,11 +1044,13 @@ declare_clippy_lint! { /// /// ### Example /// ```rust,ignore - /// // Bad /// _.split("x"); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust,ignore /// _.split('x'); + /// ``` #[clippy::version = "pre 1.29.0"] pub SINGLE_CHAR_PATTERN, perf, @@ -1099,12 +1111,14 @@ declare_clippy_lint! { /// ### Example /// ```rust /// # use std::collections::HashSet; - /// // Bad /// # let mut s = HashSet::new(); /// # s.insert(1); /// let x = s.iter().nth(0); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # use std::collections::HashSet; /// # let mut s = HashSet::new(); /// # s.insert(1); /// let x = s.iter().next(); @@ -1210,11 +1224,12 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad /// let x = vec![2, 3, 5]; /// let last_element = x.get(x.len() - 1); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust /// let x = vec![2, 3, 5]; /// let last_element = x.last(); /// ``` @@ -1273,10 +1288,14 @@ declare_clippy_lint! { /// let mut a = vec![1, 2, 3]; /// let mut b = vec![4, 5, 6]; /// - /// // Bad /// a.extend(b.drain(..)); + /// ``` + /// + /// Use instead: + /// ```rust + /// let mut a = vec![1, 2, 3]; + /// let mut b = vec![4, 5, 6]; /// - /// // Good /// a.append(&mut b); /// ``` #[clippy::version = "1.55.0"] @@ -1351,11 +1370,12 @@ declare_clippy_lint! { /// ### Example /// ```rust /// # let name = "_"; - /// - /// // Bad /// name.chars().last() == Some('_') || name.chars().next_back() == Some('-'); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # let name = "_"; /// name.ends_with('_') || name.ends_with('-'); /// ``` #[clippy::version = "pre 1.29.0"] @@ -1401,11 +1421,13 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// let _ = (0..3).fold(false, |acc, x| acc || x > 2); + /// # #[allow(unused)] + /// (0..3).fold(false, |acc, x| acc || x > 2); /// ``` - /// This could be written as: + /// + /// Use instead: /// ```rust - /// let _ = (0..3).any(|x| x > 2); + /// (0..3).any(|x| x > 2); /// ``` #[clippy::version = "pre 1.29.0"] pub UNNECESSARY_FOLD, @@ -1485,11 +1507,14 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad - /// let _ = (&vec![3, 4, 5]).into_iter(); + /// # let vec = vec![3, 4, 5]; + /// (&vec).into_iter(); + /// ``` /// - /// // Good - /// let _ = (&vec![3, 4, 5]).iter(); + /// Use instead: + /// ```rust + /// # let vec = vec![3, 4, 5]; + /// (&vec).iter(); /// ``` #[clippy::version = "1.32.0"] pub INTO_ITER_ON_REF, @@ -1704,13 +1729,14 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// let mut string = String::new(); + /// # let mut string = String::new(); /// string.insert_str(0, "R"); /// string.push_str("R"); /// ``` - /// Could be written as + /// + /// Use instead: /// ```rust - /// let mut string = String::new(); + /// # let mut string = String::new(); /// string.insert(0, 'R'); /// string.push('R'); /// ``` @@ -1881,7 +1907,7 @@ declare_clippy_lint! { /// let x = [1, 2, 3]; /// let y: Vec<_> = x.iter().map(|x| 2*x).collect(); /// ``` - #[clippy::version = "1.52.0"] + #[clippy::version = "1.47.0"] pub MAP_IDENTITY, complexity, "using iterator.map(|x| x)" @@ -1897,11 +1923,14 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad - /// let _ = "Hello".bytes().nth(3); + /// # #[allow(unused)] + /// "Hello".bytes().nth(3); + /// ``` /// - /// // Good - /// let _ = "Hello".as_bytes().get(3); + /// Use instead: + /// ```rust + /// # #[allow(unused)] + /// "Hello".as_bytes().get(3); /// ``` #[clippy::version = "1.52.0"] pub BYTES_NTH, @@ -1945,15 +1974,19 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad + /// # #![allow(unused)] /// let some_vec = vec![0, 1, 2, 3]; - /// let _ = some_vec.iter().count(); - /// let _ = &some_vec[..].iter().count(); /// - /// // Good + /// some_vec.iter().count(); + /// &some_vec[..].iter().count(); + /// ``` + /// + /// Use instead: + /// ```rust /// let some_vec = vec![0, 1, 2, 3]; - /// let _ = some_vec.len(); - /// let _ = &some_vec[..].len(); + /// + /// some_vec.len(); + /// &some_vec[..].len(); /// ``` #[clippy::version = "1.52.0"] pub ITER_COUNT, @@ -1973,16 +2006,17 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad - /// let s = ""; + /// # let s = ""; /// for x in s.splitn(1, ":") { - /// // use x + /// // .. /// } + /// ``` /// - /// // Good - /// let s = ""; + /// Use instead: + /// ```rust + /// # let s = ""; /// for x in s.splitn(2, ":") { - /// // use x + /// // .. /// } /// ``` #[clippy::version = "1.54.0"] @@ -2000,10 +2034,11 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad /// let x: String = std::iter::repeat('x').take(10).collect(); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust /// let x: String = "x".repeat(10); /// ``` #[clippy::version = "1.54.0"] @@ -2021,7 +2056,6 @@ declare_clippy_lint! { /// /// ### Example /// ```rust,ignore - /// // Bad /// let s = "key=value=add"; /// let (key, value) = s.splitn(2, '=').next_tuple()?; /// let value = s.splitn(2, '=').nth(1)?; @@ -2030,9 +2064,9 @@ declare_clippy_lint! { /// let key = parts.next()?; /// let value = parts.next()?; /// ``` + /// /// Use instead: /// ```rust,ignore - /// // Good /// let s = "key=value=add"; /// let (key, value) = s.split_once('=')?; /// let value = s.split_once('=')?.1; @@ -2057,17 +2091,16 @@ declare_clippy_lint! { /// that both functions return a lazy iterator. /// ### Example /// ```rust - /// // Bad /// let str = "key=value=add"; /// let _ = str.splitn(3, '=').next().unwrap(); /// ``` + /// /// Use instead: /// ```rust - /// // Good /// let str = "key=value=add"; /// let _ = str.split('=').next().unwrap(); /// ``` - #[clippy::version = "1.58.0"] + #[clippy::version = "1.59.0"] pub NEEDLESS_SPLITN, complexity, "usages of `str::splitn` that can be replaced with `str::split`" @@ -2098,7 +2131,7 @@ declare_clippy_lint! { /// foo(&path.to_string_lossy()); /// fn foo(s: &str) {} /// ``` - #[clippy::version = "1.58.0"] + #[clippy::version = "1.59.0"] pub UNNECESSARY_TO_OWNED, perf, "unnecessary calls to `to_owned`-like functions" @@ -2149,7 +2182,8 @@ declare_clippy_lint! { /// let a = Some(&1); /// let b = a.as_deref(); // goes from Option<&i32> to Option<&i32> /// ``` - /// Could be written as: + /// + /// Use instead: /// ```rust /// let a = Some(&1); /// let b = a; @@ -2583,8 +2617,8 @@ impl Methods { }, _ => {}, }, - (name @ "count", args @ []) => match method_call(recv) { - Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv2, name, args), + ("count", []) => match method_call(recv) { + Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, true, false), Some((name2 @ ("into_iter" | "iter" | "iter_mut"), [recv2], _)) => { iter_count::check(cx, expr, recv2, name2); }, @@ -2614,9 +2648,9 @@ impl Methods { flat_map_identity::check(cx, expr, arg, span); flat_map_option::check(cx, expr, arg, span); }, - (name @ "flatten", args @ []) => match method_call(recv) { + ("flatten", []) => match method_call(recv) { Some(("map", [recv, map_arg], map_span)) => map_flatten::check(cx, expr, recv, map_arg, map_span), - Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv2, name, args), + Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, true), _ => {}, }, ("fold", [init, acc]) => unnecessary_fold::check(cx, expr, init, acc, span), @@ -2636,10 +2670,10 @@ impl Methods { unnecessary_join::check(cx, expr, recv, join_arg, span); } }, - ("last", args @ []) | ("skip", args @ [_]) => { + ("last", []) | ("skip", [_]) => { if let Some((name2, [recv2, args2 @ ..], _span2)) = method_call(recv) { if let ("cloned", []) = (name2, args2) { - iter_overeager_cloned::check(cx, expr, recv2, name, args); + iter_overeager_cloned::check(cx, expr, recv, recv2, false, false); } } }, @@ -2660,10 +2694,10 @@ impl Methods { map_identity::check(cx, expr, recv, m_arg, name, span); }, ("map_or", [def, map]) => option_map_or_none::check(cx, expr, recv, def, map), - (name @ "next", args @ []) => { + ("next", []) => { if let Some((name2, [recv2, args2 @ ..], _)) = method_call(recv) { match (name2, args2) { - ("cloned", []) => iter_overeager_cloned::check(cx, expr, recv2, name, args), + ("cloned", []) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, false), ("filter", [arg]) => filter_next::check(cx, expr, recv2, arg), ("filter_map", [arg]) => filter_map_next::check(cx, expr, recv2, arg, self.msrv), ("iter", []) => iter_next_slice::check(cx, expr, recv2), @@ -2673,9 +2707,9 @@ impl Methods { } } }, - ("nth", args @ [n_arg]) => match method_call(recv) { + ("nth", [n_arg]) => match method_call(recv) { Some(("bytes", [recv2], _)) => bytes_nth::check(cx, expr, recv2, n_arg), - Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv2, name, args), + Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, false), Some(("iter", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, false), Some(("iter_mut", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, true), _ => iter_nth_zero::check(cx, expr, recv, n_arg), @@ -2698,10 +2732,10 @@ impl Methods { } }, ("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg), - ("take", args @ [_arg]) => { + ("take", [_arg]) => { if let Some((name2, [recv2, args2 @ ..], _span2)) = method_call(recv) { if let ("cloned", []) = (name2, args2) { - iter_overeager_cloned::check(cx, expr, recv2, name, args); + iter_overeager_cloned::check(cx, expr, recv, recv2, false, false); } } }, diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index 5566569945322..01bf871198a5c 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -45,16 +45,13 @@ declare_clippy_lint! { /// dereferences, e.g., changing `*x` to `x` within the function. /// /// ### Example - /// ```rust,ignore - /// // Bad - /// fn foo(ref x: u8) -> bool { - /// true - /// } + /// ```rust + /// fn foo(ref _x: u8) {} + /// ``` /// - /// // Good - /// fn foo(x: &u8) -> bool { - /// true - /// } + /// Use instead: + /// ```rust + /// fn foo(_x: &u8) {} /// ``` #[clippy::version = "pre 1.29.0"] pub TOPLEVEL_REF_ARG, @@ -73,11 +70,12 @@ declare_clippy_lint! { /// ### Example /// ```rust /// # let x = 1.0; - /// - /// // Bad /// if x == f32::NAN { } + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # let x = 1.0f32; /// if x.is_nan() { } /// ``` #[clippy::version = "pre 1.29.0"] @@ -139,7 +137,8 @@ declare_clippy_lint! { /// # let y = String::from("foo"); /// if x.to_owned() == y {} /// ``` - /// Could be written as + /// + /// Use instead: /// ```rust /// # let x = "foo"; /// # let y = String::from("foo"); @@ -232,10 +231,11 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad /// let a = 0 as *const u32; + /// ``` /// - /// // Good + /// Use instead: + /// ```rust /// let a = std::ptr::null::(); /// ``` #[clippy::version = "pre 1.29.0"] diff --git a/clippy_lints/src/misc_early/mod.rs b/clippy_lints/src/misc_early/mod.rs index 6860b60acbdb4..704918c0b979b 100644 --- a/clippy_lints/src/misc_early/mod.rs +++ b/clippy_lints/src/misc_early/mod.rs @@ -34,13 +34,21 @@ declare_clippy_lint! { /// # } /// let f = Foo { a: 0, b: 0, c: 0 }; /// - /// // Bad /// match f { /// Foo { a: _, b: 0, .. } => {}, /// Foo { a: _, b: _, c: _ } => {}, /// } + /// ``` + /// + /// Use instead: + /// ```rust + /// # struct Foo { + /// # a: i32, + /// # b: i32, + /// # c: i32, + /// # } + /// let f = Foo { a: 0, b: 0, c: 0 }; /// - /// // Good /// match f { /// Foo { b: 0, .. } => {}, /// Foo { .. } => {}, @@ -62,10 +70,11 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad /// fn foo(a: i32, _a: i32) {} + /// ``` /// - /// // Good + /// Use instead: + /// ```rust /// fn bar(a: i32, _b: i32) {} /// ``` #[clippy::version = "pre 1.29.0"] @@ -103,11 +112,16 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad - /// let y = 0x1a9BAcD; + /// # let _ = + /// 0x1a9BAcD + /// # ; + /// ``` /// - /// // Good - /// let y = 0x1A9BACD; + /// Use instead: + /// ```rust + /// # let _ = + /// 0x1A9BACD + /// # ; /// ``` #[clippy::version = "pre 1.29.0"] pub MIXED_CASE_HEX_LITERALS, @@ -127,11 +141,16 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad - /// let y = 123832i32; + /// # let _ = + /// 123832i32 + /// # ; + /// ``` /// - /// // Good - /// let y = 123832_i32; + /// Use instead: + /// ```rust + /// # let _ = + /// 123832_i32 + /// # ; /// ``` #[clippy::version = "pre 1.29.0"] pub UNSEPARATED_LITERAL_SUFFIX, @@ -150,11 +169,16 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad - /// let y = 123832_i32; + /// # let _ = + /// 123832_i32 + /// # ; + /// ``` /// - /// // Good - /// let y = 123832i32; + /// Use instead: + /// ```rust + /// # let _ = + /// 123832i32 + /// # ; /// ``` #[clippy::version = "1.58.0"] pub SEPARATED_LITERAL_SUFFIX, @@ -234,14 +258,15 @@ declare_clippy_lint! { /// ### Example /// ```rust /// # let v = Some("abc"); - /// - /// // Bad /// match v { /// Some(x) => (), /// y @ _ => (), /// } + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # let v = Some("abc"); /// match v { /// Some(x) => (), /// y => (), @@ -262,6 +287,7 @@ declare_clippy_lint! { /// means there are 0 or more elements left. This can make a difference /// when refactoring, but shouldn't result in errors in the refactored code, /// since the wildcard pattern isn't used anyway. + /// /// ### Why is this bad? /// The wildcard pattern is unneeded as the rest pattern /// can match that element as well. @@ -270,13 +296,16 @@ declare_clippy_lint! { /// ```rust /// # struct TupleStruct(u32, u32, u32); /// # let t = TupleStruct(1, 2, 3); - /// // Bad /// match t { /// TupleStruct(0, .., _) => (), /// _ => (), /// } + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # struct TupleStruct(u32, u32, u32); + /// # let t = TupleStruct(1, 2, 3); /// match t { /// TupleStruct(0, ..) => (), /// _ => (), diff --git a/clippy_lints/src/mixed_read_write_in_expression.rs b/clippy_lints/src/mixed_read_write_in_expression.rs index c3b850fbb9dcc..a2419c277e9c2 100644 --- a/clippy_lints/src/mixed_read_write_in_expression.rs +++ b/clippy_lints/src/mixed_read_write_in_expression.rs @@ -25,14 +25,16 @@ declare_clippy_lint! { /// ```rust /// let mut x = 0; /// - /// // Bad /// let a = { /// x = 1; /// 1 /// } + x; /// // Unclear whether a is 1 or 2. + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # let mut x = 0; /// let tmp = { /// x = 1; /// 1 diff --git a/clippy_lints/src/mut_reference.rs b/clippy_lints/src/mut_reference.rs index 9d8f8999ce409..f434a655f8aff 100644 --- a/clippy_lints/src/mut_reference.rs +++ b/clippy_lints/src/mut_reference.rs @@ -16,12 +16,17 @@ declare_clippy_lint! { /// the value. Also the code misleads about the intent of the call site. /// /// ### Example - /// ```ignore - /// // Bad - /// my_vec.push(&mut value) + /// ```rust + /// # let mut vec = Vec::new(); + /// # let mut value = 5; + /// vec.push(&mut value); + /// ``` /// - /// // Good - /// my_vec.push(&value) + /// Use instead: + /// ```rust + /// # let mut vec = Vec::new(); + /// # let value = 5; + /// vec.push(&value); /// ``` #[clippy::version = "pre 1.29.0"] pub UNNECESSARY_MUT_PASSED, diff --git a/clippy_lints/src/mutex_atomic.rs b/clippy_lints/src/mutex_atomic.rs index 73823779e493d..a98577093ed5e 100644 --- a/clippy_lints/src/mutex_atomic.rs +++ b/clippy_lints/src/mutex_atomic.rs @@ -27,12 +27,13 @@ declare_clippy_lint! { /// ### Example /// ```rust /// # let y = true; - /// - /// // Bad /// # use std::sync::Mutex; /// let x = Mutex::new(&y); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # let y = true; /// # use std::sync::atomic::AtomicBool; /// let x = AtomicBool::new(y); /// ``` @@ -60,8 +61,10 @@ declare_clippy_lint! { /// ```rust /// # use std::sync::Mutex; /// let x = Mutex::new(0usize); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust /// # use std::sync::atomic::AtomicUsize; /// let x = AtomicUsize::new(0usize); /// ``` diff --git a/clippy_lints/src/needless_bool.rs b/clippy_lints/src/needless_bool.rs index 778d49cb4b6ed..a4eec95b37159 100644 --- a/clippy_lints/src/needless_bool.rs +++ b/clippy_lints/src/needless_bool.rs @@ -30,16 +30,21 @@ declare_clippy_lint! { /// shorter code. /// /// ### Example - /// ```rust,ignore + /// ```rust + /// # let x = true; /// if x { /// false /// } else { /// true /// } + /// # ; /// ``` - /// Could be written as - /// ```rust,ignore + /// + /// Use instead: + /// ```rust + /// # let x = true; /// !x + /// # ; /// ``` #[clippy::version = "pre 1.29.0"] pub NEEDLESS_BOOL, diff --git a/clippy_lints/src/needless_borrowed_ref.rs b/clippy_lints/src/needless_borrowed_ref.rs index 0fcc419e72227..05c012b92e878 100644 --- a/clippy_lints/src/needless_borrowed_ref.rs +++ b/clippy_lints/src/needless_borrowed_ref.rs @@ -27,16 +27,17 @@ declare_clippy_lint! { /// ``` /// /// ### Example - /// Bad: /// ```rust /// let mut v = Vec::::new(); - /// let _ = v.iter_mut().filter(|&ref a| a.is_empty()); + /// # #[allow(unused)] + /// v.iter_mut().filter(|&ref a| a.is_empty()); /// ``` /// - /// Good: + /// Use instead: /// ```rust /// let mut v = Vec::::new(); - /// let _ = v.iter_mut().filter(|a| a.is_empty()); + /// # #[allow(unused)] + /// v.iter_mut().filter(|a| a.is_empty()); /// ``` #[clippy::version = "pre 1.29.0"] pub NEEDLESS_BORROWED_REFERENCE, diff --git a/clippy_lints/src/needless_late_init.rs b/clippy_lints/src/needless_late_init.rs index 1f8c4c85cc2e1..ff2999b1f4a51 100644 --- a/clippy_lints/src/needless_late_init.rs +++ b/clippy_lints/src/needless_late_init.rs @@ -56,7 +56,7 @@ declare_clippy_lint! { /// -1 /// }; /// ``` - #[clippy::version = "1.58.0"] + #[clippy::version = "1.59.0"] pub NEEDLESS_LATE_INIT, style, "late initializations that can be replaced by a `let` statement with an initializer" @@ -185,14 +185,14 @@ fn assignment_suggestions<'tcx>( let suggestions = assignments .iter() - .map(|assignment| Some((assignment.span.until(assignment.rhs_span), String::new()))) - .chain(assignments.iter().map(|assignment| { - Some(( + .flat_map(|assignment| { + [ + assignment.span.until(assignment.rhs_span), assignment.rhs_span.shrink_to_hi().with_hi(assignment.span.hi()), - String::new(), - )) - })) - .collect::>>()?; + ] + }) + .map(|span| (span, String::new())) + .collect::>(); match suggestions.len() { // All of `exprs` are never types diff --git a/clippy_lints/src/needless_parens_on_range_literals.rs b/clippy_lints/src/needless_parens_on_range_literals.rs new file mode 100644 index 0000000000000..6e54b243c0371 --- /dev/null +++ b/clippy_lints/src/needless_parens_on_range_literals.rs @@ -0,0 +1,87 @@ +use clippy_utils::{ + diagnostics::span_lint_and_then, + higher, + source::{snippet, snippet_with_applicability}, +}; + +use rustc_ast::ast; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; + +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// The lint checks for parenthesis on literals in range statements that are + /// superfluous. + /// + /// ### Why is this bad? + /// Having superfluous parenthesis makes the code less readable + /// overhead when reading. + /// + /// ### Example + /// + /// ```rust + /// for i in (0)..10 { + /// println!("{i}"); + /// } + /// ``` + /// + /// Use instead: + /// + /// ```rust + /// for i in 0..10 { + /// println!("{i}"); + /// } + /// ``` + #[clippy::version = "1.63.0"] + pub NEEDLESS_PARENS_ON_RANGE_LITERALS, + style, + "needless parenthesis on range literals can be removed" +} + +declare_lint_pass!(NeedlessParensOnRangeLiterals => [NEEDLESS_PARENS_ON_RANGE_LITERALS]); + +fn snippet_enclosed_in_parenthesis(snippet: &str) -> bool { + snippet.starts_with('(') && snippet.ends_with(')') +} + +fn check_for_parens(cx: &LateContext<'_>, e: &Expr<'_>, is_start: bool) { + if is_start && + let ExprKind::Lit(ref literal) = e.kind && + let ast::LitKind::Float(_sym, ast::LitFloatType::Unsuffixed) = literal.node + { + // don't check floating point literals on the start expression of a range + return; + } + if_chain! { + if let ExprKind::Lit(ref literal) = e.kind; + // the indicator that parenthesis surround the literal is that the span of the expression and the literal differ + if (literal.span.data().hi - literal.span.data().lo) != (e.span.data().hi - e.span.data().lo); + // inspect the source code of the expression for parenthesis + if snippet_enclosed_in_parenthesis(&snippet(cx, e.span, "")); + then { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_then(cx, NEEDLESS_PARENS_ON_RANGE_LITERALS, e.span, + "needless parenthesis on range literals can be removed", + |diag| { + let suggestion = snippet_with_applicability(cx, literal.span, "_", &mut applicability); + diag.span_suggestion(e.span, "try", suggestion, applicability); + }); + } + } +} + +impl<'tcx> LateLintPass<'tcx> for NeedlessParensOnRangeLiterals { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if let Some(higher::Range { start, end, .. }) = higher::Range::hir(expr) { + if let Some(start) = start { + check_for_parens(cx, start, true); + } + if let Some(end) = end { + check_for_parens(cx, end, false); + } + } + } +} diff --git a/clippy_lints/src/needless_update.rs b/clippy_lints/src/needless_update.rs index c87c174ef732c..0bd29d1776b28 100644 --- a/clippy_lints/src/needless_update.rs +++ b/clippy_lints/src/needless_update.rs @@ -24,16 +24,17 @@ declare_clippy_lint! { /// # z: i32, /// # } /// # let zero_point = Point { x: 0, y: 0, z: 0 }; - /// - /// // Bad /// Point { /// x: 1, /// y: 1, /// z: 1, /// ..zero_point /// }; + /// ``` /// - /// // Ok + /// Use instead: + /// ```rust,ignore + /// // Missing field `z` /// Point { /// x: 1, /// y: 1, diff --git a/clippy_lints/src/neg_cmp_op_on_partial_ord.rs b/clippy_lints/src/neg_cmp_op_on_partial_ord.rs index efe31a1544187..a7e0e35787cff 100644 --- a/clippy_lints/src/neg_cmp_op_on_partial_ord.rs +++ b/clippy_lints/src/neg_cmp_op_on_partial_ord.rs @@ -19,17 +19,17 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// use std::cmp::Ordering; - /// - /// // Bad /// let a = 1.0; /// let b = f64::NAN; /// - /// let _not_less_or_equal = !(a <= b); + /// let not_less_or_equal = !(a <= b); + /// ``` /// - /// // Good - /// let a = 1.0; - /// let b = f64::NAN; + /// Use instead: + /// ```rust + /// use std::cmp::Ordering; + /// # let a = 1.0; + /// # let b = f64::NAN; /// /// let _not_less_or_equal = match a.partial_cmp(&b) { /// None | Some(Ordering::Greater) => true, diff --git a/clippy_lints/src/neg_multiply.rs b/clippy_lints/src/neg_multiply.rs index 707f3b2181ac9..ce6bb38b7c0e9 100644 --- a/clippy_lints/src/neg_multiply.rs +++ b/clippy_lints/src/neg_multiply.rs @@ -19,12 +19,13 @@ declare_clippy_lint! { /// This only catches integers (for now). /// /// ### Example - /// ```ignore - /// // Bad + /// ```rust,ignore /// let a = x * -1; + /// ``` /// - /// // Good - /// let b = -x; + /// Use instead: + /// ```rust,ignore + /// let a = -x; /// ``` #[clippy::version = "pre 1.29.0"] pub NEG_MULTIPLY, diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index 7163cfe5e3a2c..1727275a4e06a 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -59,12 +59,14 @@ declare_clippy_lint! { /// ```rust /// use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; /// - /// // Bad. /// const CONST_ATOM: AtomicUsize = AtomicUsize::new(12); /// CONST_ATOM.store(6, SeqCst); // the content of the atomic is unchanged /// assert_eq!(CONST_ATOM.load(SeqCst), 12); // because the CONST_ATOM in these lines are distinct + /// ``` /// - /// // Good. + /// Use instead: + /// ```rust + /// # use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; /// static STATIC_ATOM: AtomicUsize = AtomicUsize::new(15); /// STATIC_ATOM.store(9, SeqCst); /// assert_eq!(STATIC_ATOM.load(SeqCst), 9); // use a `static` item to refer to the same instance @@ -105,11 +107,15 @@ declare_clippy_lint! { /// use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; /// const CONST_ATOM: AtomicUsize = AtomicUsize::new(12); /// - /// // Bad. /// CONST_ATOM.store(6, SeqCst); // the content of the atomic is unchanged /// assert_eq!(CONST_ATOM.load(SeqCst), 12); // because the CONST_ATOM in these lines are distinct + /// ``` + /// + /// Use instead: + /// ```rust + /// use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; + /// const CONST_ATOM: AtomicUsize = AtomicUsize::new(12); /// - /// // Good. /// static STATIC_ATOM: AtomicUsize = CONST_ATOM; /// STATIC_ATOM.store(9, SeqCst); /// assert_eq!(STATIC_ATOM.load(SeqCst), 9); // use a `static` item to refer to the same instance diff --git a/clippy_lints/src/octal_escapes.rs b/clippy_lints/src/octal_escapes.rs index e8532db4f711d..3ab4b6c4f6fab 100644 --- a/clippy_lints/src/octal_escapes.rs +++ b/clippy_lints/src/octal_escapes.rs @@ -33,15 +33,16 @@ declare_clippy_lint! { /// /// # Example /// ```rust - /// // Bad /// let one = "\033[1m Bold? \033[0m"; // \033 intended as escape /// let two = "\033\0"; // \033 intended as null-3-3 + /// ``` /// - /// // Good + /// Use instead: + /// ```rust /// let one = "\x1b[1mWill this be bold?\x1b[0m"; /// let two = "\x0033\x00"; /// ``` - #[clippy::version = "1.58.0"] + #[clippy::version = "1.59.0"] pub OCTAL_ESCAPES, suspicious, "string escape sequences looking like octal characters" diff --git a/clippy_lints/src/only_used_in_recursion.rs b/clippy_lints/src/only_used_in_recursion.rs index de5f77f3ad976..677ac998b5682 100644 --- a/clippy_lints/src/only_used_in_recursion.rs +++ b/clippy_lints/src/only_used_in_recursion.rs @@ -87,7 +87,7 @@ declare_clippy_lint! { /// # print!("{}", f(1)); /// # } /// ``` - #[clippy::version = "1.60.0"] + #[clippy::version = "1.61.0"] pub ONLY_USED_IN_RECURSION, nursery, "arguments that is only used in recursion can be removed" diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs index 5a93431f25a98..05ab62786f409 100644 --- a/clippy_lints/src/pass_by_ref_or_value.rs +++ b/clippy_lints/src/pass_by_ref_or_value.rs @@ -57,12 +57,11 @@ declare_clippy_lint! { /// ### Example /// /// ```rust - /// // Bad /// fn foo(v: &u32) {} /// ``` /// + /// Use instead: /// ```rust - /// // Better /// fn foo(v: u32) {} /// ``` #[clippy::version = "pre 1.29.0"] @@ -89,14 +88,13 @@ declare_clippy_lint! { /// #[derive(Clone, Copy)] /// struct TooLarge([u8; 2048]); /// - /// // Bad /// fn foo(v: TooLarge) {} /// ``` - /// ```rust - /// #[derive(Clone, Copy)] - /// struct TooLarge([u8; 2048]); /// - /// // Good + /// Use instead: + /// ```rust + /// # #[derive(Clone, Copy)] + /// # struct TooLarge([u8; 2048]); /// fn foo(v: &TooLarge) {} /// ``` #[clippy::version = "1.49.0"] diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 0b96f6ff68358..b06eba13d2fdb 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -48,10 +48,11 @@ declare_clippy_lint! { /// /// ### Example /// ```ignore - /// // Bad /// fn foo(&Vec) { .. } + /// ``` /// - /// // Good + /// Use instead: + /// ```ignore /// fn foo(&[u32]) { .. } /// ``` #[clippy::version = "pre 1.29.0"] @@ -70,15 +71,18 @@ declare_clippy_lint! { /// method instead /// /// ### Example - /// ```ignore - /// // Bad + /// ```rust,ignore + /// use std::ptr; + /// /// if x == ptr::null { - /// .. + /// // .. /// } + /// ``` /// - /// // Good + /// Use instead: + /// ```rust,ignore /// if x.is_null() { - /// .. + /// // .. /// } /// ``` #[clippy::version = "pre 1.29.0"] @@ -129,12 +133,12 @@ declare_clippy_lint! { /// /// ### Example /// ```ignore - /// // Bad. Undefined behavior + /// // Undefined behavior /// unsafe { std::slice::from_raw_parts(ptr::null(), 0); } /// ``` /// + /// Use instead: /// ```ignore - /// // Good /// unsafe { std::slice::from_raw_parts(NonNull::dangling().as_ptr(), 0); } /// ``` #[clippy::version = "1.53.0"] diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index eea036178b837..547d4da818727 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -27,12 +27,13 @@ declare_clippy_lint! { /// ### Example /// ```rust /// # let x = vec![1]; - /// x.iter().zip(0..x.len()); + /// let _ = x.iter().zip(0..x.len()); /// ``` - /// Could be written as + /// + /// Use instead: /// ```rust /// # let x = vec![1]; - /// x.iter().enumerate(); + /// let _ = x.iter().enumerate(); /// ``` #[clippy::version = "pre 1.29.0"] pub RANGE_ZIP_WITH_LEN, @@ -65,12 +66,21 @@ declare_clippy_lint! { /// ([#3307](https://github.com/rust-lang/rust-clippy/issues/3307)). /// /// ### Example - /// ```rust,ignore - /// for x..(y+1) { .. } + /// ```rust + /// # let x = 0; + /// # let y = 1; + /// for i in x..(y+1) { + /// // .. + /// } /// ``` - /// Could be written as - /// ```rust,ignore - /// for x..=y { .. } + /// + /// Use instead: + /// ```rust + /// # let x = 0; + /// # let y = 1; + /// for i in x..=y { + /// // .. + /// } /// ``` #[clippy::version = "pre 1.29.0"] pub RANGE_PLUS_ONE, @@ -94,12 +104,21 @@ declare_clippy_lint! { /// ([#3307](https://github.com/rust-lang/rust-clippy/issues/3307)). /// /// ### Example - /// ```rust,ignore - /// for x..=(y-1) { .. } + /// ```rust + /// # let x = 0; + /// # let y = 1; + /// for i in x..=(y-1) { + /// // .. + /// } /// ``` - /// Could be written as - /// ```rust,ignore - /// for x..y { .. } + /// + /// Use instead: + /// ```rust + /// # let x = 0; + /// # let y = 1; + /// for i in x..y { + /// // .. + /// } /// ``` #[clippy::version = "pre 1.29.0"] pub RANGE_MINUS_ONE, diff --git a/clippy_lints/src/read_zero_byte_vec.rs b/clippy_lints/src/read_zero_byte_vec.rs new file mode 100644 index 0000000000000..9538a8104739e --- /dev/null +++ b/clippy_lints/src/read_zero_byte_vec.rs @@ -0,0 +1,142 @@ +use clippy_utils::{ + diagnostics::{span_lint, span_lint_and_sugg}, + higher::{get_vec_init_kind, VecInitKind}, + source::snippet, + visitors::expr_visitor_no_bodies, +}; +use hir::{intravisit::Visitor, ExprKind, Local, PatKind, PathSegment, QPath, StmtKind}; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// This lint catches reads into a zero-length `Vec`. + /// Especially in the case of a call to `with_capacity`, this lint warns that read + /// gets the number of bytes from the `Vec`'s length, not its capacity. + /// + /// ### Why is this bad? + /// Reading zero bytes is almost certainly not the intended behavior. + /// + /// ### Known problems + /// In theory, a very unusual read implementation could assign some semantic meaning + /// to zero-byte reads. But it seems exceptionally unlikely that code intending to do + /// a zero-byte read would allocate a `Vec` for it. + /// + /// ### Example + /// ```rust + /// use std::io; + /// fn foo(mut f: F) { + /// let mut data = Vec::with_capacity(100); + /// f.read(&mut data).unwrap(); + /// } + /// ``` + /// Use instead: + /// ```rust + /// use std::io; + /// fn foo(mut f: F) { + /// let mut data = Vec::with_capacity(100); + /// data.resize(100, 0); + /// f.read(&mut data).unwrap(); + /// } + /// ``` + #[clippy::version = "1.63.0"] + pub READ_ZERO_BYTE_VEC, + correctness, + "checks for reads into a zero-length `Vec`" +} +declare_lint_pass!(ReadZeroByteVec => [READ_ZERO_BYTE_VEC]); + +impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec { + fn check_block(&mut self, cx: &LateContext<'tcx>, block: &hir::Block<'tcx>) { + for (idx, stmt) in block.stmts.iter().enumerate() { + if !stmt.span.from_expansion() + // matches `let v = Vec::new();` + && let StmtKind::Local(local) = stmt.kind + && let Local { pat, init: Some(init), .. } = local + && let PatKind::Binding(_, _, ident, _) = pat.kind + && let Some(vec_init_kind) = get_vec_init_kind(cx, init) + { + // finds use of `_.read(&mut v)` + let mut read_found = false; + let mut visitor = expr_visitor_no_bodies(|expr| { + if let ExprKind::MethodCall(path, [_self, arg], _) = expr.kind + && let PathSegment { ident: read_or_read_exact, .. } = *path + && matches!(read_or_read_exact.as_str(), "read" | "read_exact") + && let ExprKind::AddrOf(_, hir::Mutability::Mut, inner) = arg.kind + && let ExprKind::Path(QPath::Resolved(None, inner_path)) = inner.kind + && let [inner_seg] = inner_path.segments + && ident.name == inner_seg.ident.name + { + read_found = true; + } + !read_found + }); + + let next_stmt_span; + if idx == block.stmts.len() - 1 { + // case { .. stmt; expr } + if let Some(e) = block.expr { + visitor.visit_expr(e); + next_stmt_span = e.span; + } else { + return; + } + } else { + // case { .. stmt; stmt; .. } + let next_stmt = &block.stmts[idx + 1]; + visitor.visit_stmt(next_stmt); + next_stmt_span = next_stmt.span; + } + drop(visitor); + + if read_found && !next_stmt_span.from_expansion() { + let applicability = Applicability::MaybeIncorrect; + match vec_init_kind { + VecInitKind::WithConstCapacity(len) => { + span_lint_and_sugg( + cx, + READ_ZERO_BYTE_VEC, + next_stmt_span, + "reading zero byte data to `Vec`", + "try", + format!("{}.resize({}, 0); {}", + ident.as_str(), + len, + snippet(cx, next_stmt_span, "..") + ), + applicability, + ); + } + VecInitKind::WithExprCapacity(hir_id) => { + let e = cx.tcx.hir().expect_expr(hir_id); + span_lint_and_sugg( + cx, + READ_ZERO_BYTE_VEC, + next_stmt_span, + "reading zero byte data to `Vec`", + "try", + format!("{}.resize({}, 0); {}", + ident.as_str(), + snippet(cx, e.span, ".."), + snippet(cx, next_stmt_span, "..") + ), + applicability, + ); + } + _ => { + span_lint( + cx, + READ_ZERO_BYTE_VEC, + next_stmt_span, + "reading zero byte data to `Vec`", + ); + + } + } + } + } + } + } +} diff --git a/clippy_lints/src/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs index 4c2016fe3f723..65ed798867d19 100644 --- a/clippy_lints/src/redundant_closure_call.rs +++ b/clippy_lints/src/redundant_closure_call.rs @@ -23,12 +23,13 @@ declare_clippy_lint! { /// complexity. /// /// ### Example - /// ```rust,ignore - /// // Bad - /// let a = (|| 42)() + /// ```rust + /// let a = (|| 42)(); + /// ``` /// - /// // Good - /// let a = 42 + /// Use instead: + /// ```rust + /// let a = 42; /// ``` #[clippy::version = "pre 1.29.0"] pub REDUNDANT_CLOSURE_CALL, diff --git a/clippy_lints/src/redundant_slicing.rs b/clippy_lints/src/redundant_slicing.rs index 25a9072ef6e0c..db6c97f3739c7 100644 --- a/clippy_lints/src/redundant_slicing.rs +++ b/clippy_lints/src/redundant_slicing.rs @@ -60,7 +60,7 @@ declare_clippy_lint! { /// let vec = vec![1, 2, 3]; /// let slice = &*vec; /// ``` - #[clippy::version = "1.60.0"] + #[clippy::version = "1.61.0"] pub DEREF_BY_SLICING, restriction, "slicing instead of dereferencing" diff --git a/clippy_lints/src/reference.rs b/clippy_lints/src/reference.rs index f789cec6d6acf..a642e2da3ba12 100644 --- a/clippy_lints/src/reference.rs +++ b/clippy_lints/src/reference.rs @@ -21,11 +21,12 @@ declare_clippy_lint! { /// /// ### Example /// ```rust,ignore - /// // Bad /// let a = f(*&mut b); /// let c = *&d; + /// ``` /// - /// // Good + /// Use instead: + /// ```rust,ignore /// let a = f(b); /// let c = d; /// ``` diff --git a/clippy_lints/src/return_self_not_must_use.rs b/clippy_lints/src/return_self_not_must_use.rs index 91e5e1e8b2892..60be6bd335f68 100644 --- a/clippy_lints/src/return_self_not_must_use.rs +++ b/clippy_lints/src/return_self_not_must_use.rs @@ -26,19 +26,20 @@ declare_clippy_lint! { /// if it was added on constructors for example. /// /// ### Example - /// Missing attribute /// ```rust /// pub struct Bar; /// impl Bar { - /// // Bad + /// // Missing attribute /// pub fn bar(&self) -> Self { /// Self /// } /// } /// ``` /// - /// It's better to have the `#[must_use]` attribute on the method like this: + /// Use instead: /// ```rust + /// # { + /// // It's better to have the `#[must_use]` attribute on the method like this: /// pub struct Bar; /// impl Bar { /// #[must_use] @@ -46,10 +47,10 @@ declare_clippy_lint! { /// Self /// } /// } - /// ``` + /// # } /// - /// Or on the type definition like this: - /// ```rust + /// # { + /// // Or on the type definition like this: /// #[must_use] /// pub struct Bar; /// impl Bar { @@ -57,6 +58,7 @@ declare_clippy_lint! { /// Self /// } /// } + /// # } /// ``` #[clippy::version = "1.59.0"] pub RETURN_SELF_NOT_MUST_USE, diff --git a/clippy_lints/src/same_name_method.rs b/clippy_lints/src/same_name_method.rs index c5c174cc8f614..20184d54b76e6 100644 --- a/clippy_lints/src/same_name_method.rs +++ b/clippy_lints/src/same_name_method.rs @@ -1,7 +1,7 @@ -use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::diagnostics::span_lint_hir_and_then; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{Impl, ItemKind, Node, Path, QPath, TraitRef, TyKind}; +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, declare_tool_lint}; @@ -42,11 +42,12 @@ declare_clippy_lint! { declare_lint_pass!(SameNameMethod => [SAME_NAME_METHOD]); struct ExistingName { - impl_methods: BTreeMap, + impl_methods: BTreeMap, trait_methods: BTreeMap>, } impl<'tcx> LateLintPass<'tcx> for SameNameMethod { + #[expect(clippy::too_many_lines)] fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { let mut map = FxHashMap::::default(); @@ -97,10 +98,11 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod { }; let mut check_trait_method = |method_name: Symbol, trait_method_span: Span| { - if let Some(impl_span) = existing_name.impl_methods.get(&method_name) { - span_lint_and_then( + if let Some((impl_span, hir_id)) = existing_name.impl_methods.get(&method_name) { + span_lint_hir_and_then( cx, SAME_NAME_METHOD, + *hir_id, *impl_span, "method's name is the same as an existing method in a trait", |diag| { @@ -136,10 +138,12 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod { }) { let method_name = impl_item_ref.ident.name; let impl_span = impl_item_ref.span; + let hir_id = impl_item_ref.id.hir_id(); if let Some(trait_spans) = existing_name.trait_methods.get(&method_name) { - span_lint_and_then( + span_lint_hir_and_then( cx, SAME_NAME_METHOD, + hir_id, impl_span, "method's name is the same as an existing method in a trait", |diag| { @@ -152,7 +156,7 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod { }, ); } - existing_name.impl_methods.insert(method_name, impl_span); + existing_name.impl_methods.insert(method_name, (impl_span, hir_id)); } }, } diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index 4f74c1e44c26d..bf318c055dad1 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -23,10 +23,12 @@ declare_clippy_lint! { /// ### Example /// ```rust /// # let x = 1; - /// // Bad /// let x = &x; + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # let x = 1; /// let y = &x; // use different variable name /// ``` #[clippy::version = "pre 1.29.0"] @@ -79,11 +81,14 @@ declare_clippy_lint! { /// # let y = 1; /// # let z = 2; /// let x = y; - /// - /// // Bad /// let x = z; // shadows the earlier binding + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # let y = 1; + /// # let z = 2; + /// let x = y; /// let w = z; // use different variable name /// ``` #[clippy::version = "pre 1.29.0"] diff --git a/clippy_lints/src/single_char_lifetime_names.rs b/clippy_lints/src/single_char_lifetime_names.rs index aa306a630c467..3dc995e2fa577 100644 --- a/clippy_lints/src/single_char_lifetime_names.rs +++ b/clippy_lints/src/single_char_lifetime_names.rs @@ -33,7 +33,7 @@ declare_clippy_lint! { /// source: &'src str, /// } /// ``` - #[clippy::version = "1.59.0"] + #[clippy::version = "1.60.0"] pub SINGLE_CHAR_LIFETIME_NAMES, restriction, "warns against single-character lifetime names" diff --git a/clippy_lints/src/slow_vector_initialization.rs b/clippy_lints/src/slow_vector_initialization.rs index b4ad5dcbe3e9a..975a0a06e3885 100644 --- a/clippy_lints/src/slow_vector_initialization.rs +++ b/clippy_lints/src/slow_vector_initialization.rs @@ -23,15 +23,16 @@ declare_clippy_lint! { /// ```rust /// # use core::iter::repeat; /// # let len = 4; - /// - /// // Bad /// let mut vec1 = Vec::with_capacity(len); /// vec1.resize(len, 0); /// /// let mut vec2 = Vec::with_capacity(len); /// vec2.extend(repeat(0).take(len)); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # let len = 4; /// let mut vec1 = vec![0; len]; /// let mut vec2 = vec![0; len]; /// ``` diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 7c196ccaa8ccd..71f3e6b6a6ec6 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -99,11 +99,12 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad - /// let bs = "a byte string".as_bytes(); + /// let bstr = "a byte string".as_bytes(); + /// ``` /// - /// // Good - /// let bs = b"a byte string"; + /// Use instead: + /// ```rust + /// let bstr = b"a byte string"; /// ``` #[clippy::version = "pre 1.29.0"] pub STRING_LIT_AS_BYTES, @@ -223,11 +224,12 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// let _ = std::str::from_utf8(&"Hello World!".as_bytes()[6..11]).unwrap(); + /// std::str::from_utf8(&"Hello World!".as_bytes()[6..11]).unwrap(); /// ``` - /// could be written as + /// + /// Use instead: /// ```rust - /// let _ = &"Hello World!"[6..11]; + /// &"Hello World!"[6..11]; /// ``` #[clippy::version = "1.50.0"] pub STRING_FROM_UTF8_AS_BYTES, diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index 1e5b646f5f055..ac63d1823371a 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -29,8 +29,7 @@ declare_clippy_lint! { /// pub fn foo(t: T) where T: Copy, T: Clone {} /// ``` /// - /// Could be written as: - /// + /// Use instead: /// ```rust /// pub fn foo(t: T) where T: Copy + Clone {} /// ``` diff --git a/clippy_lints/src/transmute/useless_transmute.rs b/clippy_lints/src/transmute/useless_transmute.rs index fc9227b76f025..8ea985a898431 100644 --- a/clippy_lints/src/transmute/useless_transmute.rs +++ b/clippy_lints/src/transmute/useless_transmute.rs @@ -61,12 +61,7 @@ pub(super) fn check<'tcx>( "transmute from an integer to a pointer", |diag| { if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) { - diag.span_suggestion( - e.span, - "try", - arg.as_ty(&to_ty.to_string()), - Applicability::Unspecified, - ); + diag.span_suggestion(e.span, "try", arg.as_ty(&to_ty.to_string()), Applicability::Unspecified); } }, ); diff --git a/clippy_lints/src/unicode.rs b/clippy_lints/src/unicode.rs index afd7be89a4e28..cc64d17be0552 100644 --- a/clippy_lints/src/unicode.rs +++ b/clippy_lints/src/unicode.rs @@ -41,7 +41,8 @@ declare_clippy_lint! { /// ```rust /// let x = String::from("€"); /// ``` - /// Could be written as: + /// + /// Use instead: /// ```rust /// let x = String::from("\u{20ac}"); /// ``` diff --git a/clippy_lints/src/unused_async.rs b/clippy_lints/src/unused_async.rs index 41333bb2addf7..c8ec4442ab1a4 100644 --- a/clippy_lints/src/unused_async.rs +++ b/clippy_lints/src/unused_async.rs @@ -17,13 +17,14 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad /// async fn get_random_number() -> i64 { /// 4 // Chosen by fair dice roll. Guaranteed to be random. /// } /// let number_future = get_random_number(); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust /// fn get_random_number_improved() -> i64 { /// 4 // Chosen by fair dice roll. Guaranteed to be random. /// } diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 4a3b5383c892b..fe29bf29d0caf 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -21,11 +21,12 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad /// // format!() returns a `String` /// let s: String = format!("hello").into(); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust /// let s: String = format!("hello"); /// ``` #[clippy::version = "1.45.0"] diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index b5c5d35135f90..38e5c5e5b7365 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -9,6 +9,29 @@ use std::path::{Path, PathBuf}; use std::str::FromStr; use std::{cmp, env, fmt, fs, io, iter}; +#[rustfmt::skip] +const DEFAULT_DOC_VALID_IDENTS: &[&str] = &[ + "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", + "DirectX", + "ECMAScript", + "GPLv2", "GPLv3", + "GitHub", "GitLab", + "IPv4", "IPv6", + "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript", + "NaN", "NaNs", + "OAuth", "GraphQL", + "OCaml", + "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenDNS", + "WebGL", + "TensorFlow", + "TrueType", + "iOS", "macOS", "FreeBSD", + "TeX", "LaTeX", "BibTeX", "BibLaTeX", + "MinGW", + "CamelCase", +]; +const DEFAULT_BLACKLISTED_NAMES: &[&str] = &["foo", "baz", "quux"]; + /// Holds information used by `MISSING_ENFORCED_IMPORT_RENAMES` lint. #[derive(Clone, Debug, Deserialize)] pub struct Rename { @@ -178,8 +201,10 @@ define_Conf! { (msrv: Option = None), /// Lint: BLACKLISTED_NAME. /// - /// The list of blacklisted names to lint about. NB: `bar` is not here since it has legitimate uses - (blacklisted_names: Vec = ["foo", "baz", "quux"].iter().map(ToString::to_string).collect()), + /// The list of blacklisted names to lint about. NB: `bar` is not here since it has legitimate uses. The value + /// `".."` can be used as part of the list to indicate, that the configured values should be appended to the + /// default configuration of Clippy. By default any configuraction will replace the default value. + (blacklisted_names: Vec = super::DEFAULT_BLACKLISTED_NAMES.iter().map(ToString::to_string).collect()), /// Lint: COGNITIVE_COMPLEXITY. /// /// The maximum cognitive complexity a function can have @@ -191,27 +216,14 @@ define_Conf! { (cyclomatic_complexity_threshold: Option = None), /// Lint: DOC_MARKDOWN. /// - /// The list of words this lint should not consider as identifiers needing ticks - (doc_valid_idents: Vec = [ - "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", - "DirectX", - "ECMAScript", - "GPLv2", "GPLv3", - "GitHub", "GitLab", - "IPv4", "IPv6", - "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript", - "NaN", "NaNs", - "OAuth", "GraphQL", - "OCaml", - "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenDNS", - "WebGL", - "TensorFlow", - "TrueType", - "iOS", "macOS", "FreeBSD", - "TeX", "LaTeX", "BibTeX", "BibLaTeX", - "MinGW", - "CamelCase", - ].iter().map(ToString::to_string).collect()), + /// The list of words this lint should not consider as identifiers needing ticks. The value + /// `".."` can be used as part of the list to indicate, that the configured values should be appended to the + /// default configuration of Clippy. By default any configuraction will replace the default value. For example: + /// * `doc-valid-idents = ["ClipPy"]` would replace the default list with `["ClipPy"]`. + /// * `doc-valid-idents = ["ClipPy", ".."]` would append `ClipPy` to the default list. + /// + /// Default list: + (doc_valid_idents: Vec = super::DEFAULT_DOC_VALID_IDENTS.iter().map(ToString::to_string).collect()), /// Lint: TOO_MANY_ARGUMENTS. /// /// The maximum number of argument a function or method can have @@ -401,7 +413,21 @@ pub fn read(path: &Path) -> TryConf { Err(e) => return TryConf::from_error(e), Ok(content) => content, }; - toml::from_str(&content).unwrap_or_else(TryConf::from_error) + match toml::from_str::(&content) { + Ok(mut conf) => { + extend_vec_if_indicator_present(&mut conf.conf.doc_valid_idents, DEFAULT_DOC_VALID_IDENTS); + extend_vec_if_indicator_present(&mut conf.conf.blacklisted_names, DEFAULT_BLACKLISTED_NAMES); + + conf + }, + Err(e) => TryConf::from_error(e), + } +} + +fn extend_vec_if_indicator_present(vec: &mut Vec, default: &[&str]) { + if vec.contains(&"..".to_string()) { + vec.extend(default.iter().map(ToString::to_string)); + } } const SEPARATOR_WIDTH: usize = 4; diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 60f9887699498..b885e5132f1ec 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -89,12 +89,11 @@ declare_clippy_lint! { /// warning/error messages. /// /// ### Example - /// Bad: /// ```rust,ignore /// cx.span_lint(LINT_NAME, "message"); /// ``` /// - /// Good: + /// Use instead: /// ```rust,ignore /// utils::span_lint(cx, LINT_NAME, "message"); /// ``` @@ -112,12 +111,11 @@ declare_clippy_lint! { /// `cx.outer_expn_data()` is faster and more concise. /// /// ### Example - /// Bad: /// ```rust,ignore /// expr.span.ctxt().outer().expn_data() /// ``` /// - /// Good: + /// Use instead: /// ```rust,ignore /// expr.span.ctxt().outer_expn_data() /// ``` @@ -135,7 +133,6 @@ declare_clippy_lint! { /// ICE in large quantities can damage your teeth /// /// ### Example - /// Bad: /// ```rust,ignore /// 🍦🍦🍦🍦🍦 /// ``` @@ -153,12 +150,11 @@ declare_clippy_lint! { /// Indicates that the lint is not finished. /// /// ### Example - /// Bad: /// ```rust,ignore /// declare_lint! { pub COOL_LINT, nursery, "default lint description" } /// ``` /// - /// Good: + /// Use instead: /// ```rust,ignore /// declare_lint! { pub COOL_LINT, nursery, "a great new lint" } /// ``` @@ -183,7 +179,6 @@ declare_clippy_lint! { /// convenient, readable and less error prone. /// /// ### Example - /// Bad: /// ```rust,ignore /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| { /// diag.span_suggestion( @@ -207,7 +202,7 @@ declare_clippy_lint! { /// }); /// ``` /// - /// Good: + /// Use instead: /// ```rust,ignore /// span_lint_and_sugg( /// cx, @@ -237,12 +232,11 @@ declare_clippy_lint! { /// `utils::is_type_diagnostic_item()` does not require hardcoded paths. /// /// ### Example - /// Bad: /// ```rust,ignore /// utils::match_type(cx, ty, &paths::VEC) /// ``` /// - /// Good: + /// Use instead: /// ```rust,ignore /// utils::is_type_diagnostic_item(cx, ty, sym::Vec) /// ``` @@ -273,12 +267,11 @@ declare_clippy_lint! { /// It's faster and easier to use the symbol constant. /// /// ### Example - /// Bad: /// ```rust,ignore /// let _ = sym!(f32); /// ``` /// - /// Good: + /// Use instead: /// ```rust,ignore /// let _ = sym::f32; /// ``` @@ -295,12 +288,11 @@ declare_clippy_lint! { /// It's faster use symbols directly instead of strings. /// /// ### Example - /// Bad: /// ```rust,ignore /// symbol.as_str() == "clippy"; /// ``` /// - /// Good: + /// Use instead: /// ```rust,ignore /// symbol == sym::clippy; /// ``` @@ -672,8 +664,8 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls { if let ExprKind::Call(func, and_then_args) = expr.kind; if is_expr_path_def_path(cx, func, &["clippy_utils", "diagnostics", "span_lint_and_then"]); if and_then_args.len() == 5; - if let ExprKind::Closure(_, _, body_id, _, _) = &and_then_args[4].kind; - let body = cx.tcx.hir().body(*body_id); + if let ExprKind::Closure { body, .. } = &and_then_args[4].kind; + let body = cx.tcx.hir().body(*body); let only_expr = peel_blocks_with_stmt(&body.value); if let ExprKind::MethodCall(ps, span_call_args, _) = &only_expr.kind; then { diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index cf2de6a42af36..99e9e3275ab53 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -31,6 +31,8 @@ use std::fmt::Write as _; use std::fs::{self, OpenOptions}; use std::io::prelude::*; use std::path::Path; +use std::path::PathBuf; +use std::process::Command; /// This is the output file of the lint collector. const OUTPUT_FILE: &str = "../util/gh-pages/lints.json"; @@ -180,6 +182,7 @@ pub struct MetadataCollector { lints: BinaryHeap, applicability_info: FxHashMap, config: Vec, + clippy_project_root: PathBuf, } impl MetadataCollector { @@ -188,6 +191,7 @@ impl MetadataCollector { lints: BinaryHeap::::default(), applicability_info: FxHashMap::::default(), config: collect_configs(), + clippy_project_root: clippy_dev::clippy_project_root(), } } @@ -215,11 +219,13 @@ impl Drop for MetadataCollector { // Mapping the final data let mut lints = std::mem::take(&mut self.lints).into_sorted_vec(); - collect_renames(&mut lints); for x in &mut lints { x.applicability = Some(applicability_info.remove(&x.id).unwrap_or_default()); + replace_produces(&x.id, &mut x.docs, &self.clippy_project_root); } + collect_renames(&mut lints); + // Outputting if Path::new(OUTPUT_FILE).exists() { fs::remove_file(OUTPUT_FILE).unwrap(); @@ -263,14 +269,193 @@ impl LintMetadata { } } +fn replace_produces(lint_name: &str, docs: &mut String, clippy_project_root: &Path) { + let mut doc_lines = docs.lines().map(ToString::to_string).collect::>(); + let mut lines = doc_lines.iter_mut(); + + 'outer: loop { + // Find the start of the example + + // ```rust + loop { + match lines.next() { + Some(line) if line.trim_start().starts_with("```rust") => { + if line.contains("ignore") || line.contains("no_run") { + // A {{produces}} marker may have been put on a ignored code block by mistake, + // just seek to the end of the code block and continue checking. + if lines.any(|line| line.trim_start().starts_with("```")) { + continue; + } + + panic!("lint `{}` has an unterminated code block", lint_name) + } + + break; + }, + Some(line) if line.trim_start() == "{{produces}}" => { + panic!( + "lint `{}` has marker {{{{produces}}}} with an ignored or missing code block", + lint_name + ) + }, + Some(line) => { + let line = line.trim(); + // These are the two most common markers of the corrections section + if line.eq_ignore_ascii_case("Use instead:") || line.eq_ignore_ascii_case("Could be written as:") { + break 'outer; + } + }, + None => break 'outer, + } + } + + // Collect the example + let mut example = Vec::new(); + loop { + match lines.next() { + Some(line) if line.trim_start() == "```" => break, + Some(line) => example.push(line), + None => panic!("lint `{}` has an unterminated code block", lint_name), + } + } + + // Find the {{produces}} and attempt to generate the output + loop { + match lines.next() { + Some(line) if line.is_empty() => {}, + Some(line) if line.trim() == "{{produces}}" => { + let output = get_lint_output(lint_name, &example, clippy_project_root); + line.replace_range( + .., + &format!( + "
\ + Produces\n\ + \n\ + ```text\n\ + {}\n\ + ```\n\ +
", + output + ), + ); + + break; + }, + // No {{produces}}, we can move on to the next example + Some(_) => break, + None => break 'outer, + } + } + } + + *docs = cleanup_docs(&doc_lines); +} + +fn get_lint_output(lint_name: &str, example: &[&mut String], clippy_project_root: &Path) -> String { + let dir = tempfile::tempdir().unwrap_or_else(|e| panic!("failed to create temp dir: {e}")); + let file = dir.path().join("lint_example.rs"); + + let mut source = String::new(); + let unhidden = example + .iter() + .map(|line| line.trim_start().strip_prefix("# ").unwrap_or(line)); + + // Get any attributes + let mut lines = unhidden.peekable(); + while let Some(line) = lines.peek() { + if line.starts_with("#!") { + source.push_str(line); + source.push('\n'); + lines.next(); + } else { + break; + } + } + + let needs_main = !example.iter().any(|line| line.contains("fn main")); + if needs_main { + source.push_str("fn main() {\n"); + } + + for line in lines { + source.push_str(line); + source.push('\n'); + } + + if needs_main { + source.push_str("}\n"); + } + + if let Err(e) = fs::write(&file, &source) { + panic!("failed to write to `{}`: {e}", file.as_path().to_string_lossy()); + } + + let prefixed_name = format!("{}{lint_name}", CLIPPY_LINT_GROUP_PREFIX); + + let mut cmd = Command::new("cargo"); + + cmd.current_dir(clippy_project_root) + .env("CARGO_INCREMENTAL", "0") + .env("CLIPPY_ARGS", "") + .env("CLIPPY_DISABLE_DOCS_LINKS", "1") + // We need to disable this to enable all lints + .env("ENABLE_METADATA_COLLECTION", "0") + .args(["run", "--bin", "clippy-driver"]) + .args(["--target-dir", "./clippy_lints/target"]) + .args(["--", "--error-format=json"]) + .args(["--edition", "2021"]) + .arg("-Cdebuginfo=0") + .args(["-A", "clippy::all"]) + .args(["-W", &prefixed_name]) + .args(["-L", "./target/debug"]) + .args(["-Z", "no-codegen"]); + + let output = cmd + .arg(file.as_path()) + .output() + .unwrap_or_else(|e| panic!("failed to run `{:?}`: {e}", cmd)); + + let tmp_file_path = file.to_string_lossy(); + let stderr = std::str::from_utf8(&output.stderr).unwrap(); + let msgs = stderr + .lines() + .filter(|line| line.starts_with('{')) + .map(|line| serde_json::from_str(line).unwrap()) + .collect::>(); + + let mut rendered = String::new(); + let iter = msgs + .iter() + .filter(|msg| matches!(&msg["code"]["code"], serde_json::Value::String(s) if s == &prefixed_name)); + + for message in iter { + let rendered_part = message["rendered"].as_str().expect("rendered field should exist"); + rendered.push_str(rendered_part); + } + + if rendered.is_empty() { + let rendered: Vec<&str> = msgs.iter().filter_map(|msg| msg["rendered"].as_str()).collect(); + let non_json: Vec<&str> = stderr.lines().filter(|line| !line.starts_with('{')).collect(); + panic!( + "did not find lint `{}` in output of example, got:\n{}\n{}", + lint_name, + non_json.join("\n"), + rendered.join("\n") + ); + } + + // The reader doesn't need to see `/tmp/.tmpfiy2Qd/lint_example.rs` :) + rendered.trim_end().replace(&*tmp_file_path, "lint_example.rs") +} + #[derive(Debug, Clone, Serialize, PartialEq, Eq, PartialOrd, Ord)] struct SerializableSpan { path: String, line: usize, } -impl std::fmt::Display for SerializableSpan { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl fmt::Display for SerializableSpan { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}:{}", self.path.rsplit('/').next().unwrap_or_default(), self.line) } } @@ -435,10 +620,10 @@ impl<'hir> LateLintPass<'hir> for MetadataCollector { if !BLACK_LISTED_LINTS.contains(&lint_name.as_str()); // metadata extraction if let Some((group, level)) = get_lint_group_and_level_or_lint(cx, &lint_name, item); - if let Some(mut docs) = extract_attr_docs_or_lint(cx, item); + if let Some(mut raw_docs) = extract_attr_docs_or_lint(cx, item); then { if let Some(configuration_section) = self.get_lint_configs(&lint_name) { - docs.push_str(&configuration_section); + raw_docs.push_str(&configuration_section); } let version = get_lint_version(cx, item); @@ -448,7 +633,7 @@ impl<'hir> LateLintPass<'hir> for MetadataCollector { group, level, version, - docs, + raw_docs, )); } } @@ -459,7 +644,7 @@ impl<'hir> LateLintPass<'hir> for MetadataCollector { let lint_name = sym_to_string(item.ident.name).to_ascii_lowercase(); if !BLACK_LISTED_LINTS.contains(&lint_name.as_str()); // Metadata the little we can get from a deprecated lint - if let Some(docs) = extract_attr_docs_or_lint(cx, item); + if let Some(raw_docs) = extract_attr_docs_or_lint(cx, item); then { let version = get_lint_version(cx, item); @@ -469,7 +654,7 @@ impl<'hir> LateLintPass<'hir> for MetadataCollector { DEPRECATED_LINT_GROUP_STR.to_string(), DEPRECATED_LINT_LEVEL, version, - docs, + raw_docs, )); } } @@ -535,22 +720,28 @@ fn extract_attr_docs_or_lint(cx: &LateContext<'_>, item: &Item<'_>) -> Option, item: &Item<'_>) -> Option { + let attrs = cx.tcx.hir().attrs(item.hir_id()); + let mut lines = attrs.iter().filter_map(ast::Attribute::doc_str); + + if let Some(line) = lines.next() { + let raw_docs = lines.fold(String::from(line.as_str()) + "\n", |s, line| s + line.as_str() + "\n"); + return Some(raw_docs); + } + + None +} + /// This function may modify the doc comment to ensure that the string can be displayed using a /// markdown viewer in Clippy's lint list. The following modifications could be applied: /// * Removal of leading space after a new line. (Important to display tables) /// * Ensures that code blocks only contain language information -fn extract_attr_docs(cx: &LateContext<'_>, item: &Item<'_>) -> Option { - let attrs = cx.tcx.hir().attrs(item.hir_id()); - let mut lines = attrs.iter().filter_map(ast::Attribute::doc_str); - let mut docs = String::from(lines.next()?.as_str()); +fn cleanup_docs(docs_collection: &Vec) -> String { let mut in_code_block = false; let mut is_code_block_rust = false; - for line in lines { - let line = line.as_str(); + let mut docs = String::new(); + for line in docs_collection { // Rustdoc hides code lines starting with `# ` and this removes them from Clippy's lint list :) if is_code_block_rust && line.trim_start().starts_with("# ") { continue; @@ -583,7 +774,8 @@ fn extract_attr_docs(cx: &LateContext<'_>, item: &Item<'_>) -> Option { docs.push_str(line); } } - Some(docs) + + docs } fn get_lint_version(cx: &LateContext<'_>, item: &Item<'_>) -> String { @@ -762,9 +954,9 @@ fn resolve_applicability<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hi } fn check_is_multi_part<'hir>(cx: &LateContext<'hir>, closure_expr: &'hir hir::Expr<'hir>) -> bool { - if let ExprKind::Closure(_, _, body_id, _, _) = closure_expr.kind { + if let ExprKind::Closure { body, .. } = closure_expr.kind { let mut scanner = IsMultiSpanScanner::new(cx); - intravisit::walk_body(&mut scanner, cx.tcx.hir().body(body_id)); + intravisit::walk_body(&mut scanner, cx.tcx.hir().body(body)); return scanner.is_multi_part(); } else if let Some(local) = get_parent_local(cx, closure_expr) { if let Some(local_init) = local.init { diff --git a/clippy_lints/src/vec.rs b/clippy_lints/src/vec.rs index ba1ff65479d60..297a80e5767a1 100644 --- a/clippy_lints/src/vec.rs +++ b/clippy_lints/src/vec.rs @@ -28,12 +28,14 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// # fn foo(my_vec: &[u8]) {} + /// fn foo(_x: &[u8]) {} /// - /// // Bad /// foo(&vec![1, 2]); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # fn foo(_x: &[u8]) {} /// foo(&[1, 2]); /// ``` #[clippy::version = "pre 1.29.0"] diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index 2f74eaf3cf5c3..5418eca382da0 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -26,13 +26,18 @@ declare_clippy_lint! { /// still around. /// /// ### Example - /// ```rust,ignore - /// // Bad + /// ```rust /// use std::cmp::Ordering::*; + /// + /// # fn foo(_: std::cmp::Ordering) {} /// foo(Less); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust /// use std::cmp::Ordering; + /// + /// # fn foo(_: Ordering) {} /// foo(Ordering::Less) /// ``` #[clippy::version = "pre 1.29.0"] @@ -76,14 +81,13 @@ declare_clippy_lint! { /// /// ### Example /// ```rust,ignore - /// // Bad /// use crate1::*; /// /// foo(); /// ``` /// + /// Use instead: /// ```rust,ignore - /// // Good /// use crate1::foo; /// /// foo(); diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index d2493c055a519..67b2bc8c3f3cd 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -25,10 +25,11 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad /// println!(""); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust /// println!(); /// ``` #[clippy::version = "pre 1.29.0"] @@ -177,10 +178,13 @@ declare_clippy_lint! { /// ```rust /// # use std::fmt::Write; /// # let mut buf = String::new(); - /// // Bad /// writeln!(buf, ""); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # use std::fmt::Write; + /// # let mut buf = String::new(); /// writeln!(buf); /// ``` #[clippy::version = "pre 1.29.0"] @@ -204,10 +208,14 @@ declare_clippy_lint! { /// # use std::fmt::Write; /// # let mut buf = String::new(); /// # let name = "World"; - /// // Bad /// write!(buf, "Hello {}!\n", name); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # use std::fmt::Write; + /// # let mut buf = String::new(); + /// # let name = "World"; /// writeln!(buf, "Hello {}!", name); /// ``` #[clippy::version = "pre 1.29.0"] @@ -233,10 +241,13 @@ declare_clippy_lint! { /// ```rust /// # use std::fmt::Write; /// # let mut buf = String::new(); - /// // Bad /// writeln!(buf, "{}", "foo"); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # use std::fmt::Write; + /// # let mut buf = String::new(); /// writeln!(buf, "foo"); /// ``` #[clippy::version = "pre 1.29.0"] diff --git a/clippy_lints/src/zero_div_zero.rs b/clippy_lints/src/zero_div_zero.rs index 641681185a2f6..50d3c079fe675 100644 --- a/clippy_lints/src/zero_div_zero.rs +++ b/clippy_lints/src/zero_div_zero.rs @@ -14,10 +14,11 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad /// let nan = 0.0f32 / 0.0; + /// ``` /// - /// // Good + /// Use instead: + /// ```rust /// let nan = f32::NAN; /// ``` #[clippy::version = "pre 1.29.0"] diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index 5d0ce6cc620ac..6d4a48b53de3c 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -622,7 +622,7 @@ pub fn miri_to_const<'tcx>(tcx: TyCtxt<'tcx>, result: mir::ConstantKind<'tcx>) - ty::Float(FloatTy::F32) => match len.to_valtree().try_to_machine_usize(tcx) { Some(len) => alloc .inner() - .inspect_with_uninit_and_ptr_outside_interpreter(0..(4 * len as usize)) + .inspect_with_uninit_and_ptr_outside_interpreter(0..(4 * usize::try_from(len).unwrap())) .to_owned() .chunks(4) .map(|chunk| { @@ -637,7 +637,7 @@ pub fn miri_to_const<'tcx>(tcx: TyCtxt<'tcx>, result: mir::ConstantKind<'tcx>) - ty::Float(FloatTy::F64) => match len.to_valtree().try_to_machine_usize(tcx) { Some(len) => alloc .inner() - .inspect_with_uninit_and_ptr_outside_interpreter(0..(8 * len as usize)) + .inspect_with_uninit_and_ptr_outside_interpreter(0..(8 * usize::try_from(len).unwrap())) .to_owned() .chunks(8) .map(|chunk| { diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index 12931c56df619..af62c4afd5a51 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -91,7 +91,7 @@ pub struct HirEqInterExpr<'a, 'b, 'tcx> { // When binding are declared, the binding ID in the left expression is mapped to the one on the // right. For example, when comparing `{ let x = 1; x + 2 }` and `{ let y = 1; y + 2 }`, // these blocks are considered equal since `x` is mapped to `y`. - locals: HirIdMap, + pub locals: HirIdMap, } impl HirEqInterExpr<'_, '_, '_> { @@ -998,3 +998,15 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { } } } + +pub fn hash_stmt(cx: &LateContext<'_>, s: &Stmt<'_>) -> u64 { + let mut h = SpanlessHash::new(cx); + h.hash_stmt(s); + h.finish() +} + +pub fn hash_expr(cx: &LateContext<'_>, e: &Expr<'_>) -> u64 { + let mut h = SpanlessHash::new(cx); + h.hash_expr(e); + h.finish() +} diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 0cf23ca626c75..73c1bdd0e3f4e 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -58,7 +58,9 @@ pub mod usage; pub mod visitors; pub use self::attrs::*; -pub use self::hir_utils::{both, count_eq, eq_expr_value, over, SpanlessEq, SpanlessHash}; +pub use self::hir_utils::{ + both, count_eq, eq_expr_value, hash_expr, hash_stmt, over, HirEqInterExpr, SpanlessEq, SpanlessHash, +}; use std::collections::hash_map::Entry; use std::hash::BuildHasherDefault; diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index a10515d2fec63..227e97d37ecc3 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -78,9 +78,9 @@ pub fn get_associated_type<'tcx>( cx.tcx .associated_items(trait_id) .find_by_name_and_kind(cx.tcx, Ident::from_str(name), ty::AssocKind::Type, trait_id) - .map(|assoc| { + .and_then(|assoc| { let proj = cx.tcx.mk_projection(assoc.def_id, cx.tcx.mk_substs_trait(ty, &[])); - cx.tcx.normalize_erasing_regions(cx.param_env, proj) + cx.tcx.try_normalize_erasing_regions(cx.param_env, proj).ok() }) } diff --git a/lintcheck/src/config.rs b/lintcheck/src/config.rs index a6f93d2a1c0a6..1742cf677c0f9 100644 --- a/lintcheck/src/config.rs +++ b/lintcheck/src/config.rs @@ -1,50 +1,40 @@ -use clap::{Arg, ArgMatches, Command}; +use clap::{Arg, ArgAction, ArgMatches, Command}; use std::env; use std::path::PathBuf; fn get_clap_config() -> ArgMatches { Command::new("lintcheck") .about("run clippy on a set of crates and check output") - .arg( + .args([ Arg::new("only") - .takes_value(true) + .action(ArgAction::Set) .value_name("CRATE") .long("only") .help("Only process a single crate of the list"), - ) - .arg( Arg::new("crates-toml") - .takes_value(true) + .action(ArgAction::Set) .value_name("CRATES-SOURCES-TOML-PATH") .long("crates-toml") .help("Set the path for a crates.toml where lintcheck should read the sources from"), - ) - .arg( Arg::new("threads") - .takes_value(true) + .action(ArgAction::Set) .value_name("N") + .value_parser(clap::value_parser!(usize)) .short('j') .long("jobs") .help("Number of threads to use, 0 automatic choice"), - ) - .arg( Arg::new("fix") - .long("--fix") + .long("fix") .help("Runs cargo clippy --fix and checks if all suggestions apply"), - ) - .arg( Arg::new("filter") - .long("--filter") - .takes_value(true) - .multiple_occurrences(true) + .long("filter") + .action(ArgAction::Append) .value_name("clippy_lint_name") .help("Apply a filter to only collect specified lints, this also overrides `allow` attributes"), - ) - .arg( Arg::new("markdown") - .long("--markdown") + .long("markdown") .help("Change the reports table to use markdown links"), - ) + ]) .get_matches() } @@ -75,13 +65,13 @@ impl LintcheckConfig { // if not, use the default "lintcheck/lintcheck_crates.toml" let sources_toml = env::var("LINTCHECK_TOML").unwrap_or_else(|_| { clap_config - .value_of("crates-toml") - .clone() + .get_one::("crates-toml") + .map(|s| &**s) .unwrap_or("lintcheck/lintcheck_crates.toml") - .to_string() + .into() }); - let markdown = clap_config.is_present("markdown"); + let markdown = clap_config.contains_id("markdown"); let sources_toml_path = PathBuf::from(sources_toml); // for the path where we save the lint results, get the filename without extension (so for @@ -96,25 +86,19 @@ impl LintcheckConfig { // look at the --threads arg, if 0 is passed, ask rayon rayon how many threads it would spawn and // use half of that for the physical core count // by default use a single thread - let max_jobs = match clap_config.value_of("threads") { - Some(threads) => { - let threads: usize = threads - .parse() - .unwrap_or_else(|_| panic!("Failed to parse '{}' to a digit", threads)); - if threads == 0 { - // automatic choice - // Rayon seems to return thread count so half that for core count - (rayon::current_num_threads() / 2) as usize - } else { - threads - } + let max_jobs = match clap_config.get_one::("threads") { + Some(&0) => { + // automatic choice + // Rayon seems to return thread count so half that for core count + (rayon::current_num_threads() / 2) as usize }, + Some(&threads) => threads, // no -j passed, use a single thread None => 1, }; let lint_filter: Vec = clap_config - .values_of("filter") + .get_many::("filter") .map(|iter| { iter.map(|lint_name| { let mut filter = lint_name.replace('_', "-"); @@ -131,8 +115,8 @@ impl LintcheckConfig { max_jobs, sources_toml_path, lintcheck_results_path, - only: clap_config.value_of("only").map(String::from), - fix: clap_config.is_present("fix"), + only: clap_config.get_one::("only").map(String::from), + fix: clap_config.contains_id("fix"), lint_filter, markdown, } diff --git a/rust-toolchain b/rust-toolchain index 2386a751f04f6..6ad56aacf8c95 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2022-06-04" +channel = "nightly-2022-06-16" components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/tests/dogfood.rs b/tests/dogfood.rs index 9da80518ce916..fffc53603424b 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -21,7 +21,7 @@ fn dogfood_clippy() { // "" is the root package for package in &["", "clippy_dev", "clippy_lints", "clippy_utils", "rustc_tools_util"] { - run_clippy_for_package(package, &[]); + run_clippy_for_package(package, &["-D", "clippy::all", "-D", "clippy::pedantic"]); } } @@ -77,8 +77,6 @@ fn run_clippy_for_package(project: &str, args: &[&str]) { .arg("--all-features") .arg("--") .args(args) - .args(&["-D", "clippy::all"]) - .args(&["-D", "clippy::pedantic"]) .arg("-Cdebuginfo=0"); // disable debuginfo to generate less data in the target dir if cfg!(feature = "internal") { diff --git a/tests/ui-toml/bad_toml/conf_bad_toml.rs b/tests/ui-toml/bad_toml/conf_bad_toml.rs index 3b9458fc2840e..f328e4d9d04c3 100644 --- a/tests/ui-toml/bad_toml/conf_bad_toml.rs +++ b/tests/ui-toml/bad_toml/conf_bad_toml.rs @@ -1,3 +1 @@ -// error-pattern: error reading Clippy's configuration file - fn main() {} diff --git a/tests/ui-toml/bad_toml_type/conf_bad_type.rs b/tests/ui-toml/bad_toml_type/conf_bad_type.rs index 8a0062423ad16..f328e4d9d04c3 100644 --- a/tests/ui-toml/bad_toml_type/conf_bad_type.rs +++ b/tests/ui-toml/bad_toml_type/conf_bad_type.rs @@ -1,4 +1 @@ -// error-pattern: error reading Clippy's configuration file: `blacklisted-names` is expected to be a -// `Vec < String >` but is a `integer` - fn main() {} diff --git a/tests/ui-toml/blacklisted_names_append/blacklisted_names.rs b/tests/ui-toml/blacklisted_names_append/blacklisted_names.rs new file mode 100644 index 0000000000000..fb2395cf90be3 --- /dev/null +++ b/tests/ui-toml/blacklisted_names_append/blacklisted_names.rs @@ -0,0 +1,10 @@ +#[warn(clippy::blacklisted_name)] + +fn main() { + // `foo` is part of the default configuration + let foo = "bar"; + // `ducks` was unrightfully blacklisted + let ducks = ["quack", "quack"]; + // `fox` is okay + let fox = ["what", "does", "the", "fox", "say", "?"]; +} diff --git a/tests/ui-toml/blacklisted_names_append/blacklisted_names.stderr b/tests/ui-toml/blacklisted_names_append/blacklisted_names.stderr new file mode 100644 index 0000000000000..9169bb0e866ac --- /dev/null +++ b/tests/ui-toml/blacklisted_names_append/blacklisted_names.stderr @@ -0,0 +1,16 @@ +error: use of a blacklisted/placeholder name `foo` + --> $DIR/blacklisted_names.rs:5:9 + | +LL | let foo = "bar"; + | ^^^ + | + = note: `-D clippy::blacklisted-name` implied by `-D warnings` + +error: use of a blacklisted/placeholder name `ducks` + --> $DIR/blacklisted_names.rs:7:9 + | +LL | let ducks = ["quack", "quack"]; + | ^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui-toml/blacklisted_names_append/clippy.toml b/tests/ui-toml/blacklisted_names_append/clippy.toml new file mode 100644 index 0000000000000..0e052ef50f07b --- /dev/null +++ b/tests/ui-toml/blacklisted_names_append/clippy.toml @@ -0,0 +1 @@ +blacklisted-names = ["ducks", ".."] diff --git a/tests/ui-toml/blacklisted_names_replace/blacklisted_names.rs b/tests/ui-toml/blacklisted_names_replace/blacklisted_names.rs new file mode 100644 index 0000000000000..fb2395cf90be3 --- /dev/null +++ b/tests/ui-toml/blacklisted_names_replace/blacklisted_names.rs @@ -0,0 +1,10 @@ +#[warn(clippy::blacklisted_name)] + +fn main() { + // `foo` is part of the default configuration + let foo = "bar"; + // `ducks` was unrightfully blacklisted + let ducks = ["quack", "quack"]; + // `fox` is okay + let fox = ["what", "does", "the", "fox", "say", "?"]; +} diff --git a/tests/ui-toml/blacklisted_names_replace/blacklisted_names.stderr b/tests/ui-toml/blacklisted_names_replace/blacklisted_names.stderr new file mode 100644 index 0000000000000..ec6f7f084f2a5 --- /dev/null +++ b/tests/ui-toml/blacklisted_names_replace/blacklisted_names.stderr @@ -0,0 +1,10 @@ +error: use of a blacklisted/placeholder name `ducks` + --> $DIR/blacklisted_names.rs:7:9 + | +LL | let ducks = ["quack", "quack"]; + | ^^^^^ + | + = note: `-D clippy::blacklisted-name` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui-toml/blacklisted_names_replace/clippy.toml b/tests/ui-toml/blacklisted_names_replace/clippy.toml new file mode 100644 index 0000000000000..4582f1c06674c --- /dev/null +++ b/tests/ui-toml/blacklisted_names_replace/clippy.toml @@ -0,0 +1 @@ +blacklisted-names = ["ducks"] diff --git a/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.rs b/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.rs index 2577c1eef92bc..f328e4d9d04c3 100644 --- a/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.rs +++ b/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.rs @@ -1,4 +1 @@ -// error-pattern: error reading Clippy's configuration file: found deprecated field -// `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead. - fn main() {} diff --git a/tests/ui-toml/doc_valid_idents_append/clippy.toml b/tests/ui-toml/doc_valid_idents_append/clippy.toml new file mode 100644 index 0000000000000..daf3276854bdb --- /dev/null +++ b/tests/ui-toml/doc_valid_idents_append/clippy.toml @@ -0,0 +1 @@ +doc-valid-idents = ["ClipPy", ".."] diff --git a/tests/ui-toml/doc_valid_idents_append/doc_markdown.rs b/tests/ui-toml/doc_valid_idents_append/doc_markdown.rs new file mode 100644 index 0000000000000..327a592e9cadc --- /dev/null +++ b/tests/ui-toml/doc_valid_idents_append/doc_markdown.rs @@ -0,0 +1,12 @@ +#![warn(clippy::doc_markdown)] + +/// This is a special interface for ClipPy which doesn't require backticks +fn allowed_name() {} + +/// OAuth and LaTeX are inside Clippy's default list. +fn default_name() {} + +/// TestItemThingyOfCoolness might sound cool but is not on the list and should be linted. +fn unknown_name() {} + +fn main() {} diff --git a/tests/ui-toml/doc_valid_idents_append/doc_markdown.stderr b/tests/ui-toml/doc_valid_idents_append/doc_markdown.stderr new file mode 100644 index 0000000000000..0f767c9b8559f --- /dev/null +++ b/tests/ui-toml/doc_valid_idents_append/doc_markdown.stderr @@ -0,0 +1,14 @@ +error: item in documentation is missing backticks + --> $DIR/doc_markdown.rs:9:5 + | +LL | /// TestItemThingyOfCoolness might sound cool but is not on the list and should be linted. + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::doc-markdown` implied by `-D warnings` +help: try + | +LL | /// `TestItemThingyOfCoolness` might sound cool but is not on the list and should be linted. + | ~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: aborting due to previous error + diff --git a/tests/ui-toml/doc_valid_idents_replace/clippy.toml b/tests/ui-toml/doc_valid_idents_replace/clippy.toml new file mode 100644 index 0000000000000..70bc477b08c6a --- /dev/null +++ b/tests/ui-toml/doc_valid_idents_replace/clippy.toml @@ -0,0 +1 @@ +doc-valid-idents = ["ClipPy"] diff --git a/tests/ui-toml/doc_valid_idents_replace/doc_markdown.rs b/tests/ui-toml/doc_valid_idents_replace/doc_markdown.rs new file mode 100644 index 0000000000000..327a592e9cadc --- /dev/null +++ b/tests/ui-toml/doc_valid_idents_replace/doc_markdown.rs @@ -0,0 +1,12 @@ +#![warn(clippy::doc_markdown)] + +/// This is a special interface for ClipPy which doesn't require backticks +fn allowed_name() {} + +/// OAuth and LaTeX are inside Clippy's default list. +fn default_name() {} + +/// TestItemThingyOfCoolness might sound cool but is not on the list and should be linted. +fn unknown_name() {} + +fn main() {} diff --git a/tests/ui-toml/doc_valid_idents_replace/doc_markdown.stderr b/tests/ui-toml/doc_valid_idents_replace/doc_markdown.stderr new file mode 100644 index 0000000000000..e0613eb863b39 --- /dev/null +++ b/tests/ui-toml/doc_valid_idents_replace/doc_markdown.stderr @@ -0,0 +1,36 @@ +error: item in documentation is missing backticks + --> $DIR/doc_markdown.rs:6:5 + | +LL | /// OAuth and LaTeX are inside Clippy's default list. + | ^^^^^ + | + = note: `-D clippy::doc-markdown` implied by `-D warnings` +help: try + | +LL | /// `OAuth` and LaTeX are inside Clippy's default list. + | ~~~~~~~ + +error: item in documentation is missing backticks + --> $DIR/doc_markdown.rs:6:15 + | +LL | /// OAuth and LaTeX are inside Clippy's default list. + | ^^^^^ + | +help: try + | +LL | /// OAuth and `LaTeX` are inside Clippy's default list. + | ~~~~~~~ + +error: item in documentation is missing backticks + --> $DIR/doc_markdown.rs:9:5 + | +LL | /// TestItemThingyOfCoolness might sound cool but is not on the list and should be linted. + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL | /// `TestItemThingyOfCoolness` might sound cool but is not on the list and should be linted. + | ~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: aborting due to 3 previous errors + diff --git a/tests/ui-toml/good_toml_no_false_negatives/conf_no_false_negatives.rs b/tests/ui-toml/good_toml_no_false_negatives/conf_no_false_negatives.rs index 270b9c5c43c13..f328e4d9d04c3 100644 --- a/tests/ui-toml/good_toml_no_false_negatives/conf_no_false_negatives.rs +++ b/tests/ui-toml/good_toml_no_false_negatives/conf_no_false_negatives.rs @@ -1,3 +1 @@ -// error-pattern: should give absolutely no error - fn main() {} diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.rs b/tests/ui-toml/toml_unknown_key/conf_unknown_key.rs index a47569f62a322..f328e4d9d04c3 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.rs +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.rs @@ -1,3 +1 @@ -// error-pattern: error reading Clippy's configuration file: unknown key `foobar` - fn main() {} diff --git a/tests/ui/almost_complete_letter_range.fixed b/tests/ui/almost_complete_letter_range.fixed index 39f8f0c29495b..e69b40f35f4c6 100644 --- a/tests/ui/almost_complete_letter_range.fixed +++ b/tests/ui/almost_complete_letter_range.fixed @@ -6,6 +6,7 @@ #![feature(stmt_expr_attributes)] #![warn(clippy::almost_complete_letter_range)] #![allow(ellipsis_inclusive_range_patterns)] +#![allow(clippy::needless_parens_on_range_literals)] macro_rules! a { () => { diff --git a/tests/ui/almost_complete_letter_range.rs b/tests/ui/almost_complete_letter_range.rs index 3dc0219925760..f2240981d45fa 100644 --- a/tests/ui/almost_complete_letter_range.rs +++ b/tests/ui/almost_complete_letter_range.rs @@ -6,6 +6,7 @@ #![feature(stmt_expr_attributes)] #![warn(clippy::almost_complete_letter_range)] #![allow(ellipsis_inclusive_range_patterns)] +#![allow(clippy::needless_parens_on_range_literals)] macro_rules! a { () => { diff --git a/tests/ui/almost_complete_letter_range.stderr b/tests/ui/almost_complete_letter_range.stderr index 74980ec1a923f..5b5dc40ee54d0 100644 --- a/tests/ui/almost_complete_letter_range.stderr +++ b/tests/ui/almost_complete_letter_range.stderr @@ -1,5 +1,5 @@ error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:19:17 + --> $DIR/almost_complete_letter_range.rs:20:17 | LL | let _ = ('a') ..'z'; | ^^^^^^--^^^ @@ -9,7 +9,7 @@ LL | let _ = ('a') ..'z'; = note: `-D clippy::almost-complete-letter-range` implied by `-D warnings` error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:20:17 + --> $DIR/almost_complete_letter_range.rs:21:17 | LL | let _ = 'A' .. ('Z'); | ^^^^--^^^^^^ @@ -17,7 +17,7 @@ LL | let _ = 'A' .. ('Z'); | help: use an inclusive range: `..=` error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:26:13 + --> $DIR/almost_complete_letter_range.rs:27:13 | LL | let _ = (b'a')..(b'z'); | ^^^^^^--^^^^^^ @@ -25,7 +25,7 @@ LL | let _ = (b'a')..(b'z'); | help: use an inclusive range: `..=` error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:27:13 + --> $DIR/almost_complete_letter_range.rs:28:13 | LL | let _ = b'A'..b'Z'; | ^^^^--^^^^ @@ -33,7 +33,7 @@ LL | let _ = b'A'..b'Z'; | help: use an inclusive range: `..=` error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:32:13 + --> $DIR/almost_complete_letter_range.rs:33:13 | LL | let _ = a!()..'z'; | ^^^^--^^^ @@ -41,7 +41,7 @@ LL | let _ = a!()..'z'; | help: use an inclusive range: `..=` error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:35:9 + --> $DIR/almost_complete_letter_range.rs:36:9 | LL | b'a'..b'z' if true => 1, | ^^^^--^^^^ @@ -49,7 +49,7 @@ LL | b'a'..b'z' if true => 1, | help: use an inclusive range: `..=` error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:36:9 + --> $DIR/almost_complete_letter_range.rs:37:9 | LL | b'A'..b'Z' if true => 2, | ^^^^--^^^^ @@ -57,7 +57,7 @@ LL | b'A'..b'Z' if true => 2, | help: use an inclusive range: `..=` error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:43:9 + --> $DIR/almost_complete_letter_range.rs:44:9 | LL | 'a'..'z' if true => 1, | ^^^--^^^ @@ -65,7 +65,7 @@ LL | 'a'..'z' if true => 1, | help: use an inclusive range: `..=` error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:44:9 + --> $DIR/almost_complete_letter_range.rs:45:9 | LL | 'A'..'Z' if true => 2, | ^^^--^^^ @@ -73,7 +73,7 @@ LL | 'A'..'Z' if true => 2, | help: use an inclusive range: `..=` error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:54:9 + --> $DIR/almost_complete_letter_range.rs:55:9 | LL | 'a'..'z' => 1, | ^^^--^^^ @@ -81,7 +81,7 @@ LL | 'a'..'z' => 1, | help: use an inclusive range: `...` error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:61:13 + --> $DIR/almost_complete_letter_range.rs:62:13 | LL | let _ = 'a'..'z'; | ^^^--^^^ @@ -89,7 +89,7 @@ LL | let _ = 'a'..'z'; | help: use an inclusive range: `..=` error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:63:9 + --> $DIR/almost_complete_letter_range.rs:64:9 | LL | 'a'..'z' => 1, | ^^^--^^^ diff --git a/tests/ui/async_yields_async.fixed b/tests/ui/async_yields_async.fixed index e20b58269b93e..3cf380d2b954b 100644 --- a/tests/ui/async_yields_async.fixed +++ b/tests/ui/async_yields_async.fixed @@ -1,5 +1,5 @@ // run-rustfix - +#![feature(lint_reasons)] #![feature(async_closure)] #![warn(clippy::async_yields_async)] @@ -65,3 +65,14 @@ fn main() { let _n = async || custom_future_type_ctor(); let _o = async || f(); } + +#[rustfmt::skip] +#[allow(dead_code)] +fn check_expect_suppression() { + #[expect(clippy::async_yields_async)] + let _j = async || { + async { + 3 + } + }; +} diff --git a/tests/ui/async_yields_async.rs b/tests/ui/async_yields_async.rs index c1dfa39845025..dd4131b60ab3a 100644 --- a/tests/ui/async_yields_async.rs +++ b/tests/ui/async_yields_async.rs @@ -1,5 +1,5 @@ // run-rustfix - +#![feature(lint_reasons)] #![feature(async_closure)] #![warn(clippy::async_yields_async)] @@ -65,3 +65,14 @@ fn main() { let _n = async || custom_future_type_ctor(); let _o = async || f(); } + +#[rustfmt::skip] +#[allow(dead_code)] +fn check_expect_suppression() { + #[expect(clippy::async_yields_async)] + let _j = async || { + async { + 3 + } + }; +} diff --git a/tests/ui/branches_sharing_code/false_positives.rs b/tests/ui/branches_sharing_code/false_positives.rs index 7f42df463411d..064482009517a 100644 --- a/tests/ui/branches_sharing_code/false_positives.rs +++ b/tests/ui/branches_sharing_code/false_positives.rs @@ -25,4 +25,17 @@ impl FooBar { fn baz(&mut self) {} } -fn main() {} +fn foo(x: u32, y: u32) -> u32 { + x / y +} + +fn main() { + let x = (1, 2); + let _ = if true { + let (x, y) = x; + foo(x, y) + } else { + let (y, x) = x; + foo(x, y) + }; +} diff --git a/tests/ui/branches_sharing_code/shared_at_bottom.stderr b/tests/ui/branches_sharing_code/shared_at_bottom.stderr index e3c1bbee99423..5e1a68d216ea8 100644 --- a/tests/ui/branches_sharing_code/shared_at_bottom.stderr +++ b/tests/ui/branches_sharing_code/shared_at_bottom.stderr @@ -12,8 +12,8 @@ note: the lint level is defined here | LL | #![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: The end suggestion probably needs some adjustments to use the expression result correctly -help: consider moving the end statements out like this + = note: the end suggestion probably needs some adjustments to use the expression result correctly +help: consider moving these statements after the if | LL ~ } LL + let result = false; @@ -28,7 +28,7 @@ LL | / println!("Same end of block"); LL | | } | |_____^ | -help: consider moving the end statements out like this +help: consider moving these statements after the if | LL ~ } LL + println!("Same end of block"); @@ -44,7 +44,7 @@ LL | | ); LL | | } | |_____^ | -help: consider moving the end statements out like this +help: consider moving these statements after the if | LL ~ } LL + println!( @@ -60,7 +60,7 @@ LL | / println!("Hello World"); LL | | } | |_________^ | -help: consider moving the end statements out like this +help: consider moving these statements after the if | LL ~ } LL + println!("Hello World"); @@ -75,8 +75,8 @@ LL | | // I'm expecting a note about this LL | | } | |_____^ | - = warning: Some moved values might need to be renamed to avoid wrong references -help: consider moving the end statements out like this + = warning: some moved values might need to be renamed to avoid wrong references +help: consider moving these statements after the if | LL ~ } LL + let later_used_value = "A string value"; @@ -91,8 +91,8 @@ LL | | println!("This is the new simple_example: {}", simple_examples); LL | | } | |_____^ | - = warning: Some moved values might need to be renamed to avoid wrong references -help: consider moving the end statements out like this + = warning: some moved values might need to be renamed to avoid wrong references +help: consider moving these statements after the if | LL ~ } LL + let simple_examples = "I now identify as a &str :)"; @@ -106,8 +106,8 @@ LL | / x << 2 LL | | }; | |_____^ | - = note: The end suggestion probably needs some adjustments to use the expression result correctly -help: consider moving the end statements out like this + = note: the end suggestion probably needs some adjustments to use the expression result correctly +help: consider moving these statements after the if | LL ~ } LL ~ x << 2; @@ -120,8 +120,8 @@ LL | / x * 4 LL | | } | |_____^ | - = note: The end suggestion probably needs some adjustments to use the expression result correctly -help: consider moving the end statements out like this + = note: the end suggestion probably needs some adjustments to use the expression result correctly +help: consider moving these statements after the if | LL ~ } LL + x * 4 @@ -133,7 +133,7 @@ error: all if blocks contain the same code at the end LL | if x == 17 { b = 1; a = 0x99; } else { a = 0x99; } | ^^^^^^^^^^^ | -help: consider moving the end statements out like this +help: consider moving these statements after the if | LL ~ if x == 17 { b = 1; a = 0x99; } else { } LL + a = 0x99; diff --git a/tests/ui/branches_sharing_code/shared_at_top.stderr b/tests/ui/branches_sharing_code/shared_at_top.stderr index 8d78fa5de7e49..d890b12ecbb4c 100644 --- a/tests/ui/branches_sharing_code/shared_at_top.stderr +++ b/tests/ui/branches_sharing_code/shared_at_top.stderr @@ -10,7 +10,7 @@ note: the lint level is defined here | LL | #![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: consider moving the start statements out like this +help: consider moving these statements before the if | LL ~ println!("Hello World!"); LL + if true { @@ -25,8 +25,8 @@ LL | | println!("The value y was set to: `{}`", y); LL | | let _z = y; | |___________________^ | - = warning: Some moved values might need to be renamed to avoid wrong references -help: consider moving the start statements out like this + = warning: some moved values might need to be renamed to avoid wrong references +help: consider moving these statements before the if | LL ~ let y = 9; LL + println!("The value y was set to: `{}`", y); @@ -41,7 +41,7 @@ LL | / let _ = if x == 7 { LL | | let y = 16; | |___________________^ | -help: consider moving the start statements out like this +help: consider moving these statements before the if | LL ~ let y = 16; LL + let _ = if x == 7 { @@ -55,8 +55,8 @@ LL | | let used_value_name = "Different type"; LL | | println!("Str: {}", used_value_name); | |_____________________________________________^ | - = warning: Some moved values might need to be renamed to avoid wrong references -help: consider moving the start statements out like this + = warning: some moved values might need to be renamed to avoid wrong references +help: consider moving these statements before the if | LL ~ let used_value_name = "Different type"; LL + println!("Str: {}", used_value_name); @@ -71,8 +71,8 @@ LL | | let can_be_overridden = "Move me"; LL | | println!("I'm also moveable"); | |______________________________________^ | - = warning: Some moved values might need to be renamed to avoid wrong references -help: consider moving the start statements out like this + = warning: some moved values might need to be renamed to avoid wrong references +help: consider moving these statements before the if | LL ~ let can_be_overridden = "Move me"; LL + println!("I'm also moveable"); @@ -87,7 +87,7 @@ LL | | println!("This should trigger the `SHARED_CODE_IN_IF_BLOCKS` lint LL | | println!("Because `IF_SAME_THEN_ELSE` is allowed here"); | |________________________________________________________________^ | -help: consider moving the start statements out like this +help: consider moving these statements before the if | LL ~ println!("This should trigger the `SHARED_CODE_IN_IF_BLOCKS` lint."); LL + println!("Because `IF_SAME_THEN_ELSE` is allowed here"); diff --git a/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr b/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr index 1db2343d3fe97..11843cc03d8d1 100644 --- a/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr +++ b/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr @@ -1,4 +1,4 @@ -error: all if blocks contain the same code at the start and the end. Here at the start +error: all if blocks contain the same code at both the start and the end --> $DIR/shared_at_top_and_bottom.rs:16:5 | LL | / if x == 7 { @@ -12,26 +12,26 @@ note: the lint level is defined here | LL | #![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: and here at the end +note: this code is shared at the end --> $DIR/shared_at_top_and_bottom.rs:28:5 | LL | / let _u = 9; LL | | } | |_____^ -help: consider moving the start statements out like this +help: consider moving these statements before the if | LL ~ let t = 7; LL + let _overlap_start = t * 2; LL + let _overlap_end = 2 * t; LL + if x == 7 { | -help: and consider moving the end statements out like this +help: consider moving these statements after the if | LL ~ } LL + let _u = 9; | -error: all if blocks contain the same code at the start and the end. Here at the start +error: all if blocks contain the same code at both the start and the end --> $DIR/shared_at_top_and_bottom.rs:32:5 | LL | / if x == 99 { @@ -40,29 +40,29 @@ LL | | let _overlap_start = r; LL | | let _overlap_middle = r * r; | |____________________________________^ | -note: and here at the end +note: this code is shared at the end --> $DIR/shared_at_top_and_bottom.rs:43:5 | LL | / let _overlap_end = r * r * r; LL | | let z = "end"; LL | | } | |_____^ - = warning: Some moved values might need to be renamed to avoid wrong references -help: consider moving the start statements out like this + = warning: some moved values might need to be renamed to avoid wrong references +help: consider moving these statements before the if | LL ~ let r = 7; LL + let _overlap_start = r; LL + let _overlap_middle = r * r; LL + if x == 99 { | -help: and consider moving the end statements out like this +help: consider moving these statements after the if | LL ~ } LL + let _overlap_end = r * r * r; LL + let z = "end"; | -error: all if blocks contain the same code at the start and the end. Here at the start +error: all if blocks contain the same code at both the start and the end --> $DIR/shared_at_top_and_bottom.rs:61:5 | LL | / if (x > 7 && y < 13) || (x + y) % 2 == 1 { @@ -71,7 +71,7 @@ LL | | let b = 0xffff00ff; LL | | let e_id = gen_id(a, b); | |________________________________^ | -note: and here at the end +note: this code is shared at the end --> $DIR/shared_at_top_and_bottom.rs:81:5 | LL | / let pack = DataPack { @@ -82,15 +82,15 @@ LL | | }; LL | | process_data(pack); LL | | } | |_____^ - = warning: Some moved values might need to be renamed to avoid wrong references -help: consider moving the start statements out like this + = warning: some moved values might need to be renamed to avoid wrong references +help: consider moving these statements before the if | LL ~ let a = 0xcafe; LL + let b = 0xffff00ff; LL + let e_id = gen_id(a, b); LL + if (x > 7 && y < 13) || (x + y) % 2 == 1 { | -help: and consider moving the end statements out like this +help: consider moving these statements after the if | LL ~ } LL + let pack = DataPack { @@ -100,51 +100,51 @@ LL + some_data: vec![0x12, 0x34, 0x56, 0x78, 0x90], LL + }; ... -error: all if blocks contain the same code at the start and the end. Here at the start +error: all if blocks contain the same code at both the start and the end --> $DIR/shared_at_top_and_bottom.rs:94:5 | LL | / let _ = if x == 7 { LL | | let _ = 19; | |___________________^ | -note: and here at the end +note: this code is shared at the end --> $DIR/shared_at_top_and_bottom.rs:103:5 | LL | / x << 2 LL | | }; | |_____^ - = note: The end suggestion probably needs some adjustments to use the expression result correctly -help: consider moving the start statements out like this + = note: the end suggestion probably needs some adjustments to use the expression result correctly +help: consider moving these statements before the if | LL ~ let _ = 19; LL + let _ = if x == 7 { | -help: and consider moving the end statements out like this +help: consider moving these statements after the if | LL ~ } LL ~ x << 2; | -error: all if blocks contain the same code at the start and the end. Here at the start +error: all if blocks contain the same code at both the start and the end --> $DIR/shared_at_top_and_bottom.rs:106:5 | LL | / if x == 9 { LL | | let _ = 17; | |___________________^ | -note: and here at the end +note: this code is shared at the end --> $DIR/shared_at_top_and_bottom.rs:115:5 | LL | / x * 4 LL | | } | |_____^ - = note: The end suggestion probably needs some adjustments to use the expression result correctly -help: consider moving the start statements out like this + = note: the end suggestion probably needs some adjustments to use the expression result correctly +help: consider moving these statements before the if | LL ~ let _ = 17; LL + if x == 9 { | -help: and consider moving the end statements out like this +help: consider moving these statements after the if | LL ~ } LL + x * 4 diff --git a/tests/ui/checked_conversions.fixed b/tests/ui/checked_conversions.fixed index 0983d393b560e..cb7100bc9efae 100644 --- a/tests/ui/checked_conversions.fixed +++ b/tests/ui/checked_conversions.fixed @@ -71,4 +71,9 @@ pub fn i8_to_u8(value: i8) { let _ = value >= 0; } +// Do not lint +pub const fn issue_8898(i: u32) -> bool { + i <= i32::MAX as u32 +} + fn main() {} diff --git a/tests/ui/checked_conversions.rs b/tests/ui/checked_conversions.rs index 7d26ace47fdf5..ed4e0692388a5 100644 --- a/tests/ui/checked_conversions.rs +++ b/tests/ui/checked_conversions.rs @@ -71,4 +71,9 @@ pub fn i8_to_u8(value: i8) { let _ = value >= 0; } +// Do not lint +pub const fn issue_8898(i: u32) -> bool { + i <= i32::MAX as u32 +} + fn main() {} diff --git a/tests/ui/default_numeric_fallback_i32.fixed b/tests/ui/default_numeric_fallback_i32.fixed index fa85d278c8fca..55451cf2f7d0f 100644 --- a/tests/ui/default_numeric_fallback_i32.fixed +++ b/tests/ui/default_numeric_fallback_i32.fixed @@ -1,6 +1,7 @@ // run-rustfix // aux-build:macro_rules.rs +#![feature(lint_reasons)] #![warn(clippy::default_numeric_fallback)] #![allow( unused, @@ -173,4 +174,9 @@ mod in_macro { } } +fn check_expect_suppression() { + #[expect(clippy::default_numeric_fallback)] + let x = 21; +} + fn main() {} diff --git a/tests/ui/default_numeric_fallback_i32.rs b/tests/ui/default_numeric_fallback_i32.rs index 71acccd702b06..62d72f2febaaa 100644 --- a/tests/ui/default_numeric_fallback_i32.rs +++ b/tests/ui/default_numeric_fallback_i32.rs @@ -1,6 +1,7 @@ // run-rustfix // aux-build:macro_rules.rs +#![feature(lint_reasons)] #![warn(clippy::default_numeric_fallback)] #![allow( unused, @@ -173,4 +174,9 @@ mod in_macro { } } +fn check_expect_suppression() { + #[expect(clippy::default_numeric_fallback)] + let x = 21; +} + fn main() {} diff --git a/tests/ui/default_numeric_fallback_i32.stderr b/tests/ui/default_numeric_fallback_i32.stderr index 3cc84ff113232..f7c5e724c403c 100644 --- a/tests/ui/default_numeric_fallback_i32.stderr +++ b/tests/ui/default_numeric_fallback_i32.stderr @@ -1,5 +1,5 @@ error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:20:17 + --> $DIR/default_numeric_fallback_i32.rs:21:17 | LL | let x = 22; | ^^ help: consider adding suffix: `22_i32` @@ -7,145 +7,145 @@ LL | let x = 22; = note: `-D clippy::default-numeric-fallback` implied by `-D warnings` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:21:18 + --> $DIR/default_numeric_fallback_i32.rs:22:18 | LL | let x = [1, 2, 3]; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:21:21 + --> $DIR/default_numeric_fallback_i32.rs:22:21 | LL | let x = [1, 2, 3]; | ^ help: consider adding suffix: `2_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:21:24 + --> $DIR/default_numeric_fallback_i32.rs:22:24 | LL | let x = [1, 2, 3]; | ^ help: consider adding suffix: `3_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:22:28 + --> $DIR/default_numeric_fallback_i32.rs:23:28 | LL | let x = if true { (1, 2) } else { (3, 4) }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:22:31 + --> $DIR/default_numeric_fallback_i32.rs:23:31 | LL | let x = if true { (1, 2) } else { (3, 4) }; | ^ help: consider adding suffix: `2_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:22:44 + --> $DIR/default_numeric_fallback_i32.rs:23:44 | LL | let x = if true { (1, 2) } else { (3, 4) }; | ^ help: consider adding suffix: `3_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:22:47 + --> $DIR/default_numeric_fallback_i32.rs:23:47 | LL | let x = if true { (1, 2) } else { (3, 4) }; | ^ help: consider adding suffix: `4_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:23:23 + --> $DIR/default_numeric_fallback_i32.rs:24:23 | LL | let x = match 1 { | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:24:13 + --> $DIR/default_numeric_fallback_i32.rs:25:13 | LL | 1 => 1, | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:24:18 + --> $DIR/default_numeric_fallback_i32.rs:25:18 | LL | 1 => 1, | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:25:18 + --> $DIR/default_numeric_fallback_i32.rs:26:18 | LL | _ => 2, | ^ help: consider adding suffix: `2_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:42:21 + --> $DIR/default_numeric_fallback_i32.rs:43:21 | LL | let y = 1; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:50:21 + --> $DIR/default_numeric_fallback_i32.rs:51:21 | LL | let y = 1; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:56:21 + --> $DIR/default_numeric_fallback_i32.rs:57:21 | LL | let y = 1; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:68:9 + --> $DIR/default_numeric_fallback_i32.rs:69:9 | LL | 1 | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:74:27 + --> $DIR/default_numeric_fallback_i32.rs:75:27 | LL | let f = || -> _ { 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:78:29 + --> $DIR/default_numeric_fallback_i32.rs:79:29 | LL | let f = || -> i32 { 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:92:21 + --> $DIR/default_numeric_fallback_i32.rs:93:21 | LL | generic_arg(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:95:32 + --> $DIR/default_numeric_fallback_i32.rs:96:32 | LL | let x: _ = generic_arg(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:113:28 + --> $DIR/default_numeric_fallback_i32.rs:114:28 | LL | GenericStruct { x: 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:116:36 + --> $DIR/default_numeric_fallback_i32.rs:117:36 | LL | let _ = GenericStruct { x: 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:134:24 + --> $DIR/default_numeric_fallback_i32.rs:135:24 | LL | GenericEnum::X(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:154:23 + --> $DIR/default_numeric_fallback_i32.rs:155:23 | LL | s.generic_arg(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:161:21 + --> $DIR/default_numeric_fallback_i32.rs:162:21 | LL | let x = 22; | ^^ help: consider adding suffix: `22_i32` diff --git a/tests/ui/derive_partial_eq_without_eq.fixed b/tests/ui/derive_partial_eq_without_eq.fixed index 012780258fc3a..bbbe467590f91 100644 --- a/tests/ui/derive_partial_eq_without_eq.fixed +++ b/tests/ui/derive_partial_eq_without_eq.fixed @@ -4,28 +4,28 @@ #![warn(clippy::derive_partial_eq_without_eq)] // Don't warn on structs that aren't PartialEq -struct NotPartialEq { +pub struct NotPartialEq { foo: u32, bar: String, } // Eq can be derived but is missing #[derive(Debug, PartialEq, Eq)] -struct MissingEq { +pub struct MissingEq { foo: u32, bar: String, } // Eq is derived #[derive(PartialEq, Eq)] -struct NotMissingEq { +pub struct NotMissingEq { foo: u32, bar: String, } // Eq is manually implemented #[derive(PartialEq)] -struct ManualEqImpl { +pub struct ManualEqImpl { foo: u32, bar: String, } @@ -34,13 +34,13 @@ impl Eq for ManualEqImpl {} // Cannot be Eq because f32 isn't Eq #[derive(PartialEq)] -struct CannotBeEq { +pub struct CannotBeEq { foo: u32, bar: f32, } // Don't warn if PartialEq is manually implemented -struct ManualPartialEqImpl { +pub struct ManualPartialEqImpl { foo: u32, bar: String, } @@ -52,53 +52,75 @@ impl PartialEq for ManualPartialEqImpl { } // Generic fields should be properly checked for Eq-ness -#[derive(PartialEq)] -struct GenericNotEq { +#[derive(PartialEq, Eq)] +pub struct GenericNotEq { foo: T, bar: U, } #[derive(PartialEq, Eq)] -struct GenericEq { +pub struct GenericEq { foo: T, bar: U, } #[derive(PartialEq, Eq)] -struct TupleStruct(u32); +pub struct TupleStruct(u32); #[derive(PartialEq, Eq)] -struct GenericTupleStruct(T); +pub struct GenericTupleStruct(T); #[derive(PartialEq)] -struct TupleStructNotEq(f32); +pub struct TupleStructNotEq(f32); #[derive(PartialEq, Eq)] -enum Enum { +pub enum Enum { Foo(u32), Bar { a: String, b: () }, } #[derive(PartialEq, Eq)] -enum GenericEnum { +pub enum GenericEnum { Foo(T), Bar { a: U, b: V }, } #[derive(PartialEq)] -enum EnumNotEq { +pub enum EnumNotEq { Foo(u32), Bar { a: String, b: f32 }, } // Ensure that rustfix works properly when `PartialEq` has other derives on either side #[derive(Debug, PartialEq, Eq, Clone)] -struct RustFixWithOtherDerives; +pub struct RustFixWithOtherDerives; -#[derive(PartialEq)] -struct Generic(T); +#[derive(PartialEq, Eq)] +pub struct Generic(T); #[derive(PartialEq, Eq)] -struct GenericPhantom(core::marker::PhantomData); +pub struct GenericPhantom(core::marker::PhantomData); + +mod _hidden { + #[derive(PartialEq, Eq)] + pub struct Reexported; + + #[derive(PartialEq, Eq)] + pub struct InPubFn; + + #[derive(PartialEq)] + pub(crate) struct PubCrate; + + #[derive(PartialEq)] + pub(super) struct PubSuper; +} + +pub use _hidden::Reexported; +pub fn _from_mod() -> _hidden::InPubFn { + _hidden::InPubFn +} + +#[derive(PartialEq)] +struct InternalTy; fn main() {} diff --git a/tests/ui/derive_partial_eq_without_eq.rs b/tests/ui/derive_partial_eq_without_eq.rs index fc8285b0c6b75..88d6fbd1af7e8 100644 --- a/tests/ui/derive_partial_eq_without_eq.rs +++ b/tests/ui/derive_partial_eq_without_eq.rs @@ -4,28 +4,28 @@ #![warn(clippy::derive_partial_eq_without_eq)] // Don't warn on structs that aren't PartialEq -struct NotPartialEq { +pub struct NotPartialEq { foo: u32, bar: String, } // Eq can be derived but is missing #[derive(Debug, PartialEq)] -struct MissingEq { +pub struct MissingEq { foo: u32, bar: String, } // Eq is derived #[derive(PartialEq, Eq)] -struct NotMissingEq { +pub struct NotMissingEq { foo: u32, bar: String, } // Eq is manually implemented #[derive(PartialEq)] -struct ManualEqImpl { +pub struct ManualEqImpl { foo: u32, bar: String, } @@ -34,13 +34,13 @@ impl Eq for ManualEqImpl {} // Cannot be Eq because f32 isn't Eq #[derive(PartialEq)] -struct CannotBeEq { +pub struct CannotBeEq { foo: u32, bar: f32, } // Don't warn if PartialEq is manually implemented -struct ManualPartialEqImpl { +pub struct ManualPartialEqImpl { foo: u32, bar: String, } @@ -53,52 +53,74 @@ impl PartialEq for ManualPartialEqImpl { // Generic fields should be properly checked for Eq-ness #[derive(PartialEq)] -struct GenericNotEq { +pub struct GenericNotEq { foo: T, bar: U, } #[derive(PartialEq)] -struct GenericEq { +pub struct GenericEq { foo: T, bar: U, } #[derive(PartialEq)] -struct TupleStruct(u32); +pub struct TupleStruct(u32); #[derive(PartialEq)] -struct GenericTupleStruct(T); +pub struct GenericTupleStruct(T); #[derive(PartialEq)] -struct TupleStructNotEq(f32); +pub struct TupleStructNotEq(f32); #[derive(PartialEq)] -enum Enum { +pub enum Enum { Foo(u32), Bar { a: String, b: () }, } #[derive(PartialEq)] -enum GenericEnum { +pub enum GenericEnum { Foo(T), Bar { a: U, b: V }, } #[derive(PartialEq)] -enum EnumNotEq { +pub enum EnumNotEq { Foo(u32), Bar { a: String, b: f32 }, } // Ensure that rustfix works properly when `PartialEq` has other derives on either side #[derive(Debug, PartialEq, Clone)] -struct RustFixWithOtherDerives; +pub struct RustFixWithOtherDerives; #[derive(PartialEq)] -struct Generic(T); +pub struct Generic(T); #[derive(PartialEq, Eq)] -struct GenericPhantom(core::marker::PhantomData); +pub struct GenericPhantom(core::marker::PhantomData); + +mod _hidden { + #[derive(PartialEq)] + pub struct Reexported; + + #[derive(PartialEq)] + pub struct InPubFn; + + #[derive(PartialEq)] + pub(crate) struct PubCrate; + + #[derive(PartialEq)] + pub(super) struct PubSuper; +} + +pub use _hidden::Reexported; +pub fn _from_mod() -> _hidden::InPubFn { + _hidden::InPubFn +} + +#[derive(PartialEq)] +struct InternalTy; fn main() {} diff --git a/tests/ui/derive_partial_eq_without_eq.stderr b/tests/ui/derive_partial_eq_without_eq.stderr index bf55165890a5a..794c5dab8445b 100644 --- a/tests/ui/derive_partial_eq_without_eq.stderr +++ b/tests/ui/derive_partial_eq_without_eq.stderr @@ -6,6 +6,12 @@ LL | #[derive(Debug, PartialEq)] | = note: `-D clippy::derive-partial-eq-without-eq` implied by `-D warnings` +error: you are deriving `PartialEq` and can implement `Eq` + --> $DIR/derive_partial_eq_without_eq.rs:55:10 + | +LL | #[derive(PartialEq)] + | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq` + error: you are deriving `PartialEq` and can implement `Eq` --> $DIR/derive_partial_eq_without_eq.rs:61:10 | @@ -42,5 +48,23 @@ error: you are deriving `PartialEq` and can implement `Eq` LL | #[derive(Debug, PartialEq, Clone)] | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq` -error: aborting due to 7 previous errors +error: you are deriving `PartialEq` and can implement `Eq` + --> $DIR/derive_partial_eq_without_eq.rs:98:10 + | +LL | #[derive(PartialEq)] + | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq` + +error: you are deriving `PartialEq` and can implement `Eq` + --> $DIR/derive_partial_eq_without_eq.rs:105:14 + | +LL | #[derive(PartialEq)] + | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq` + +error: you are deriving `PartialEq` and can implement `Eq` + --> $DIR/derive_partial_eq_without_eq.rs:108:14 + | +LL | #[derive(PartialEq)] + | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq` + +error: aborting due to 11 previous errors diff --git a/tests/ui/eta.fixed b/tests/ui/eta.fixed index 6c2272f4dff97..f8d559bf226f1 100644 --- a/tests/ui/eta.fixed +++ b/tests/ui/eta.fixed @@ -291,3 +291,15 @@ fn coerced_closure() { fn slice_fn(_: impl FnOnce() -> &'static [u8]) {} slice_fn(|| arr()); } + +// https://github.com/rust-lang/rust-clippy/issues/7861 +fn box_dyn() { + fn f(_: impl Fn(usize) -> Box) {} + f(|x| Box::new(x)); +} + +// https://github.com/rust-lang/rust-clippy/issues/5939 +fn not_general_enough() { + fn f(_: impl FnMut(&Path) -> std::io::Result<()>) {} + f(|path| std::fs::remove_file(path)); +} diff --git a/tests/ui/eta.rs b/tests/ui/eta.rs index a1a9c0dfbf381..f0fb55a1e5f06 100644 --- a/tests/ui/eta.rs +++ b/tests/ui/eta.rs @@ -291,3 +291,15 @@ fn coerced_closure() { fn slice_fn(_: impl FnOnce() -> &'static [u8]) {} slice_fn(|| arr()); } + +// https://github.com/rust-lang/rust-clippy/issues/7861 +fn box_dyn() { + fn f(_: impl Fn(usize) -> Box) {} + f(|x| Box::new(x)); +} + +// https://github.com/rust-lang/rust-clippy/issues/5939 +fn not_general_enough() { + fn f(_: impl FnMut(&Path) -> std::io::Result<()>) {} + f(|path| std::fs::remove_file(path)); +} diff --git a/tests/ui/for_loops_over_fallibles.rs b/tests/ui/for_loops_over_fallibles.rs index 1b9dde87cd5a2..3390111d0a8fe 100644 --- a/tests/ui/for_loops_over_fallibles.rs +++ b/tests/ui/for_loops_over_fallibles.rs @@ -2,7 +2,7 @@ fn for_loops_over_fallibles() { let option = Some(1); - let result = option.ok_or("x not found"); + let mut result = option.ok_or("x not found"); let v = vec![0, 1, 2]; // check over an `Option` @@ -10,11 +10,26 @@ fn for_loops_over_fallibles() { println!("{}", x); } + // check over an `Option` + for x in option.iter() { + println!("{}", x); + } + // check over a `Result` for x in result { println!("{}", x); } + // check over a `Result` + for x in result.iter_mut() { + println!("{}", x); + } + + // check over a `Result` + for x in result.into_iter() { + println!("{}", x); + } + for x in option.ok_or("x not found") { println!("{}", x); } diff --git a/tests/ui/for_loops_over_fallibles.stderr b/tests/ui/for_loops_over_fallibles.stderr index 52b94875aec4d..8c8c022243aeb 100644 --- a/tests/ui/for_loops_over_fallibles.stderr +++ b/tests/ui/for_loops_over_fallibles.stderr @@ -7,16 +7,40 @@ LL | for x in option { = note: `-D clippy::for-loops-over-fallibles` implied by `-D warnings` = help: consider replacing `for x in option` with `if let Some(x) = option` -error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement +error: for loop over `option`, which is an `Option`. This is more readably written as an `if let` statement --> $DIR/for_loops_over_fallibles.rs:14:14 | +LL | for x in option.iter() { + | ^^^^^^ + | + = help: consider replacing `for x in option.iter()` with `if let Some(x) = option` + +error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement + --> $DIR/for_loops_over_fallibles.rs:19:14 + | LL | for x in result { | ^^^^^^ | = help: consider replacing `for x in result` with `if let Ok(x) = result` +error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement + --> $DIR/for_loops_over_fallibles.rs:24:14 + | +LL | for x in result.iter_mut() { + | ^^^^^^ + | + = help: consider replacing `for x in result.iter_mut()` with `if let Ok(x) = result` + +error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement + --> $DIR/for_loops_over_fallibles.rs:29:14 + | +LL | for x in result.into_iter() { + | ^^^^^^ + | + = help: consider replacing `for x in result.into_iter()` with `if let Ok(x) = result` + error: for loop over `option.ok_or("x not found")`, which is a `Result`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:18:14 + --> $DIR/for_loops_over_fallibles.rs:33:14 | LL | for x in option.ok_or("x not found") { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -24,7 +48,7 @@ LL | for x in option.ok_or("x not found") { = help: consider replacing `for x in option.ok_or("x not found")` with `if let Ok(x) = option.ok_or("x not found")` error: you are iterating over `Iterator::next()` which is an Option; this will compile but is probably not what you want - --> $DIR/for_loops_over_fallibles.rs:24:14 + --> $DIR/for_loops_over_fallibles.rs:39:14 | LL | for x in v.iter().next() { | ^^^^^^^^^^^^^^^ @@ -32,7 +56,7 @@ LL | for x in v.iter().next() { = note: `#[deny(clippy::iter_next_loop)]` on by default error: for loop over `v.iter().next().and(Some(0))`, which is an `Option`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:29:14 + --> $DIR/for_loops_over_fallibles.rs:44:14 | LL | for x in v.iter().next().and(Some(0)) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -40,7 +64,7 @@ LL | for x in v.iter().next().and(Some(0)) { = help: consider replacing `for x in v.iter().next().and(Some(0))` with `if let Some(x) = v.iter().next().and(Some(0))` error: for loop over `v.iter().next().ok_or("x not found")`, which is a `Result`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:33:14 + --> $DIR/for_loops_over_fallibles.rs:48:14 | LL | for x in v.iter().next().ok_or("x not found") { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -48,7 +72,7 @@ LL | for x in v.iter().next().ok_or("x not found") { = help: consider replacing `for x in v.iter().next().ok_or("x not found")` with `if let Ok(x) = v.iter().next().ok_or("x not found")` error: this loop never actually loops - --> $DIR/for_loops_over_fallibles.rs:45:5 + --> $DIR/for_loops_over_fallibles.rs:60:5 | LL | / while let Some(x) = option { LL | | println!("{}", x); @@ -59,7 +83,7 @@ LL | | } = note: `#[deny(clippy::never_loop)]` on by default error: this loop never actually loops - --> $DIR/for_loops_over_fallibles.rs:51:5 + --> $DIR/for_loops_over_fallibles.rs:66:5 | LL | / while let Ok(x) = result { LL | | println!("{}", x); @@ -67,5 +91,5 @@ LL | | break; LL | | } | |_____^ -error: aborting due to 8 previous errors +error: aborting due to 11 previous errors diff --git a/tests/ui/iter_overeager_cloned.fixed b/tests/ui/iter_overeager_cloned.fixed index 7c2b05d837ba8..c100705d01781 100644 --- a/tests/ui/iter_overeager_cloned.fixed +++ b/tests/ui/iter_overeager_cloned.fixed @@ -18,7 +18,8 @@ fn main() { let _ = vec.iter().filter(|x| x == &"2").nth(2).cloned(); let _ = [Some(Some("str".to_string())), Some(Some("str".to_string()))] - .iter().flatten().cloned(); + .iter() + .flatten().cloned(); // Not implemented yet let _ = vec.iter().cloned().filter(|x| x.starts_with('2')); @@ -43,6 +44,9 @@ fn main() { // Should probably stay as it is. let _ = [0, 1, 2, 3, 4].iter().cloned().take(10); + + // `&Range<_>` doesn't implement `IntoIterator` + let _ = [0..1, 2..5].iter().cloned().flatten(); } // #8527 diff --git a/tests/ui/iter_overeager_cloned.rs b/tests/ui/iter_overeager_cloned.rs index f2d0b155d2c2a..2caa88020662d 100644 --- a/tests/ui/iter_overeager_cloned.rs +++ b/tests/ui/iter_overeager_cloned.rs @@ -45,6 +45,9 @@ fn main() { // Should probably stay as it is. let _ = [0, 1, 2, 3, 4].iter().cloned().take(10); + + // `&Range<_>` doesn't implement `IntoIterator` + let _ = [0..1, 2..5].iter().cloned().flatten(); } // #8527 diff --git a/tests/ui/iter_overeager_cloned.stderr b/tests/ui/iter_overeager_cloned.stderr index 0582700fd16a8..dcae7cecd33e9 100644 --- a/tests/ui/iter_overeager_cloned.stderr +++ b/tests/ui/iter_overeager_cloned.stderr @@ -1,44 +1,56 @@ -error: called `cloned().last()` on an `Iterator`. It may be more efficient to call `last().cloned()` instead +error: unnecessarily eager cloning of iterator items --> $DIR/iter_overeager_cloned.rs:8:29 | LL | let _: Option = vec.iter().cloned().last(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec.iter().last().cloned()` + | ^^^^^^^^^^---------------- + | | + | help: try this: `.last().cloned()` | = note: `-D clippy::iter-overeager-cloned` implied by `-D warnings` -error: called `cloned().next()` on an `Iterator`. It may be more efficient to call `next().cloned()` instead +error: unnecessarily eager cloning of iterator items --> $DIR/iter_overeager_cloned.rs:10:29 | LL | let _: Option = vec.iter().chain(vec.iter()).cloned().next(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec.iter().chain(vec.iter()).next().cloned()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^---------------- + | | + | help: try this: `.next().cloned()` -error: called `cloned().count()` on an `Iterator`. It may be more efficient to call `count()` instead +error: unneeded cloning of iterator items --> $DIR/iter_overeager_cloned.rs:12:20 | LL | let _: usize = vec.iter().filter(|x| x == &"2").cloned().count(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec.iter().filter(|x| x == &"2").count()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^----------------- + | | + | help: try this: `.count()` | = note: `-D clippy::redundant-clone` implied by `-D warnings` -error: called `cloned().take(...)` on an `Iterator`. It may be more efficient to call `take(...).cloned()` instead +error: unnecessarily eager cloning of iterator items --> $DIR/iter_overeager_cloned.rs:14:21 | LL | let _: Vec<_> = vec.iter().cloned().take(2).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec.iter().take(2).cloned()` + | ^^^^^^^^^^----------------- + | | + | help: try this: `.take(2).cloned()` -error: called `cloned().skip(...)` on an `Iterator`. It may be more efficient to call `skip(...).cloned()` instead +error: unnecessarily eager cloning of iterator items --> $DIR/iter_overeager_cloned.rs:16:21 | LL | let _: Vec<_> = vec.iter().cloned().skip(2).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec.iter().skip(2).cloned()` + | ^^^^^^^^^^----------------- + | | + | help: try this: `.skip(2).cloned()` -error: called `cloned().nth(...)` on an `Iterator`. It may be more efficient to call `nth(...).cloned()` instead +error: unnecessarily eager cloning of iterator items --> $DIR/iter_overeager_cloned.rs:18:13 | LL | let _ = vec.iter().filter(|x| x == &"2").cloned().nth(2); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec.iter().filter(|x| x == &"2").nth(2).cloned()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---------------- + | | + | help: try this: `.nth(2).cloned()` -error: called `cloned().flatten()` on an `Iterator`. It may be more efficient to call `flatten().cloned()` instead +error: unnecessarily eager cloning of iterator items --> $DIR/iter_overeager_cloned.rs:20:13 | LL | let _ = [Some(Some("str".to_string())), Some(Some("str".to_string()))] @@ -50,8 +62,8 @@ LL | | .flatten(); | help: try this | -LL ~ let _ = [Some(Some("str".to_string())), Some(Some("str".to_string()))] -LL ~ .iter().flatten().cloned(); +LL ~ .iter() +LL ~ .flatten().cloned(); | error: aborting due to 7 previous errors diff --git a/tests/ui/manual_filter_map.fixed b/tests/ui/manual_filter_map.fixed index fc8f58f8ea5cd..de0d86148899d 100644 --- a/tests/ui/manual_filter_map.fixed +++ b/tests/ui/manual_filter_map.fixed @@ -35,3 +35,53 @@ fn to_opt(_: T) -> Option { fn to_res(_: T) -> Result { unimplemented!() } + +struct Issue8920<'a> { + option_field: Option, + result_field: Result, + ref_field: Option<&'a usize>, +} + +fn issue_8920() { + let mut vec = vec![Issue8920 { + option_field: Some(String::from("str")), + result_field: Ok(String::from("str")), + ref_field: Some(&1), + }]; + + let _ = vec + .iter() + .filter_map(|f| f.option_field.clone()); + + let _ = vec + .iter() + .filter_map(|f| f.ref_field.cloned()); + + let _ = vec + .iter() + .filter_map(|f| f.ref_field.copied()); + + let _ = vec + .iter() + .filter_map(|f| f.result_field.clone().ok()); + + let _ = vec + .iter() + .filter_map(|f| f.result_field.as_ref().ok()); + + let _ = vec + .iter() + .filter_map(|f| f.result_field.as_deref().ok()); + + let _ = vec + .iter_mut() + .filter_map(|f| f.result_field.as_mut().ok()); + + let _ = vec + .iter_mut() + .filter_map(|f| f.result_field.as_deref_mut().ok()); + + let _ = vec + .iter() + .filter_map(|f| f.result_field.to_owned().ok()); +} diff --git a/tests/ui/manual_filter_map.rs b/tests/ui/manual_filter_map.rs index 3af4bbee3bf82..bd6516f038b29 100644 --- a/tests/ui/manual_filter_map.rs +++ b/tests/ui/manual_filter_map.rs @@ -35,3 +35,62 @@ fn to_opt(_: T) -> Option { fn to_res(_: T) -> Result { unimplemented!() } + +struct Issue8920<'a> { + option_field: Option, + result_field: Result, + ref_field: Option<&'a usize>, +} + +fn issue_8920() { + let mut vec = vec![Issue8920 { + option_field: Some(String::from("str")), + result_field: Ok(String::from("str")), + ref_field: Some(&1), + }]; + + let _ = vec + .iter() + .filter(|f| f.option_field.is_some()) + .map(|f| f.option_field.clone().unwrap()); + + let _ = vec + .iter() + .filter(|f| f.ref_field.is_some()) + .map(|f| f.ref_field.cloned().unwrap()); + + let _ = vec + .iter() + .filter(|f| f.ref_field.is_some()) + .map(|f| f.ref_field.copied().unwrap()); + + let _ = vec + .iter() + .filter(|f| f.result_field.is_ok()) + .map(|f| f.result_field.clone().unwrap()); + + let _ = vec + .iter() + .filter(|f| f.result_field.is_ok()) + .map(|f| f.result_field.as_ref().unwrap()); + + let _ = vec + .iter() + .filter(|f| f.result_field.is_ok()) + .map(|f| f.result_field.as_deref().unwrap()); + + let _ = vec + .iter_mut() + .filter(|f| f.result_field.is_ok()) + .map(|f| f.result_field.as_mut().unwrap()); + + let _ = vec + .iter_mut() + .filter(|f| f.result_field.is_ok()) + .map(|f| f.result_field.as_deref_mut().unwrap()); + + let _ = vec + .iter() + .filter(|f| f.result_field.is_ok()) + .map(|f| f.result_field.to_owned().unwrap()); +} diff --git a/tests/ui/manual_filter_map.stderr b/tests/ui/manual_filter_map.stderr index 4d4e2d5c12fe9..465f1b1911017 100644 --- a/tests/ui/manual_filter_map.stderr +++ b/tests/ui/manual_filter_map.stderr @@ -18,5 +18,77 @@ error: `filter(..).map(..)` can be simplified as `filter_map(..)` LL | let _ = (0..).filter(|&n| to_res(n).is_ok()).map(|a| to_res(a).unwrap_or(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `filter_map(|a| to_res(a).ok())` -error: aborting due to 3 previous errors +error: `filter(..).map(..)` can be simplified as `filter_map(..)` + --> $DIR/manual_filter_map.rs:54:10 + | +LL | .filter(|f| f.option_field.is_some()) + | __________^ +LL | | .map(|f| f.option_field.clone().unwrap()); + | |_________________________________________________^ help: try: `filter_map(|f| f.option_field.clone())` + +error: `filter(..).map(..)` can be simplified as `filter_map(..)` + --> $DIR/manual_filter_map.rs:59:10 + | +LL | .filter(|f| f.ref_field.is_some()) + | __________^ +LL | | .map(|f| f.ref_field.cloned().unwrap()); + | |_______________________________________________^ help: try: `filter_map(|f| f.ref_field.cloned())` + +error: `filter(..).map(..)` can be simplified as `filter_map(..)` + --> $DIR/manual_filter_map.rs:64:10 + | +LL | .filter(|f| f.ref_field.is_some()) + | __________^ +LL | | .map(|f| f.ref_field.copied().unwrap()); + | |_______________________________________________^ help: try: `filter_map(|f| f.ref_field.copied())` + +error: `filter(..).map(..)` can be simplified as `filter_map(..)` + --> $DIR/manual_filter_map.rs:69:10 + | +LL | .filter(|f| f.result_field.is_ok()) + | __________^ +LL | | .map(|f| f.result_field.clone().unwrap()); + | |_________________________________________________^ help: try: `filter_map(|f| f.result_field.clone().ok())` + +error: `filter(..).map(..)` can be simplified as `filter_map(..)` + --> $DIR/manual_filter_map.rs:74:10 + | +LL | .filter(|f| f.result_field.is_ok()) + | __________^ +LL | | .map(|f| f.result_field.as_ref().unwrap()); + | |__________________________________________________^ help: try: `filter_map(|f| f.result_field.as_ref().ok())` + +error: `filter(..).map(..)` can be simplified as `filter_map(..)` + --> $DIR/manual_filter_map.rs:79:10 + | +LL | .filter(|f| f.result_field.is_ok()) + | __________^ +LL | | .map(|f| f.result_field.as_deref().unwrap()); + | |____________________________________________________^ help: try: `filter_map(|f| f.result_field.as_deref().ok())` + +error: `filter(..).map(..)` can be simplified as `filter_map(..)` + --> $DIR/manual_filter_map.rs:84:10 + | +LL | .filter(|f| f.result_field.is_ok()) + | __________^ +LL | | .map(|f| f.result_field.as_mut().unwrap()); + | |__________________________________________________^ help: try: `filter_map(|f| f.result_field.as_mut().ok())` + +error: `filter(..).map(..)` can be simplified as `filter_map(..)` + --> $DIR/manual_filter_map.rs:89:10 + | +LL | .filter(|f| f.result_field.is_ok()) + | __________^ +LL | | .map(|f| f.result_field.as_deref_mut().unwrap()); + | |________________________________________________________^ help: try: `filter_map(|f| f.result_field.as_deref_mut().ok())` + +error: `filter(..).map(..)` can be simplified as `filter_map(..)` + --> $DIR/manual_filter_map.rs:94:10 + | +LL | .filter(|f| f.result_field.is_ok()) + | __________^ +LL | | .map(|f| f.result_field.to_owned().unwrap()); + | |____________________________________________________^ help: try: `filter_map(|f| f.result_field.to_owned().ok())` + +error: aborting due to 12 previous errors diff --git a/tests/ui/manual_find_map.fixed b/tests/ui/manual_find_map.fixed index 95e97c4fd1ff4..d69b6c1dcf3bb 100644 --- a/tests/ui/manual_find_map.fixed +++ b/tests/ui/manual_find_map.fixed @@ -35,3 +35,53 @@ fn to_opt(_: T) -> Option { fn to_res(_: T) -> Result { unimplemented!() } + +struct Issue8920<'a> { + option_field: Option, + result_field: Result, + ref_field: Option<&'a usize>, +} + +fn issue_8920() { + let mut vec = vec![Issue8920 { + option_field: Some(String::from("str")), + result_field: Ok(String::from("str")), + ref_field: Some(&1), + }]; + + let _ = vec + .iter() + .find_map(|f| f.option_field.clone()); + + let _ = vec + .iter() + .find_map(|f| f.ref_field.cloned()); + + let _ = vec + .iter() + .find_map(|f| f.ref_field.copied()); + + let _ = vec + .iter() + .find_map(|f| f.result_field.clone().ok()); + + let _ = vec + .iter() + .find_map(|f| f.result_field.as_ref().ok()); + + let _ = vec + .iter() + .find_map(|f| f.result_field.as_deref().ok()); + + let _ = vec + .iter_mut() + .find_map(|f| f.result_field.as_mut().ok()); + + let _ = vec + .iter_mut() + .find_map(|f| f.result_field.as_deref_mut().ok()); + + let _ = vec + .iter() + .find_map(|f| f.result_field.to_owned().ok()); +} diff --git a/tests/ui/manual_find_map.rs b/tests/ui/manual_find_map.rs index cd3c82e3b25ab..1c4e18e31c8b1 100644 --- a/tests/ui/manual_find_map.rs +++ b/tests/ui/manual_find_map.rs @@ -35,3 +35,62 @@ fn to_opt(_: T) -> Option { fn to_res(_: T) -> Result { unimplemented!() } + +struct Issue8920<'a> { + option_field: Option, + result_field: Result, + ref_field: Option<&'a usize>, +} + +fn issue_8920() { + let mut vec = vec![Issue8920 { + option_field: Some(String::from("str")), + result_field: Ok(String::from("str")), + ref_field: Some(&1), + }]; + + let _ = vec + .iter() + .find(|f| f.option_field.is_some()) + .map(|f| f.option_field.clone().unwrap()); + + let _ = vec + .iter() + .find(|f| f.ref_field.is_some()) + .map(|f| f.ref_field.cloned().unwrap()); + + let _ = vec + .iter() + .find(|f| f.ref_field.is_some()) + .map(|f| f.ref_field.copied().unwrap()); + + let _ = vec + .iter() + .find(|f| f.result_field.is_ok()) + .map(|f| f.result_field.clone().unwrap()); + + let _ = vec + .iter() + .find(|f| f.result_field.is_ok()) + .map(|f| f.result_field.as_ref().unwrap()); + + let _ = vec + .iter() + .find(|f| f.result_field.is_ok()) + .map(|f| f.result_field.as_deref().unwrap()); + + let _ = vec + .iter_mut() + .find(|f| f.result_field.is_ok()) + .map(|f| f.result_field.as_mut().unwrap()); + + let _ = vec + .iter_mut() + .find(|f| f.result_field.is_ok()) + .map(|f| f.result_field.as_deref_mut().unwrap()); + + let _ = vec + .iter() + .find(|f| f.result_field.is_ok()) + .map(|f| f.result_field.to_owned().unwrap()); +} diff --git a/tests/ui/manual_find_map.stderr b/tests/ui/manual_find_map.stderr index 9e7f798df4573..9dea42b76868b 100644 --- a/tests/ui/manual_find_map.stderr +++ b/tests/ui/manual_find_map.stderr @@ -18,5 +18,77 @@ error: `find(..).map(..)` can be simplified as `find_map(..)` LL | let _ = (0..).find(|&n| to_res(n).is_ok()).map(|a| to_res(a).unwrap_or(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|a| to_res(a).ok())` -error: aborting due to 3 previous errors +error: `find(..).map(..)` can be simplified as `find_map(..)` + --> $DIR/manual_find_map.rs:54:10 + | +LL | .find(|f| f.option_field.is_some()) + | __________^ +LL | | .map(|f| f.option_field.clone().unwrap()); + | |_________________________________________________^ help: try: `find_map(|f| f.option_field.clone())` + +error: `find(..).map(..)` can be simplified as `find_map(..)` + --> $DIR/manual_find_map.rs:59:10 + | +LL | .find(|f| f.ref_field.is_some()) + | __________^ +LL | | .map(|f| f.ref_field.cloned().unwrap()); + | |_______________________________________________^ help: try: `find_map(|f| f.ref_field.cloned())` + +error: `find(..).map(..)` can be simplified as `find_map(..)` + --> $DIR/manual_find_map.rs:64:10 + | +LL | .find(|f| f.ref_field.is_some()) + | __________^ +LL | | .map(|f| f.ref_field.copied().unwrap()); + | |_______________________________________________^ help: try: `find_map(|f| f.ref_field.copied())` + +error: `find(..).map(..)` can be simplified as `find_map(..)` + --> $DIR/manual_find_map.rs:69:10 + | +LL | .find(|f| f.result_field.is_ok()) + | __________^ +LL | | .map(|f| f.result_field.clone().unwrap()); + | |_________________________________________________^ help: try: `find_map(|f| f.result_field.clone().ok())` + +error: `find(..).map(..)` can be simplified as `find_map(..)` + --> $DIR/manual_find_map.rs:74:10 + | +LL | .find(|f| f.result_field.is_ok()) + | __________^ +LL | | .map(|f| f.result_field.as_ref().unwrap()); + | |__________________________________________________^ help: try: `find_map(|f| f.result_field.as_ref().ok())` + +error: `find(..).map(..)` can be simplified as `find_map(..)` + --> $DIR/manual_find_map.rs:79:10 + | +LL | .find(|f| f.result_field.is_ok()) + | __________^ +LL | | .map(|f| f.result_field.as_deref().unwrap()); + | |____________________________________________________^ help: try: `find_map(|f| f.result_field.as_deref().ok())` + +error: `find(..).map(..)` can be simplified as `find_map(..)` + --> $DIR/manual_find_map.rs:84:10 + | +LL | .find(|f| f.result_field.is_ok()) + | __________^ +LL | | .map(|f| f.result_field.as_mut().unwrap()); + | |__________________________________________________^ help: try: `find_map(|f| f.result_field.as_mut().ok())` + +error: `find(..).map(..)` can be simplified as `find_map(..)` + --> $DIR/manual_find_map.rs:89:10 + | +LL | .find(|f| f.result_field.is_ok()) + | __________^ +LL | | .map(|f| f.result_field.as_deref_mut().unwrap()); + | |________________________________________________________^ help: try: `find_map(|f| f.result_field.as_deref_mut().ok())` + +error: `find(..).map(..)` can be simplified as `find_map(..)` + --> $DIR/manual_find_map.rs:94:10 + | +LL | .find(|f| f.result_field.is_ok()) + | __________^ +LL | | .map(|f| f.result_field.to_owned().unwrap()); + | |____________________________________________________^ help: try: `find_map(|f| f.result_field.to_owned().ok())` + +error: aborting due to 12 previous errors diff --git a/tests/ui/manual_map_option.fixed b/tests/ui/manual_map_option.fixed index fc6a7abca0ebd..a59da4ae10bce 100644 --- a/tests/ui/manual_map_option.fixed +++ b/tests/ui/manual_map_option.fixed @@ -7,6 +7,7 @@ clippy::unit_arg, clippy::match_ref_pats, clippy::redundant_pattern_matching, + clippy::for_loops_over_fallibles, dead_code )] diff --git a/tests/ui/manual_map_option.rs b/tests/ui/manual_map_option.rs index 16508270f64d9..0bdbefa51e8b4 100644 --- a/tests/ui/manual_map_option.rs +++ b/tests/ui/manual_map_option.rs @@ -7,6 +7,7 @@ clippy::unit_arg, clippy::match_ref_pats, clippy::redundant_pattern_matching, + clippy::for_loops_over_fallibles, dead_code )] diff --git a/tests/ui/manual_map_option.stderr b/tests/ui/manual_map_option.stderr index 0036b8151ded0..cdc2c0e62a9b9 100644 --- a/tests/ui/manual_map_option.stderr +++ b/tests/ui/manual_map_option.stderr @@ -1,5 +1,5 @@ error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:14:5 + --> $DIR/manual_map_option.rs:15:5 | LL | / match Some(0) { LL | | Some(_) => Some(2), @@ -10,7 +10,7 @@ LL | | }; = note: `-D clippy::manual-map` implied by `-D warnings` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:19:5 + --> $DIR/manual_map_option.rs:20:5 | LL | / match Some(0) { LL | | Some(x) => Some(x + 1), @@ -19,7 +19,7 @@ LL | | }; | |_____^ help: try this: `Some(0).map(|x| x + 1)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:24:5 + --> $DIR/manual_map_option.rs:25:5 | LL | / match Some("") { LL | | Some(x) => Some(x.is_empty()), @@ -28,7 +28,7 @@ LL | | }; | |_____^ help: try this: `Some("").map(|x| x.is_empty())` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:29:5 + --> $DIR/manual_map_option.rs:30:5 | LL | / if let Some(x) = Some(0) { LL | | Some(!x) @@ -38,7 +38,7 @@ LL | | }; | |_____^ help: try this: `Some(0).map(|x| !x)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:36:5 + --> $DIR/manual_map_option.rs:37:5 | LL | / match Some(0) { LL | | Some(x) => { Some(std::convert::identity(x)) } @@ -47,7 +47,7 @@ LL | | }; | |_____^ help: try this: `Some(0).map(std::convert::identity)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:41:5 + --> $DIR/manual_map_option.rs:42:5 | LL | / match Some(&String::new()) { LL | | Some(x) => Some(str::len(x)), @@ -56,7 +56,7 @@ LL | | }; | |_____^ help: try this: `Some(&String::new()).map(|x| str::len(x))` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:51:5 + --> $DIR/manual_map_option.rs:52:5 | LL | / match &Some([0, 1]) { LL | | Some(x) => Some(x[0]), @@ -65,7 +65,7 @@ LL | | }; | |_____^ help: try this: `Some([0, 1]).as_ref().map(|x| x[0])` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:56:5 + --> $DIR/manual_map_option.rs:57:5 | LL | / match &Some(0) { LL | | &Some(x) => Some(x * 2), @@ -74,7 +74,7 @@ LL | | }; | |_____^ help: try this: `Some(0).map(|x| x * 2)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:61:5 + --> $DIR/manual_map_option.rs:62:5 | LL | / match Some(String::new()) { LL | | Some(ref x) => Some(x.is_empty()), @@ -83,7 +83,7 @@ LL | | }; | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.is_empty())` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:66:5 + --> $DIR/manual_map_option.rs:67:5 | LL | / match &&Some(String::new()) { LL | | Some(x) => Some(x.len()), @@ -92,7 +92,7 @@ LL | | }; | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.len())` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:71:5 + --> $DIR/manual_map_option.rs:72:5 | LL | / match &&Some(0) { LL | | &&Some(x) => Some(x + x), @@ -101,7 +101,7 @@ LL | | }; | |_____^ help: try this: `Some(0).map(|x| x + x)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:84:9 + --> $DIR/manual_map_option.rs:85:9 | LL | / match &mut Some(String::new()) { LL | | Some(x) => Some(x.push_str("")), @@ -110,7 +110,7 @@ LL | | }; | |_________^ help: try this: `Some(String::new()).as_mut().map(|x| x.push_str(""))` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:90:5 + --> $DIR/manual_map_option.rs:91:5 | LL | / match &mut Some(String::new()) { LL | | Some(ref x) => Some(x.len()), @@ -119,7 +119,7 @@ LL | | }; | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.len())` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:95:5 + --> $DIR/manual_map_option.rs:96:5 | LL | / match &mut &Some(String::new()) { LL | | Some(x) => Some(x.is_empty()), @@ -128,7 +128,7 @@ LL | | }; | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.is_empty())` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:100:5 + --> $DIR/manual_map_option.rs:101:5 | LL | / match Some((0, 1, 2)) { LL | | Some((x, y, z)) => Some(x + y + z), @@ -137,7 +137,7 @@ LL | | }; | |_____^ help: try this: `Some((0, 1, 2)).map(|(x, y, z)| x + y + z)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:105:5 + --> $DIR/manual_map_option.rs:106:5 | LL | / match Some([1, 2, 3]) { LL | | Some([first, ..]) => Some(first), @@ -146,7 +146,7 @@ LL | | }; | |_____^ help: try this: `Some([1, 2, 3]).map(|[first, ..]| first)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:110:5 + --> $DIR/manual_map_option.rs:111:5 | LL | / match &Some((String::new(), "test")) { LL | | Some((x, y)) => Some((y, x)), @@ -155,7 +155,7 @@ LL | | }; | |_____^ help: try this: `Some((String::new(), "test")).as_ref().map(|(x, y)| (y, x))` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:168:5 + --> $DIR/manual_map_option.rs:169:5 | LL | / match Some(0) { LL | | Some(x) => Some(vec![x]), @@ -164,7 +164,7 @@ LL | | }; | |_____^ help: try this: `Some(0).map(|x| vec![x])` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:173:5 + --> $DIR/manual_map_option.rs:174:5 | LL | / match option_env!("") { LL | | Some(x) => Some(String::from(x)), @@ -173,7 +173,7 @@ LL | | }; | |_____^ help: try this: `option_env!("").map(String::from)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:193:12 + --> $DIR/manual_map_option.rs:194:12 | LL | } else if let Some(x) = Some(0) { | ____________^ @@ -184,7 +184,7 @@ LL | | }; | |_____^ help: try this: `{ Some(0).map(|x| x + 1) }` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:201:12 + --> $DIR/manual_map_option.rs:202:12 | LL | } else if let Some(x) = Some(0) { | ____________^ diff --git a/tests/ui/needless_borrow.fixed b/tests/ui/needless_borrow.fixed index efeb5cf5b2b25..e7a483c058295 100644 --- a/tests/ui/needless_borrow.fixed +++ b/tests/ui/needless_borrow.fixed @@ -1,5 +1,7 @@ // run-rustfix +#![feature(lint_reasons)] + #[warn(clippy::all, clippy::needless_borrow)] #[allow(unused_variables, clippy::unnecessary_mut_passed)] fn main() { @@ -96,3 +98,10 @@ trait Trait {} impl<'a> Trait for &'a str {} fn h(_: &dyn Trait) {} + +#[allow(dead_code)] +fn check_expect_suppression() { + let a = 5; + #[expect(clippy::needless_borrow)] + let _ = x(&&a); +} diff --git a/tests/ui/needless_borrow.rs b/tests/ui/needless_borrow.rs index 3e416a0eb84aa..1d6bf46405a2f 100644 --- a/tests/ui/needless_borrow.rs +++ b/tests/ui/needless_borrow.rs @@ -1,5 +1,7 @@ // run-rustfix +#![feature(lint_reasons)] + #[warn(clippy::all, clippy::needless_borrow)] #[allow(unused_variables, clippy::unnecessary_mut_passed)] fn main() { @@ -96,3 +98,10 @@ trait Trait {} impl<'a> Trait for &'a str {} fn h(_: &dyn Trait) {} + +#[allow(dead_code)] +fn check_expect_suppression() { + let a = 5; + #[expect(clippy::needless_borrow)] + let _ = x(&&a); +} diff --git a/tests/ui/needless_borrow.stderr b/tests/ui/needless_borrow.stderr index 05591ce4117b2..be59d8f546d23 100644 --- a/tests/ui/needless_borrow.stderr +++ b/tests/ui/needless_borrow.stderr @@ -1,5 +1,5 @@ error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:9:15 + --> $DIR/needless_borrow.rs:11:15 | LL | let _ = x(&&a); // warn | ^^^ help: change this to: `&a` @@ -7,91 +7,91 @@ LL | let _ = x(&&a); // warn = note: `-D clippy::needless-borrow` implied by `-D warnings` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:13:13 + --> $DIR/needless_borrow.rs:15:13 | LL | mut_ref(&mut &mut b); // warn | ^^^^^^^^^^^ help: change this to: `&mut b` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:25:13 + --> $DIR/needless_borrow.rs:27:13 | LL | &&a | ^^^ help: change this to: `&a` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:27:15 + --> $DIR/needless_borrow.rs:29:15 | LL | 46 => &&a, | ^^^ help: change this to: `&a` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:33:27 + --> $DIR/needless_borrow.rs:35:27 | LL | break &ref_a; | ^^^^^^ help: change this to: `ref_a` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:40:15 + --> $DIR/needless_borrow.rs:42:15 | LL | let _ = x(&&&a); | ^^^^ help: change this to: `&a` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:41:15 + --> $DIR/needless_borrow.rs:43:15 | LL | let _ = x(&mut &&a); | ^^^^^^^^ help: change this to: `&a` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:42:15 + --> $DIR/needless_borrow.rs:44:15 | LL | let _ = x(&&&mut b); | ^^^^^^^^ help: change this to: `&mut b` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:43:15 + --> $DIR/needless_borrow.rs:45:15 | LL | let _ = x(&&ref_a); | ^^^^^^^ help: change this to: `ref_a` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:46:11 + --> $DIR/needless_borrow.rs:48:11 | LL | x(&b); | ^^ help: change this to: `b` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:53:13 + --> $DIR/needless_borrow.rs:55:13 | LL | mut_ref(&mut x); | ^^^^^^ help: change this to: `x` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:54:13 + --> $DIR/needless_borrow.rs:56:13 | LL | mut_ref(&mut &mut x); | ^^^^^^^^^^^ help: change this to: `x` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:55:23 + --> $DIR/needless_borrow.rs:57:23 | LL | let y: &mut i32 = &mut x; | ^^^^^^ help: change this to: `x` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:56:23 + --> $DIR/needless_borrow.rs:58:23 | LL | let y: &mut i32 = &mut &mut x; | ^^^^^^^^^^^ help: change this to: `x` error: this expression borrows a value the compiler would automatically borrow - --> $DIR/needless_borrow.rs:72:13 + --> $DIR/needless_borrow.rs:74:13 | LL | let _ = (&x).0; | ^^^^ help: change this to: `x` error: this expression borrows a value the compiler would automatically borrow - --> $DIR/needless_borrow.rs:74:22 + --> $DIR/needless_borrow.rs:76:22 | LL | let _ = unsafe { (&*x).0 }; | ^^^^^ help: change this to: `(*x)` diff --git a/tests/ui/needless_parens_on_range_literals.fixed b/tests/ui/needless_parens_on_range_literals.fixed new file mode 100644 index 0000000000000..1bd75c806bc94 --- /dev/null +++ b/tests/ui/needless_parens_on_range_literals.fixed @@ -0,0 +1,14 @@ +// run-rustfix +// edition:2018 + +#![warn(clippy::needless_parens_on_range_literals)] +#![allow(clippy::almost_complete_letter_range)] + +fn main() { + let _ = 'a'..='z'; + let _ = 'a'..'z'; + let _ = (1.)..2.; + let _ = (1.)..2.; + let _ = 'a'..; + let _ = ..'z'; +} diff --git a/tests/ui/needless_parens_on_range_literals.rs b/tests/ui/needless_parens_on_range_literals.rs new file mode 100644 index 0000000000000..7abb8a1adc1bc --- /dev/null +++ b/tests/ui/needless_parens_on_range_literals.rs @@ -0,0 +1,14 @@ +// run-rustfix +// edition:2018 + +#![warn(clippy::needless_parens_on_range_literals)] +#![allow(clippy::almost_complete_letter_range)] + +fn main() { + let _ = ('a')..=('z'); + let _ = 'a'..('z'); + let _ = (1.)..2.; + let _ = (1.)..(2.); + let _ = ('a')..; + let _ = ..('z'); +} diff --git a/tests/ui/needless_parens_on_range_literals.stderr b/tests/ui/needless_parens_on_range_literals.stderr new file mode 100644 index 0000000000000..505f7ac916dda --- /dev/null +++ b/tests/ui/needless_parens_on_range_literals.stderr @@ -0,0 +1,40 @@ +error: needless parenthesis on range literals can be removed + --> $DIR/needless_parens_on_range_literals.rs:8:13 + | +LL | let _ = ('a')..=('z'); + | ^^^^^ help: try: `'a'` + | + = note: `-D clippy::needless-parens-on-range-literals` implied by `-D warnings` + +error: needless parenthesis on range literals can be removed + --> $DIR/needless_parens_on_range_literals.rs:8:21 + | +LL | let _ = ('a')..=('z'); + | ^^^^^ help: try: `'z'` + +error: needless parenthesis on range literals can be removed + --> $DIR/needless_parens_on_range_literals.rs:9:18 + | +LL | let _ = 'a'..('z'); + | ^^^^^ help: try: `'z'` + +error: needless parenthesis on range literals can be removed + --> $DIR/needless_parens_on_range_literals.rs:11:19 + | +LL | let _ = (1.)..(2.); + | ^^^^ help: try: `2.` + +error: needless parenthesis on range literals can be removed + --> $DIR/needless_parens_on_range_literals.rs:12:13 + | +LL | let _ = ('a')..; + | ^^^^^ help: try: `'a'` + +error: needless parenthesis on range literals can be removed + --> $DIR/needless_parens_on_range_literals.rs:13:15 + | +LL | let _ = ..('z'); + | ^^^^^ help: try: `'z'` + +error: aborting due to 6 previous errors + diff --git a/tests/ui/never_loop.rs b/tests/ui/never_loop.rs index 2770eb2b2ab43..0a21589dd0d40 100644 --- a/tests/ui/never_loop.rs +++ b/tests/ui/never_loop.rs @@ -186,6 +186,23 @@ pub fn test16() { } } +// Issue #9001: `continue` in struct expression fields +pub fn test17() { + struct Foo { + f: (), + } + + let mut n = 0; + let _ = loop { + break Foo { + f: if n < 5 { + n += 1; + continue; + }, + }; + }; +} + fn main() { test1(); test2(); diff --git a/tests/ui/read_zero_byte_vec.rs b/tests/ui/read_zero_byte_vec.rs new file mode 100644 index 0000000000000..30807e0f8b92f --- /dev/null +++ b/tests/ui/read_zero_byte_vec.rs @@ -0,0 +1,87 @@ +#![warn(clippy::read_zero_byte_vec)] +#![allow(clippy::unused_io_amount)] +use std::fs::File; +use std::io; +use std::io::prelude::*; + +extern crate futures; +use futures::io::{AsyncRead, AsyncReadExt}; +use tokio::io::{AsyncRead as TokioAsyncRead, AsyncReadExt as _, AsyncWrite as TokioAsyncWrite, AsyncWriteExt as _}; + +fn test() -> io::Result<()> { + let cap = 1000; + let mut f = File::open("foo.txt").unwrap(); + + // should lint + let mut data = Vec::with_capacity(20); + f.read_exact(&mut data).unwrap(); + + // should lint + let mut data2 = Vec::with_capacity(cap); + f.read_exact(&mut data2)?; + + // should lint + let mut data3 = Vec::new(); + f.read_exact(&mut data3)?; + + // should lint + let mut data4 = vec![]; + let _ = f.read(&mut data4)?; + + // should lint + let _ = { + let mut data5 = Vec::new(); + f.read(&mut data5) + }; + + // should lint + let _ = { + let mut data6: Vec = Default::default(); + f.read(&mut data6) + }; + + // should not lint + let mut buf = [0u8; 100]; + f.read(&mut buf)?; + + // should not lint + let mut empty = vec![]; + let mut data7 = vec![]; + f.read(&mut empty); + + // should not lint + f.read(&mut data7); + + // should not lint + let mut data8 = Vec::new(); + data8.resize(100, 0); + f.read_exact(&mut data8)?; + + // should not lint + let mut data9 = vec![1, 2, 3]; + f.read_exact(&mut data9)?; + + Ok(()) +} + +async fn test_futures(r: &mut R) { + // should lint + let mut data = Vec::new(); + r.read(&mut data).await.unwrap(); + + // should lint + let mut data2 = Vec::new(); + r.read_exact(&mut data2).await.unwrap(); +} + +async fn test_tokio(r: &mut R) { + // should lint + let mut data = Vec::new(); + r.read(&mut data).await.unwrap(); + + // should lint + let mut data2 = Vec::new(); + r.read_exact(&mut data2).await.unwrap(); +} + +fn main() {} diff --git a/tests/ui/read_zero_byte_vec.stderr b/tests/ui/read_zero_byte_vec.stderr new file mode 100644 index 0000000000000..08ba9753d7c41 --- /dev/null +++ b/tests/ui/read_zero_byte_vec.stderr @@ -0,0 +1,64 @@ +error: reading zero byte data to `Vec` + --> $DIR/read_zero_byte_vec.rs:17:5 + | +LL | f.read_exact(&mut data).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `data.resize(20, 0); f.read_exact(&mut data).unwrap();` + | + = note: `-D clippy::read-zero-byte-vec` implied by `-D warnings` + +error: reading zero byte data to `Vec` + --> $DIR/read_zero_byte_vec.rs:21:5 + | +LL | f.read_exact(&mut data2)?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `data2.resize(cap, 0); f.read_exact(&mut data2)?;` + +error: reading zero byte data to `Vec` + --> $DIR/read_zero_byte_vec.rs:25:5 + | +LL | f.read_exact(&mut data3)?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: reading zero byte data to `Vec` + --> $DIR/read_zero_byte_vec.rs:29:5 + | +LL | let _ = f.read(&mut data4)?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: reading zero byte data to `Vec` + --> $DIR/read_zero_byte_vec.rs:34:9 + | +LL | f.read(&mut data5) + | ^^^^^^^^^^^^^^^^^^ + +error: reading zero byte data to `Vec` + --> $DIR/read_zero_byte_vec.rs:40:9 + | +LL | f.read(&mut data6) + | ^^^^^^^^^^^^^^^^^^ + +error: reading zero byte data to `Vec` + --> $DIR/read_zero_byte_vec.rs:70:5 + | +LL | r.read(&mut data).await.unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: reading zero byte data to `Vec` + --> $DIR/read_zero_byte_vec.rs:74:5 + | +LL | r.read_exact(&mut data2).await.unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: reading zero byte data to `Vec` + --> $DIR/read_zero_byte_vec.rs:80:5 + | +LL | r.read(&mut data).await.unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: reading zero byte data to `Vec` + --> $DIR/read_zero_byte_vec.rs:84:5 + | +LL | r.read_exact(&mut data2).await.unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 10 previous errors + diff --git a/tests/ui/ref_binding_to_reference.rs b/tests/ui/ref_binding_to_reference.rs index fe742a4c2f4c5..570ef406e4a99 100644 --- a/tests/ui/ref_binding_to_reference.rs +++ b/tests/ui/ref_binding_to_reference.rs @@ -1,5 +1,6 @@ // FIXME: run-rustfix waiting on multi-span suggestions +#![feature(lint_reasons)] #![warn(clippy::ref_binding_to_reference)] #![allow(clippy::needless_borrowed_reference)] @@ -73,3 +74,12 @@ impl T1 for S { let _: &&String = x; } } + +fn check_expect_suppression() { + let x = String::new(); + #[expect(clippy::ref_binding_to_reference)] + let _: &&String = match Some(&x) { + Some(ref x) => x, + None => return, + }; +} diff --git a/tests/ui/ref_binding_to_reference.stderr b/tests/ui/ref_binding_to_reference.stderr index c5856e15fa987..eb36cd516a246 100644 --- a/tests/ui/ref_binding_to_reference.stderr +++ b/tests/ui/ref_binding_to_reference.stderr @@ -1,5 +1,5 @@ error: this pattern creates a reference to a reference - --> $DIR/ref_binding_to_reference.rs:30:14 + --> $DIR/ref_binding_to_reference.rs:31:14 | LL | Some(ref x) => x, | ^^^^^ @@ -11,7 +11,7 @@ LL | Some(x) => &x, | ~ ~~ error: this pattern creates a reference to a reference - --> $DIR/ref_binding_to_reference.rs:36:14 + --> $DIR/ref_binding_to_reference.rs:37:14 | LL | Some(ref x) => { | ^^^^^ @@ -25,7 +25,7 @@ LL ~ &x | error: this pattern creates a reference to a reference - --> $DIR/ref_binding_to_reference.rs:46:14 + --> $DIR/ref_binding_to_reference.rs:47:14 | LL | Some(ref x) => m2!(x), | ^^^^^ @@ -36,7 +36,7 @@ LL | Some(x) => m2!(&x), | ~ ~~ error: this pattern creates a reference to a reference - --> $DIR/ref_binding_to_reference.rs:51:15 + --> $DIR/ref_binding_to_reference.rs:52:15 | LL | let _ = |&ref x: &&String| { | ^^^^^ @@ -48,7 +48,7 @@ LL ~ let _: &&String = &x; | error: this pattern creates a reference to a reference - --> $DIR/ref_binding_to_reference.rs:57:12 + --> $DIR/ref_binding_to_reference.rs:58:12 | LL | fn f2<'a>(&ref x: &&'a String) -> &'a String { | ^^^^^ @@ -61,7 +61,7 @@ LL ~ x | error: this pattern creates a reference to a reference - --> $DIR/ref_binding_to_reference.rs:64:11 + --> $DIR/ref_binding_to_reference.rs:65:11 | LL | fn f(&ref x: &&String) { | ^^^^^ @@ -73,7 +73,7 @@ LL ~ let _: &&String = &x; | error: this pattern creates a reference to a reference - --> $DIR/ref_binding_to_reference.rs:72:11 + --> $DIR/ref_binding_to_reference.rs:73:11 | LL | fn f(&ref x: &&String) { | ^^^^^ diff --git a/tests/ui/same_name_method.rs b/tests/ui/same_name_method.rs index 12e10ba6c493b..9562b47f0c4ff 100644 --- a/tests/ui/same_name_method.rs +++ b/tests/ui/same_name_method.rs @@ -1,3 +1,4 @@ +#![feature(lint_reasons)] #![warn(clippy::same_name_method)] #![allow(dead_code, non_camel_case_types)] @@ -108,4 +109,19 @@ mod should_not_lint { } } +mod check_expect_suppression { + use crate::T1; + + struct S; + + impl S { + #[expect(clippy::same_name_method)] + fn foo() {} + } + + impl T1 for S { + fn foo() {} + } +} + fn main() {} diff --git a/tests/ui/same_name_method.stderr b/tests/ui/same_name_method.stderr index cf06eb32e0c7f..f55ec9f3cc66b 100644 --- a/tests/ui/same_name_method.stderr +++ b/tests/ui/same_name_method.stderr @@ -1,61 +1,61 @@ error: method's name is the same as an existing method in a trait - --> $DIR/same_name_method.rs:20:13 + --> $DIR/same_name_method.rs:21:13 | LL | fn foo() {} | ^^^^^^^^^^^ | = note: `-D clippy::same-name-method` implied by `-D warnings` note: existing `foo` defined here - --> $DIR/same_name_method.rs:24:13 + --> $DIR/same_name_method.rs:25:13 | LL | fn foo() {} | ^^^^^^^^^^^ error: method's name is the same as an existing method in a trait - --> $DIR/same_name_method.rs:34:13 + --> $DIR/same_name_method.rs:35:13 | LL | fn clone() {} | ^^^^^^^^^^^^^ | note: existing `clone` defined here - --> $DIR/same_name_method.rs:30:18 + --> $DIR/same_name_method.rs:31:18 | LL | #[derive(Clone)] | ^^^^^ = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) error: method's name is the same as an existing method in a trait - --> $DIR/same_name_method.rs:44:13 + --> $DIR/same_name_method.rs:45:13 | LL | fn foo() {} | ^^^^^^^^^^^ | note: existing `foo` defined here - --> $DIR/same_name_method.rs:48:13 + --> $DIR/same_name_method.rs:49:13 | LL | fn foo() {} | ^^^^^^^^^^^ error: method's name is the same as an existing method in a trait - --> $DIR/same_name_method.rs:58:13 + --> $DIR/same_name_method.rs:59:13 | LL | fn foo() {} | ^^^^^^^^^^^ | note: existing `foo` defined here - --> $DIR/same_name_method.rs:61:9 + --> $DIR/same_name_method.rs:62:9 | LL | impl T1 for S {} | ^^^^^^^^^^^^^^^^ error: method's name is the same as an existing method in a trait - --> $DIR/same_name_method.rs:70:13 + --> $DIR/same_name_method.rs:71:13 | LL | fn foo() {} | ^^^^^^^^^^^ | note: existing `foo` defined here - --> $DIR/same_name_method.rs:73:9 + --> $DIR/same_name_method.rs:74:9 | LL | impl T1 for S {} | ^^^^^^^^^^^^^^^^ diff --git a/util/gh-pages/index.html b/util/gh-pages/index.html index 2076d1299783d..4999cce75114b 100644 --- a/util/gh-pages/index.html +++ b/util/gh-pages/index.html @@ -206,6 +206,26 @@ margin: auto 5px; font-family: monospace; } + + details { + border-radius: 4px; + padding: .5em .5em 0; + } + + code { + white-space: pre !important; + } + + summary { + font-weight: bold; + margin: -.5em -.5em 0; + padding: .5em; + display: revert; + } + + details[open] { + padding: .5em; + }