Skip to content

Commit f5ca68f

Browse files
authored
Do not remove identity mapping if mandatory mutability would be lost (#13905)
Removing `.map(identity)` may result in invalid code if the receiver of `map()` is an immutable binding, and the result of `map()` is used as the receiver of a method call expecting a mutable reference. Fix #13904 changelog: [`map_identity`]: do not lint if this would cause mandatory mutability to be lost
2 parents b7b69b1 + a9fe043 commit f5ca68f

File tree

4 files changed

+56
-3
lines changed

4 files changed

+56
-3
lines changed

clippy_lints/src/methods/map_identity.rs

+13-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
use clippy_utils::diagnostics::span_lint_and_sugg;
22
use clippy_utils::ty::is_type_diagnostic_item;
3-
use clippy_utils::{is_expr_untyped_identity_function, is_trait_method};
3+
use clippy_utils::{is_expr_untyped_identity_function, is_trait_method, path_to_local};
4+
use rustc_ast::BindingMode;
45
use rustc_errors::Applicability;
5-
use rustc_hir as hir;
6+
use rustc_hir::{self as hir, Node, PatKind};
67
use rustc_lint::LateContext;
78
use rustc_span::{Span, sym};
89

@@ -24,6 +25,16 @@ pub(super) fn check(
2425
&& is_expr_untyped_identity_function(cx, map_arg)
2526
&& let Some(sugg_span) = expr.span.trim_start(caller.span)
2627
{
28+
// If the result of `.map(identity)` is used as a mutable reference,
29+
// the caller must not be an immutable binding.
30+
if cx.typeck_results().expr_ty_adjusted(expr).is_mutable_ptr()
31+
&& let Some(hir_id) = path_to_local(caller)
32+
&& let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
33+
&& !matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..))
34+
{
35+
return;
36+
}
37+
2738
span_lint_and_sugg(
2839
cx,
2940
MAP_IDENTITY,

tests/ui/map_identity.fixed

+15
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,18 @@ fn issue11764() {
6161
// no match ergonomics for `(i32, i32)`
6262
let _ = x.iter().copied();
6363
}
64+
65+
fn issue13904() {
66+
// don't lint: `it.next()` would not be legal as `it` is immutable
67+
let it = [1, 2, 3].into_iter();
68+
let _ = it.map(|x| x).next();
69+
70+
// lint
71+
#[allow(unused_mut)]
72+
let mut it = [1, 2, 3].into_iter();
73+
let _ = it.next();
74+
75+
// lint
76+
let it = [1, 2, 3].into_iter();
77+
let _ = { it }.next();
78+
}

tests/ui/map_identity.rs

+15
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,18 @@ fn issue11764() {
6565
// no match ergonomics for `(i32, i32)`
6666
let _ = x.iter().copied().map(|(x, y)| (x, y));
6767
}
68+
69+
fn issue13904() {
70+
// don't lint: `it.next()` would not be legal as `it` is immutable
71+
let it = [1, 2, 3].into_iter();
72+
let _ = it.map(|x| x).next();
73+
74+
// lint
75+
#[allow(unused_mut)]
76+
let mut it = [1, 2, 3].into_iter();
77+
let _ = it.map(|x| x).next();
78+
79+
// lint
80+
let it = [1, 2, 3].into_iter();
81+
let _ = { it }.map(|x| x).next();
82+
}

tests/ui/map_identity.stderr

+13-1
Original file line numberDiff line numberDiff line change
@@ -73,5 +73,17 @@ error: unnecessary map of the identity function
7373
LL | let _ = x.iter().copied().map(|(x, y)| (x, y));
7474
| ^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map`
7575

76-
error: aborting due to 11 previous errors
76+
error: unnecessary map of the identity function
77+
--> tests/ui/map_identity.rs:77:15
78+
|
79+
LL | let _ = it.map(|x| x).next();
80+
| ^^^^^^^^^^^ help: remove the call to `map`
81+
82+
error: unnecessary map of the identity function
83+
--> tests/ui/map_identity.rs:81:19
84+
|
85+
LL | let _ = { it }.map(|x| x).next();
86+
| ^^^^^^^^^^^ help: remove the call to `map`
87+
88+
error: aborting due to 13 previous errors
7789

0 commit comments

Comments
 (0)