diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs index 1c7f372af6b5..7fad43029aca 100644 --- a/clippy_dev/src/lib.rs +++ b/clippy_dev/src/lib.rs @@ -47,7 +47,7 @@ impl Lint { name: name.to_lowercase(), group: group.to_string(), desc: NL_ESCAPE_RE.replace(&desc.replace("\\\"", "\""), "").to_string(), - deprecation: deprecation.map(|d| d.to_string()), + deprecation: deprecation.map(std::string::ToString::to_string), module: module.to_string(), } } @@ -178,7 +178,7 @@ fn lint_files() -> impl Iterator { // Otherwise we would not collect all the lints, for example in `clippy_lints/src/methods/`. WalkDir::new("../clippy_lints/src") .into_iter() - .filter_map(|f| f.ok()) + .filter_map(std::result::Result::ok) .filter(|f| f.path().extension() == Some(OsStr::new("rs"))) } diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 89dbba56130a..a72944bbe028 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -326,7 +326,7 @@ fn check_clippy_lint_names(cx: &LateContext<'_, '_>, items: &[NestedMetaItem]) { lint.span, &format!("unknown clippy lint: clippy::{}", name), |db| { - if name.as_str().chars().any(|c| c.is_uppercase()) { + if name.as_str().chars().any(char::is_uppercase) { let name_lower = name.as_str().to_lowercase(); match lint_store.check_lint_name( &name_lower, diff --git a/clippy_lints/src/cargo_common_metadata.rs b/clippy_lints/src/cargo_common_metadata.rs index 124b11cc78c6..1d37c03ff454 100644 --- a/clippy_lints/src/cargo_common_metadata.rs +++ b/clippy_lints/src/cargo_common_metadata.rs @@ -53,7 +53,7 @@ fn is_empty_str(value: &Option) -> bool { fn is_empty_vec(value: &[String]) -> bool { // This works because empty iterators return true - value.iter().all(|v| v.is_empty()) + value.iter().all(std::string::String::is_empty) } pub struct Pass; diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 4dbb390cd504..83aca243275b 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_adjusted, iter_input_pats, snippet_opt, span_lint_and_then}; +use crate::utils::{is_adjusted, iter_input_pats, snippet_opt, span_lint_and_then, type_is_unsafe_function}; +use if_chain::if_chain; use rustc::hir::*; use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass}; use rustc::ty; @@ -59,56 +60,136 @@ fn check_closure(cx: &LateContext<'_, '_>, expr: &Expr) { if let ExprKind::Closure(_, ref decl, eid, _, _) = expr.node { let body = cx.tcx.hir().body(eid); let ex = &body.value; - if let ExprKind::Call(ref caller, ref args) = ex.node { - if args.len() != decl.inputs.len() { - // Not the same number of arguments, there - // is no way the closure is the same as the function - return; - } - if is_adjusted(cx, ex) || args.iter().any(|arg| is_adjusted(cx, arg)) { - // Are the expression or the arguments type-adjusted? Then we need the closure - return; - } + + if_chain!( + if let ExprKind::Call(ref caller, ref args) = ex.node; + + // Not the same number of arguments, there is no way the closure is the same as the function return; + if args.len() == decl.inputs.len(); + + // Are the expression or the arguments type-adjusted? Then we need the closure + if !(is_adjusted(cx, ex) || args.iter().any(|arg| is_adjusted(cx, arg))); + let fn_ty = cx.tables.expr_ty(caller); - match fn_ty.sty { - // Is it an unsafe function? They don't implement the closure traits - ty::FnDef(..) | ty::FnPtr(_) => { - let sig = fn_ty.fn_sig(cx.tcx); - if sig.skip_binder().unsafety == Unsafety::Unsafe || sig.skip_binder().output().sty == ty::Never { - return; + if !type_is_unsafe_function(cx, fn_ty); + + if compare_inputs(&mut iter_input_pats(decl, body), &mut args.into_iter()); + + then { + span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure found", |db| { + if let Some(snippet) = snippet_opt(cx, caller.span) { + db.span_suggestion( + expr.span, + "remove closure as shown", + snippet, + Applicability::MachineApplicable, + ); } - }, - _ => (), + }); } - for (a1, a2) in iter_input_pats(decl, body).zip(args) { - if let PatKind::Binding(.., ident, _) = a1.pat.node { - // XXXManishearth Should I be checking the binding mode here? - if let ExprKind::Path(QPath::Resolved(None, ref p)) = a2.node { - if p.segments.len() != 1 { - // If it's a proper path, it can't be a local variable - return; - } - if p.segments[0].ident.name != ident.name { - // The two idents should be the same - return; - } - } else { - return; - } - } else { - return; - } - } - span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure found", |db| { - if let Some(snippet) = snippet_opt(cx, caller.span) { + ); + + if_chain!( + if let ExprKind::MethodCall(ref path, _, ref args) = ex.node; + + // Not the same number of arguments, there is no way the closure is the same as the function return; + if args.len() == decl.inputs.len(); + + // Are the expression or the arguments type-adjusted? Then we need the closure + if !(is_adjusted(cx, ex) || args.iter().skip(1).any(|arg| is_adjusted(cx, arg))); + + let method_def_id = cx.tables.type_dependent_defs()[ex.hir_id].def_id(); + if !type_is_unsafe_function(cx, cx.tcx.type_of(method_def_id)); + + if compare_inputs(&mut iter_input_pats(decl, body), &mut args.into_iter()); + + if let Some(name) = get_ufcs_type_name(cx, method_def_id, &args[0]); + + then { + span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure found", |db| { db.span_suggestion( expr.span, "remove closure as shown", - snippet, + format!("{}::{}", name, path.ident.name), Applicability::MachineApplicable, ); - } - }); - } + }); + } + ); } } + +/// Tries to determine the type for universal function call to be used instead of the closure +fn get_ufcs_type_name( + cx: &LateContext<'_, '_>, + method_def_id: def_id::DefId, + self_arg: &Expr, +) -> std::option::Option { + let expected_type_of_self = &cx.tcx.fn_sig(method_def_id).inputs_and_output().skip_binder()[0].sty; + let actual_type_of_self = &cx.tables.node_id_to_type(self_arg.hir_id).sty; + + if let Some(trait_id) = cx.tcx.trait_of_item(method_def_id) { + //if the method expectes &self, ufcs requires explicit borrowing so closure can't be removed + return match (expected_type_of_self, actual_type_of_self) { + (ty::Ref(_, _, _), ty::Ref(_, _, _)) => Some(cx.tcx.item_path_str(trait_id)), + (l, r) => match (l, r) { + (ty::Ref(_, _, _), _) | (_, ty::Ref(_, _, _)) => None, + (_, _) => Some(cx.tcx.item_path_str(trait_id)), + }, + }; + } + + cx.tcx.impl_of_method(method_def_id).and_then(|_| { + //a type may implicitly implement other types methods (e.g. Deref) + if match_types(expected_type_of_self, actual_type_of_self) { + return Some(get_type_name(cx, &actual_type_of_self)); + } + None + }) +} + +fn match_types(lhs: &ty::TyKind<'_>, rhs: &ty::TyKind<'_>) -> bool { + match (lhs, rhs) { + (ty::Bool, ty::Bool) + | (ty::Char, ty::Char) + | (ty::Int(_), ty::Int(_)) + | (ty::Uint(_), ty::Uint(_)) + | (ty::Str, ty::Str) => true, + (ty::Ref(_, t1, _), ty::Ref(_, t2, _)) + | (ty::Array(t1, _), ty::Array(t2, _)) + | (ty::Slice(t1), ty::Slice(t2)) => match_types(&t1.sty, &t2.sty), + (ty::Adt(def1, _), ty::Adt(def2, _)) => def1 == def2, + (_, _) => false, + } +} + +fn get_type_name(cx: &LateContext<'_, '_>, kind: &ty::TyKind<'_>) -> String { + match kind { + ty::Adt(t, _) => cx.tcx.item_path_str(t.did), + ty::Ref(_, r, _) => get_type_name(cx, &r.sty), + _ => kind.to_string(), + } +} + +fn compare_inputs(closure_inputs: &mut dyn Iterator, call_args: &mut dyn Iterator) -> bool { + for (closure_input, function_arg) in closure_inputs.zip(call_args) { + if let PatKind::Binding(_, _, _, ident, _) = closure_input.pat.node { + // XXXManishearth Should I be checking the binding mode here? + if let ExprKind::Path(QPath::Resolved(None, ref p)) = function_arg.node { + if p.segments.len() != 1 { + // If it's a proper path, it can't be a local variable + return false; + } + if p.segments[0].ident.name != ident.name { + // The two idents should be the same + return false; + } + } else { + return false; + } + } else { + return false; + } + } + true +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 2f114c065bb1..88224763f0c4 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -321,7 +321,7 @@ pub fn read_conf(reg: &rustc_plugin::Registry<'_>) -> Conf { } }); - let (conf, errors) = utils::conf::read(file_name.as_ref().map(|p| p.as_ref())); + let (conf, errors) = utils::conf::read(file_name.as_ref().map(std::convert::AsRef::as_ref)); // all conf errors are non-fatal, we just use the default conf in case of error for error in errors { diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 4ce2ae03b9fb..c9b27ef16152 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -829,7 +829,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass { let (method_names, arg_lists) = method_calls(expr, 2); let method_names: Vec = method_names.iter().map(|s| s.as_str()).collect(); - let method_names: Vec<&str> = method_names.iter().map(|s| s.as_ref()).collect(); + let method_names: Vec<&str> = method_names.iter().map(std::convert::AsRef::as_ref).collect(); match method_names.as_slice() { ["unwrap", "get"] => lint_get_unwrap(cx, expr, arg_lists[1], false), @@ -1695,7 +1695,7 @@ fn derefs_to_slice(cx: &LateContext<'_, '_>, expr: &hir::Expr, ty: Ty<'_>) -> Op if let hir::ExprKind::MethodCall(ref path, _, ref args) = expr.node { if path.ident.name == "iter" && may_slice(cx, cx.tables.expr_ty(&args[0])) { - sugg::Sugg::hir_opt(cx, &args[0]).map(|sugg| sugg.addr()) + sugg::Sugg::hir_opt(cx, &args[0]).map(sugg::Sugg::addr) } else { None } diff --git a/clippy_lints/src/non_expressive_names.rs b/clippy_lints/src/non_expressive_names.rs index fbf60db28ee4..b1cd1910f5b1 100644 --- a/clippy_lints/src/non_expressive_names.rs +++ b/clippy_lints/src/non_expressive_names.rs @@ -241,7 +241,7 @@ impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> { // or too many chars differ (x_foo, y_boo) or (xfoo, yboo) continue; } - split_at = interned_name.chars().next().map(|c| c.len_utf8()); + split_at = interned_name.chars().next().map(char::len_utf8); } } span_lint_and_then( diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index b0b4394ebb88..ff802dbb3a8a 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -14,7 +14,7 @@ use toml; pub fn file_from_args( args: &[source_map::Spanned], ) -> Result, (&'static str, source_map::Span)> { - for arg in args.iter().filter_map(|a| a.meta_item()) { + for arg in args.iter().filter_map(syntax::source_map::Spanned::meta_item) { if arg.name() == "conf_file" { return match arg.node { ast::MetaItemKind::Word | ast::MetaItemKind::List(_) => { diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index a8b6a756b78b..b5221bca0073 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -142,7 +142,10 @@ pub fn match_def_path(tcx: TyCtxt<'_, '_, '_>, def_id: DefId, path: &[&str]) -> pub fn get_def_path(tcx: TyCtxt<'_, '_, '_>, def_id: DefId) -> Vec<&'static str> { let mut apb = AbsolutePathBuffer { names: vec![] }; tcx.push_item_path(&mut apb, def_id, false); - apb.names.iter().map(|n| n.get()).collect() + apb.names + .iter() + .map(syntax_pos::symbol::LocalInternedString::get) + .collect() } /// Check if type is struct, enum or union type with given def path. diff --git a/rustc_tools_util/src/lib.rs b/rustc_tools_util/src/lib.rs index f13fa12ccca4..19c27754839e 100644 --- a/rustc_tools_util/src/lib.rs +++ b/rustc_tools_util/src/lib.rs @@ -9,8 +9,8 @@ macro_rules! get_version_info { let crate_name = String::from(env!("CARGO_PKG_NAME")); let host_compiler = $crate::get_channel(); - let commit_hash = option_env!("GIT_HASH").map(|s| s.to_string()); - let commit_date = option_env!("COMMIT_DATE").map(|s| s.to_string()); + let commit_hash = option_env!("GIT_HASH").map(str::to_string); + let commit_date = option_env!("COMMIT_DATE").map(str::to_string); VersionInfo { major, diff --git a/src/driver.rs b/src/driver.rs index fbff693f8870..34fc6fc7f9fb 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -47,7 +47,7 @@ fn arg_value<'a>( fn test_arg_value() { let args: Vec<_> = ["--bar=bar", "--foobar", "123", "--foo"] .iter() - .map(|s| s.to_string()) + .map(std::string::ToString::to_string) .collect(); assert_eq!(arg_value(None, "--foobar", |_| true), None); @@ -84,7 +84,7 @@ pub fn main() { let sys_root_arg = arg_value(&orig_args, "--sysroot", |_| true); let have_sys_root_arg = sys_root_arg.is_some(); let sys_root = sys_root_arg - .map(|s| s.to_string()) + .map(std::string::ToString::to_string) .or_else(|| std::env::var("SYSROOT").ok()) .or_else(|| { let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME")); diff --git a/tests/missing-test-files.rs b/tests/missing-test-files.rs index bd0cee756444..d87bb4be3c3f 100644 --- a/tests/missing-test-files.rs +++ b/tests/missing-test-files.rs @@ -32,7 +32,7 @@ fn explore_directory(dir: &Path) -> Vec { let mut missing_files: Vec = Vec::new(); let mut current_file = String::new(); let mut files: Vec = fs::read_dir(dir).unwrap().filter_map(Result::ok).collect(); - files.sort_by_key(|e| e.path()); + files.sort_by_key(std::fs::DirEntry::path); for entry in &files { let path = entry.path(); if path.is_dir() { diff --git a/tests/ui/eta.rs b/tests/ui/eta.rs index b39de4c15a43..6eeb093eae99 100644 --- a/tests/ui/eta.rs +++ b/tests/ui/eta.rs @@ -9,6 +9,8 @@ )] #![warn(clippy::redundant_closure, clippy::needless_borrow)] +use std::path::PathBuf; + fn main() { let a = Some(1u8).map(|a| foo(a)); meta(|a| foo(a)); @@ -28,6 +30,66 @@ fn main() { Some(vec![1i32, 2]).map(|v| -> Box<::std::ops::Deref> { Box::new(v) }); } +trait TestTrait { + fn trait_foo(self) -> bool; + fn trait_foo_ref(&self) -> bool; +} + +struct TestStruct<'a> { + some_ref: &'a i32, +} + +impl<'a> TestStruct<'a> { + fn foo(self) -> bool { + false + } + unsafe fn foo_unsafe(self) -> bool { + true + } +} + +impl<'a> TestTrait for TestStruct<'a> { + fn trait_foo(self) -> bool { + false + } + fn trait_foo_ref(&self) -> bool { + false + } +} + +impl<'a> std::ops::Deref for TestStruct<'a> { + type Target = char; + fn deref(&self) -> &char { + &'a' + } +} + +fn test_redundant_closures_containing_method_calls() { + let i = 10; + let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo()); + let e = Some(TestStruct { some_ref: &i }).map(TestStruct::foo); + let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo()); + let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo_ref()); + let e = Some(TestStruct { some_ref: &i }).map(TestTrait::trait_foo); + let e = Some(&mut vec![1, 2, 3]).map(|v| v.clear()); + let e = Some(&mut vec![1, 2, 3]).map(std::vec::Vec::clear); + unsafe { + let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo_unsafe()); + } + let e = Some("str").map(|s| s.to_string()); + let e = Some("str").map(str::to_string); + let e = Some('a').map(|s| s.to_uppercase()); + let e = Some('a').map(char::to_uppercase); + let e: std::vec::Vec = vec!['a', 'b', 'c'].iter().map(|c| c.len_utf8()).collect(); + let e: std::vec::Vec = vec!['a', 'b', 'c'].iter().map(|c| c.to_ascii_uppercase()).collect(); + let e: std::vec::Vec = vec!['a', 'b', 'c'].iter().map(char::to_ascii_uppercase).collect(); + let p = Some(PathBuf::new()); + let e = p.as_ref().and_then(|s| s.to_str()); + let c = Some(TestStruct { some_ref: &i }) + .as_ref() + .map(|c| c.to_ascii_uppercase()); +} + fn meta(f: F) where F: Fn(u8), diff --git a/tests/ui/eta.stderr b/tests/ui/eta.stderr index 218e46b40a8b..5f56cd7912a7 100644 --- a/tests/ui/eta.stderr +++ b/tests/ui/eta.stderr @@ -1,5 +1,5 @@ error: redundant closure found - --> $DIR/eta.rs:13:27 + --> $DIR/eta.rs:15:27 | LL | let a = Some(1u8).map(|a| foo(a)); | ^^^^^^^^^^ help: remove closure as shown: `foo` @@ -7,19 +7,19 @@ LL | let a = Some(1u8).map(|a| foo(a)); = note: `-D clippy::redundant-closure` implied by `-D warnings` error: redundant closure found - --> $DIR/eta.rs:14:10 + --> $DIR/eta.rs:16:10 | LL | meta(|a| foo(a)); | ^^^^^^^^^^ help: remove closure as shown: `foo` error: redundant closure found - --> $DIR/eta.rs:15:27 + --> $DIR/eta.rs:17:27 | LL | let c = Some(1u8).map(|a| {1+2; foo}(a)); | ^^^^^^^^^^^^^^^^^ help: remove closure as shown: `{1+2; foo}` error: this expression borrows a reference that is immediately dereferenced by the compiler - --> $DIR/eta.rs:17:21 + --> $DIR/eta.rs:19:21 | LL | all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted | ^^^ help: change this to: `&2` @@ -27,10 +27,46 @@ LL | all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted = note: `-D clippy::needless-borrow` implied by `-D warnings` error: redundant closure found - --> $DIR/eta.rs:24:27 + --> $DIR/eta.rs:26:27 | LL | let e = Some(1u8).map(|a| generic(a)); | ^^^^^^^^^^^^^^ help: remove closure as shown: `generic` -error: aborting due to 5 previous errors +error: redundant closure found + --> $DIR/eta.rs:69:51 + | +LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo()); + | ^^^^^^^^^^^ help: remove closure as shown: `TestStruct::foo` + +error: redundant closure found + --> $DIR/eta.rs:71:51 + | +LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo()); + | ^^^^^^^^^^^^^^^^^ help: remove closure as shown: `TestTrait::trait_foo` + +error: redundant closure found + --> $DIR/eta.rs:74:42 + | +LL | let e = Some(&mut vec![1, 2, 3]).map(|v| v.clear()); + | ^^^^^^^^^^^^^ help: remove closure as shown: `std::vec::Vec::clear` + +error: redundant closure found + --> $DIR/eta.rs:79:29 + | +LL | let e = Some("str").map(|s| s.to_string()); + | ^^^^^^^^^^^^^^^^^ help: remove closure as shown: `std::string::ToString::to_string` + +error: redundant closure found + --> $DIR/eta.rs:81:27 + | +LL | let e = Some('a').map(|s| s.to_uppercase()); + | ^^^^^^^^^^^^^^^^^^^^ help: remove closure as shown: `char::to_uppercase` + +error: redundant closure found + --> $DIR/eta.rs:84:65 + | +LL | let e: std::vec::Vec = vec!['a', 'b', 'c'].iter().map(|c| c.to_ascii_uppercase()).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove closure as shown: `char::to_ascii_uppercase` + +error: aborting due to 11 previous errors diff --git a/tests/ui/map_clone.fixed b/tests/ui/map_clone.fixed index af417815ed1d..d804e838d5a6 100644 --- a/tests/ui/map_clone.fixed +++ b/tests/ui/map_clone.fixed @@ -3,6 +3,7 @@ #![allow(clippy::iter_cloned_collect)] #![allow(clippy::clone_on_copy)] #![allow(clippy::missing_docs_in_private_items)] +#![allow(clippy::redundant_closure)] fn main() { let _: Vec = vec![5_i8; 6].iter().cloned().collect(); diff --git a/tests/ui/map_clone.rs b/tests/ui/map_clone.rs index 7dd2ce302023..d98cd939d8cc 100644 --- a/tests/ui/map_clone.rs +++ b/tests/ui/map_clone.rs @@ -3,6 +3,7 @@ #![allow(clippy::iter_cloned_collect)] #![allow(clippy::clone_on_copy)] #![allow(clippy::missing_docs_in_private_items)] +#![allow(clippy::redundant_closure)] fn main() { let _: Vec = vec![5_i8; 6].iter().map(|x| *x).collect(); diff --git a/tests/ui/map_clone.stderr b/tests/ui/map_clone.stderr index 504f4a01a4cc..db7fa4f52fce 100644 --- a/tests/ui/map_clone.stderr +++ b/tests/ui/map_clone.stderr @@ -1,5 +1,5 @@ error: You are using an explicit closure for cloning elements - --> $DIR/map_clone.rs:8:22 + --> $DIR/map_clone.rs:9:22 | LL | let _: Vec = vec![5_i8; 6].iter().map(|x| *x).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `cloned` method: `vec![5_i8; 6].iter().cloned()` @@ -7,19 +7,19 @@ LL | let _: Vec = vec![5_i8; 6].iter().map(|x| *x).collect(); = note: `-D clippy::map-clone` implied by `-D warnings` error: You are using an explicit closure for cloning elements - --> $DIR/map_clone.rs:9:26 + --> $DIR/map_clone.rs:10:26 | LL | let _: Vec = vec![String::new()].iter().map(|x| x.clone()).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `cloned` method: `vec![String::new()].iter().cloned()` error: You are using an explicit closure for cloning elements - --> $DIR/map_clone.rs:10:23 + --> $DIR/map_clone.rs:11:23 | LL | let _: Vec = vec![42, 43].iter().map(|&x| x).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `cloned` method: `vec![42, 43].iter().cloned()` error: You are needlessly cloning iterator elements - --> $DIR/map_clone.rs:22:29 + --> $DIR/map_clone.rs:23:29 | LL | let _ = std::env::args().map(|v| v.clone()); | ^^^^^^^^^^^^^^^^^^^ help: Remove the map call