diff options
| author | bors <bors@rust-lang.org> | 2020-03-31 22:28:18 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2020-03-31 22:28:18 +0000 |
| commit | 58dd1ce8383aaebcad9b6027b89a316fd868b35c (patch) | |
| tree | 21ba3ae00a56b802477ae80fbbeec3187dfe1c5f | |
| parent | a5b09d35473615e7142f5570f5c5fad0caf68bd2 (diff) | |
| parent | 8310320ebd68d5f23ad21bb5a15326abeaf91dff (diff) | |
| download | rust-58dd1ce8383aaebcad9b6027b89a316fd868b35c.tar.gz rust-58dd1ce8383aaebcad9b6027b89a316fd868b35c.zip | |
Auto merge of #70638 - Dylan-DPC:rollup-2tgfxjt, r=Dylan-DPC
Rollup of 6 pull requests Successful merges: - #68770 (BTreeMap/BTreeSet: implement drain_filter ) - #70081 (add `unused_braces` lint) - #70556 (parse_and_disallow_postfix_after_cast: account for `ExprKind::Err`.) - #70605 (Add missing -lmsvcrt on mingw after -lpthread) - #70630 (Update books.) - #70632 (expand vec![] to Vec::new()) Failed merges: r? @ghost
60 files changed, 1250 insertions, 190 deletions
diff --git a/src/doc/book b/src/doc/book -Subproject 6fb3705e5230311b096d47f7e2c91f9ce24393d +Subproject c8841f2841a2d26124319ddadd1b6a245f9a185 diff --git a/src/doc/nomicon b/src/doc/nomicon -Subproject 9f797e65e6bcc79419975b17aff8e21c9adc039 +Subproject 411197b0e77590c967e37e8f6ec681abd359afe diff --git a/src/doc/reference b/src/doc/reference -Subproject e2f11fe4d6a5ecb471c70323197da43c70cb96b +Subproject 89dd146154474559536d5d4049a03831c501dee diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example -Subproject cb369ae95ca36b841960182d26f6d5d9b2e3cc1 +Subproject edd2a7e687358712608896730c083cb76c7b401 diff --git a/src/liballoc/benches/btree/set.rs b/src/liballoc/benches/btree/set.rs index d9e75ab7fa4..2518506b9b5 100644 --- a/src/liballoc/benches/btree/set.rs +++ b/src/liballoc/benches/btree/set.rs @@ -63,6 +63,22 @@ pub fn clone_100_and_clear(b: &mut Bencher) { } #[bench] +pub fn clone_100_and_drain_all(b: &mut Bencher) { + let src = pos(100); + b.iter(|| src.clone().drain_filter(|_| true).count()) +} + +#[bench] +pub fn clone_100_and_drain_half(b: &mut Bencher) { + let src = pos(100); + b.iter(|| { + let mut set = src.clone(); + assert_eq!(set.drain_filter(|i| i % 2 == 0).count(), 100 / 2); + assert_eq!(set.len(), 100 / 2); + }) +} + +#[bench] pub fn clone_100_and_into_iter(b: &mut Bencher) { let src = pos(100); b.iter(|| src.clone().into_iter().count()) @@ -116,6 +132,22 @@ pub fn clone_10k_and_clear(b: &mut Bencher) { } #[bench] +pub fn clone_10k_and_drain_all(b: &mut Bencher) { + let src = pos(10_000); + b.iter(|| src.clone().drain_filter(|_| true).count()) +} + +#[bench] +pub fn clone_10k_and_drain_half(b: &mut Bencher) { + let src = pos(10_000); + b.iter(|| { + let mut set = src.clone(); + assert_eq!(set.drain_filter(|i| i % 2 == 0).count(), 10_000 / 2); + assert_eq!(set.len(), 10_000 / 2); + }) +} + +#[bench] pub fn clone_10k_and_into_iter(b: &mut Bencher) { let src = pos(10_000); b.iter(|| src.clone().into_iter().count()) diff --git a/src/liballoc/benches/lib.rs b/src/liballoc/benches/lib.rs index 951477a24c8..f31717d9fd5 100644 --- a/src/liballoc/benches/lib.rs +++ b/src/liballoc/benches/lib.rs @@ -1,3 +1,4 @@ +#![feature(btree_drain_filter)] #![feature(map_first_last)] #![feature(repr_simd)] #![feature(test)] diff --git a/src/liballoc/collections/btree/map.rs b/src/liballoc/collections/btree/map.rs index bde66c406af..bbeced1751d 100644 --- a/src/liballoc/collections/btree/map.rs +++ b/src/liballoc/collections/btree/map.rs @@ -1256,6 +1256,48 @@ impl<K: Ord, V> BTreeMap<K, V> { right } + /// Creates an iterator which uses a closure to determine if an element should be removed. + /// + /// If the closure returns true, the element is removed from the map and yielded. + /// If the closure returns false, or panics, the element remains in the map and will not be + /// yielded. + /// + /// Note that `drain_filter` lets you mutate every value in the filter closure, regardless of + /// whether you choose to keep or remove it. + /// + /// If the iterator is only partially consumed or not consumed at all, each of the remaining + /// elements will still be subjected to the closure and removed and dropped if it returns true. + /// + /// It is unspecified how many more elements will be subjected to the closure + /// if a panic occurs in the closure, or a panic occurs while dropping an element, + /// or if the `DrainFilter` value is leaked. + /// + /// # Examples + /// + /// Splitting a map into even and odd keys, reusing the original map: + /// + /// ``` + /// #![feature(btree_drain_filter)] + /// use std::collections::BTreeMap; + /// + /// let mut map: BTreeMap<i32, i32> = (0..8).map(|x| (x, x)).collect(); + /// let evens: BTreeMap<_, _> = map.drain_filter(|k, _v| k % 2 == 0).collect(); + /// let odds = map; + /// assert_eq!(evens.keys().copied().collect::<Vec<_>>(), vec![0, 2, 4, 6]); + /// assert_eq!(odds.keys().copied().collect::<Vec<_>>(), vec![1, 3, 5, 7]); + /// ``` + #[unstable(feature = "btree_drain_filter", issue = "70530")] + pub fn drain_filter<F>(&mut self, pred: F) -> DrainFilter<'_, K, V, F> + where + F: FnMut(&K, &mut V) -> bool, + { + DrainFilter { pred, inner: self.drain_filter_inner() } + } + pub(super) fn drain_filter_inner(&mut self) -> DrainFilterInner<'_, K, V> { + let front = self.root.as_mut().map(|r| r.as_mut().first_leaf_edge()); + DrainFilterInner { length: &mut self.length, cur_leaf_edge: front } + } + /// Calculates the number of elements if it is incorrect. fn recalc_length(&mut self) { fn dfs<'a, K, V>(node: NodeRef<marker::Immut<'a>, K, V, marker::LeafOrInternal>) -> usize @@ -1653,6 +1695,124 @@ impl<K, V> Clone for Values<'_, K, V> { } } +/// An iterator produced by calling `drain_filter` on BTreeMap. +#[unstable(feature = "btree_drain_filter", issue = "70530")] +pub struct DrainFilter<'a, K, V, F> +where + K: 'a + Ord, // This Ord bound should be removed before stabilization. + V: 'a, + F: 'a + FnMut(&K, &mut V) -> bool, +{ + pred: F, + inner: DrainFilterInner<'a, K, V>, +} +pub(super) struct DrainFilterInner<'a, K, V> +where + K: 'a + Ord, + V: 'a, +{ + length: &'a mut usize, + cur_leaf_edge: Option<Handle<NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, marker::Edge>>, +} + +#[unstable(feature = "btree_drain_filter", issue = "70530")] +impl<'a, K, V, F> Drop for DrainFilter<'a, K, V, F> +where + K: 'a + Ord, + V: 'a, + F: 'a + FnMut(&K, &mut V) -> bool, +{ + fn drop(&mut self) { + self.for_each(drop); + } +} + +#[unstable(feature = "btree_drain_filter", issue = "70530")] +impl<'a, K, V, F> fmt::Debug for DrainFilter<'a, K, V, F> +where + K: 'a + fmt::Debug + Ord, + V: 'a + fmt::Debug, + F: 'a + FnMut(&K, &mut V) -> bool, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("DrainFilter").field(&self.inner.peek()).finish() + } +} + +#[unstable(feature = "btree_drain_filter", issue = "70530")] +impl<'a, K, V, F> Iterator for DrainFilter<'a, K, V, F> +where + K: 'a + Ord, + V: 'a, + F: 'a + FnMut(&K, &mut V) -> bool, +{ + type Item = (K, V); + + fn next(&mut self) -> Option<(K, V)> { + self.inner.next(&mut self.pred) + } + + fn size_hint(&self) -> (usize, Option<usize>) { + self.inner.size_hint() + } +} + +impl<'a, K, V> DrainFilterInner<'a, K, V> +where + K: 'a + Ord, + V: 'a, +{ + /// Allow Debug implementations to predict the next element. + pub(super) fn peek(&self) -> Option<(&K, &V)> { + let edge = self.cur_leaf_edge.as_ref()?; + edge.reborrow().next_kv().ok().map(|kv| kv.into_kv()) + } + + unsafe fn next_kv( + &mut self, + ) -> Option<Handle<NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal>, marker::KV>> { + let edge = self.cur_leaf_edge.as_ref()?; + ptr::read(edge).next_kv().ok() + } + + /// Implementation of a typical `DrainFilter::next` method, given the predicate. + pub(super) fn next<F>(&mut self, pred: &mut F) -> Option<(K, V)> + where + F: FnMut(&K, &mut V) -> bool, + { + while let Some(kv) = unsafe { self.next_kv() } { + let (k, v) = unsafe { ptr::read(&kv) }.into_kv_mut(); + if pred(k, v) { + *self.length -= 1; + let (k, v, leaf_edge_location) = kv.remove_kv_tracking(); + // `remove_kv_tracking` has either preserved or invalidated `self.cur_leaf_edge` + if let Some(node) = leaf_edge_location { + match search::search_tree(node, &k) { + search::SearchResult::Found(_) => unreachable!(), + search::SearchResult::GoDown(leaf) => self.cur_leaf_edge = Some(leaf), + } + }; + return Some((k, v)); + } + self.cur_leaf_edge = Some(kv.next_leaf_edge()); + } + None + } + + /// Implementation of a typical `DrainFilter::size_hint` method. + pub(super) fn size_hint(&self) -> (usize, Option<usize>) { + (0, Some(*self.length)) + } +} + +#[unstable(feature = "btree_drain_filter", issue = "70530")] +impl<K, V, F> FusedIterator for DrainFilter<'_, K, V, F> +where + K: Ord, + F: FnMut(&K, &mut V) -> bool, +{ +} + #[stable(feature = "btree_range", since = "1.17.0")] impl<'a, K, V> Iterator for Range<'a, K, V> { type Item = (&'a K, &'a V); @@ -2531,12 +2691,31 @@ impl<'a, K: Ord, V> OccupiedEntry<'a, K, V> { fn remove_kv(self) -> (K, V) { *self.length -= 1; - let (small_leaf, old_key, old_val) = match self.handle.force() { + let (old_key, old_val, _) = self.handle.remove_kv_tracking(); + (old_key, old_val) + } +} + +impl<'a, K: 'a, V: 'a> Handle<NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal>, marker::KV> { + /// Removes a key/value-pair from the map, and returns that pair, as well as + /// the whereabouts of the leaf edge corresponding to that former pair: + /// if None is returned, the leaf edge is still the left leaf edge of the KV handle; + /// if a node is returned, it heads the subtree where the leaf edge may be found. + fn remove_kv_tracking( + self, + ) -> (K, V, Option<NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal>>) { + let mut levels_down_handled: isize; + let (small_leaf, old_key, old_val) = match self.force() { Leaf(leaf) => { + levels_down_handled = 1; // handled at same level, but affects only the right side let (hole, old_key, old_val) = leaf.remove(); (hole.into_node(), old_key, old_val) } Internal(mut internal) => { + // Replace the location freed in the internal node with the next KV, + // and remove that next KV from its leaf. + levels_down_handled = unsafe { ptr::read(&internal).into_node().height() } as isize; + let key_loc = internal.kv_mut().0 as *mut K; let val_loc = internal.kv_mut().1 as *mut V; @@ -2556,27 +2735,39 @@ impl<'a, K: Ord, V> OccupiedEntry<'a, K, V> { let mut cur_node = small_leaf.forget_type(); while cur_node.len() < node::MIN_LEN { match handle_underfull_node(cur_node) { - AtRoot => break, + AtRoot(root) => { + cur_node = root; + break; + } EmptyParent(_) => unreachable!(), Merged(parent) => { + levels_down_handled -= 1; if parent.len() == 0 { // We must be at the root - parent.into_root_mut().pop_level(); + let root = parent.into_root_mut(); + root.pop_level(); + cur_node = root.as_mut(); break; } else { cur_node = parent.forget_type(); } } - Stole(_) => break, + Stole(internal_node) => { + levels_down_handled -= 1; + cur_node = internal_node.forget_type(); + // This internal node might be underfull, but only if it's the root. + break; + } } } - (old_key, old_val) + let leaf_edge_location = if levels_down_handled > 0 { None } else { Some(cur_node) }; + (old_key, old_val, leaf_edge_location) } } enum UnderflowResult<'a, K, V> { - AtRoot, + AtRoot(NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal>), EmptyParent(NodeRef<marker::Mut<'a>, K, V, marker::Internal>), Merged(NodeRef<marker::Mut<'a>, K, V, marker::Internal>), Stole(NodeRef<marker::Mut<'a>, K, V, marker::Internal>), @@ -2585,10 +2776,9 @@ enum UnderflowResult<'a, K, V> { fn handle_underfull_node<K, V>( node: NodeRef<marker::Mut<'_>, K, V, marker::LeafOrInternal>, ) -> UnderflowResult<'_, K, V> { - let parent = if let Ok(parent) = node.ascend() { - parent - } else { - return AtRoot; + let parent = match node.ascend() { + Ok(parent) => parent, + Err(root) => return AtRoot(root), }; let (is_left, mut handle) = match parent.left_kv() { diff --git a/src/liballoc/collections/btree/set.rs b/src/liballoc/collections/btree/set.rs index b100ce754ca..0b02223def4 100644 --- a/src/liballoc/collections/btree/set.rs +++ b/src/liballoc/collections/btree/set.rs @@ -8,8 +8,8 @@ use core::fmt::{self, Debug}; use core::iter::{FromIterator, FusedIterator, Peekable}; use core::ops::{BitAnd, BitOr, BitXor, RangeBounds, Sub}; +use super::map::{BTreeMap, Keys}; use super::Recover; -use crate::collections::btree_map::{self, BTreeMap, Keys}; // FIXME(conventions): implement bounded iterators @@ -102,7 +102,7 @@ impl<T: fmt::Debug> fmt::Debug for Iter<'_, T> { #[stable(feature = "rust1", since = "1.0.0")] #[derive(Debug)] pub struct IntoIter<T> { - iter: btree_map::IntoIter<T, ()>, + iter: super::map::IntoIter<T, ()>, } /// An iterator over a sub-range of items in a `BTreeSet`. @@ -115,7 +115,7 @@ pub struct IntoIter<T> { #[derive(Debug)] #[stable(feature = "btree_range", since = "1.17.0")] pub struct Range<'a, T: 'a> { - iter: btree_map::Range<'a, T, ()>, + iter: super::map::Range<'a, T, ()>, } /// Core of SymmetricDifference and Union. @@ -944,6 +944,41 @@ impl<T: Ord> BTreeSet<T> { { BTreeSet { map: self.map.split_off(key) } } + + /// Creates an iterator which uses a closure to determine if a value should be removed. + /// + /// If the closure returns true, then the value is removed and yielded. + /// If the closure returns false, the value will remain in the list and will not be yielded + /// by the iterator. + /// + /// If the iterator is only partially consumed or not consumed at all, each of the remaining + /// values will still be subjected to the closure and removed and dropped if it returns true. + /// + /// It is unspecified how many more values will be subjected to the closure + /// if a panic occurs in the closure, or if a panic occurs while dropping a value, or if the + /// `DrainFilter` itself is leaked. + /// + /// # Examples + /// + /// Splitting a set into even and odd values, reusing the original set: + /// + /// ``` + /// #![feature(btree_drain_filter)] + /// use std::collections::BTreeSet; + /// + /// let mut set: BTreeSet<i32> = (0..8).collect(); + /// let evens: BTreeSet<_> = set.drain_filter(|v| v % 2 == 0).collect(); + /// let odds = set; + /// assert_eq!(evens.into_iter().collect::<Vec<_>>(), vec![0, 2, 4, 6]); + /// assert_eq!(odds.into_iter().collect::<Vec<_>>(), vec![1, 3, 5, 7]); + /// ``` + #[unstable(feature = "btree_drain_filter", issue = "70530")] + pub fn drain_filter<'a, F>(&'a mut self, pred: F) -> DrainFilter<'a, T, F> + where + F: 'a + FnMut(&T) -> bool, + { + DrainFilter { pred, inner: self.map.drain_filter_inner() } + } } impl<T> BTreeSet<T> { @@ -1055,6 +1090,66 @@ impl<'a, T> IntoIterator for &'a BTreeSet<T> { } } +/// An iterator produced by calling `drain_filter` on BTreeSet. +#[unstable(feature = "btree_drain_filter", issue = "70530")] +pub struct DrainFilter<'a, T, F> +where + T: 'a + Ord, + F: 'a + FnMut(&T) -> bool, +{ + pred: F, + inner: super::map::DrainFilterInner<'a, T, ()>, +} + +#[unstable(feature = "btree_drain_filter", issue = "70530")] +impl<'a, T, F> Drop for DrainFilter<'a, T, F> +where + T: 'a + Ord, + F: 'a + FnMut(&T) -> bool, +{ + fn drop(&mut self) { + self.for_each(drop); + } +} + +#[unstable(feature = "btree_drain_filter", issue = "70530")] +impl<'a, T, F> fmt::Debug for DrainFilter<'a, T, F> +where + T: 'a + Ord + fmt::Debug, + F: 'a + FnMut(&T) -> bool, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("DrainFilter").field(&self.inner.peek().map(|(k, _)| k)).finish() + } +} + +#[unstable(feature = "btree_drain_filter", issue = "70530")] +impl<'a, 'f, T, F> Iterator for DrainFilter<'a, T, F> +where + T: 'a + Ord, + F: 'a + 'f + FnMut(&T) -> bool, +{ + type Item = T; + + fn next(&mut self) -> Option<T> { + let pred = &mut self.pred; + let mut mapped_pred = |k: &T, _v: &mut ()| pred(k); + self.inner.next(&mut mapped_pred).map(|(k, _)| k) + } + + fn size_hint(&self) -> (usize, Option<usize>) { + self.inner.size_hint() + } +} + +#[unstable(feature = "btree_drain_filter", issue = "70530")] +impl<'a, T, F> FusedIterator for DrainFilter<'a, T, F> +where + T: 'a + Ord, + F: 'a + FnMut(&T) -> bool, +{ +} + #[stable(feature = "rust1", since = "1.0.0")] impl<T: Ord> Extend<T> for BTreeSet<T> { #[inline] diff --git a/src/liballoc/macros.rs b/src/liballoc/macros.rs index 422d3486f92..4bc0c3a079d 100644 --- a/src/liballoc/macros.rs +++ b/src/liballoc/macros.rs @@ -36,6 +36,9 @@ #[stable(feature = "rust1", since = "1.0.0")] #[allow_internal_unstable(box_syntax)] macro_rules! vec { + () => ( + $crate::vec::Vec::new() + ); ($elem:expr; $n:expr) => ( $crate::vec::from_elem($elem, $n) ); @@ -51,6 +54,9 @@ macro_rules! vec { // NB see the slice::hack module in slice.rs for more information #[cfg(test)] macro_rules! vec { + () => ( + $crate::vec::Vec::new() + ); ($elem:expr; $n:expr) => ( $crate::vec::from_elem($elem, $n) ); diff --git a/src/liballoc/tests/btree/map.rs b/src/liballoc/tests/btree/map.rs index e28b71510ce..14f12ca2d77 100644 --- a/src/liballoc/tests/btree/map.rs +++ b/src/liballoc/tests/btree/map.rs @@ -5,7 +5,7 @@ use std::fmt::Debug; use std::iter::FromIterator; use std::ops::Bound::{self, Excluded, Included, Unbounded}; use std::ops::RangeBounds; -use std::panic::catch_unwind; +use std::panic::{catch_unwind, AssertUnwindSafe}; use std::rc::Rc; use std::sync::atomic::{AtomicUsize, Ordering}; @@ -609,6 +609,263 @@ fn test_range_mut() { } } +mod test_drain_filter { + use super::*; + + #[test] + fn empty() { + let mut map: BTreeMap<i32, i32> = BTreeMap::new(); + map.drain_filter(|_, _| unreachable!("there's nothing to decide on")); + assert!(map.is_empty()); + } + + #[test] + fn consuming_nothing() { + let pairs = (0..3).map(|i| (i, i)); + let mut map: BTreeMap<_, _> = pairs.collect(); + assert!(map.drain_filter(|_, _| false).eq(std::iter::empty())); + } + + #[test] + fn consuming_all() { + let pairs = (0..3).map(|i| (i, i)); + let mut map: BTreeMap<_, _> = pairs.clone().collect(); + assert!(map.drain_filter(|_, _| true).eq(pairs)); + } + + #[test] + fn mutating_and_keeping() { + let pairs = (0..3).map(|i| (i, i)); + let mut map: BTreeMap<_, _> = pairs.collect(); + assert!( + map.drain_filter(|_, v| { + *v += 6; + false + }) + .eq(std::iter::empty()) + ); + assert!(map.keys().copied().eq(0..3)); + assert!(map.values().copied().eq(6..9)); + } + + #[test] + fn mutating_and_removing() { + let pairs = (0..3).map(|i| (i, i)); + let mut map: BTreeMap<_, _> = pairs.collect(); + assert!( + map.drain_filter(|_, v| { + *v += 6; + true + }) + .eq((0..3).map(|i| (i, i + 6))) + ); + assert!(map.is_empty()); + } + + #[test] + fn underfull_keeping_all() { + let pairs = (0..3).map(|i| (i, i)); + let mut map: BTreeMap<_, _> = pairs.collect(); + map.drain_filter(|_, _| false); + assert!(map.keys().copied().eq(0..3)); + } + + #[test] + fn underfull_removing_one() { + let pairs = (0..3).map(|i| (i, i)); + for doomed in 0..3 { + let mut map: BTreeMap<_, _> = pairs.clone().collect(); + map.drain_filter(|i, _| *i == doomed); + assert_eq!(map.len(), 2); + } + } + + #[test] + fn underfull_keeping_one() { + let pairs = (0..3).map(|i| (i, i)); + for sacred in 0..3 { + let mut map: BTreeMap<_, _> = pairs.clone().collect(); + map.drain_filter(|i, _| *i != sacred); + assert!(map.keys().copied().eq(sacred..=sacred)); + } + } + + #[test] + fn underfull_removing_all() { + let pairs = (0..3).map(|i| (i, i)); + let mut map: BTreeMap<_, _> = pairs.collect(); + map.drain_filter(|_, _| true); + assert!(map.is_empty()); + } + + #[test] + fn height_0_keeping_all() { + let pairs = (0..NODE_CAPACITY).map(|i| (i, i)); + let mut map: BTreeMap<_, _> = pairs.collect(); + map.drain_filter(|_, _| false); + assert!(map.keys().copied().eq(0..NODE_CAPACITY)); + } + + #[test] + fn height_0_removing_one() { + let pairs = (0..NODE_CAPACITY).map(|i| (i, i)); + for doomed in 0..NODE_CAPACITY { + let mut map: BTreeMap<_, _> = pairs.clone().collect(); + map.drain_filter(|i, _| *i == doomed); + assert_eq!(map.len(), NODE_CAPACITY - 1); + } + } + + #[test] + fn height_0_keeping_one() { + let pairs = (0..NODE_CAPACITY).map(|i| (i, i)); + for sacred in 0..NODE_CAPACITY { + let mut map: BTreeMap<_, _> = pairs.clone().collect(); + map.drain_filter(|i, _| *i != sacred); + assert!(map.keys().copied().eq(sacred..=sacred)); + } + } + + #[test] + fn height_0_removing_all() { + let pairs = (0..NODE_CAPACITY).map(|i| (i, i)); + let mut map: BTreeMap<_, _> = pairs.collect(); + map.drain_filter(|_, _| true); + assert!(map.is_empty()); + } + + #[test] + fn height_0_keeping_half() { + let mut map: BTreeMap<_, _> = (0..16).map(|i| (i, i)).collect(); + assert_eq!(map.drain_filter(|i, _| *i % 2 == 0).count(), 8); + assert_eq!(map.len(), 8); + } + + #[test] + fn height_1_removing_all() { + let pairs = (0..MIN_INSERTS_HEIGHT_1).map(|i| (i, i)); + let mut map: BTreeMap<_, _> = pairs.collect(); + map.drain_filter(|_, _| true); + assert!(map.is_empty()); + } + + #[test] + fn height_1_removing_one() { + let pairs = (0..MIN_INSERTS_HEIGHT_1).map(|i| (i, i)); + for doomed in 0..MIN_INSERTS_HEIGHT_1 { + let mut map: BTreeMap<_, _> = pairs.clone().collect(); + map.drain_filter(|i, _| *i == doomed); + assert_eq!(map.len(), MIN_INSERTS_HEIGHT_1 - 1); + } + } + + #[test] + fn height_1_keeping_one() { + let pairs = (0..MIN_INSERTS_HEIGHT_1).map(|i| (i, i)); + for sacred in 0..MIN_INSERTS_HEIGHT_1 { + let mut map: BTreeMap<_, _> = pairs.clone().collect(); + map.drain_filter(|i, _| *i != sacred); + assert!(map.keys().copied().eq(sacred..=sacred)); + } + } + + #[cfg(not(miri))] // Miri is too slow + #[test] + fn height_2_removing_one() { + let pairs = (0..MIN_INSERTS_HEIGHT_2).map(|i| (i, i)); + for doomed in (0..MIN_INSERTS_HEIGHT_2).step_by(12) { + let mut map: BTreeMap<_, _> = pairs.clone().collect(); + map.drain_filter(|i, _| *i == doomed); + assert_eq!(map.len(), MIN_INSERTS_HEIGHT_2 - 1); + } + } + + #[cfg(not(miri))] // Miri is too slow + #[test] + fn height_2_keeping_one() { + let pairs = (0..MIN_INSERTS_HEIGHT_2).map(|i| (i, i)); + for sacred in (0..MIN_INSERTS_HEIGHT_2).step_by(12) { + let mut map: BTreeMap<_, _> = pairs.clone().collect(); + map.drain_filter(|i, _| *i != sacred); + assert!(map.keys().copied().eq(sacred..=sacred)); + } + } + + #[test] + fn height_2_removing_all() { + let pairs = (0..MIN_INSERTS_HEIGHT_2).map(|i| (i, i)); + let mut map: BTreeMap<_, _> = pairs.collect(); + map.drain_filter(|_, _| true); + assert!(map.is_empty()); + } + + #[test] + fn drop_panic_leak() { + static PREDS: AtomicUsize = AtomicUsize::new(0); + static DROPS: AtomicUsize = AtomicUsize::new(0); + + struct D; + impl Drop for D { + fn drop(&mut self) { + if DROPS.fetch_add(1, Ordering::SeqCst) == 1 { + panic!("panic in `drop`"); + } + } + } + + let mut map = BTreeMap::new(); + map.insert(0, D); + map.insert(4, D); + map.insert(8, D); + + catch_unwind(move || { + drop(map.drain_filter(|i, _| { + PREDS.fetch_add(1usize << i, Ordering::SeqCst); + true + })) + }) + .ok(); + + assert_eq!(PREDS.load(Ordering::SeqCst), 0x011); + assert_eq!(DROPS.load(Ordering::SeqCst), 3); + } + + #[test] + fn pred_panic_leak() { + static PREDS: AtomicUsize = AtomicUsize::new(0); + static DROPS: AtomicUsize = AtomicUsize::new(0); + + struct D; + impl Drop for D { + fn drop(&mut self) { + DROPS.fetch_add(1, Ordering::SeqCst); + } + } + + let mut map = BTreeMap::new(); + map.insert(0, D); + map.insert(4, D); + map.insert(8, D); + + catch_unwind(AssertUnwindSafe(|| { + drop(map.drain_filter(|i, _| { + PREDS.fetch_add(1usize << i, Ordering::SeqCst); + match i { + 0 => true, + _ => panic!(), + } + })) + })) + .ok(); + + assert_eq!(PREDS.load(Ordering::SeqCst), 0x011); + assert_eq!(DROPS.load(Ordering::SeqCst), 1); + assert_eq!(map.len(), 2); + assert_eq!(map.first_entry().unwrap().key(), &4); + assert_eq!(map.last_entry().unwrap().key(), &8); + } +} + #[test] fn test_borrow() { // make sure these compile -- using the Borrow trait diff --git a/src/liballoc/tests/btree/set.rs b/src/liballoc/tests/btree/set.rs index 1a2b62d026b..136018b9f7d 100644 --- a/src/liballoc/tests/btree/set.rs +++ b/src/liballoc/tests/btree/set.rs @@ -1,5 +1,7 @@ use std::collections::BTreeSet; use std::iter::FromIterator; +use std::panic::{catch_unwind, AssertUnwindSafe}; +use std::sync::atomic::{AtomicU32, Ordering}; use super::DeterministicRng; @@ -303,6 +305,85 @@ fn test_is_subset() { } #[test] +fn test_drain_filter() { + let mut x: BTreeSet<_> = [1].iter().copied().collect(); + let mut y: BTreeSet<_> = [1].iter().copied().collect(); + + x.drain_filter(|_| true); + y.drain_filter(|_| false); + assert_eq!(x.len(), 0); + assert_eq!(y.len(), 1); +} + +#[test] +fn test_drain_filter_drop_panic_leak() { + static PREDS: AtomicU32 = AtomicU32::new(0); + static DROPS: AtomicU32 = AtomicU32::new(0); + + #[derive(PartialEq, Eq, PartialOrd, Ord)] + struct D(i32); + impl Drop for D { + fn drop(&mut self) { + if DROPS.fetch_add(1, Ordering::SeqCst) == 1 { + panic!("panic in `drop`"); + } + } + } + + let mut set = BTreeSet::new(); + set.insert(D(0)); + set.insert(D(4)); + set.insert(D(8)); + + catch_unwind(move || { + drop(set.drain_filter(|d| { + PREDS.fetch_add(1u32 << d.0, Ordering::SeqCst); + true + })) + }) + .ok(); + + assert_eq!(PREDS.load(Ordering::SeqCst), 0x011); + assert_eq!(DROPS.load(Ordering::SeqCst), 3); +} + +#[test] +fn test_drain_filter_pred_panic_leak() { + static PREDS: AtomicU32 = AtomicU32::new(0); + static DROPS: AtomicU32 = AtomicU32::new(0); + + #[derive(PartialEq, Eq, PartialOrd, Ord)] + struct D(i32); + impl Drop for D { + fn drop(&mut self) { + DROPS.fetch_add(1, Ordering::SeqCst); + } + } + + let mut set = BTreeSet::new(); + set.insert(D(0)); + set.insert(D(4)); + set.insert(D(8)); + + catch_unwind(AssertUnwindSafe(|| { + drop(set.drain_filter(|d| { + PREDS.fetch_add(1u32 << d.0, Ordering::SeqCst); + match d.0 { + 0 => true, + _ => panic!(), + } + })) + })) + .ok(); + + assert_eq!(PREDS.load(Ordering::SeqCst), 0x011); + assert_eq!(DROPS.load(Ordering::SeqCst), 1); + assert_eq!(set.len(), 2); + assert_eq!(set.first().unwrap().0, 4); + assert_eq!(set.last().unwrap().0, 8); +} + +#[test] fn test_clear() { let mut x = BTreeSet::new(); x.insert(1); diff --git a/src/liballoc/tests/lib.rs b/src/liballoc/tests/lib.rs index ea75f8903c3..ad6feaeebc6 100644 --- a/src/liballoc/tests/lib.rs +++ b/src/liballoc/tests/lib.rs @@ -1,5 +1,6 @@ #![feature(allocator_api)] #![feature(box_syntax)] +#![feature(btree_drain_filter)] #![feature(drain_filter)] #![feature(exact_size_is_empty)] #![feature(map_first_last)] diff --git a/src/libcore/array/iter.rs b/src/libcore/array/iter.rs index 80eaae0d4af..f6b8d4ba081 100644 --- a/src/libcore/array/iter.rs +++ b/src/libcore/array/iter.rs @@ -39,7 +39,7 @@ where alive: Range<usize>, } -impl<T, const N: usize> IntoIter<T, { N }> +impl<T, const N: usize> IntoIter<T, N> where [T; N]: LengthAtMost32, { @@ -99,7 +99,7 @@ where } #[stable(feature = "array_value_iter_impls", since = "1.40.0")] -impl<T, const N: usize> Iterator for IntoIter<T, { N }> +impl<T, const N: usize> Iterator for IntoIter<T, N> where [T; N]: LengthAtMost32, { @@ -146,7 +146,7 @@ where } #[stable(feature = "array_value_iter_impls", since = "1.40.0")] -impl<T, const N: usize> DoubleEndedIterator for IntoIter<T, { N }> +impl<T, const N: usize> DoubleEndedIterator for IntoIter<T, N> where [T; N]: LengthAtMost32, { @@ -182,7 +182,7 @@ where } #[stable(feature = "array_value_iter_impls", since = "1.40.0")] -impl<T, const N: usize> Drop for IntoIter<T, { N }> +impl<T, const N: usize> Drop for IntoIter<T, N> where [T; N]: LengthAtMost32, { @@ -195,7 +195,7 @@ where } #[stable(feature = "array_value_iter_impls", since = "1.40.0")] -impl<T, const N: usize> ExactSizeIterator for IntoIter<T, { N }> +impl<T, const N: usize> ExactSizeIterator for IntoIter<T, N> where [T; N]: LengthAtMost32, { @@ -210,17 +210,17 @@ where } #[stable(feature = "array_value_iter_impls", since = "1.40.0")] -impl<T, const N: usize> FusedIterator for IntoIter<T, { N }> where [T; N]: LengthAtMost32 {} +impl<T, const N: usize> FusedIterator for IntoIter<T, N> where [T; N]: LengthAtMost32 {} // The iterator indeed reports the correct length. The number of "alive" // elements (that will still be yielded) is the length of the range `alive`. // This range is decremented in length in either `next` or `next_back`. It is // always decremented by 1 in those methods, but only if `Some(_)` is returned. #[stable(feature = "array_value_iter_impls", since = "1.40.0")] -unsafe impl<T, const N: usize> TrustedLen for IntoIter<T, { N }> where [T; N]: LengthAtMost32 {} +unsafe impl<T, const N: usize> TrustedLen for IntoIter<T, N> where [T; N]: LengthAtMost32 {} #[stable(feature = "array_value_iter_impls", since = "1.40.0")] -impl<T: Clone, const N: usize> Clone for IntoIter<T, { N }> +impl<T: Clone, const N: usize> Clone for IntoIter<T, N> where [T; N]: LengthAtMost32, { @@ -249,7 +249,7 @@ where } #[stable(feature = "array_value_iter_impls", since = "1.40.0")] -impl<T: fmt::Debug, const N: usize> fmt::Debug for IntoIter<T, { N }> +impl<T: fmt::Debug, const N: usize> fmt::Debug for IntoIter<T, N> where [T; N]: LengthAtMost32, { diff --git a/src/librustc_lint/early.rs b/src/librustc_lint/early.rs index 34da29c9747..018e9da243c 100644 --- a/src/librustc_lint/early.rs +++ b/src/librustc_lint/early.rs @@ -104,6 +104,11 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> run_early_pass!(self, check_pat_post, p); } + fn visit_anon_const(&mut self, c: &'a ast::AnonConst) { + run_early_pass!(self, check_anon_const, c); + ast_visit::walk_anon_const(self, c); + } + fn visit_expr(&mut self, e: &'a ast::Expr) { self.with_lint_attrs(e.id, &e.attrs, |cx| { run_early_pass!(cx, check_expr, e); diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index 97261759aeb..af1fad2c660 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -104,6 +104,7 @@ macro_rules! early_lint_passes { $args, [ UnusedParens: UnusedParens, + UnusedBraces: UnusedBraces, UnusedImportBraces: UnusedImportBraces, UnsafeCode: UnsafeCode, AnonymousParameters: AnonymousParameters, @@ -275,6 +276,7 @@ fn register_builtins(store: &mut LintStore, no_interleave_lints: bool) { UNUSED_FEATURES, UNUSED_LABELS, UNUSED_PARENS, + UNUSED_BRACES, REDUNDANT_SEMICOLONS ); diff --git a/src/librustc_lint/passes.rs b/src/librustc_lint/passes.rs index ace15471445..c9e12afedbb 100644 --- a/src/librustc_lint/passes.rs +++ b/src/librustc_lint/passes.rs @@ -170,6 +170,7 @@ macro_rules! early_lint_methods { fn check_stmt(a: &ast::Stmt); fn check_arm(a: &ast::Arm); fn check_pat(a: &ast::Pat); + fn check_anon_const(a: &ast::AnonConst); fn check_pat_post(a: &ast::Pat); fn check_expr(a: &ast::Expr); fn check_expr_post(a: &ast::Expr); diff --git a/src/librustc_lint/unused.rs b/src/librustc_lint/unused.rs index 78a67ae6afe..c74b399555a 100644 --- a/src/librustc_lint/unused.rs +++ b/src/librustc_lint/unused.rs @@ -1,5 +1,7 @@ +use crate::Lint; use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; use rustc_ast::ast; +use rustc_ast::ast::{ExprKind, StmtKind}; use rustc_ast::attr; use rustc_ast::util::parser; use rustc_ast_pretty::pprust; @@ -315,16 +317,58 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedAttributes { } } -declare_lint! { - pub(super) UNUSED_PARENS, - Warn, - "`if`, `match`, `while` and `return` do not need parentheses" +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +enum UnusedDelimsCtx { + FunctionArg, + MethodArg, + AssignedValue, + IfCond, + WhileCond, + ForIterExpr, + MatchScrutineeExpr, + ReturnValue, + BlockRetValue, + LetScrutineeExpr, + ArrayLenExpr, + AnonConst, } -declare_lint_pass!(UnusedParens => [UNUSED_PARENS]); +impl From<UnusedDelimsCtx> for &'static str { + fn from(ctx: UnusedDelimsCtx) -> &'static str { + match ctx { + UnusedDelimsCtx::FunctionArg => "function argument", + UnusedDelimsCtx::MethodArg => "method argument", + UnusedDelimsCtx::AssignedValue => "assigned value", + UnusedDelimsCtx::IfCond => "`if` condition", + UnusedDelimsCtx::WhileCond => "`while` condition", + UnusedDelimsCtx::ForIterExpr => "`for` iterator expression", + UnusedDelimsCtx::MatchScrutineeExpr => "`match` scrutinee expression", + UnusedDelimsCtx::ReturnValue => "`return` value", + UnusedDelimsCtx::BlockRetValue => "block return value", + UnusedDelimsCtx::LetScrutineeExpr => "`let` scrutinee expression", + UnusedDelimsCtx::ArrayLenExpr | UnusedDelimsCtx::AnonConst => "const expression", + } + } +} -impl UnusedParens { - fn is_expr_parens_necessary(inner: &ast::Expr, followed_by_block: bool) -> bool { +/// Used by both `UnusedParens` and `UnusedBraces` to prevent code duplication. +trait UnusedDelimLint { + const DELIM_STR: &'static str; + + // this cannot be a constant is it refers to a static. + fn lint(&self) -> &'static Lint; + + fn check_unused_delims_expr( + &self, + cx: &EarlyContext<'_>, + value: &ast::Expr, + ctx: UnusedDelimsCtx, + followed_by_block: bool, + left_pos: Option<BytePos>, + right_pos: Option<BytePos>, + ); + + fn is_expr_delims_necessary(inner: &ast::Expr, followed_by_block: bool) -> bool { followed_by_block && match inner.kind { ast::ExprKind::Ret(_) | ast::ExprKind::Break(..) => true, @@ -332,98 +376,41 @@ impl UnusedParens { } } - fn check_unused_parens_expr( + fn emit_unused_delims_expr( &self, cx: &EarlyContext<'_>, value: &ast::Expr, - msg: &str, - followed_by_block: bool, + ctx: UnusedDelimsCtx, left_pos: Option<BytePos>, right_pos: Option<BytePos>, ) { - match value.kind { - ast::ExprKind::Paren(ref inner) => { - if !Self::is_expr_parens_necessary(inner, followed_by_block) - && value.attrs.is_empty() - && !value.span.from_expansion() - { - let expr_text = - if let Ok(snippet) = cx.sess().source_map().span_to_snippet(value.span) { - snippet - } else { - pprust::expr_to_string(value) - }; - let keep_space = ( - left_pos.map(|s| s >= value.span.lo()).unwrap_or(false), - right_pos.map(|s| s <= value.span.hi()).unwrap_or(false), - ); - Self::remove_outer_parens(cx, value.span, &expr_text, msg, keep_space); - } - } - ast::ExprKind::Let(_, ref expr) => { - // FIXME(#60336): Properly handle `let true = (false && true)` - // actually needing the parenthesis. - self.check_unused_parens_expr( - cx, - expr, - "`let` head expression", - followed_by_block, - None, - None, - ); - } - _ => {} - } + let expr_text = if let Ok(snippet) = cx.sess().source_map().span_to_snippet(value.span) { + snippet + } else { + pprust::expr_to_string(value) + }; + let keep_space = ( + left_pos.map(|s| s >= value.span.lo()).unwrap_or(false), + right_pos.map(|s| s <= value.span.hi()).unwrap_or(false), + ); + self.emit_unused_delims(cx, value.span, &expr_text, ctx.into(), keep_space); } - fn check_unused_parens_pat( + fn emit_unused_delims( &self, cx: &EarlyContext<'_>, - value: &ast::Pat, - avoid_or: bool, - avoid_mut: bool, - ) { - use ast::{BindingMode, Mutability, PatKind}; - - if let PatKind::Paren(inner) = &value.kind { - match inner.kind { - // The lint visitor will visit each subpattern of `p`. We do not want to lint - // any range pattern no matter where it occurs in the pattern. For something like - // `&(a..=b)`, there is a recursive `check_pat` on `a` and `b`, but we will assume - // that if there are unnecessary parens they serve a purpose of readability. - PatKind::Range(..) => return, - // Avoid `p0 | .. | pn` if we should. - PatKind::Or(..) if avoid_or => return, - // Avoid `mut x` and `mut x @ p` if we should: - PatKind::Ident(BindingMode::ByValue(Mutability::Mut), ..) if avoid_mut => return, - // Otherwise proceed with linting. - _ => {} - } - - let pattern_text = - if let Ok(snippet) = cx.sess().source_map().span_to_snippet(value.span) { - snippet - } else { - pprust::pat_to_string(value) - }; - Self::remove_outer_parens(cx, value.span, &pattern_text, "pattern", (false, false)); - } - } - - fn remove_outer_parens( - cx: &EarlyContext<'_>, span: Span, pattern: &str, msg: &str, keep_space: (bool, bool), ) { - cx.struct_span_lint(UNUSED_PARENS, span, |lint| { - let span_msg = format!("unnecessary parentheses around {}", msg); + cx.struct_span_lint(self.lint(), span, |lint| { + let span_msg = format!("unnecessary {} around {}", Self::DELIM_STR, msg); let mut err = lint.build(&span_msg); let mut ate_left_paren = false; let mut ate_right_paren = false; let parens_removed = pattern.trim_matches(|c| match c { - '(' => { + '(' | '{' => { if ate_left_paren { false } else { @@ -431,7 +418,7 @@ impl UnusedParens { true } } - ')' => { + ')' | '}' => { if ate_right_paren { false } else { @@ -457,61 +444,51 @@ impl UnusedParens { replace }; - err.span_suggestion_short( - span, - "remove these parentheses", - replace, - Applicability::MachineApplicable, - ); + let suggestion = format!("remove these {}", Self::DELIM_STR); + + err.span_suggestion_short(span, &suggestion, replace, Applicability::MachineApplicable); err.emit(); }); } -} -impl EarlyLintPass for UnusedParens { fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { use rustc_ast::ast::ExprKind::*; - let (value, msg, followed_by_block, left_pos, right_pos) = match e.kind { - Let(ref pat, ..) => { - self.check_unused_parens_pat(cx, pat, false, false); - return; - } - + let (value, ctx, followed_by_block, left_pos, right_pos) = match e.kind { If(ref cond, ref block, ..) => { let left = e.span.lo() + rustc_span::BytePos(2); let right = block.span.lo(); - (cond, "`if` condition", true, Some(left), Some(right)) + (cond, UnusedDelimsCtx::IfCond, true, Some(left), Some(right)) } While(ref cond, ref block, ..) => { let left = e.span.lo() + rustc_span::BytePos(5); let right = block.span.lo(); - (cond, "`while` condition", true, Some(left), Some(right)) + (cond, UnusedDelimsCtx::WhileCond, true, Some(left), Some(right)) } - ForLoop(ref pat, ref cond, ref block, ..) => { - self.check_unused_parens_pat(cx, pat, false, false); - (cond, "`for` head expression", true, None, Some(block.span.lo())) + ForLoop(_, ref cond, ref block, ..) => { + (cond, UnusedDelimsCtx::ForIterExpr, true, None, Some(block.span.lo())) } Match(ref head, _) => { let left = e.span.lo() + rustc_span::BytePos(5); - (head, "`match` head expression", true, Some(left), None) + (head, UnusedDelimsCtx::MatchScrutineeExpr, true, Some(left), None) } Ret(Some(ref value)) => { let left = e.span.lo() + rustc_span::BytePos(3); - (value, "`return` value", false, Some(left), None) + (value, UnusedDelimsCtx::ReturnValue, false, Some(left), None) } - Assign(_, ref value, _) => (value, "assigned value", false, None, None), - AssignOp(.., ref value) => (value, "assigned value", false, None, None), + Assign(_, ref value, _) | AssignOp(.., ref value) => { + (value, UnusedDelimsCtx::AssignedValue, false, None, None) + } // either function/method call, or something this lint doesn't care about ref call_or_other => { - let (args_to_check, call_kind) = match *call_or_other { - Call(_, ref args) => (&args[..], "function"), - // first "argument" is self (which sometimes needs parens) - MethodCall(_, ref args) => (&args[1..], "method"), + let (args_to_check, ctx) = match *call_or_other { + Call(_, ref args) => (&args[..], UnusedDelimsCtx::FunctionArg), + // first "argument" is self (which sometimes needs delims) + MethodCall(_, ref args) => (&args[1..], UnusedDelimsCtx::MethodArg), // actual catch-all arm _ => { return; @@ -524,14 +501,152 @@ impl EarlyLintPass for UnusedParens { if e.span.ctxt().outer_expn_data().call_site.from_expansion() { return; } - let msg = format!("{} argument", call_kind); for arg in args_to_check { - self.check_unused_parens_expr(cx, arg, &msg, false, None, None); + self.check_unused_delims_expr(cx, arg, ctx, false, None, None); } return; } }; - self.check_unused_parens_expr(cx, &value, msg, followed_by_block, left_pos, right_pos); + self.check_unused_delims_expr(cx, &value, ctx, followed_by_block, left_pos, right_pos); + } + + fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) { + match s.kind { + StmtKind::Local(ref local) => { + if let Some(ref value) = local.init { + self.check_unused_delims_expr( + cx, + &value, + UnusedDelimsCtx::AssignedValue, + false, + None, + None, + ); + } + } + StmtKind::Expr(ref expr) => { + self.check_unused_delims_expr( + cx, + &expr, + UnusedDelimsCtx::BlockRetValue, + false, + None, + None, + ); + } + _ => {} + } + } + + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { + use ast::ItemKind::*; + + if let Const(.., Some(expr)) | Static(.., Some(expr)) = &item.kind { + self.check_unused_delims_expr( + cx, + expr, + UnusedDelimsCtx::AssignedValue, + false, + None, + None, + ); + } + } +} + +declare_lint! { + pub(super) UNUSED_PARENS, + Warn, + "`if`, `match`, `while` and `return` do not need parentheses" +} + +declare_lint_pass!(UnusedParens => [UNUSED_PARENS]); + +impl UnusedDelimLint for UnusedParens { + const DELIM_STR: &'static str = "parentheses"; + + fn lint(&self) -> &'static Lint { + UNUSED_PARENS + } + + fn check_unused_delims_expr( + &self, + cx: &EarlyContext<'_>, + value: &ast::Expr, + ctx: UnusedDelimsCtx, + followed_by_block: bool, + left_pos: Option<BytePos>, + right_pos: Option<BytePos>, + ) { + match value.kind { + ast::ExprKind::Paren(ref inner) => { + if !Self::is_expr_delims_necessary(inner, followed_by_block) + && value.attrs.is_empty() + && !value.span.from_expansion() + { + self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos) + } + } + ast::ExprKind::Let(_, ref expr) => { + // FIXME(#60336): Properly handle `let true = (false && true)` + // actually needing the parenthesis. + self.check_unused_delims_expr( + cx, + expr, + UnusedDelimsCtx::LetScrutineeExpr, + followed_by_block, + None, + None, + ); + } + _ => {} + } + } +} + +impl UnusedParens { + fn check_unused_parens_pat( + &self, + cx: &EarlyContext<'_>, + value: &ast::Pat, + avoid_or: bool, + avoid_mut: bool, + ) { + use ast::{BindingMode, Mutability, PatKind}; + + if let PatKind::Paren(inner) = &value.kind { + match inner.kind { + // The lint visitor will visit each subpattern of `p`. We do not want to lint + // any range pattern no matter where it occurs in the pattern. For something like + // `&(a..=b)`, there is a recursive `check_pat` on `a` and `b`, but we will assume + // that if there are unnecessary parens they serve a purpose of readability. + PatKind::Range(..) => return, + // Avoid `p0 | .. | pn` if we should. + PatKind::Or(..) if avoid_or => return, + // Avoid `mut x` and `mut x @ p` if we should: + PatKind::Ident(BindingMode::ByValue(Mutability::Mut), ..) if avoid_mut => return, + // Otherwise proceed with linting. + _ => {} + } + + let pattern_text = + if let Ok(snippet) = cx.sess().source_map().span_to_snippet(value.span) { + snippet + } else { + pprust::pat_to_string(value) + }; + self.emit_unused_delims(cx, value.span, &pattern_text, "pattern", (false, false)); + } + } +} + +impl EarlyLintPass for UnusedParens { + fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { + if let ExprKind::Let(ref pat, ..) | ExprKind::ForLoop(ref pat, ..) = e.kind { + self.check_unused_parens_pat(cx, pat, false, false); + } + + <Self as UnusedDelimLint>::check_expr(self, cx, e) } fn check_pat(&mut self, cx: &EarlyContext<'_>, p: &ast::Pat) { @@ -556,22 +671,16 @@ impl EarlyLintPass for UnusedParens { } } - fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) { - use ast::StmtKind::*; - - match s.kind { - Local(ref local) => { - self.check_unused_parens_pat(cx, &local.pat, false, false); + fn check_anon_const(&mut self, cx: &EarlyContext<'_>, c: &ast::AnonConst) { + self.check_unused_delims_expr(cx, &c.value, UnusedDelimsCtx::AnonConst, false, None, None); + } - if let Some(ref value) = local.init { - self.check_unused_parens_expr(cx, &value, "assigned value", false, None, None); - } - } - Expr(ref expr) => { - self.check_unused_parens_expr(cx, &expr, "block return value", false, None, None); - } - _ => {} + fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) { + if let StmtKind::Local(ref local) = s.kind { + self.check_unused_parens_pat(cx, &local.pat, false, false); } + + <Self as UnusedDelimLint>::check_stmt(self, cx, s) } fn check_param(&mut self, cx: &EarlyContext<'_>, param: &ast::Param) { @@ -587,6 +696,16 @@ impl EarlyLintPass for UnusedParens { match &r.kind { &ast::TyKind::TraitObject(..) => {} &ast::TyKind::ImplTrait(_, ref bounds) if bounds.len() > 1 => {} + &ast::TyKind::Array(_, ref len) => { + self.check_unused_delims_expr( + cx, + &len.value, + UnusedDelimsCtx::ArrayLenExpr, + false, + None, + None, + ); + } _ => { let pattern_text = if let Ok(snippet) = cx.sess().source_map().span_to_snippet(ty.span) { @@ -595,19 +714,134 @@ impl EarlyLintPass for UnusedParens { pprust::ty_to_string(ty) }; - Self::remove_outer_parens(cx, ty.span, &pattern_text, "type", (false, false)); + self.emit_unused_delims(cx, ty.span, &pattern_text, "type", (false, false)); } } } } fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { - use ast::ItemKind::*; + <Self as UnusedDelimLint>::check_item(self, cx, item) + } +} - if let Const(.., Some(expr)) | Static(.., Some(expr)) = &item.kind { - self.check_unused_parens_expr(cx, expr, "assigned value", false, None, None); +declare_lint! { + pub(super) UNUSED_BRACES, + Warn, + "unnecessary braces around an expression" +} + +declare_lint_pass!(UnusedBraces => [UNUSED_BRACES]); + +impl UnusedDelimLint for UnusedBraces { + const DELIM_STR: &'static str = "braces"; + + fn lint(&self) -> &'static Lint { + UNUSED_BRACES + } + + fn check_unused_delims_expr( + &self, + cx: &EarlyContext<'_>, + value: &ast::Expr, + ctx: UnusedDelimsCtx, + followed_by_block: bool, + left_pos: Option<BytePos>, + right_pos: Option<BytePos>, + ) { + match value.kind { + ast::ExprKind::Block(ref inner, None) + if inner.rules == ast::BlockCheckMode::Default => + { + // emit a warning under the following conditions: + // + // - the block does not have a label + // - the block is not `unsafe` + // - the block contains exactly one expression (do not lint `{ expr; }`) + // - `followed_by_block` is true and the internal expr may contain a `{` + // - the block is not multiline (do not lint multiline match arms) + // ``` + // match expr { + // Pattern => { + // somewhat_long_expression + // } + // // ... + // } + // ``` + // - the block has no attribute and was not created inside a macro + // - if the block is an `anon_const`, the inner expr must be a literal + // (do not lint `struct A<const N: usize>; let _: A<{ 2 + 3 }>;`) + // + // FIXME(const_generics): handle paths when #67075 is fixed. + if let [stmt] = inner.stmts.as_slice() { + if let ast::StmtKind::Expr(ref expr) = stmt.kind { + if !Self::is_expr_delims_necessary(expr, followed_by_block) + && (ctx != UnusedDelimsCtx::AnonConst + || matches!(expr.kind, ast::ExprKind::Lit(_))) + // array length expressions are checked during `check_anon_const` and `check_ty`, + // once as `ArrayLenExpr` and once as `AnonConst`. + // + // As we do not want to lint this twice, we do not emit an error for + // `ArrayLenExpr` if `AnonConst` would do the same. + && (ctx != UnusedDelimsCtx::ArrayLenExpr + || !matches!(expr.kind, ast::ExprKind::Lit(_))) + && !cx.sess().source_map().is_multiline(value.span) + && value.attrs.is_empty() + && !value.span.from_expansion() + { + self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos) + } + } + } + } + ast::ExprKind::Let(_, ref expr) => { + // FIXME(#60336): Properly handle `let true = (false && true)` + // actually needing the parenthesis. + self.check_unused_delims_expr( + cx, + expr, + UnusedDelimsCtx::LetScrutineeExpr, + followed_by_block, + None, + None, + ); + } + _ => {} + } + } +} + +impl EarlyLintPass for UnusedBraces { + fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { + <Self as UnusedDelimLint>::check_expr(self, cx, e) + } + + fn check_anon_const(&mut self, cx: &EarlyContext<'_>, c: &ast::AnonConst) { + self.check_unused_delims_expr(cx, &c.value, UnusedDelimsCtx::AnonConst, false, None, None); + } + + fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) { + <Self as UnusedDelimLint>::check_stmt(self, cx, s) + } + + fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) { + if let &ast::TyKind::Paren(ref r) = &ty.kind { + if let ast::TyKind::Array(_, ref len) = r.kind { + self.check_unused_delims_expr( + cx, + &len.value, + UnusedDelimsCtx::ArrayLenExpr, + false, + None, + None, + ); + } } } + + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { + <Self as UnusedDelimLint>::check_item(self, cx, item) + } } declare_lint! { diff --git a/src/librustc_macros/src/query.rs b/src/librustc_macros/src/query.rs index 08dc854e9ff..7ed77304a90 100644 --- a/src/librustc_macros/src/query.rs +++ b/src/librustc_macros/src/query.rs @@ -356,9 +356,11 @@ fn add_query_description_impl( quote! { #t } }) .unwrap_or(quote! { _ }); + // expr is a `Block`, meaning that `{ #expr }` gets expanded + // to `{ { stmts... } }`, which triggers the `unused_braces` lint. quote! { #[inline] - #[allow(unused_variables)] + #[allow(unused_variables, unused_braces)] fn cache_on_disk( #tcx: TyCtxt<'tcx>, #key: Self::Key, diff --git a/src/librustc_parse/parser/expr.rs b/src/librustc_parse/parser/expr.rs index b205a4b3222..cbff99f8da6 100644 --- a/src/librustc_parse/parser/expr.rs +++ b/src/librustc_parse/parser/expr.rs @@ -638,6 +638,7 @@ impl<'a> Parser<'a> { ExprKind::MethodCall(_, _) => "a method call", ExprKind::Call(_, _) => "a function call", ExprKind::Await(_) => "`.await`", + ExprKind::Err => return Ok(with_postfix), _ => unreachable!("parse_dot_or_call_expr_with_ shouldn't produce this"), } ); diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 75c5dfb8d18..21f43b6fd4f 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -2808,7 +2808,7 @@ impl<'a> Resolver<'a> { ast::Path { span, segments: iter::once(Ident::with_dummy_span(kw::PathRoot)) - .chain({ path_str.split("::").skip(1).map(Ident::from_str) }) + .chain(path_str.split("::").skip(1).map(Ident::from_str)) .map(|i| self.new_ast_path_segment(i)) .collect(), } diff --git a/src/librustc_target/spec/windows_base.rs b/src/librustc_target/spec/windows_base.rs index 188548b41fe..5f59ca8a5a3 100644 --- a/src/librustc_target/spec/windows_base.rs +++ b/src/librustc_target/spec/windows_base.rs @@ -60,6 +60,8 @@ pub fn opts() -> TargetOptions { "-lgcc".to_string(), "-lgcc_eh".to_string(), "-lpthread".to_string(), + // libpthread depends on libmsvcrt, so we need to link it *again*. + "-lmsvcrt".to_string(), "-lkernel32".to_string(), ], ); diff --git a/src/libstd/sys/windows/handle.rs b/src/libstd/sys/windows/handle.rs index f2ad057b6b6..d00381792e3 100644 --- a/src/libstd/sys/windows/handle.rs +++ b/src/libstd/sys/windows/handle.rs @@ -115,8 +115,7 @@ impl RawHandle { ) -> io::Result<Option<usize>> { let len = cmp::min(buf.len(), <c::DWORD>::max_value() as usize) as c::DWORD; let mut amt = 0; - let res = - cvt({ c::ReadFile(self.0, buf.as_ptr() as c::LPVOID, len, &mut amt, overlapped) }); + let res = cvt(c::ReadFile(self.0, buf.as_ptr() as c::LPVOID, len, &mut amt, overlapped)); match res { Ok(_) => Ok(Some(amt as usize)), Err(e) => { @@ -139,7 +138,7 @@ impl RawHandle { unsafe { let mut bytes = 0; let wait = if wait { c::TRUE } else { c::FALSE }; - let res = cvt({ c::GetOverlappedResult(self.raw(), overlapped, &mut bytes, wait) }); + let res = cvt(c::GetOverlappedResult(self.raw(), overlapped, &mut bytes, wait)); match res { Ok(_) => Ok(bytes as usize), Err(e) => { diff --git a/src/test/incremental/const-generics/issue-61516.rs b/src/test/incremental/const-generics/issue-61516.rs index a7465b77267..a193bf998dc 100644 --- a/src/test/incremental/const-generics/issue-61516.rs +++ b/src/test/incremental/const-generics/issue-61516.rs @@ -4,7 +4,7 @@ struct FakeArray<T, const N: usize>(T); -impl<T, const N: usize> FakeArray<T, { N }> { +impl<T, const N: usize> FakeArray<T, N> { fn len(&self) -> usize { N } diff --git a/src/test/ui/array-slice-vec/vec-fixed-length.rs b/src/test/ui/array-slice-vec/vec-fixed-length.rs index 5db02ee066b..908c39c7951 100644 --- a/src/test/ui/array-slice-vec/vec-fixed-length.rs +++ b/src/test/ui/array-slice-vec/vec-fixed-length.rs @@ -9,7 +9,7 @@ fn test_big_vec() {} #[cfg(target_pointer_width = "64")] fn test_big_vec() { - assert_eq!(size_of::<[u8; (1 << 32)]>(), (1 << 32)); + assert_eq!(size_of::<[u8; 1 << 32]>(), (1 << 32)); } fn main() { diff --git a/src/test/ui/block-fn-coerce.rs b/src/test/ui/block-fn-coerce.rs index fc5f51d46b2..d993ad99459 100644 --- a/src/test/ui/block-fn-coerce.rs +++ b/src/test/ui/block-fn-coerce.rs @@ -1,4 +1,5 @@ // run-pass +#![allow(unused_braces)] fn force<F>(f: F) -> isize where F: FnOnce() -> isize { return f(); } diff --git a/src/test/ui/cleanup-rvalue-scopes.rs b/src/test/ui/cleanup-rvalue-scopes.rs index f51f13abf79..c5dd87c0f5a 100644 --- a/src/test/ui/cleanup-rvalue-scopes.rs +++ b/src/test/ui/cleanup-rvalue-scopes.rs @@ -1,5 +1,5 @@ // run-pass - +#![allow(unused_braces)] #![allow(non_snake_case)] #![allow(unused_variables)] // Test that destructors for rvalue temporaries run either at end of diff --git a/src/test/ui/coerce/coerce-expect-unsized.rs b/src/test/ui/coerce/coerce-expect-unsized.rs index b44aa6ab377..d486fdf73ab 100644 --- a/src/test/ui/coerce/coerce-expect-unsized.rs +++ b/src/test/ui/coerce/coerce-expect-unsized.rs @@ -1,4 +1,5 @@ // run-pass +#![allow(unused_braces)] #![feature(box_syntax)] use std::cell::RefCell; diff --git a/src/test/ui/coerce/coerce-overloaded-autoderef.rs b/src/test/ui/coerce/coerce-overloaded-autoderef.rs index 3fe18103ef8..d5484607c8b 100644 --- a/src/test/ui/coerce/coerce-overloaded-autoderef.rs +++ b/src/test/ui/coerce/coerce-overloaded-autoderef.rs @@ -1,4 +1,5 @@ // run-pass +#![allow(unused_braces)] #![allow(dead_code)] // pretty-expanded FIXME #23616 diff --git a/src/test/ui/const-generics/issues/issue-62504.rs b/src/test/ui/const-generics/issues/issue-62504.rs index cd3cfaac3b9..212e16253f6 100644 --- a/src/test/ui/const-generics/issues/issue-62504.rs +++ b/src/test/ui/const-generics/issues/issue-62504.rs @@ -7,13 +7,13 @@ trait HasSize { const SIZE: usize; } -impl<const X: usize> HasSize for ArrayHolder<{ X }> { +impl<const X: usize> HasSize for ArrayHolder<X> { const SIZE: usize = X; } struct ArrayHolder<const X: usize>([u32; X]); -impl<const X: usize> ArrayHolder<{ X }> { +impl<const X: usize> ArrayHolder<X> { pub const fn new() -> Self { ArrayHolder([0; Self::SIZE]) //~^ ERROR: mismatched types diff --git a/src/test/ui/const-generics/issues/issue-70125-2.rs b/src/test/ui/const-generics/issues/issue-70125-2.rs index ea7a68c2f93..a3eca0dd7d9 100644 --- a/src/test/ui/const-generics/issues/issue-70125-2.rs +++ b/src/test/ui/const-generics/issues/issue-70125-2.rs @@ -13,4 +13,4 @@ trait Foo<const X: usize> { } } -impl Foo<{3}> for () {} +impl Foo<3> for () {} diff --git a/src/test/ui/const-generics/unused_braces.rs b/src/test/ui/const-generics/unused_braces.rs new file mode 100644 index 00000000000..05234faf714 --- /dev/null +++ b/src/test/ui/const-generics/unused_braces.rs @@ -0,0 +1,13 @@ +// check-pass +#![warn(unused_braces)] + +#![feature(const_generics)] +//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash + +struct A<const N: usize>; + +fn main() { + let _: A<7>; // ok + let _: A<{ 7 }>; //~ WARN unnecessary braces + let _: A<{ 3 + 5 }>; // ok +} diff --git a/src/test/ui/const-generics/unused_braces.stderr b/src/test/ui/const-generics/unused_braces.stderr new file mode 100644 index 00000000000..fc3da6096e7 --- /dev/null +++ b/src/test/ui/const-generics/unused_braces.stderr @@ -0,0 +1,20 @@ +warning: the feature `const_generics` is incomplete and may cause the compiler to crash + --> $DIR/unused_braces.rs:4:12 + | +LL | #![feature(const_generics)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + +warning: unnecessary braces around const expression + --> $DIR/unused_braces.rs:11:14 + | +LL | let _: A<{ 7 }>; + | ^^^^^ help: remove these braces + | +note: the lint level is defined here + --> $DIR/unused_braces.rs:2:9 + | +LL | #![warn(unused_braces)] + | ^^^^^^^^^^^^^ + diff --git a/src/test/ui/consts/const-block.rs b/src/test/ui/consts/const-block.rs index 7172a34c8cf..ec99c70f6e0 100644 --- a/src/test/ui/consts/const-block.rs +++ b/src/test/ui/consts/const-block.rs @@ -1,5 +1,5 @@ // run-pass - +#![allow(unused_braces)] #![allow(dead_code)] #![allow(unused_unsafe)] diff --git a/src/test/ui/expr-block-generic-unique1.rs b/src/test/ui/expr-block-generic-unique1.rs index c14191f2ffc..d081cb2be7e 100644 --- a/src/test/ui/expr-block-generic-unique1.rs +++ b/src/test/ui/expr-block-generic-unique1.rs @@ -1,5 +1,5 @@ // run-pass - +#![allow(unused_braces)] #![feature(box_syntax)] fn test_generic<T, F>(expected: Box<T>, eq: F) where T: Clone, F: FnOnce(Box<T>, Box<T>) -> bool { diff --git a/src/test/ui/expr-block-generic-unique2.rs b/src/test/ui/expr-block-generic-unique2.rs index 90ebc02931a..9362eb86fc3 100644 --- a/src/test/ui/expr-block-generic-unique2.rs +++ b/src/test/ui/expr-block-generic-unique2.rs @@ -1,5 +1,5 @@ // run-pass - +#![allow(unused_braces)] #![feature(box_syntax)] fn test_generic<T, F>(expected: T, eq: F) where T: Clone, F: FnOnce(T, T) -> bool { diff --git a/src/test/ui/expr-block-generic.rs b/src/test/ui/expr-block-generic.rs index ec93f59722d..29c7c42219c 100644 --- a/src/test/ui/expr-block-generic.rs +++ b/src/test/ui/expr-block-generic.rs @@ -1,4 +1,5 @@ // run-pass +#![allow(unused_braces)] fn test_generic<T: Clone, F>(expected: T, eq: F) where F: FnOnce(T, T) -> bool { let actual: T = { expected.clone() }; diff --git a/src/test/ui/expr-block-unique.rs b/src/test/ui/expr-block-unique.rs index fe1a7d9f1fb..eff3fd3a151 100644 --- a/src/test/ui/expr-block-unique.rs +++ b/src/test/ui/expr-block-unique.rs @@ -1,5 +1,5 @@ // run-pass - +#![allow(unused_braces)] #![feature(box_syntax)] pub fn main() { let x: Box<_> = { box 100 }; assert_eq!(*x, 100); } diff --git a/src/test/ui/expr-block.rs b/src/test/ui/expr-block.rs index 549ccf9774f..ff87595c934 100644 --- a/src/test/ui/expr-block.rs +++ b/src/test/ui/expr-block.rs @@ -1,10 +1,7 @@ // run-pass - +#![allow(unused_braces)] #![allow(dead_code)] - - - // Tests for standalone blocks as expressions fn test_basic() { let rs: bool = { true }; assert!((rs)); } diff --git a/src/test/ui/expr-fn.rs b/src/test/ui/expr-fn.rs index af809f563fc..253cbfd5d38 100644 --- a/src/test/ui/expr-fn.rs +++ b/src/test/ui/expr-fn.rs @@ -1,4 +1,5 @@ // run-pass +#![allow(unused_braces)] fn test_int() { fn f() -> isize { 10 } diff --git a/src/test/ui/functions-closures/closure-inference.rs b/src/test/ui/functions-closures/closure-inference.rs index 96878445245..1877414f099 100644 --- a/src/test/ui/functions-closures/closure-inference.rs +++ b/src/test/ui/functions-closures/closure-inference.rs @@ -1,5 +1,5 @@ // run-pass - +#![allow(unused_braces)] fn foo(i: isize) -> isize { i + 1 } diff --git a/src/test/ui/functions-closures/closure-inference2.rs b/src/test/ui/functions-closures/closure-inference2.rs index f2dfa5888aa..4ce132e86ca 100644 --- a/src/test/ui/functions-closures/closure-inference2.rs +++ b/src/test/ui/functions-closures/closure-inference2.rs @@ -1,6 +1,6 @@ // run-pass // Test a rather underspecified example: - +#![allow(unused_braces)] pub fn main() { let f = {|i| i}; diff --git a/src/test/ui/intrinsics/intrinsic-move-val-cleanups.rs b/src/test/ui/intrinsics/intrinsic-move-val-cleanups.rs index a2068429af5..9804c421db0 100644 --- a/src/test/ui/intrinsics/intrinsic-move-val-cleanups.rs +++ b/src/test/ui/intrinsics/intrinsic-move-val-cleanups.rs @@ -1,4 +1,5 @@ // run-pass +#![allow(unused_braces)] #![allow(unused_unsafe)] #![allow(unreachable_code)] // ignore-emscripten no threads support diff --git a/src/test/ui/issues/issue-23898.rs b/src/test/ui/issues/issue-23898.rs index a8787f279b7..3de365675ad 100644 --- a/src/test/ui/issues/issue-23898.rs +++ b/src/test/ui/issues/issue-23898.rs @@ -1,4 +1,5 @@ // run-pass +#![allow(unused_parens)] #![allow(non_camel_case_types)] // Note: This test was used to demonstrate #5873 (now #23898). diff --git a/src/test/ui/issues/issue-28777.rs b/src/test/ui/issues/issue-28777.rs index 74de00adadb..1f426b7185e 100644 --- a/src/test/ui/issues/issue-28777.rs +++ b/src/test/ui/issues/issue-28777.rs @@ -1,4 +1,5 @@ // run-pass +#![allow(unused_braces)] fn main() { let v1 = { 1 + {2} * {3} }; let v2 = 1 + {2} * {3} ; diff --git a/src/test/ui/lint/lint-unnecessary-parens.rs b/src/test/ui/lint/lint-unnecessary-parens.rs index 5ce1f576081..623cd04d9bc 100644 --- a/src/test/ui/lint/lint-unnecessary-parens.rs +++ b/src/test/ui/lint/lint-unnecessary-parens.rs @@ -48,11 +48,11 @@ fn main() { if (true) {} //~ ERROR unnecessary parentheses around `if` condition while (true) {} //~ ERROR unnecessary parentheses around `while` condition //~^ WARN denote infinite loops with - match (true) { //~ ERROR unnecessary parentheses around `match` head expression + match (true) { //~ ERROR unnecessary parentheses around `match` scrutinee expression _ => {} } - if let 1 = (1) {} //~ ERROR unnecessary parentheses around `let` head expression - while let 1 = (2) {} //~ ERROR unnecessary parentheses around `let` head expression + if let 1 = (1) {} //~ ERROR unnecessary parentheses around `let` scrutinee expression + while let 1 = (2) {} //~ ERROR unnecessary parentheses around `let` scrutinee expression let v = X { y: false }; // struct lits needs parens, so these shouldn't warn. if (v == X { y: true }) {} diff --git a/src/test/ui/lint/lint-unnecessary-parens.stderr b/src/test/ui/lint/lint-unnecessary-parens.stderr index 8858c953273..15184ba36ae 100644 --- a/src/test/ui/lint/lint-unnecessary-parens.stderr +++ b/src/test/ui/lint/lint-unnecessary-parens.stderr @@ -72,19 +72,19 @@ LL | while (true) {} | = note: `#[warn(while_true)]` on by default -error: unnecessary parentheses around `match` head expression +error: unnecessary parentheses around `match` scrutinee expression --> $DIR/lint-unnecessary-parens.rs:51:11 | LL | match (true) { | ^^^^^^ help: remove these parentheses -error: unnecessary parentheses around `let` head expression +error: unnecessary parentheses around `let` scrutinee expression --> $DIR/lint-unnecessary-parens.rs:54:16 | LL | if let 1 = (1) {} | ^^^ help: remove these parentheses -error: unnecessary parentheses around `let` head expression +error: unnecessary parentheses around `let` scrutinee expression --> $DIR/lint-unnecessary-parens.rs:55:19 | LL | while let 1 = (2) {} diff --git a/src/test/ui/lint/unused_braces.rs b/src/test/ui/lint/unused_braces.rs new file mode 100644 index 00000000000..de456ee6c23 --- /dev/null +++ b/src/test/ui/lint/unused_braces.rs @@ -0,0 +1,31 @@ +// check-pass +#![warn(unused_braces, unused_parens)] + +fn main() { + let _ = (7); + //~^WARN unnecessary parentheses + + let _ = { 7 }; + //~^ WARN unnecessary braces + + if let 7 = { 7 } { + //~^ WARN unnecessary braces + } + + let _: [u8; { 3 }]; + //~^ WARN unnecessary braces + + // do not emit error for multiline blocks. + let _ = { + 7 + }; + + // do not emit error for unsafe blocks. + let _ = unsafe { 7 }; + + // do not emit error, as the `{` would then + // be parsed as part of the `return`. + if { return } { + + } +} diff --git a/src/test/ui/lint/unused_braces.stderr b/src/test/ui/lint/unused_braces.stderr new file mode 100644 index 00000000000..72f425ffc3e --- /dev/null +++ b/src/test/ui/lint/unused_braces.stderr @@ -0,0 +1,36 @@ +warning: unnecessary parentheses around assigned value + --> $DIR/unused_braces.rs:5:13 + | +LL | let _ = (7); + | ^^^ help: remove these parentheses + | +note: the lint level is defined here + --> $DIR/unused_braces.rs:2:24 + | +LL | #![warn(unused_braces, unused_parens)] + | ^^^^^^^^^^^^^ + +warning: unnecessary braces around assigned value + --> $DIR/unused_braces.rs:8:13 + | +LL | let _ = { 7 }; + | ^^^^^ help: remove these braces + | +note: the lint level is defined here + --> $DIR/unused_braces.rs:2:9 + | +LL | #![warn(unused_braces, unused_parens)] + | ^^^^^^^^^^^^^ + +warning: unnecessary braces around `let` scrutinee expression + --> $DIR/unused_braces.rs:11:16 + | +LL | if let 7 = { 7 } { + | ^^^^^ help: remove these braces + +warning: unnecessary braces around const expression + --> $DIR/unused_braces.rs:15:17 + | +LL | let _: [u8; { 3 }]; + | ^^^^^ help: remove these braces + diff --git a/src/test/ui/lint/unused_parens_borrow.rs b/src/test/ui/lint/unused_parens_borrow.rs new file mode 100644 index 00000000000..98dbbecfedd --- /dev/null +++ b/src/test/ui/lint/unused_parens_borrow.rs @@ -0,0 +1,22 @@ +// check-pass +#![warn(unused_braces)] + +// changing `&{ expr }` to `&expr` changes the semantic of the program +// so we should not warn this case + +#[repr(packed)] +struct A { + a: u8, + b: u32, +} + +fn main() { + let a = A { + a: 42, + b: 1729, + }; + + let _ = &{ a.b }; + let _ = { a.b }; + //~^ WARN unnecessary braces +} diff --git a/src/test/ui/lint/unused_parens_borrow.stderr b/src/test/ui/lint/unused_parens_borrow.stderr new file mode 100644 index 00000000000..7e3839ae4e0 --- /dev/null +++ b/src/test/ui/lint/unused_parens_borrow.stderr @@ -0,0 +1,12 @@ +warning: unnecessary braces around assigned value + --> $DIR/unused_parens_borrow.rs:20:13 + | +LL | let _ = { a.b }; + | ^^^^^^^ help: remove these braces + | +note: the lint level is defined here + --> $DIR/unused_parens_borrow.rs:2:9 + | +LL | #![warn(unused_braces)] + | ^^^^^^^^^^^^^ + diff --git a/src/test/ui/lint/unused_parens_remove_json_suggestion.stderr b/src/test/ui/lint/unused_parens_remove_json_suggestion.stderr index c3bf77a3a6f..5fb67fd7c95 100644 --- a/src/test/ui/lint/unused_parens_remove_json_suggestion.stderr +++ b/src/test/ui/lint/unused_parens_remove_json_suggestion.stderr @@ -46,14 +46,14 @@ LL | while(true && false) { | ^^^^^^^^^^^^^^^ help: remove these parentheses "} -{"message":"unnecessary parentheses around `for` head expression","code":{"code":"unused_parens","explanation":null},"level":"error","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":987,"byte_end":995,"line_start":44,"line_end":44,"column_start":18,"column_end":26,"is_primary":true,"text":[{"text":" for _ in (0 .. 3){ +{"message":"unnecessary parentheses around `for` iterator expression","code":{"code":"unused_parens","explanation":null},"level":"error","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":987,"byte_end":995,"line_start":44,"line_end":44,"column_start":18,"column_end":26,"is_primary":true,"text":[{"text":" for _ in (0 .. 3){ --> $DIR/unused_parens_remove_json_suggestion.rs:44:18 | LL | for _ in (0 .. 3){ | ^^^^^^^^ help: remove these parentheses "} -{"message":"unnecessary parentheses around `for` head expression","code":{"code":"unused_parens","explanation":null},"level":"error","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":1088,"byte_end":1096,"line_start":49,"line_end":49,"column_start":14,"column_end":22,"is_primary":true,"text":[{"text":" for _ in (0 .. 3) { +{"message":"unnecessary parentheses around `for` iterator expression","code":{"code":"unused_parens","explanation":null},"level":"error","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":1088,"byte_end":1096,"line_start":49,"line_end":49,"column_start":14,"column_end":22,"is_primary":true,"text":[{"text":" for _ in (0 .. 3) { --> $DIR/unused_parens_remove_json_suggestion.rs:49:14 | LL | for _ in (0 .. 3) { diff --git a/src/test/ui/parser/issue-70552-ascription-in-parens-after-call.rs b/src/test/ui/parser/issue-70552-ascription-in-parens-after-call.rs new file mode 100644 index 00000000000..9b6dd7db4be --- /dev/null +++ b/src/test/ui/parser/issue-70552-ascription-in-parens-after-call.rs @@ -0,0 +1,3 @@ +fn main() { + expr as fun()(:); //~ ERROR expected expression +} diff --git a/src/test/ui/parser/issue-70552-ascription-in-parens-after-call.stderr b/src/test/ui/parser/issue-70552-ascription-in-parens-after-call.stderr new file mode 100644 index 00000000000..f03c92e1b1f --- /dev/null +++ b/src/test/ui/parser/issue-70552-ascription-in-parens-after-call.stderr @@ -0,0 +1,8 @@ +error: expected expression, found `:` + --> $DIR/issue-70552-ascription-in-parens-after-call.rs:2:19 + | +LL | expr as fun()(:); + | ^ expected expression + +error: aborting due to previous error + diff --git a/src/test/ui/range.rs b/src/test/ui/range.rs index 82983e37ea1..f3f7508d124 100644 --- a/src/test/ui/range.rs +++ b/src/test/ui/range.rs @@ -1,5 +1,5 @@ // run-pass - +#![allow(unused_braces)] #![allow(unused_comparisons)] #![allow(dead_code)] #![allow(unused_mut)] diff --git a/src/test/ui/range_inclusive.rs b/src/test/ui/range_inclusive.rs index 68d9bf7d26b..540b35e0392 100644 --- a/src/test/ui/range_inclusive.rs +++ b/src/test/ui/range_inclusive.rs @@ -1,7 +1,7 @@ // run-pass // Test inclusive range syntax. - #![feature(range_is_empty)] +#![allow(unused_braces)] #![allow(unused_comparisons)] use std::ops::RangeToInclusive; diff --git a/src/test/ui/structs-enums/empty-tag.rs b/src/test/ui/structs-enums/empty-tag.rs index 56a438200c0..271ab72c74f 100644 --- a/src/test/ui/structs-enums/empty-tag.rs +++ b/src/test/ui/structs-enums/empty-tag.rs @@ -1,4 +1,5 @@ // run-pass +#![allow(unused_braces)] #![allow(non_camel_case_types)] #[derive(Copy, Clone, Debug)] diff --git a/src/test/ui/type-alias-impl-trait/assoc-type-const.rs b/src/test/ui/type-alias-impl-trait/assoc-type-const.rs index 2907c21c620..7f3f86e4df0 100644 --- a/src/test/ui/type-alias-impl-trait/assoc-type-const.rs +++ b/src/test/ui/type-alias-impl-trait/assoc-type-const.rs @@ -18,16 +18,16 @@ trait MyTrait<'a, const C: usize> { const MY_CONST: usize; } -impl<'a, const C: usize> MyTrait<'a, { C }> for MyStruct<{ C }> { +impl<'a, const C: usize> MyTrait<'a, C> for MyStruct<C> { type MyItem = u8; const MY_CONST: usize = C; } -impl<'a, I, const C: usize> UnwrapItemsExt<'a, { C }> for I { - type Iter = impl MyTrait<'a, { C }>; +impl<'a, I, const C: usize> UnwrapItemsExt<'a, C> for I { + type Iter = impl MyTrait<'a, C>; fn unwrap_items(self) -> Self::Iter { - MyStruct::<{ C }> {} + MyStruct::<C> {} } } diff --git a/src/test/ui/unsized-locals/unsized-exprs-rpass.rs b/src/test/ui/unsized-locals/unsized-exprs-rpass.rs index bc64fcdec2e..24c2758a0a2 100644 --- a/src/test/ui/unsized-locals/unsized-exprs-rpass.rs +++ b/src/test/ui/unsized-locals/unsized-exprs-rpass.rs @@ -1,5 +1,5 @@ // run-pass - +#![allow(unused_braces, unused_parens)] #![feature(unsized_tuple_coercion, unsized_locals)] struct A<X: ?Sized>(X); @@ -30,7 +30,6 @@ fn main() { *foo() }); udrop::<[u8]>({*foo()}); - #[allow(unused_parens)] udrop::<[u8]>((*foo())); udrop::<[u8]>((*tfoo()).1); *afoo() + 42; diff --git a/src/test/ui/weird-exprs.rs b/src/test/ui/weird-exprs.rs index ca68a5af0dd..d812bbd011e 100644 --- a/src/test/ui/weird-exprs.rs +++ b/src/test/ui/weird-exprs.rs @@ -5,7 +5,7 @@ #![allow(non_camel_case_types)] #![allow(dead_code)] #![allow(unreachable_code)] -#![allow(unused_parens)] +#![allow(unused_braces, unused_parens)] #![recursion_limit = "256"] diff --git a/src/test/ui/zero-sized/zero-sized-tuple-struct.rs b/src/test/ui/zero-sized/zero-sized-tuple-struct.rs index 6c438720e5d..2208590f7d6 100644 --- a/src/test/ui/zero-sized/zero-sized-tuple-struct.rs +++ b/src/test/ui/zero-sized/zero-sized-tuple-struct.rs @@ -1,4 +1,5 @@ // run-pass +#![allow(unused_braces)] #![allow(unused_assignments)] // Make sure that the constructor args are codegened for zero-sized tuple structs |
