Skip to content

Commit

Permalink
Auto merge of #12136 - y21:issue12135, r=Jarcho
Browse files Browse the repository at this point in the history
[`useless_asref`]: check that the clone receiver is the parameter

Fixes #12135

There was no check for the receiver of the `clone` call in the map closure. This makes sure that it's a path to the parameter.

changelog: [`useless_asref`]: check that the clone receiver is the closure parameter
  • Loading branch information
bors committed Jan 15, 2024
2 parents a71211d + be5707c commit ec4d027
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 6 deletions.
18 changes: 13 additions & 5 deletions clippy_lints/src/methods/useless_asref.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
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_diag_trait_item, match_def_path, paths, peel_blocks};
use clippy_utils::{
get_parent_expr, is_diag_trait_item, match_def_path, path_to_local_id, paths, peel_blocks, strip_pat_refs,
};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
Expand Down Expand Up @@ -108,9 +110,12 @@ fn check_qpath(cx: &LateContext<'_>, qpath: hir::QPath<'_>, hir_id: hir::HirId)

fn is_calling_clone(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool {
match arg.kind {
hir::ExprKind::Closure(&hir::Closure { body, .. }) => {
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);
if let closure_body = cx.tcx.hir().body(body)
&& let [param] = closure_body.params
&& let hir::PatKind::Binding(_, local_id, ..) = strip_pat_refs(param.pat).kind =>
{
let closure_expr = peel_blocks(closure_body.value);
match closure_expr.kind {
hir::ExprKind::MethodCall(method, obj, [], _) => {
Expand All @@ -122,14 +127,17 @@ fn is_calling_clone(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool {
// no autoderefs
&& !cx.typeck_results().expr_adjustments(obj).iter()
.any(|a| matches!(a.kind, Adjust::Deref(Some(..))))
&& path_to_local_id(obj, local_id)
{
true
} else {
false
}
},
hir::ExprKind::Call(call, [_]) => {
if let hir::ExprKind::Path(qpath) = call.kind {
hir::ExprKind::Call(call, [recv]) => {
if let hir::ExprKind::Path(qpath) = call.kind
&& path_to_local_id(recv, local_id)
{
check_qpath(cx, qpath, call.hir_id)
} else {
false
Expand Down
36 changes: 36 additions & 0 deletions tests/ui/useless_asref.fixed
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,42 @@ fn foo() {
//~^ ERROR: this call to `as_ref.map(...)` does nothing
}

mod issue12135 {
pub struct Struct {
field: Option<InnerStruct>,
}

#[derive(Clone)]
pub struct Foo;

#[derive(Clone)]
struct InnerStruct {
x: Foo,
}

impl InnerStruct {
fn method(&self) -> &Foo {
&self.x
}
}

pub fn f(x: &Struct) -> Option<Foo> {
x.field.clone();
//~^ ERROR: this call to `as_ref.map(...)` does nothing
x.field.clone();
//~^ ERROR: this call to `as_ref.map(...)` does nothing
x.field.clone();
//~^ ERROR: this call to `as_ref.map(...)` does nothing

// https://github.com/rust-lang/rust-clippy/pull/12136#discussion_r1451565223
#[allow(clippy::clone_on_copy)]
Some(1).clone();
//~^ ERROR: this call to `as_ref.map(...)` does nothing

x.field.as_ref().map(|v| v.method().clone())
}
}

fn main() {
not_ok();
ok();
Expand Down
36 changes: 36 additions & 0 deletions tests/ui/useless_asref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,42 @@ fn foo() {
//~^ ERROR: this call to `as_ref.map(...)` does nothing
}

mod issue12135 {
pub struct Struct {
field: Option<InnerStruct>,
}

#[derive(Clone)]
pub struct Foo;

#[derive(Clone)]
struct InnerStruct {
x: Foo,
}

impl InnerStruct {
fn method(&self) -> &Foo {
&self.x
}
}

pub fn f(x: &Struct) -> Option<Foo> {
x.field.as_ref().map(|v| v.clone());
//~^ ERROR: this call to `as_ref.map(...)` does nothing
x.field.as_ref().map(Clone::clone);
//~^ ERROR: this call to `as_ref.map(...)` does nothing
x.field.as_ref().map(|v| Clone::clone(v));
//~^ ERROR: this call to `as_ref.map(...)` does nothing

// https://github.com/rust-lang/rust-clippy/pull/12136#discussion_r1451565223
#[allow(clippy::clone_on_copy)]
Some(1).as_ref().map(|&x| x.clone());
//~^ ERROR: this call to `as_ref.map(...)` does nothing

x.field.as_ref().map(|v| v.method().clone())
}
}

fn main() {
not_ok();
ok();
Expand Down
26 changes: 25 additions & 1 deletion tests/ui/useless_asref.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -88,5 +88,29 @@ error: this call to `as_ref.map(...)` does nothing
LL | let z = x.as_ref().map(|z| String::clone(z));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `x.clone()`

error: aborting due to 14 previous errors
error: this call to `as_ref.map(...)` does nothing
--> $DIR/useless_asref.rs:167:9
|
LL | x.field.as_ref().map(|v| v.clone());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `x.field.clone()`

error: this call to `as_ref.map(...)` does nothing
--> $DIR/useless_asref.rs:169:9
|
LL | x.field.as_ref().map(Clone::clone);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `x.field.clone()`

error: this call to `as_ref.map(...)` does nothing
--> $DIR/useless_asref.rs:171:9
|
LL | x.field.as_ref().map(|v| Clone::clone(v));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `x.field.clone()`

error: this call to `as_ref.map(...)` does nothing
--> $DIR/useless_asref.rs:176:9
|
LL | Some(1).as_ref().map(|&x| x.clone());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(1).clone()`

error: aborting due to 18 previous errors

0 comments on commit ec4d027

Please sign in to comment.