Skip to content

Commit 1b89724

Browse files
committed
Auto merge of #3662 - mikerite:fix-498, r=oli-obk
Fix `map_clone` bad suggestion `cloned` requires that the elements of the iterator must be references. This change determines if that is the case by examining the type of the closure argument and suggesting `.cloned` only if it is a reference. When the closure argument is not a reference, it suggests removing the `map` call instead. A minor problem with this change is that the new check sometimes overlaps with the `clone_on_copy` lint. Fixes #498
2 parents 19553ae + 89de4c9 commit 1b89724

File tree

4 files changed

+81
-28
lines changed

4 files changed

+81
-28
lines changed

clippy_lints/src/map_clone.rs

+47-24
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use crate::utils::{
55
use if_chain::if_chain;
66
use rustc::hir;
77
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
8+
use rustc::ty;
89
use rustc::{declare_tool_lint, lint_array};
910
use rustc_errors::Applicability;
1011
use syntax::ast::Ident;
@@ -18,9 +19,7 @@ pub struct Pass;
1819
///
1920
/// **Why is this bad?** Readability, this can be written more concisely
2021
///
21-
/// **Known problems:** Sometimes `.cloned()` requires stricter trait
22-
/// bound than `.map(|e| e.clone())` (which works because of the coercion).
23-
/// See [#498](https://github.com/rust-lang-nursery/rust-clippy/issues/498).
22+
/// **Known problems:** None
2423
///
2524
/// **Example:**
2625
///
@@ -69,19 +68,27 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
6968
hir::PatKind::Ref(ref inner, _) => if let hir::PatKind::Binding(
7069
hir::BindingAnnotation::Unannotated, _, name, None
7170
) = inner.node {
72-
lint(cx, e.span, args[0].span, name, closure_expr);
71+
if ident_eq(name, closure_expr) {
72+
lint(cx, e.span, args[0].span);
73+
}
7374
},
7475
hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, _, name, None) => {
7576
match closure_expr.node {
7677
hir::ExprKind::Unary(hir::UnOp::UnDeref, ref inner) => {
77-
if !cx.tables.expr_ty(inner).is_box() {
78-
lint(cx, e.span, args[0].span, name, inner);
78+
if ident_eq(name, inner) && !cx.tables.expr_ty(inner).is_box() {
79+
lint(cx, e.span, args[0].span);
7980
}
8081
},
8182
hir::ExprKind::MethodCall(ref method, _, ref obj) => {
82-
if method.ident.as_str() == "clone"
83+
if ident_eq(name, &obj[0]) && method.ident.as_str() == "clone"
8384
&& match_trait_method(cx, closure_expr, &paths::CLONE_TRAIT) {
84-
lint(cx, e.span, args[0].span, name, &obj[0]);
85+
86+
let obj_ty = cx.tables.expr_ty(&obj[0]);
87+
if let ty::Ref(..) = obj_ty.sty {
88+
lint(cx, e.span, args[0].span);
89+
} else {
90+
lint_needless_cloning(cx, e.span, args[0].span);
91+
}
8592
}
8693
},
8794
_ => {},
@@ -94,22 +101,38 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
94101
}
95102
}
96103

97-
fn lint(cx: &LateContext<'_, '_>, replace: Span, root: Span, name: Ident, path: &hir::Expr) {
104+
fn ident_eq(name: Ident, path: &hir::Expr) -> bool {
98105
if let hir::ExprKind::Path(hir::QPath::Resolved(None, ref path)) = path.node {
99-
if path.segments.len() == 1 && path.segments[0].ident == name {
100-
let mut applicability = Applicability::MachineApplicable;
101-
span_lint_and_sugg(
102-
cx,
103-
MAP_CLONE,
104-
replace,
105-
"You are using an explicit closure for cloning elements",
106-
"Consider calling the dedicated `cloned` method",
107-
format!(
108-
"{}.cloned()",
109-
snippet_with_applicability(cx, root, "..", &mut applicability)
110-
),
111-
applicability,
112-
)
113-
}
106+
path.segments.len() == 1 && path.segments[0].ident == name
107+
} else {
108+
false
114109
}
115110
}
111+
112+
fn lint_needless_cloning(cx: &LateContext<'_, '_>, root: Span, receiver: Span) {
113+
span_lint_and_sugg(
114+
cx,
115+
MAP_CLONE,
116+
root.trim_start(receiver).unwrap(),
117+
"You are needlessly cloning iterator elements",
118+
"Remove the map call",
119+
String::new(),
120+
Applicability::MachineApplicable,
121+
)
122+
}
123+
124+
fn lint(cx: &LateContext<'_, '_>, replace: Span, root: Span) {
125+
let mut applicability = Applicability::MachineApplicable;
126+
span_lint_and_sugg(
127+
cx,
128+
MAP_CLONE,
129+
replace,
130+
"You are using an explicit closure for cloning elements",
131+
"Consider calling the dedicated `cloned` method",
132+
format!(
133+
"{}.cloned()",
134+
snippet_with_applicability(cx, root, "..", &mut applicability)
135+
),
136+
applicability,
137+
)
138+
}

tests/ui/map_clone.fixed

+12
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,23 @@
11
// run-rustfix
22
#![warn(clippy::all, clippy::pedantic)]
33
#![allow(clippy::iter_cloned_collect)]
4+
#![allow(clippy::clone_on_copy)]
45
#![allow(clippy::missing_docs_in_private_items)]
56

67
fn main() {
78
let _: Vec<i8> = vec![5_i8; 6].iter().cloned().collect();
89
let _: Vec<String> = vec![String::new()].iter().cloned().collect();
910
let _: Vec<u32> = vec![42, 43].iter().cloned().collect();
1011
let _: Option<u64> = Some(Box::new(16)).map(|b| *b);
12+
13+
// Don't lint these
14+
let v = vec![5_i8; 6];
15+
let a = 0;
16+
let b = &a;
17+
let _ = v.iter().map(|_x| *b);
18+
let _ = v.iter().map(|_x| a.clone());
19+
let _ = v.iter().map(|&_x| a);
20+
21+
// Issue #498
22+
let _ = std::env::args();
1123
}

tests/ui/map_clone.rs

+12
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,23 @@
11
// run-rustfix
22
#![warn(clippy::all, clippy::pedantic)]
33
#![allow(clippy::iter_cloned_collect)]
4+
#![allow(clippy::clone_on_copy)]
45
#![allow(clippy::missing_docs_in_private_items)]
56

67
fn main() {
78
let _: Vec<i8> = vec![5_i8; 6].iter().map(|x| *x).collect();
89
let _: Vec<String> = vec![String::new()].iter().map(|x| x.clone()).collect();
910
let _: Vec<u32> = vec![42, 43].iter().map(|&x| x).collect();
1011
let _: Option<u64> = Some(Box::new(16)).map(|b| *b);
12+
13+
// Don't lint these
14+
let v = vec![5_i8; 6];
15+
let a = 0;
16+
let b = &a;
17+
let _ = v.iter().map(|_x| *b);
18+
let _ = v.iter().map(|_x| a.clone());
19+
let _ = v.iter().map(|&_x| a);
20+
21+
// Issue #498
22+
let _ = std::env::args().map(|v| v.clone());
1123
}

tests/ui/map_clone.stderr

+10-4
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,28 @@
11
error: You are using an explicit closure for cloning elements
2-
--> $DIR/map_clone.rs:7:22
2+
--> $DIR/map_clone.rs:8:22
33
|
44
LL | let _: Vec<i8> = vec![5_i8; 6].iter().map(|x| *x).collect();
55
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `cloned` method: `vec![5_i8; 6].iter().cloned()`
66
|
77
= note: `-D clippy::map-clone` implied by `-D warnings`
88

99
error: You are using an explicit closure for cloning elements
10-
--> $DIR/map_clone.rs:8:26
10+
--> $DIR/map_clone.rs:9:26
1111
|
1212
LL | let _: Vec<String> = vec![String::new()].iter().map(|x| x.clone()).collect();
1313
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `cloned` method: `vec![String::new()].iter().cloned()`
1414

1515
error: You are using an explicit closure for cloning elements
16-
--> $DIR/map_clone.rs:9:23
16+
--> $DIR/map_clone.rs:10:23
1717
|
1818
LL | let _: Vec<u32> = vec![42, 43].iter().map(|&x| x).collect();
1919
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `cloned` method: `vec![42, 43].iter().cloned()`
2020

21-
error: aborting due to 3 previous errors
21+
error: You are needlessly cloning iterator elements
22+
--> $DIR/map_clone.rs:22:29
23+
|
24+
LL | let _ = std::env::args().map(|v| v.clone());
25+
| ^^^^^^^^^^^^^^^^^^^ help: Remove the map call
26+
27+
error: aborting due to 4 previous errors
2228

0 commit comments

Comments
 (0)