about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--clippy_lints/src/methods/map_identity.rs15
-rw-r--r--tests/ui/map_identity.fixed15
-rw-r--r--tests/ui/map_identity.rs15
-rw-r--r--tests/ui/map_identity.stderr14
4 files changed, 56 insertions, 3 deletions
diff --git a/clippy_lints/src/methods/map_identity.rs b/clippy_lints/src/methods/map_identity.rs
index 1f204de01da..05360144657 100644
--- a/clippy_lints/src/methods/map_identity.rs
+++ b/clippy_lints/src/methods/map_identity.rs
@@ -1,8 +1,9 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{is_expr_untyped_identity_function, is_trait_method};
+use clippy_utils::{is_expr_untyped_identity_function, is_trait_method, path_to_local};
+use rustc_ast::BindingMode;
 use rustc_errors::Applicability;
-use rustc_hir as hir;
+use rustc_hir::{self as hir, Node, PatKind};
 use rustc_lint::LateContext;
 use rustc_span::{Span, sym};
 
@@ -24,6 +25,16 @@ pub(super) fn check(
         && is_expr_untyped_identity_function(cx, map_arg)
         && let Some(sugg_span) = expr.span.trim_start(caller.span)
     {
+        // If the result of `.map(identity)` is used as a mutable reference,
+        // the caller must not be an immutable binding.
+        if cx.typeck_results().expr_ty_adjusted(expr).is_mutable_ptr()
+            && let Some(hir_id) = path_to_local(caller)
+            && let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
+            && !matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..))
+        {
+            return;
+        }
+
         span_lint_and_sugg(
             cx,
             MAP_IDENTITY,
diff --git a/tests/ui/map_identity.fixed b/tests/ui/map_identity.fixed
index 53ebfb40ba0..3257ddc6f72 100644
--- a/tests/ui/map_identity.fixed
+++ b/tests/ui/map_identity.fixed
@@ -61,3 +61,18 @@ fn issue11764() {
     // no match ergonomics for `(i32, i32)`
     let _ = x.iter().copied();
 }
+
+fn issue13904() {
+    // don't lint: `it.next()` would not be legal as `it` is immutable
+    let it = [1, 2, 3].into_iter();
+    let _ = it.map(|x| x).next();
+
+    // lint
+    #[allow(unused_mut)]
+    let mut it = [1, 2, 3].into_iter();
+    let _ = it.next();
+
+    // lint
+    let it = [1, 2, 3].into_iter();
+    let _ = { it }.next();
+}
diff --git a/tests/ui/map_identity.rs b/tests/ui/map_identity.rs
index c646c056859..be3bb9a4f10 100644
--- a/tests/ui/map_identity.rs
+++ b/tests/ui/map_identity.rs
@@ -65,3 +65,18 @@ fn issue11764() {
     // no match ergonomics for `(i32, i32)`
     let _ = x.iter().copied().map(|(x, y)| (x, y));
 }
+
+fn issue13904() {
+    // don't lint: `it.next()` would not be legal as `it` is immutable
+    let it = [1, 2, 3].into_iter();
+    let _ = it.map(|x| x).next();
+
+    // lint
+    #[allow(unused_mut)]
+    let mut it = [1, 2, 3].into_iter();
+    let _ = it.map(|x| x).next();
+
+    // lint
+    let it = [1, 2, 3].into_iter();
+    let _ = { it }.map(|x| x).next();
+}
diff --git a/tests/ui/map_identity.stderr b/tests/ui/map_identity.stderr
index 0a0dc9c8f07..aa3fc4ae0b5 100644
--- a/tests/ui/map_identity.stderr
+++ b/tests/ui/map_identity.stderr
@@ -73,5 +73,17 @@ error: unnecessary map of the identity function
 LL |     let _ = x.iter().copied().map(|(x, y)| (x, y));
    |                              ^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map`
 
-error: aborting due to 11 previous errors
+error: unnecessary map of the identity function
+  --> tests/ui/map_identity.rs:77:15
+   |
+LL |     let _ = it.map(|x| x).next();
+   |               ^^^^^^^^^^^ help: remove the call to `map`
+
+error: unnecessary map of the identity function
+  --> tests/ui/map_identity.rs:81:19
+   |
+LL |     let _ = { it }.map(|x| x).next();
+   |                   ^^^^^^^^^^^ help: remove the call to `map`
+
+error: aborting due to 13 previous errors