Skip to content

Commit 6308a8a

Browse files
committed
add Iterator::dedup and friends
1 parent 6ba0ce4 commit 6308a8a

File tree

6 files changed

+252
-39
lines changed

6 files changed

+252
-39
lines changed

compiler/rustc_codegen_ssa/src/back/link.rs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1457,10 +1457,8 @@ fn print_native_static_libs(
14571457
all_native_libs: &[NativeLib],
14581458
all_rust_dylibs: &[&Path],
14591459
) {
1460-
let mut lib_args: Vec<_> = all_native_libs
1461-
.iter()
1462-
.filter(|l| relevant_lib(sess, l))
1463-
.filter_map(|lib| {
1460+
let mut lib_args: Vec<_> = Itertools::dedup(
1461+
all_native_libs.iter().filter(|l| relevant_lib(sess, l)).filter_map(|lib| {
14641462
let name = lib.name;
14651463
match lib.kind {
14661464
NativeLibKind::Static { bundle: Some(false), .. }
@@ -1486,10 +1484,9 @@ fn print_native_static_libs(
14861484
| NativeLibKind::WasmImportModule
14871485
| NativeLibKind::RawDylib => None,
14881486
}
1489-
})
1490-
// deduplication of consecutive repeated libraries, see rust-lang/rust#113209
1491-
.dedup()
1492-
.collect();
1487+
}), // deduplication of consecutive repeated libraries, see rust-lang/rust#113209
1488+
)
1489+
.collect();
14931490
for path in all_rust_dylibs {
14941491
// FIXME deduplicate with add_dynamic_crate
14951492

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", reason = "recently added", 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", reason = "recently added", 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
@@ -6,6 +6,7 @@ use super::super::{
66
};
77
use crate::array;
88
use crate::cmp::{self, Ordering};
9+
use crate::iter::adapters::{Dedup, DedupEq, DedupKey};
910
use crate::num::NonZero;
1011
use crate::ops::{ChangeOutputType, ControlFlow, FromResidual, Residual, Try};
1112

@@ -1863,6 +1864,142 @@ pub trait Iterator {
18631864
Inspect::new(self, f)
18641865
}
18651866

1867+
/// Removes all but the first of consecutive repeated elements in the iterator
1868+
/// according to the [`PartialEq`] trait implementation.
1869+
///
1870+
/// For an iterator yielding infinitely many consecutive duplicates,
1871+
/// calling [`next`][Iterator::next] on this iterator may never halt.
1872+
///
1873+
/// If the iterator is sorted, this removes all duplicates.
1874+
///
1875+
/// # Examples
1876+
///
1877+
/// Basic usage:
1878+
///
1879+
/// ```
1880+
/// #![feature(iter_dedup)]
1881+
///
1882+
/// let vec = vec![1, 2, 2, 3, 2];
1883+
///
1884+
/// let mut iter = vec.into_iter().dedup();
1885+
///
1886+
/// assert_eq!(iter.next(), Some(1));
1887+
/// assert_eq!(iter.next(), Some(2));
1888+
/// assert_eq!(iter.next(), Some(3));
1889+
/// assert_eq!(iter.next(), Some(2));
1890+
/// assert_eq!(iter.next(), None);
1891+
/// ```
1892+
///
1893+
/// Example of an infinite loop:
1894+
///
1895+
/// ```no_run
1896+
/// #![feature(iter_dedup)]
1897+
///
1898+
/// // this will never terminate
1899+
/// let _ = std::iter::repeat(2).dedup().next();
1900+
/// ```
1901+
#[unstable(feature = "iter_dedup", issue = "83747")]
1902+
#[inline]
1903+
fn dedup<F>(self) -> Dedup<Self, DedupEq>
1904+
where
1905+
Self: Sized,
1906+
Self::Item: PartialEq,
1907+
{
1908+
Dedup::new(self, DedupEq)
1909+
}
1910+
1911+
/// Removes all but the first of consecutive elements in the iterator
1912+
/// satisfying a given equality relation.
1913+
///
1914+
/// The `same_bucket` function is passed a references to two elements from
1915+
/// the iterator and must determine if the elements compare equal.
1916+
///
1917+
/// For an iterator yielding infinitely many consecutive duplicates,
1918+
/// calling [`next`][Iterator::next] on this iterator may never halt.
1919+
///
1920+
/// If the iterator is sorted, this removes all duplicates.
1921+
///
1922+
/// # Examples
1923+
///
1924+
/// Basic usage:
1925+
///
1926+
/// ```
1927+
/// #![feature(iter_dedup)]
1928+
///
1929+
/// let vec = vec!["foo", "bar", "Bar", "baz", "bar"];
1930+
///
1931+
/// let mut iter = vec.into_iter().dedup_by(|a, b| a.eq_ignore_ascii_case(b));
1932+
///
1933+
/// assert_eq!(iter.next(), Some("foo"));
1934+
/// assert_eq!(iter.next(), Some("bar"));
1935+
/// assert_eq!(iter.next(), Some("baz"));
1936+
/// assert_eq!(iter.next(), Some("bar"));
1937+
/// assert_eq!(iter.next(), None);
1938+
/// ```
1939+
///
1940+
/// Example of an infinite loop:
1941+
///
1942+
/// ```no_run
1943+
/// #![feature(iter_dedup)]
1944+
///
1945+
/// // this will never terminate
1946+
/// let _ = std::iter::repeat(2).dedup_by(|a, b| a == b).next();
1947+
/// ```
1948+
#[unstable(feature = "iter_dedup", issue = "83747")]
1949+
#[inline]
1950+
fn dedup_by<F>(self, f: F) -> Dedup<Self, F>
1951+
where
1952+
Self: Sized,
1953+
F: FnMut(&Self::Item, &Self::Item) -> bool,
1954+
{
1955+
Dedup::new(self, f)
1956+
}
1957+
1958+
/// Removes all but the first of consecutive elements in the iterator
1959+
/// that resolve to the same key.
1960+
///
1961+
/// For an iterator yielding infinitely many consecutive duplicates,
1962+
/// calling [`next`][Iterator::next] on this iterator may never halt.
1963+
///
1964+
/// If the iterator is sorted, this removes all duplicates.
1965+
///
1966+
/// # Examples
1967+
///
1968+
/// Basic usage:
1969+
///
1970+
/// ```
1971+
/// #![feature(iter_dedup)]
1972+
///
1973+
/// let vec = vec![10, 20, 21, 30, 20];
1974+
///
1975+
/// let mut iter = vec.into_iter().dedup_by_key(|&i| i / 10);
1976+
///
1977+
/// assert_eq!(iter.next(), Some(10));
1978+
/// assert_eq!(iter.next(), Some(20));
1979+
/// assert_eq!(iter.next(), Some(30));
1980+
/// assert_eq!(iter.next(), Some(20));
1981+
/// assert_eq!(iter.next(), None);
1982+
/// ```
1983+
///
1984+
/// Example of an infinite loop:
1985+
///
1986+
/// ```no_run
1987+
/// #![feature(iter_dedup)]
1988+
///
1989+
/// // this will never terminate
1990+
/// let _ = std::iter::repeat(2).dedup_by_key(|&n| n).next();
1991+
/// ```
1992+
#[unstable(feature = "iter_dedup", issue = "83747")]
1993+
#[inline]
1994+
fn dedup_by_key<F, K>(self, f: F) -> Dedup<Self, DedupKey<F>>
1995+
where
1996+
Self: Sized,
1997+
F: FnMut(&Self::Item) -> K,
1998+
K: PartialEq,
1999+
{
2000+
Dedup::new(self, DedupKey(f))
2001+
}
2002+
18662003
/// Creates a "by reference" adapter for this instance of `Iterator`.
18672004
///
18682005
/// 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
@@ -241,38 +241,40 @@ impl<'tcx> LateLintPass<'tcx> for ExprMetavarsInUnsafe {
241241
// $y: [unsafe#1]
242242
// ```
243243
// We want to lint unsafe blocks #0 and #1
244-
let bad_unsafe_blocks = self
245-
.metavar_expns
246-
.iter()
247-
.filter_map(|(_, state)| match state {
248-
MetavarState::ReferencedInUnsafe { unsafe_blocks } => Some(unsafe_blocks.as_slice()),
249-
MetavarState::ReferencedInSafe => None,
250-
})
251-
.flatten()
252-
.copied()
253-
.inspect(|&unsafe_block| {
254-
if let LevelAndSource {
255-
level: Level::Expect,
256-
lint_id: Some(id),
257-
..
258-
} = cx.tcx.lint_level_at_node(MACRO_METAVARS_IN_UNSAFE, unsafe_block)
259-
{
260-
// Since we're going to deduplicate expanded unsafe blocks by its enclosing macro definition soon,
261-
// which would lead to unfulfilled `#[expect()]`s in all other unsafe blocks that are filtered out
262-
// except for the one we emit the warning at, we must manually fulfill the lint
263-
// for all unsafe blocks here.
264-
cx.fulfill_expectation(id);
265-
}
266-
})
267-
.map(|id| {
268-
// Remove the syntax context to hide "in this macro invocation" in the diagnostic.
269-
// The invocation doesn't matter. Also we want to dedupe by the unsafe block and not by anything
270-
// related to the callsite.
271-
let span = cx.tcx.hir_span(id);
244+
let bad_unsafe_blocks = Itertools::dedup_by(
245+
self.metavar_expns
246+
.iter()
247+
.filter_map(|(_, state)| match state {
248+
MetavarState::ReferencedInUnsafe { unsafe_blocks } => Some(unsafe_blocks.as_slice()),
249+
MetavarState::ReferencedInSafe => None,
250+
})
251+
.flatten()
252+
.copied()
253+
.inspect(|&unsafe_block| {
254+
if let LevelAndSource {
255+
level: Level::Expect,
256+
lint_id: Some(id),
257+
..
258+
} = cx.tcx.lint_level_at_node(MACRO_METAVARS_IN_UNSAFE, unsafe_block)
259+
{
260+
// Since we're going to deduplicate expanded unsafe blocks by its enclosing macro definition
261+
// soon, which would lead to unfulfilled `#[expect()]`s in all other
262+
// unsafe blocks that are filtered out except for the one we emit the
263+
// warning at, we must manually fulfill the lint for all unsafe blocks
264+
// here.
265+
cx.fulfill_expectation(id);
266+
}
267+
})
268+
.map(|id| {
269+
// Remove the syntax context to hide "in this macro invocation" in the diagnostic.
270+
// The invocation doesn't matter. Also we want to dedupe by the unsafe block and not by anything
271+
// related to the callsite.
272+
let span = cx.tcx.hir_span(id);
272273

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

277279
for (id, span) in bad_unsafe_blocks {
278280
span_lint_hir_and_then(

0 commit comments

Comments
 (0)