Auto merge of #10159 - khuey:iter_kv_map_ref_mut, r=llogiq

Make the iter_kv_map lint handle ref/mut annotations.

For the degenerate (`map(|(k, _)| k)`/`map(|(_, v)| v)`) cases a mut annotation is superfluous and a ref annotation won't compile, so no additional handling is required. For cases where the `map` call must be preserved ref/mut annotations should also be presereved so that the map body continues to work as expected.

*Please write a short comment explaining your change (or "none" for internal only changes)*

changelog: [`iter_kv_map`]: handle ref/mut annotations
This commit is contained in:
bors 2023-01-04 23:13:39 +00:00
commit d5d8ef13d7
4 changed files with 175 additions and 35 deletions

View file

@ -6,7 +6,7 @@ use clippy_utils::source::{snippet, snippet_with_applicability};
use clippy_utils::sugg;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::visitors::is_local_used;
use rustc_hir::{BindingAnnotation, Body, BorrowKind, Expr, ExprKind, Mutability, Pat, PatKind};
use rustc_hir::{BindingAnnotation, Body, BorrowKind, ByRef, Expr, ExprKind, Mutability, Pat, PatKind};
use rustc_lint::{LateContext, LintContext};
use rustc_middle::ty;
use rustc_span::sym;
@ -30,9 +30,9 @@ pub(super) fn check<'tcx>(
if let Body {params: [p], value: body_expr, generator_kind: _ } = cx.tcx.hir().body(c.body);
if let PatKind::Tuple([key_pat, val_pat], _) = p.pat.kind;
let (replacement_kind, binded_ident) = match (&key_pat.kind, &val_pat.kind) {
(key, PatKind::Binding(_, _, value, _)) if pat_is_wild(cx, key, m_arg) => ("value", value),
(PatKind::Binding(_, _, key, _), value) if pat_is_wild(cx, value, m_arg) => ("key", key),
let (replacement_kind, annotation, bound_ident) = match (&key_pat.kind, &val_pat.kind) {
(key, PatKind::Binding(ann, _, value, _)) if pat_is_wild(cx, key, m_arg) => ("value", ann, value),
(PatKind::Binding(ann, _, key, _), value) if pat_is_wild(cx, value, m_arg) => ("key", ann, key),
_ => return,
};
@ -47,7 +47,7 @@ pub(super) fn check<'tcx>(
if_chain! {
if let ExprKind::Path(rustc_hir::QPath::Resolved(_, path)) = body_expr.kind;
if let [local_ident] = path.segments;
if local_ident.ident.as_str() == binded_ident.as_str();
if local_ident.ident.as_str() == bound_ident.as_str();
then {
span_lint_and_sugg(
@ -60,13 +60,23 @@ pub(super) fn check<'tcx>(
applicability,
);
} else {
let ref_annotation = if annotation.0 == ByRef::Yes {
"ref "
} else {
""
};
let mut_annotation = if annotation.1 == Mutability::Mut {
"mut "
} else {
""
};
span_lint_and_sugg(
cx,
ITER_KV_MAP,
expr.span,
&format!("iterating on a map's {replacement_kind}s"),
"try",
format!("{recv_snippet}.{into_prefix}{replacement_kind}s().map(|{binded_ident}| {})",
format!("{recv_snippet}.{into_prefix}{replacement_kind}s().map(|{ref_annotation}{mut_annotation}{bound_ident}| {})",
snippet_with_applicability(cx, body_expr.span, "/* body */", &mut applicability)),
applicability,
);

View file

@ -1,14 +1,15 @@
// run-rustfix
#![warn(clippy::iter_kv_map)]
#![allow(clippy::redundant_clone)]
#![allow(clippy::suspicious_map)]
#![allow(clippy::map_identity)]
#![allow(unused_mut, clippy::redundant_clone, clippy::suspicious_map, clippy::map_identity)]
use std::collections::{BTreeMap, HashMap};
fn main() {
let get_key = |(key, _val)| key;
fn ref_acceptor(v: &u32) -> u32 {
*v
}
let map: HashMap<u32, u32> = HashMap::new();
@ -36,6 +37,20 @@ fn main() {
let _ = map.keys().map(|key| key * 9).count();
let _ = map.values().map(|value| value * 17).count();
// Preserve the ref in the fix.
let _ = map.clone().into_values().map(|ref val| ref_acceptor(val)).count();
// Preserve the mut in the fix.
let _ = map
.clone().into_values().map(|mut val| {
val += 2;
val
})
.count();
// Don't let a mut interfere.
let _ = map.clone().into_values().count();
let map: BTreeMap<u32, u32> = BTreeMap::new();
let _ = map.keys().collect::<Vec<_>>();
@ -61,4 +76,18 @@ fn main() {
// Lint
let _ = map.keys().map(|key| key * 9).count();
let _ = map.values().map(|value| value * 17).count();
// Preserve the ref in the fix.
let _ = map.clone().into_values().map(|ref val| ref_acceptor(val)).count();
// Preserve the mut in the fix.
let _ = map
.clone().into_values().map(|mut val| {
val += 2;
val
})
.count();
// Don't let a mut interfere.
let _ = map.clone().into_values().count();
}

View file

@ -1,14 +1,15 @@
// run-rustfix
#![warn(clippy::iter_kv_map)]
#![allow(clippy::redundant_clone)]
#![allow(clippy::suspicious_map)]
#![allow(clippy::map_identity)]
#![allow(unused_mut, clippy::redundant_clone, clippy::suspicious_map, clippy::map_identity)]
use std::collections::{BTreeMap, HashMap};
fn main() {
let get_key = |(key, _val)| key;
fn ref_acceptor(v: &u32) -> u32 {
*v
}
let map: HashMap<u32, u32> = HashMap::new();
@ -36,6 +37,22 @@ fn main() {
let _ = map.iter().map(|(key, _value)| key * 9).count();
let _ = map.iter().map(|(_key, value)| value * 17).count();
// Preserve the ref in the fix.
let _ = map.clone().into_iter().map(|(_, ref val)| ref_acceptor(val)).count();
// Preserve the mut in the fix.
let _ = map
.clone()
.into_iter()
.map(|(_, mut val)| {
val += 2;
val
})
.count();
// Don't let a mut interfere.
let _ = map.clone().into_iter().map(|(_, mut val)| val).count();
let map: BTreeMap<u32, u32> = BTreeMap::new();
let _ = map.iter().map(|(key, _)| key).collect::<Vec<_>>();
@ -61,4 +78,20 @@ fn main() {
// Lint
let _ = map.iter().map(|(key, _value)| key * 9).count();
let _ = map.iter().map(|(_key, value)| value * 17).count();
// Preserve the ref in the fix.
let _ = map.clone().into_iter().map(|(_, ref val)| ref_acceptor(val)).count();
// Preserve the mut in the fix.
let _ = map
.clone()
.into_iter()
.map(|(_, mut val)| {
val += 2;
val
})
.count();
// Don't let a mut interfere.
let _ = map.clone().into_iter().map(|(_, mut val)| val).count();
}

View file

@ -1,5 +1,5 @@
error: iterating on a map's keys
--> $DIR/iter_kv_map.rs:15:13
--> $DIR/iter_kv_map.rs:16:13
|
LL | let _ = map.iter().map(|(key, _)| key).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()`
@ -7,130 +7,198 @@ LL | let _ = map.iter().map(|(key, _)| key).collect::<Vec<_>>();
= note: `-D clippy::iter-kv-map` implied by `-D warnings`
error: iterating on a map's values
--> $DIR/iter_kv_map.rs:16:13
--> $DIR/iter_kv_map.rs:17:13
|
LL | let _ = map.iter().map(|(_, value)| value).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values()`
error: iterating on a map's values
--> $DIR/iter_kv_map.rs:17:13
--> $DIR/iter_kv_map.rs:18:13
|
LL | let _ = map.iter().map(|(_, v)| v + 2).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|v| v + 2)`
error: iterating on a map's keys
--> $DIR/iter_kv_map.rs:19:13
--> $DIR/iter_kv_map.rs:20:13
|
LL | let _ = map.clone().into_iter().map(|(key, _)| key).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys()`
error: iterating on a map's keys
--> $DIR/iter_kv_map.rs:20:13
--> $DIR/iter_kv_map.rs:21:13
|
LL | let _ = map.clone().into_iter().map(|(key, _)| key + 2).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys().map(|key| key + 2)`
error: iterating on a map's values
--> $DIR/iter_kv_map.rs:22:13
--> $DIR/iter_kv_map.rs:23:13
|
LL | let _ = map.clone().into_iter().map(|(_, val)| val).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values()`
error: iterating on a map's values
--> $DIR/iter_kv_map.rs:23:13
--> $DIR/iter_kv_map.rs:24:13
|
LL | let _ = map.clone().into_iter().map(|(_, val)| val + 2).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values().map(|val| val + 2)`
error: iterating on a map's values
--> $DIR/iter_kv_map.rs:25:13
--> $DIR/iter_kv_map.rs:26:13
|
LL | let _ = map.clone().iter().map(|(_, val)| val).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().values()`
error: iterating on a map's keys
--> $DIR/iter_kv_map.rs:26:13
--> $DIR/iter_kv_map.rs:27:13
|
LL | let _ = map.iter().map(|(key, _)| key).filter(|x| *x % 2 == 0).count();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()`
error: iterating on a map's keys
--> $DIR/iter_kv_map.rs:36:13
--> $DIR/iter_kv_map.rs:37:13
|
LL | let _ = map.iter().map(|(key, _value)| key * 9).count();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys().map(|key| key * 9)`
error: iterating on a map's values
--> $DIR/iter_kv_map.rs:37:13
--> $DIR/iter_kv_map.rs:38:13
|
LL | let _ = map.iter().map(|(_key, value)| value * 17).count();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|value| value * 17)`
error: iterating on a map's keys
error: iterating on a map's values
--> $DIR/iter_kv_map.rs:41:13
|
LL | let _ = map.clone().into_iter().map(|(_, ref val)| ref_acceptor(val)).count();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values().map(|ref val| ref_acceptor(val))`
error: iterating on a map's values
--> $DIR/iter_kv_map.rs:44:13
|
LL | let _ = map
| _____________^
LL | | .clone()
LL | | .into_iter()
LL | | .map(|(_, mut val)| {
LL | | val += 2;
LL | | val
LL | | })
| |__________^
|
help: try
|
LL ~ let _ = map
LL + .clone().into_values().map(|mut val| {
LL + val += 2;
LL + val
LL + })
|
error: iterating on a map's values
--> $DIR/iter_kv_map.rs:54:13
|
LL | let _ = map.clone().into_iter().map(|(_, mut val)| val).count();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values()`
error: iterating on a map's keys
--> $DIR/iter_kv_map.rs:58:13
|
LL | let _ = map.iter().map(|(key, _)| key).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()`
error: iterating on a map's values
--> $DIR/iter_kv_map.rs:42:13
--> $DIR/iter_kv_map.rs:59:13
|
LL | let _ = map.iter().map(|(_, value)| value).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values()`
error: iterating on a map's values
--> $DIR/iter_kv_map.rs:43:13
--> $DIR/iter_kv_map.rs:60:13
|
LL | let _ = map.iter().map(|(_, v)| v + 2).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|v| v + 2)`
error: iterating on a map's keys
--> $DIR/iter_kv_map.rs:45:13
--> $DIR/iter_kv_map.rs:62:13
|
LL | let _ = map.clone().into_iter().map(|(key, _)| key).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys()`
error: iterating on a map's keys
--> $DIR/iter_kv_map.rs:46:13
--> $DIR/iter_kv_map.rs:63:13
|
LL | let _ = map.clone().into_iter().map(|(key, _)| key + 2).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys().map(|key| key + 2)`
error: iterating on a map's values
--> $DIR/iter_kv_map.rs:48:13
--> $DIR/iter_kv_map.rs:65:13
|
LL | let _ = map.clone().into_iter().map(|(_, val)| val).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values()`
error: iterating on a map's values
--> $DIR/iter_kv_map.rs:49:13
--> $DIR/iter_kv_map.rs:66:13
|
LL | let _ = map.clone().into_iter().map(|(_, val)| val + 2).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values().map(|val| val + 2)`
error: iterating on a map's values
--> $DIR/iter_kv_map.rs:51:13
--> $DIR/iter_kv_map.rs:68:13
|
LL | let _ = map.clone().iter().map(|(_, val)| val).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().values()`
error: iterating on a map's keys
--> $DIR/iter_kv_map.rs:52:13
--> $DIR/iter_kv_map.rs:69:13
|
LL | let _ = map.iter().map(|(key, _)| key).filter(|x| *x % 2 == 0).count();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()`
error: iterating on a map's keys
--> $DIR/iter_kv_map.rs:62:13
--> $DIR/iter_kv_map.rs:79:13
|
LL | let _ = map.iter().map(|(key, _value)| key * 9).count();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys().map(|key| key * 9)`
error: iterating on a map's values
--> $DIR/iter_kv_map.rs:63:13
--> $DIR/iter_kv_map.rs:80:13
|
LL | let _ = map.iter().map(|(_key, value)| value * 17).count();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|value| value * 17)`
error: aborting due to 22 previous errors
error: iterating on a map's values
--> $DIR/iter_kv_map.rs:83:13
|
LL | let _ = map.clone().into_iter().map(|(_, ref val)| ref_acceptor(val)).count();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values().map(|ref val| ref_acceptor(val))`
error: iterating on a map's values
--> $DIR/iter_kv_map.rs:86:13
|
LL | let _ = map
| _____________^
LL | | .clone()
LL | | .into_iter()
LL | | .map(|(_, mut val)| {
LL | | val += 2;
LL | | val
LL | | })
| |__________^
|
help: try
|
LL ~ let _ = map
LL + .clone().into_values().map(|mut val| {
LL + val += 2;
LL + val
LL + })
|
error: iterating on a map's values
--> $DIR/iter_kv_map.rs:96:13
|
LL | let _ = map.clone().into_iter().map(|(_, mut val)| val).count();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values()`
error: aborting due to 28 previous errors