about summary refs log tree commit diff
diff options
context:
space:
mode:
authorTimo <30553356+y21@users.noreply.github.com>2025-07-12 13:10:23 +0000
committerGitHub <noreply@github.com>2025-07-12 13:10:23 +0000
commit3c3ee9bddbab942ed4eb6af585cfc6f2fd755753 (patch)
tree14d60ef67a9dc2bbaa509d3775fd73f56784868c
parent3d7188d06df87b230b8464871f701397330535fb (diff)
parente610584a9a6ff66cbebfcf517ab04c3bd612d271 (diff)
downloadrust-3c3ee9bddbab942ed4eb6af585cfc6f2fd755753.tar.gz
rust-3c3ee9bddbab942ed4eb6af585cfc6f2fd755753.zip
`{flat_,}map_identity`: recognize `|[x, y]| [x, y]` as an identity function as well (#15229)
changelog: [`map_identity`,`flat_map_identity`]: also recognize `|[x,
y]| [x, y]`

fixes rust-lang/rust-clippy#15198
-rw-r--r--clippy_utils/src/lib.rs14
-rw-r--r--tests/ui/flat_map_identity.fixed13
-rw-r--r--tests/ui/flat_map_identity.rs13
-rw-r--r--tests/ui/flat_map_identity.stderr8
-rw-r--r--tests/ui/map_identity.fixed12
-rw-r--r--tests/ui/map_identity.rs12
-rw-r--r--tests/ui/map_identity.stderr8
7 files changed, 75 insertions, 5 deletions
diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs
index 2e03743e621..8b9cd6a54dd 100644
--- a/clippy_utils/src/lib.rs
+++ b/clippy_utils/src/lib.rs
@@ -1896,6 +1896,7 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 /// * `|x| { return x }`
 /// * `|x| { return x; }`
 /// * `|(x, y)| (x, y)`
+/// * `|[x, y]| [x, y]`
 ///
 /// Consider calling [`is_expr_untyped_identity_function`] or [`is_expr_identity_function`] instead.
 fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
@@ -1906,9 +1907,9 @@ fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
             .get(pat.hir_id)
             .is_some_and(|mode| matches!(mode.0, ByRef::Yes(_)))
         {
-            // If a tuple `(x, y)` is of type `&(i32, i32)`, then due to match ergonomics,
-            // the inner patterns become references. Don't consider this the identity function
-            // as that changes types.
+            // If the parameter is `(x, y)` of type `&(T, T)`, or `[x, y]` of type `&[T; 2]`, then
+            // due to match ergonomics, the inner patterns become references. Don't consider this
+            // the identity function as that changes types.
             return false;
         }
 
@@ -1921,6 +1922,13 @@ fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
             {
                 pats.iter().zip(tup).all(|(pat, expr)| check_pat(cx, pat, expr))
             },
+            (PatKind::Slice(before, slice, after), ExprKind::Array(arr))
+                if slice.is_none() && before.len() + after.len() == arr.len() =>
+            {
+                (before.iter().chain(after))
+                    .zip(arr)
+                    .all(|(pat, expr)| check_pat(cx, pat, expr))
+            },
             _ => false,
         }
     }
diff --git a/tests/ui/flat_map_identity.fixed b/tests/ui/flat_map_identity.fixed
index f6206232612..06a3eee9d84 100644
--- a/tests/ui/flat_map_identity.fixed
+++ b/tests/ui/flat_map_identity.fixed
@@ -16,3 +16,16 @@ fn main() {
     let _ = iterator.flatten();
     //~^ flat_map_identity
 }
+
+fn issue15198() {
+    let x = [[1, 2], [3, 4]];
+    // don't lint: this is an `Iterator<Item = &[i32, i32]>`
+    // match ergonomics makes the binding patterns into references
+    // so that its type changes to `Iterator<Item = [&i32, &i32]>`
+    let _ = x.iter().flat_map(|[x, y]| [x, y]);
+    let _ = x.iter().flat_map(|x| [x[0]]);
+
+    // no match ergonomics for `[i32, i32]`
+    let _ = x.iter().copied().flatten();
+    //~^ flat_map_identity
+}
diff --git a/tests/ui/flat_map_identity.rs b/tests/ui/flat_map_identity.rs
index c59e749474e..1cab7d559d8 100644
--- a/tests/ui/flat_map_identity.rs
+++ b/tests/ui/flat_map_identity.rs
@@ -16,3 +16,16 @@ fn main() {
     let _ = iterator.flat_map(|x| return x);
     //~^ flat_map_identity
 }
