diff options
| author | bors <bors@rust-lang.org> | 2024-01-10 01:13:42 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2024-01-10 01:13:42 +0000 |
| commit | e89968425495ad99ce16bd00a6d7a3f63648271b (patch) | |
| tree | 7e8ac38b3409cfdbd7601d86911cbd5da33de8f3 | |
| parent | 3b8323d79024efb4ed4664faa4f29a1a7117177a (diff) | |
| parent | f7ef9a6f1fd1ea4126ec1e9173b23baa8449906c (diff) | |
| download | rust-e89968425495ad99ce16bd00a6d7a3f63648271b.tar.gz rust-e89968425495ad99ce16bd00a6d7a3f63648271b.zip | |
Auto merge of #12105 - GuillaumeGomez:useless-asf-extension, r=llogiq
Extend `useless_asref` lint on `map(clone)` If you have code like: ```rust Some(String::new()).as_ref().map(Clone::clone) ``` the `as_ref` call is unneeded. Interestingly enough, this lint and `map_clone` are starting to share a same "space" where both lints warn about different things for the same code. Not sure what's the policy about such cases though... r? `@llogiq` changelog: Extend `useless_asref` lint on `map(clone)`
| -rw-r--r-- | clippy_lints/src/methods/map_clone.rs | 31 | ||||
| -rw-r--r-- | clippy_lints/src/methods/useless_asref.rs | 123 | ||||
| -rw-r--r-- | tests/ui/map_clone.fixed | 21 | ||||
| -rw-r--r-- | tests/ui/map_clone.rs | 21 | ||||
| -rw-r--r-- | tests/ui/map_clone.stderr | 44 | ||||
| -rw-r--r-- | tests/ui/useless_asref.fixed | 14 | ||||
| -rw-r--r-- | tests/ui/useless_asref.rs | 14 | ||||
| -rw-r--r-- | tests/ui/useless_asref.stderr | 47 |
8 files changed, 265 insertions, 50 deletions
diff --git a/clippy_lints/src/methods/map_clone.rs b/clippy_lints/src/methods/map_clone.rs index 652d2b80081..f9f636bbbf7 100644 --- a/clippy_lints/src/methods/map_clone.rs +++ b/clippy_lints/src/methods/map_clone.rs @@ -5,6 +5,7 @@ use clippy_utils::ty::{is_copy, is_type_diagnostic_item}; use clippy_utils::{is_diag_trait_item, match_def_path, paths, peel_blocks}; use rustc_errors::Applicability; use rustc_hir as hir; +use rustc_hir::def_id::DefId; use rustc_lint::LateContext; use rustc_middle::mir::Mutability; use rustc_middle::ty; @@ -14,11 +15,35 @@ use rustc_span::{sym, Span}; use super::MAP_CLONE; +// If this `map` is called on an `Option` or a `Result` and the previous call is `as_ref`, we don't +// run this lint because it would overlap with `useless_asref` which provides a better suggestion +// in this case. +fn should_run_lint(cx: &LateContext<'_>, e: &hir::Expr<'_>, method_id: DefId) -> bool { + if is_diag_trait_item(cx, method_id, sym::Iterator) { + return true; + } + // We check if it's an `Option` or a `Result`. + if let Some(id) = cx.tcx.impl_of_method(method_id) { + let identity = cx.tcx.type_of(id).instantiate_identity(); + if !is_type_diagnostic_item(cx, identity, sym::Option) && !is_type_diagnostic_item(cx, identity, sym::Result) { + return false; + } + } else { + return false; + } + // We check if the previous method call is `as_ref`. + if let hir::ExprKind::MethodCall(path1, receiver, _, _) = &e.kind + && let hir::ExprKind::MethodCall(path2, _, _, _) = &receiver.kind + { + return path2.ident.name != sym::as_ref || path1.ident.name != sym::map; + } + + true +} + pub(super) fn check(cx: &LateContext<'_>, e: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>, msrv: &Msrv) { if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id) - && (cx.tcx.impl_of_method(method_id).map_or(false, |id| { - is_type_diagnostic_item(cx, cx.tcx.type_of(id).instantiate_identity(), sym::Option) - }) || is_diag_trait_item(cx, method_id, sym::Iterator)) + && should_run_lint(cx, e, method_id) { match arg.kind { hir::ExprKind::Closure(&hir::Closure { body, .. }) => { diff --git a/clippy_lints/src/methods/useless_asref.rs b/clippy_lints/src/methods/useless_asref.rs index 84ee64e88a6..66727e5a29d 100644 --- a/clippy_lints/src/methods/useless_asref.rs +++ b/clippy_lints/src/methods/useless_asref.rs @@ -1,19 +1,52 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::walk_ptrs_ty_depth; -use clippy_utils::{get_parent_expr, is_trait_method}; +use clippy_utils::{get_parent_expr, is_diag_trait_item, match_def_path, paths, peel_blocks}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; -use rustc_span::sym; +use rustc_middle::ty::adjustment::Adjust; +use rustc_middle::ty::{Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor}; +use rustc_span::{sym, Span}; + +use core::ops::ControlFlow; use super::USELESS_ASREF; +/// Returns the first type inside the `Option`/`Result` type passed as argument. +fn get_enum_ty(enum_ty: Ty<'_>) -> Option<Ty<'_>> { + struct ContainsTyVisitor { + level: usize, + } + + impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ContainsTyVisitor { + type BreakTy = Ty<'tcx>; + + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { + self.level += 1; + if self.level == 1 { + t.super_visit_with(self) + } else { + ControlFlow::Break(t) + } + } + } + + match enum_ty.visit_with(&mut ContainsTyVisitor { level: 0 }) { + ControlFlow::Break(ty) => Some(ty), + ControlFlow::Continue(()) => None, + } +} + /// Checks for the `USELESS_ASREF` lint. pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, recvr: &hir::Expr<'_>) { // when we get here, we've already checked that the call name is "as_ref" or "as_mut" // check if the call is to the actual `AsRef` or `AsMut` trait - if is_trait_method(cx, expr, sym::AsRef) || is_trait_method(cx, expr, sym::AsMut) { + let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) else { + return; + }; + + if is_diag_trait_item(cx, def_id, sym::AsRef) || is_diag_trait_item(cx, def_id, sym::AsMut) { // check if the type after `as_ref` or `as_mut` is the same as before let rcv_ty = cx.typeck_results().expr_ty(recvr); let res_ty = cx.typeck_results().expr_ty(expr); @@ -39,5 +72,89 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, applicability, ); } + } else if match_def_path(cx, def_id, &["core", "option", "Option", call_name]) + || match_def_path(cx, def_id, &["core", "result", "Result", call_name]) + { + let rcv_ty = cx.typeck_results().expr_ty(recvr).peel_refs(); + let res_ty = cx.typeck_results().expr_ty(expr).peel_refs(); + + if let Some(rcv_ty) = get_enum_ty(rcv_ty) + && let Some(res_ty) = get_enum_ty(res_ty) + // If the only thing the `as_mut`/`as_ref` call is doing is adding references and not + // changing the type, then we can move forward. + && rcv_ty.peel_refs() == res_ty.peel_refs() + && let Some(parent) = get_parent_expr(cx, expr) + && let hir::ExprKind::MethodCall(segment, _, args, _) = parent.kind + && segment.ident.span != expr.span + // We check that the called method name is `map`. + && segment.ident.name == sym::map + // And that it only has one argument. + && let [arg] = args + && is_calling_clone(cx, arg) + { + lint_as_ref_clone(cx, expr.span.with_hi(parent.span.hi()), recvr, call_name); + } + } +} + +fn check_qpath(cx: &LateContext<'_>, qpath: hir::QPath<'_>, hir_id: hir::HirId) -> bool { + // We check it's calling the `clone` method of the `Clone` trait. + if let Some(path_def_id) = cx.qpath_res(&qpath, hir_id).opt_def_id() { + match_def_path(cx, path_def_id, &paths::CLONE_TRAIT_METHOD) + } else { + false } } + +fn is_calling_clone(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool { + match arg.kind { + hir::ExprKind::Closure(&hir::Closure { body, .. }) => { + // If it's a closure, we need to check what is called. + let closure_body = cx.tcx.hir().body(body); + let closure_expr = peel_blocks(closure_body.value); + match closure_expr.kind { + hir::ExprKind::MethodCall(method, obj, [], _) => { + if method.ident.name == sym::clone + && let Some(fn_id) = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id) + && let Some(trait_id) = cx.tcx.trait_of_item(fn_id) + // We check it's the `Clone` trait. + && cx.tcx.lang_items().clone_trait().map_or(false, |id| id == trait_id) + // no autoderefs + && !cx.typeck_results().expr_adjustments(obj).iter() + .any(|a| matches!(a.kind, Adjust::Deref(Some(..)))) + { + true + } else { + false + } + }, + hir::ExprKind::Call(call, [_]) => { + if let hir::ExprKind::Path(qpath) = call.kind { + check_qpath(cx, qpath, call.hir_id) + } else { + false + } + }, + _ => false, + } + }, + hir::ExprKind::Path(qpath) => check_qpath(cx, qpath, arg.hir_id), + _ => false, + } +} + +fn lint_as_ref_clone(cx: &LateContext<'_>, span: Span, recvr: &hir::Expr<'_>, call_name: &str) { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + USELESS_ASREF, + span, + &format!("this call to `{call_name}.map(...)` does nothing"), + "try", + format!( + "{}.clone()", + snippet_with_applicability(cx, recvr.span, "..", &mut applicability) + ), + applicability, + ); +} diff --git a/tests/ui/map_clone.fixed b/tests/ui/map_clone.fixed index b7cf12c1a84..08b155a1aea 100644 --- a/tests/ui/map_clone.fixed +++ b/tests/ui/map_clone.fixed @@ -5,6 +5,7 @@ clippy::many_single_char_names, clippy::redundant_clone, clippy::redundant_closure, + clippy::useless_asref, clippy::useless_vec )] @@ -63,6 +64,24 @@ fn main() { } let x = Some(String::new()); - let y = x.as_ref().cloned(); + let x = x.as_ref(); // We do this to prevent triggering the `useless_asref` lint. + let y = x.cloned(); //~^ ERROR: you are explicitly cloning with `.map()` + let y = x.cloned(); + //~^ ERROR: you are explicitly cloning with `.map()` + let y = x.cloned(); + //~^ ERROR: you are explicitly cloning with `.map()` + + // Testing with `Result` now. + let x: Result<String, ()> = Ok(String::new()); + let x = x.as_ref(); // We do this to prevent triggering the `useless_asref` lint. + let y = x.cloned(); + //~^ ERROR: you are explicitly cloning with `.map()` + let y = x.cloned(); + + // We ensure that no warning is emitted here because `useless_asref` is taking over. + let x = Some(String::new()); + let y = x.as_ref().map(|x| String::clone(x)); + let x: Result<String, ()> = Ok(String::new()); + let y = x.as_ref().map(|x| String::clone(x)); } diff --git a/tests/ui/map_clone.rs b/tests/ui/map_clone.rs index 693914ec30a..901d9b278b4 100644 --- a/tests/ui/map_clone.rs +++ b/tests/ui/map_clone.rs @@ -5,6 +5,7 @@ clippy::many_single_char_names, clippy::redundant_clone, clippy::redundant_closure, + clippy::useless_asref, clippy::useless_vec )] @@ -63,6 +64,24 @@ fn main() { } let x = Some(String::new()); - let y = x.as_ref().map(|x| String::clone(x)); + let x = x.as_ref(); // We do this to prevent triggering the `useless_asref` lint. + let y = x.map(|x| String::clone(x)); + //~^ ERROR: you are explicitly cloning with `.map()` + let y = x.map(Clone::clone); + //~^ ERROR: you are explicitly cloning with `.map()` + let y = x.map(String::clone); + //~^ ERROR: you are explicitly cloning with `.map()` + + // Testing with `Result` now. + let x: Result<String, ()> = Ok(String::new()); + let x = x.as_ref(); // We do this to prevent triggering the `useless_asref` lint. + let y = x.map(|x| String::clone(x)); //~^ ERROR: you are explicitly cloning with `.map()` + let y = x.map(|x| String::clone(x)); + + // We ensure that no warning is emitted here because `useless_asref` is taking over. + let x = Some(String::new()); + let y = x.as_ref().map(|x| String::clone(x)); + let x: Result<String, ()> = Ok(String::new()); + let y = x.as_ref().map(|x| String::clone(x)); } diff --git a/tests/ui/map_clone.stderr b/tests/ui/map_clone.stderr index e6192a487ab..9d7e9317b58 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 copying elements - --> $DIR/map_clone.rs:12:22 + --> $DIR/map_clone.rs:13:22 | LL | let _: Vec<i8> = vec![5_i8; 6].iter().map(|x| *x).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `vec![5_i8; 6].iter().copied()` @@ -8,40 +8,64 @@ LL | let _: Vec<i8> = vec![5_i8; 6].iter().map(|x| *x).collect(); = help: to override `-D warnings` add `#[allow(clippy::map_clone)]` error: you are using an explicit closure for cloning elements - --> $DIR/map_clone.rs:13:26 + --> $DIR/map_clone.rs:14:26 | LL | let _: Vec<String> = 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 copying elements - --> $DIR/map_clone.rs:14:23 + --> $DIR/map_clone.rs:15:23 | LL | let _: Vec<u32> = vec![42, 43].iter().map(|&x| x).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `vec![42, 43].iter().copied()` error: you are using an explicit closure for copying elements - --> $DIR/map_clone.rs:16:26 + --> $DIR/map_clone.rs:17:26 | LL | let _: Option<u64> = Some(&16).map(|b| *b); | ^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `Some(&16).copied()` error: you are using an explicit closure for copying elements - --> $DIR/map_clone.rs:17:25 + --> $DIR/map_clone.rs:18:25 | LL | let _: Option<u8> = Some(&1).map(|x| x.clone()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `Some(&1).copied()` error: you are needlessly cloning iterator elements - --> $DIR/map_clone.rs:28:29 + --> $DIR/map_clone.rs:29:29 | LL | let _ = std::env::args().map(|v| v.clone()); | ^^^^^^^^^^^^^^^^^^^ help: remove the `map` call error: you are explicitly cloning with `.map()` - --> $DIR/map_clone.rs:66:13 + --> $DIR/map_clone.rs:68:13 | -LL | let y = x.as_ref().map(|x| String::clone(x)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `cloned` method: `x.as_ref().cloned()` +LL | let y = x.map(|x| String::clone(x)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `cloned` method: `x.cloned()` -error: aborting due to 7 previous errors +error: you are explicitly cloning with `.map()` + --> $DIR/map_clone.rs:70:13 + | +LL | let y = x.map(Clone::clone); + | ^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `cloned` method: `x.cloned()` + +error: you are explicitly cloning with `.map()` + --> $DIR/map_clone.rs:72:13 + | +LL | let y = x.map(String::clone); + | ^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `cloned` method: `x.cloned()` + +error: you are explicitly cloning with `.map()` + --> $DIR/map_clone.rs:78:13 + | +LL | let y = x.map(|x| String::clone(x)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `cloned` method: `x.cloned()` + +error: you are explicitly cloning with `.map()` + --> $DIR/map_clone.rs:80:13 + | +LL | let y = x.map(|x| String::clone(x)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `cloned` method: `x.cloned()` + +error: aborting due to 11 previous errors diff --git a/tests/ui/useless_asref.fixed b/tests/ui/useless_asref.fixed index 9083ae2d0b8..88b95095bc0 100644 --- a/tests/ui/useless_asref.fixed +++ b/tests/ui/useless_asref.fixed @@ -2,7 +2,9 @@ #![allow( clippy::explicit_auto_deref, clippy::uninlined_format_args, - clippy::needless_pass_by_ref_mut + clippy::map_clone, + clippy::needless_pass_by_ref_mut, + clippy::redundant_closure )] use std::fmt::Debug; @@ -134,10 +136,12 @@ fn generic_ok<U: AsMut<T> + AsRef<T> + ?Sized, T: Debug + ?Sized>(mru: &mut U) { fn foo() { let x = Some(String::new()); - let y = x.as_ref().cloned(); - //~^ ERROR: you are explicitly cloning with `.map()` - let y = x.as_ref().cloned(); - //~^ ERROR: you are explicitly cloning with `.map()` + let z = x.clone(); + //~^ ERROR: this call to `as_ref.map(...)` does nothing + let z = x.clone(); + //~^ ERROR: this call to `as_ref.map(...)` does nothing + let z = x.clone(); + //~^ ERROR: this call to `as_ref.map(...)` does nothing } fn main() { diff --git a/tests/ui/useless_asref.rs b/tests/ui/useless_asref.rs index 65f361dc2ae..504dc1f5cbf 100644 --- a/tests/ui/useless_asref.rs +++ b/tests/ui/useless_asref.rs @@ -2,7 +2,9 @@ #![allow( clippy::explicit_auto_deref, clippy::uninlined_format_args, - clippy::needless_pass_by_ref_mut + clippy::map_clone, + clippy::needless_pass_by_ref_mut, + clippy::redundant_closure )] use std::fmt::Debug; @@ -134,10 +136,12 @@ fn generic_ok<U: AsMut<T> + AsRef<T> + ?Sized, T: Debug + ?Sized>(mru: &mut U) { fn foo() { let x = Some(String::new()); - let y = x.as_ref().map(Clone::clone); - //~^ ERROR: you are explicitly cloning with `.map()` - let y = x.as_ref().map(String::clone); - //~^ ERROR: you are explicitly cloning with `.map()` + let z = x.as_ref().map(String::clone); + //~^ ERROR: this call to `as_ref.map(...)` does nothing + let z = x.as_ref().map(|z| z.clone()); + //~^ ERROR: this call to `as_ref.map(...)` does nothing + let z = x.as_ref().map(|z| String::clone(z)); + //~^ ERROR: this call to `as_ref.map(...)` does nothing } fn main() { diff --git a/tests/ui/useless_asref.stderr b/tests/ui/useless_asref.stderr index e91a9db4e3d..deb5d90f2f6 100644 --- a/tests/ui/useless_asref.stderr +++ b/tests/ui/useless_asref.stderr @@ -1,5 +1,5 @@ error: this call to `as_ref` does nothing - --> $DIR/useless_asref.rs:46:18 + --> $DIR/useless_asref.rs:48:18 | LL | foo_rstr(rstr.as_ref()); | ^^^^^^^^^^^^^ help: try: `rstr` @@ -11,79 +11,82 @@ LL | #![deny(clippy::useless_asref)] | ^^^^^^^^^^^^^^^^^^^^^ error: this call to `as_ref` does nothing - --> $DIR/useless_asref.rs:48:20 + --> $DIR/useless_asref.rs:50:20 | LL | foo_rslice(rslice.as_ref()); | ^^^^^^^^^^^^^^^ help: try: `rslice` error: this call to `as_mut` does nothing - --> $DIR/useless_asref.rs:52:21 + --> $DIR/useless_asref.rs:54:21 | LL | foo_mrslice(mrslice.as_mut()); | ^^^^^^^^^^^^^^^^ help: try: `mrslice` error: this call to `as_ref` does nothing - --> $DIR/useless_asref.rs:54:20 + --> $DIR/useless_asref.rs:56:20 | LL | foo_rslice(mrslice.as_ref()); | ^^^^^^^^^^^^^^^^ help: try: `mrslice` error: this call to `as_ref` does nothing - --> $DIR/useless_asref.rs:61:20 + --> $DIR/useless_asref.rs:63:20 | LL | foo_rslice(rrrrrslice.as_ref()); | ^^^^^^^^^^^^^^^^^^^ help: try: `rrrrrslice` error: this call to `as_ref` does nothing - --> $DIR/useless_asref.rs:63:18 + --> $DIR/useless_asref.rs:65:18 | LL | foo_rstr(rrrrrstr.as_ref()); | ^^^^^^^^^^^^^^^^^ help: try: `rrrrrstr` error: this call to `as_mut` does nothing - --> $DIR/useless_asref.rs:68:21 + --> $DIR/useless_asref.rs:70:21 | LL | foo_mrslice(mrrrrrslice.as_mut()); | ^^^^^^^^^^^^^^^^^^^^ help: try: `mrrrrrslice` error: this call to `as_ref` does nothing - --> $DIR/useless_asref.rs:70:20 + --> $DIR/useless_asref.rs:72:20 | LL | foo_rslice(mrrrrrslice.as_ref()); | ^^^^^^^^^^^^^^^^^^^^ help: try: `mrrrrrslice` error: this call to `as_ref` does nothing - --> $DIR/useless_asref.rs:74:16 + --> $DIR/useless_asref.rs:76:16 | LL | foo_rrrrmr((&&&&MoreRef).as_ref()); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `(&&&&MoreRef)` error: this call to `as_mut` does nothing - --> $DIR/useless_asref.rs:124:13 + --> $DIR/useless_asref.rs:126:13 | LL | foo_mrt(mrt.as_mut()); | ^^^^^^^^^^^^ help: try: `mrt` error: this call to `as_ref` does nothing - --> $DIR/useless_asref.rs:126:12 + --> $DIR/useless_asref.rs:128:12 | LL | foo_rt(mrt.as_ref()); | ^^^^^^^^^^^^ help: try: `mrt` -error: you are explicitly cloning with `.map()` - --> $DIR/useless_asref.rs:137:13 +error: this call to `as_ref.map(...)` does nothing + --> $DIR/useless_asref.rs:139:13 | -LL | let y = x.as_ref().map(Clone::clone); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `cloned` method: `x.as_ref().cloned()` +LL | let z = x.as_ref().map(String::clone); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `x.clone()` + +error: this call to `as_ref.map(...)` does nothing + --> $DIR/useless_asref.rs:141:13 | - = note: `-D clippy::map-clone` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::map_clone)]` +LL | let z = x.as_ref().map(|z| z.clone()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `x.clone()` -error: you are explicitly cloning with `.map()` - --> $DIR/useless_asref.rs:139:13 +error: this call to `as_ref.map(...)` does nothing + --> $DIR/useless_asref.rs:143:13 | -LL | let y = x.as_ref().map(String::clone); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `cloned` method: `x.as_ref().cloned()` +LL | let z = x.as_ref().map(|z| String::clone(z)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `x.clone()` -error: aborting due to 13 previous errors +error: aborting due to 14 previous errors |
