Skip to content

Commit b2cf8f7

Browse files
committed
[map_identity]: respect match ergonomics
1 parent 34b7d15 commit b2cf8f7

File tree

4 files changed

+85
-43
lines changed

4 files changed

+85
-43
lines changed

clippy_utils/src/lib.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2034,6 +2034,18 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
20342034
/// Consider calling [`is_expr_untyped_identity_function`] or [`is_expr_identity_function`] instead.
20352035
fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
20362036
fn check_pat(cx: &LateContext<'_>, pat: &Pat<'_>, expr: &Expr<'_>) -> bool {
2037+
if cx
2038+
.typeck_results()
2039+
.pat_binding_modes()
2040+
.get(pat.hir_id)
2041+
.is_some_and(|mode| matches!(mode, BindingMode::BindByReference(_)))
2042+
{
2043+
// If a tuple `(x, y)` is of type `&(i32, i32)`, then due to match ergonomics,
2044+
// the inner patterns become references. Don't consider this the identity function
2045+
// as that changes types.
2046+
return false;
2047+
}
2048+
20372049
match (pat.kind, expr.kind) {
20382050
(PatKind::Binding(_, id, _, _), _) => {
20392051
path_to_local_id(expr, id) && cx.typeck_results().expr_adjustments(expr).is_empty()

tests/ui/map_identity.fixed

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -24,28 +24,40 @@ fn main() {
2424

2525
fn issue7189() {
2626
// should lint
27-
let x = [(1, 2), (3, 4)];
28-
let _ = x.iter();
29-
let _ = x.iter();
30-
let _ = x.iter();
27+
let x = [(1, 2), (3, 4)].iter().copied();
28+
let _ = x.clone();
29+
let _ = x.clone();
30+
let _ = x.clone();
3131

32-
let y = [(1, 2, (3, (4,))), (5, 6, (7, (8,)))];
33-
let _ = y.iter();
32+
let y = [(1, 2, (3, (4,))), (5, 6, (7, (8,)))].iter().copied();
33+
let _ = y.clone();
3434

3535
// should not lint
36-
let _ = x.iter().map(|(x, y)| (x, y, y));
37-
let _ = x.iter().map(|(x, _y)| (x,));
38-
let _ = x.iter().map(|(x, _)| (x,));
39-
let _ = x.iter().map(|(x, ..)| (x,));
40-
let _ = y.iter().map(|(x, y, (z, _))| (x, y, (z, z)));
36+
let _ = x.clone().map(|(x, y)| (x, y, y));
37+
let _ = x.clone().map(|(x, _y)| (x,));
38+
let _ = x.clone().map(|(x, _)| (x,));
39+
let _ = x.clone().map(|(x, ..)| (x,));
40+
let _ = y.clone().map(|(x, y, (z, _))| (x, y, (z, z)));
4141
let _ = y
42-
.iter()
43-
.map(|(x, y, (z, _)): &(i32, i32, (i32, (i32,)))| (x, y, (z, z)));
42+
.clone()
43+
.map(|(x, y, (z, _)): (i32, i32, (i32, (i32,)))| (x, y, (z, z)));
4444
let _ = y
45-
.iter()
46-
.map(|(x, y, (z, (w,))): &(i32, i32, (i32, (i32,)))| (x, y, (z, (w,))));
45+
.clone()
46+
.map(|(x, y, (z, (w,))): (i32, i32, (i32, (i32,)))| (x, y, (z, (w,))));
4747
}
4848

4949
fn not_identity(x: &u16) -> u16 {
5050
*x
5151
}
52+
53+
fn issue11764() {
54+
let x = [(1, 2), (3, 4)];
55+
// don't lint: this is an `Iterator<Item = &(i32, i32)>`
56+
// match ergonomics makes the binding patterns into references
57+
// so that its type changes to `Iterator<Item = (&i32, &i32)>`
58+
let _ = x.iter().map(|(x, y)| (x, y));
59+
let _ = x.iter().map(|x| (x.0,)).map(|(x,)| x);
60+
61+
// no match ergonomics for `(i32, i32)`
62+
let _ = x.iter().copied();
63+
}

tests/ui/map_identity.rs

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -26,30 +26,42 @@ fn main() {
2626

2727
fn issue7189() {
2828
// should lint
29-
let x = [(1, 2), (3, 4)];
30-
let _ = x.iter().map(|(x, y)| (x, y));
31-
let _ = x.iter().map(|(x, y)| {
29+
let x = [(1, 2), (3, 4)].iter().copied();
30+
let _ = x.clone().map(|(x, y)| (x, y));
31+
let _ = x.clone().map(|(x, y)| {
3232
return (x, y);
3333
});
34-
let _ = x.iter().map(|(x, y)| return (x, y));
34+
let _ = x.clone().map(|(x, y)| return (x, y));
3535

36-
let y = [(1, 2, (3, (4,))), (5, 6, (7, (8,)))];
37-
let _ = y.iter().map(|(x, y, (z, (w,)))| (x, y, (z, (w,))));
36+
let y = [(1, 2, (3, (4,))), (5, 6, (7, (8,)))].iter().copied();
37+
let _ = y.clone().map(|(x, y, (z, (w,)))| (x, y, (z, (w,))));
3838

3939
// should not lint
40-
let _ = x.iter().map(|(x, y)| (x, y, y));
41-
let _ = x.iter().map(|(x, _y)| (x,));
42-
let _ = x.iter().map(|(x, _)| (x,));
43-
let _ = x.iter().map(|(x, ..)| (x,));
44-
let _ = y.iter().map(|(x, y, (z, _))| (x, y, (z, z)));
40+
let _ = x.clone().map(|(x, y)| (x, y, y));
41+
let _ = x.clone().map(|(x, _y)| (x,));
42+
let _ = x.clone().map(|(x, _)| (x,));
43+
let _ = x.clone().map(|(x, ..)| (x,));
44+
let _ = y.clone().map(|(x, y, (z, _))| (x, y, (z, z)));
4545
let _ = y
46-
.iter()
47-
.map(|(x, y, (z, _)): &(i32, i32, (i32, (i32,)))| (x, y, (z, z)));
46+
.clone()
47+
.map(|(x, y, (z, _)): (i32, i32, (i32, (i32,)))| (x, y, (z, z)));
4848
let _ = y
49-
.iter()
50-
.map(|(x, y, (z, (w,))): &(i32, i32, (i32, (i32,)))| (x, y, (z, (w,))));
49+
.clone()
50+
.map(|(x, y, (z, (w,))): (i32, i32, (i32, (i32,)))| (x, y, (z, (w,))));
5151
}
5252

5353
fn not_identity(x: &u16) -> u16 {
5454
*x
5555
}
56+
57+
fn issue11764() {
58+
let x = [(1, 2), (3, 4)];
59+
// don't lint: this is an `Iterator<Item = &(i32, i32)>`
60+
// match ergonomics makes the binding patterns into references
61+
// so that its type changes to `Iterator<Item = (&i32, &i32)>`
62+
let _ = x.iter().map(|(x, y)| (x, y));
63+
let _ = x.iter().map(|x| (x.0,)).map(|(x,)| x);
64+
65+
// no match ergonomics for `(i32, i32)`
66+
let _ = x.iter().copied().map(|(x, y)| (x, y));
67+
}

tests/ui/map_identity.stderr

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -41,31 +41,37 @@ LL | let _: Result<u32, u32> = Ok(1).map_err(|a| a);
4141
| ^^^^^^^^^^^^^^^ help: remove the call to `map_err`
4242

4343
error: unnecessary map of the identity function
44-
--> $DIR/map_identity.rs:30:21
44+
--> $DIR/map_identity.rs:30:22
4545
|
46-
LL | let _ = x.iter().map(|(x, y)| (x, y));
47-
| ^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map`
46+
LL | let _ = x.clone().map(|(x, y)| (x, y));
47+
| ^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map`
4848

4949
error: unnecessary map of the identity function
50-
--> $DIR/map_identity.rs:31:21
50+
--> $DIR/map_identity.rs:31:22
5151
|
52-
LL | let _ = x.iter().map(|(x, y)| {
53-
| _____________________^
52+
LL | let _ = x.clone().map(|(x, y)| {
53+
| ______________________^
5454
LL | | return (x, y);
5555
LL | | });
5656
| |______^ help: remove the call to `map`
5757

5858
error: unnecessary map of the identity function
59-
--> $DIR/map_identity.rs:34:21
59+
--> $DIR/map_identity.rs:34:22
6060
|
61-
LL | let _ = x.iter().map(|(x, y)| return (x, y));
62-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map`
61+
LL | let _ = x.clone().map(|(x, y)| return (x, y));
62+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map`
6363

6464
error: unnecessary map of the identity function
65-
--> $DIR/map_identity.rs:37:21
65+
--> $DIR/map_identity.rs:37:22
6666
|
67-
LL | let _ = y.iter().map(|(x, y, (z, (w,)))| (x, y, (z, (w,))));
68-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map`
67+
LL | let _ = y.clone().map(|(x, y, (z, (w,)))| (x, y, (z, (w,))));
68+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map`
6969

70-
error: aborting due to 10 previous errors
70+
error: unnecessary map of the identity function
71+
--> $DIR/map_identity.rs:66:30
72+
|
73+
LL | let _ = x.iter().copied().map(|(x, y)| (x, y));
74+
| ^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map`
75+
76+
error: aborting due to 11 previous errors
7177

0 commit comments

Comments
 (0)