+
+fn issue15198() {
+    let x = [[1, 2], [3, 4]];
+    // don't lint: this is an `Iterator<Item = &[i32, i32]>`
+    // match ergonomics makes the binding patterns into references
+    // so that its type changes to `Iterator<Item = [&i32, &i32]>`
+    let _ = x.iter().flat_map(|[x, y]| [x, y]);
+    let _ = x.iter().flat_map(|x| [x[0]]);
+
+    // no match ergonomics for `[i32, i32]`
+    let _ = x.iter().copied().flat_map(|[x, y]| [x, y]);
+    //~^ flat_map_identity
+}
diff --git a/tests/ui/flat_map_identity.stderr b/tests/ui/flat_map_identity.stderr
index 75137f5d9e5..18c863bf96d 100644
--- a/tests/ui/flat_map_identity.stderr
+++ b/tests/ui/flat_map_identity.stderr
@@ -19,5 +19,11 @@ error: use of `flat_map` with an identity function
 LL |     let _ = iterator.flat_map(|x| return x);
    |                      ^^^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()`
 
-error: aborting due to 3 previous errors
+error: use of `flat_map` with an identity function
+  --> tests/ui/flat_map_identity.rs:29:31
+   |
+LL |     let _ = x.iter().copied().flat_map(|[x, y]| [x, y]);
+   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()`
+
+error: aborting due to 4 previous errors
 
diff --git a/tests/ui/map_identity.fixed b/tests/ui/map_identity.fixed
index 83b2dac5fc5..b82d3e6d956 100644
--- a/tests/ui/map_identity.fixed
+++ b/tests/ui/map_identity.fixed
@@ -87,3 +87,15 @@ fn issue13904() {
     let _ = { it }.next();
     //~^ map_identity
 }
+
+// same as `issue11764`, but for arrays
+fn issue15198() {
+    let x = [[1, 2], [3, 4]];
+    // don't lint: `&[i32; 2]` becomes `[&i32; 2]`
+    let _ = x.iter().map(|[x, y]| [x, y]);
+    let _ = x.iter().map(|x| [x[0]]).map(|[x]| x);
+
+    // no match ergonomics for `[i32, i32]`
+    let _ = x.iter().copied();
+    //~^ map_identity
+}
diff --git a/tests/ui/map_identity.rs b/tests/ui/map_identity.rs
index e839c551364..c295bf87270 100644
--- a/tests/ui/map_identity.rs
+++ b/tests/ui/map_identity.rs
@@ -93,3 +93,15 @@ fn issue13904() {
     let _ = { it }.map(|x| x).next();
     //~^ map_identity
 }
+
+// same as `issue11764`, but for arrays
+fn issue15198() {
+    let x = [[1, 2], [3, 4]];
+    // don't lint: `&[i32; 2]` becomes `[&i32; 2]`
+    let _ = x.iter().map(|[x, y]| [x, y]);
+    let _ = x.iter().map(|x| [x[0]]).map(|[x]| x);
+
+    // no match ergonomics for `[i32, i32]`
+    let _ = x.iter().copied().map(|[x, y]| [x, y]);
+    //~^ map_identity
+}
diff --git a/tests/ui/map_identity.stderr b/tests/ui/map_identity.stderr
index 9836f3b4cc5..9b624a0dc75 100644
--- a/tests/ui/map_identity.stderr
+++ b/tests/ui/map_identity.stderr
@@ -87,5 +87,11 @@ error: unnecessary map of the identity function
 LL |     let _ = { it }.map(|x| x).next();
    |                   ^^^^^^^^^^^ help: remove the call to `map`
 
-error: aborting due to 13 previous errors
+error: unnecessary map of the identity function
+  --> tests/ui/map_identity.rs:105:30
+   |
+LL |     let _ = x.iter().copied().map(|[x, y]| [x, y]);
+   |                              ^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map`
+
+error: aborting due to 14 previous errors