Skip to content

Commit a4d5b1c

Browse files
committed
add Iterator::dedup and friends
1 parent 9f6cd6d commit a4d5b1c

6 files changed

Lines changed: 252 additions & 39 deletions

File tree

compiler/rustc_codegen_ssa/src/back/link.rs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1464,10 +1464,8 @@ fn print_native_static_libs(
14641464
all_native_libs: &[NativeLib],
14651465
all_rust_dylibs: &[&Path],
14661466
) {
1467-
let mut lib_args: Vec<_> = all_native_libs
1468-
.iter()
1469-
.filter(|l| relevant_lib(sess, l))
1470-
.filter_map(|lib| {
1467+
let mut lib_args: Vec<_> = Itertools::dedup(
1468+
all_native_libs.iter().filter(|l| relevant_lib(sess, l)).filter_map(|lib| {
14711469
let name = lib.name;
14721470
match lib.kind {
14731471
NativeLibKind::Static { bundle: Some(false), .. }
@@ -1493,10 +1491,9 @@ fn print_native_static_libs(
14931491
| NativeLibKind::WasmImportModule
14941492
| NativeLibKind::RawDylib { .. } => None,
14951493
}
1496-
})
1497-
// deduplication of consecutive repeated libraries, see rust-lang/rust#113209
1498-
.dedup()
1499-
.collect();
1494+
}), // deduplication of consecutive repeated libraries, see rust-lang/rust#113209
1495+
)
1496+
.collect();
15001497
for path in all_rust_dylibs {
15011498
// FIXME deduplicate with add_dynamic_crate
15021499

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
trait DedupPredicate<T> {
2+
fn eq(&mut self, a: &T, b: &T) -> bool;
3+
}
4+
5+
impl<T, F: FnMut(&T, &T) -> bool> DedupPredicate<T> for F {
6+
fn eq(&mut self, a: &T, b: &T) -> bool {
7+
self(a, b)
8+
}
9+
}
10+
11+
#[unstable(feature = "iter_dedup", issue = "83747")]
12+
#[doc(hidden)]
13+
#[derive(Debug)]
14+
pub struct DedupEq;
15+
16+
impl<T: PartialEq> DedupPredicate<T> for DedupEq {
17+
fn eq(&mut self, a: &T, b: &T) -> bool {
18+
a == b
19+
}
20+
}
21+
22+
#[unstable(feature = "iter_dedup", issue = "83747")]
23+
#[doc(hidden)]
24+
#[derive(Debug)]
25+
pub struct DedupKey<F>(pub F);
26+
27+
impl<T, K: PartialEq, F: Fn(&T) -> K> DedupPredicate<T> for DedupKey<F> {
28+
fn eq(&mut self, a: &T, b: &T) -> bool {
29+
(self.0)(a) == (self.0)(b)
30+
}
31+
}
32+
33+
/// An iterator to deduplicate adjacent items in another iterator.
34+
///
35+
/// This `struct` is created by the [`dedup`], [`dedup_by`], and
36+
/// [`dedup_by_key`] methods on [`Iterator`]. See their documentation for more.
37+
///
38+
/// [`dedup`]: Iterator::dedup
39+
/// [`dedup_by`]: Iterator::dedup_by
40+
/// [`dedup_by_key`]: Iterator::dedup_by_key
41+
#[unstable(feature = "iter_dedup", issue = "83747")]
42+
#[derive(Debug)]
43+
pub struct Dedup<I: Iterator, F> {
44+
inner: I,
45+
f: F,
46+
last: Option<I::Item>,
47+
}
48+
49+
impl<I: Iterator, F> Dedup<I, F> {
50+
pub(in crate::iter) fn new(mut it: I, f: F) -> Self {
51+
let first = it.next();
52+
Self { inner: it, f, last: first }
53+
}
54+
}
55+
56+
#[unstable(feature = "iter_dedup", issue = "83747")]
57+
impl<I, F> Iterator for Dedup<I, F>
58+
where
59+
I: Iterator,
60+
I::Item: Clone,
61+
F: DedupPredicate<I::Item>,
62+
{
63+
type Item = I::Item;
64+
65+
fn next(&mut self) -> Option<Self::Item> {
66+
let last = self.last.as_ref()?;
67+
self.last = self.inner.find(|e| self.f.eq(e, last));
68+
return self.last.clone();
69+
}
70+
}

library/core/src/iter/adapters/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ mod chain;
88
mod cloned;
99
mod copied;
1010
mod cycle;
11+
mod dedup;
1112
mod enumerate;
1213
mod filter;
1314
mod filter_map;
@@ -38,6 +39,10 @@ pub use self::chain::chain;
3839
pub use self::cloned::Cloned;
3940
#[stable(feature = "iter_copied", since = "1.36.0")]
4041
pub use self::copied::Copied;
42+
#[unstable(feature = "iter_dedup", issue = "83747")]
43+
pub use self::dedup::Dedup;
44+
#[unstable(feature = "iter_dedup", issue = "83747")]
45+
pub use self::dedup::{DedupEq, DedupKey};
4146
#[stable(feature = "iterator_flatten", since = "1.29.0")]
4247
pub use self::flatten::Flatten;
4348
#[unstable(feature = "iter_intersperse", issue = "79524")]

library/core/src/iter/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,8 @@ pub use self::adapters::{
414414
Chain, Cycle, Enumerate, Filter, FilterMap, FlatMap, Fuse, Inspect, Map, Peekable, Rev, Scan,
415415
Skip, SkipWhile, Take, TakeWhile, Zip,
416416
};
417+
#[unstable(feature = "iter_dedup", issue = "83747")]
418+
pub use self::adapters::{Dedup, DedupEq, DedupKey};
417419
#[unstable(feature = "iter_intersperse", issue = "79524")]
418420
pub use self::adapters::{Intersperse, IntersperseWith};
419421
#[unstable(

library/core/src/iter/traits/iterator.rs

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use super::super::{
77
use super::TrustedLen;
88
use crate::array;
99
use crate::cmp::{self, Ordering};
10+
use crate::iter::adapters::{Dedup, DedupEq, DedupKey};
1011
use crate::num::NonZero;
1112
use crate::ops::{ChangeOutputType, ControlFlow, FromResidual, Residual, Try};
1213

@@ -1868,6 +1869,142 @@ pub trait Iterator {
18681869
Inspect::new(self, f)
18691870
}
18701871

1872+
/// Removes all but the first of consecutive repeated elements in the iterator
1873+
/// according to the [`PartialEq`] trait implementation.
1874+
///
1875+
/// For an iterator yielding infinitely many consecutive duplicates,
1876+
/// calling [`next`][Iterator::next] on this iterator may never halt.
1877+
///
1878+
/// If the iterator is sorted, this removes all duplicates.
1879+
///
1880+
/// # Examples
1881+
///
1882+
/// Basic usage:
1883+
///
1884+
/// ```
1885+
/// #![feature(iter_dedup)]
1886+
///
1887+
/// let vec = vec![1, 2, 2, 3, 2];
1888+
///
1889+
/// let mut iter = vec.into_iter().dedup();
1890+
///
1891+
/// assert_eq!(iter.next(), Some(1));
1892+
/// assert_eq!(iter.next(), Some(2));
1893+
/// assert_eq!(iter.next(), Some(3));
1894+
/// assert_eq!(iter.next(), Some(2));
1895+
/// assert_eq!(iter.next(), None);
1896+
/// ```
1897+
///
1898+
/// Example of an infinite loop:
1899+
///
1900+
/// ```no_run
1901+
/// #![feature(iter_dedup)]
1902+
///
1903+
/// // this will never terminate
1904+
/// let _ = std::iter::repeat(2).dedup().next();
1905+
/// ```
1906+
#[unstable(feature = "iter_dedup", issue = "83747")]
1907+
#[inline]
1908+
fn dedup<F>(self) -> Dedup<Self, DedupEq>
1909+
where
1910+
Self: Sized,
1911+
Self::Item: PartialEq,
1912+
{
1913+
Dedup::new(self, DedupEq)
1914+
}
1915+
1916+
/// Removes all but the first of consecutive elements in the iterator
1917+
/// satisfying a given equality relation.
1918+
///
1919+
/// The `same_bucket` function is passed a references to two elements from
1920+
/// the iterator and must determine if the elements compare equal.
1921+
///
1922+
/// For an iterator yielding infinitely many consecutive duplicates,
1923+
/// calling [`next`][Iterator::next] on this iterator may never halt.
1924+
///
1925+
/// If the iterator is sorted, this removes all duplicates.
1926+
///
1927+
/// # Examples
1928+
///
1929+
/// Basic usage:
1930+
///
1931+
/// ```
1932+
/// #![feature(iter_dedup)]
1933+
///
1934+
/// let vec = vec!["foo", "bar", "Bar", "baz", "bar"];
1935+
///
1936+
/// let mut iter = vec.into_iter().dedup_by(|a, b| a.eq_ignore_ascii_case(b));
1937+
///
1938+
/// assert_eq!(iter.next(), Some("foo"));
1939+
/// assert_eq!(iter.next(), Some("bar"));
1940+
/// assert_eq!(iter.next(), Some("baz"));
1941+
/// assert_eq!(iter.next(), Some("bar"));
1942+
/// assert_eq!(iter.next(), None);
1943+
/// ```
1944+
///
1945+
/// Example of an infinite loop:
1946+
///
1947+
/// ```no_run
1948+
/// #![feature(iter_dedup)]
1949+
///
1950+
/// // this will never terminate
1951+
/// let _ = std::iter::repeat(2).dedup_by(|a, b| a == b).next();
1952+
/// ```
1953+
#[unstable(feature = "iter_dedup", issue = "83747")]
1954+
#[inline]
1955+
fn dedup_by<F>(self, f: F) -> Dedup<Self, F>
1956+
where
1957+
Self: Sized,
1958+
F: FnMut(&Self::Item, &Self::Item) -> bool,
1959+
{
1960+
Dedup::new(self, f)
1961+
}
1962+
1963+
/// Removes all but the first of consecutive elements in the iterator
1964+
/// that resolve to the same key.
1965+
///
1966+
/// For an iterator yielding infinitely many consecutive duplicates,
1967+
/// calling [`next`][Iterator::next] on this iterator may never halt.
1968+
///
1969+
/// If the iterator is sorted, this removes all duplicates.
1970+
///
1971+
/// # Examples
1972+
///
1973+
/// Basic usage:
1974+
///
1975+
/// ```
1976+
/// #![feature(iter_dedup)]
1977+
///
1978+
/// let vec = vec![10, 20, 21, 30, 20];
1979+
///
1980+
/// let mut iter = vec.into_iter().dedup_by_key(|&i| i / 10);
1981+
///
1982+
/// assert_eq!(iter.next(), Some(10));
1983+
/// assert_eq!(iter.next(), Some(20));
1984+
/// assert_eq!(iter.next(), Some(30));
1985+
/// assert_eq!(iter.next(), Some(20));
1986+
/// assert_eq!(iter.next(), None);
1987+
/// ```
1988+
///
1989+
/// Example of an infinite loop:
1990+
///
1991+
/// ```no_run
1992+
/// #![feature(iter_dedup)]
1993+
///
1994+
/// // this will never terminate
1995+
/// let _ = std::iter::repeat(2).dedup_by_key(|&n| n).next();
1996+
/// ```
1997+
#[unstable(feature = "iter_dedup", issue = "83747")]
1998+
#[inline]
1999+
fn dedup_by_key<F, K>(self, f: F) -> Dedup<Self, DedupKey<F>>
2000+
where
2001+
Self: Sized,
2002+
F: FnMut(&Self::Item) -> K,
2003+
K: PartialEq,
2004+
{
2005+
Dedup::new(self, DedupKey(f))
2006+
}
2007+
18712008
/// Creates a "by reference" adapter for this instance of `Iterator`.
18722009
///
18732010
/// Consuming method calls (direct or indirect calls to `next`)

src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs

Lines changed: 33 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -243,38 +243,40 @@ impl<'tcx> LateLintPass<'tcx> for ExprMetavarsInUnsafe {
243243
// $y: [unsafe#1]
244244
// ```
245245
// We want to lint unsafe blocks #0 and #1
246-
let bad_unsafe_blocks = self
247-
.metavar_expns
248-
.iter()
249-
.filter_map(|(_, state)| match state {
250-
MetavarState::ReferencedInUnsafe { unsafe_blocks } => Some(unsafe_blocks.as_slice()),
251-
MetavarState::ReferencedInSafe => None,
252-
})
253-
.flatten()
254-
.copied()
255-
.inspect(|&unsafe_block| {
256-
if let LevelAndSource {
257-
level: Level::Expect,
258-
lint_id: Some(id),
259-
..
260-
} = cx.tcx.lint_level_at_node(MACRO_METAVARS_IN_UNSAFE, unsafe_block)
261-
{
262-
// Since we're going to deduplicate expanded unsafe blocks by its enclosing macro definition soon,
263-
// which would lead to unfulfilled `#[expect()]`s in all other unsafe blocks that are filtered out
264-
// except for the one we emit the warning at, we must manually fulfill the lint
265-
// for all unsafe blocks here.
266-
cx.fulfill_expectation(id);
267-
}
268-
})
269-
.map(|id| {
270-
// Remove the syntax context to hide "in this macro invocation" in the diagnostic.
271-
// The invocation doesn't matter. Also we want to dedupe by the unsafe block and not by anything
272-
// related to the callsite.
273-
let span = cx.tcx.hir_span(id);
246+
let bad_unsafe_blocks = Itertools::dedup_by(
247+
self.metavar_expns
248+
.iter()
249+
.filter_map(|(_, state)| match state {
250+
MetavarState::ReferencedInUnsafe { unsafe_blocks } => Some(unsafe_blocks.as_slice()),
251+
MetavarState::ReferencedInSafe => None,
252+
})
253+
.flatten()
254+
.copied()
255+
.inspect(|&unsafe_block| {
256+
if let LevelAndSource {
257+
level: Level::Expect,
258+
lint_id: Some(id),
259+
..
260+
} = cx.tcx.lint_level_at_node(MACRO_METAVARS_IN_UNSAFE, unsafe_block)
261+
{
262+
// Since we're going to deduplicate expanded unsafe blocks by its enclosing macro definition
263+
// soon, which would lead to unfulfilled `#[expect()]`s in all other
264+
// unsafe blocks that are filtered out except for the one we emit the
265+
// warning at, we must manually fulfill the lint for all unsafe blocks
266+
// here.
267+
cx.fulfill_expectation(id);
268+
}
269+
})
270+
.map(|id| {
271+
// Remove the syntax context to hide "in this macro invocation" in the diagnostic.
272+
// The invocation doesn't matter. Also we want to dedupe by the unsafe block and not by anything
273+
// related to the callsite.
274+
let span = cx.tcx.hir_span(id);
274275

275-
(id, Span::new(span.lo(), span.hi(), SyntaxContext::root(), None))
276-
})
277-
.dedup_by(|&(_, a), &(_, b)| a == b);
276+
(id, Span::new(span.lo(), span.hi(), SyntaxContext::root(), None))
277+
}),
278+
|&(_, a), &(_, b)| a == b,
279+
);
278280

279281
for (id, span) in bad_unsafe_blocks {
280282
span_lint_hir_and_then(

0 commit comments

Comments
 (0)