Skip to content

Commit ad93cb8

Browse files
committed
noop_method_call: remove nonlocal derive suggestion, add suggestion to derive on generic argument
1 parent 7cb0849 commit ad93cb8

File tree

6 files changed

+156
-15
lines changed

6 files changed

+156
-15
lines changed

compiler/rustc_lint/messages.ftl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -615,7 +615,7 @@ lint_non_upper_case_global = {$sort} `{$name}` should have an upper case name
615615
lint_noop_method_call = call to `.{$method}()` on a reference in this situation does nothing
616616
.suggestion = remove this redundant call
617617
.note = the type `{$orig_ty}` does not implement `{$trait_}`, so calling `{$method}` on `&{$orig_ty}` copies the reference, which does not do anything and can be removed
618-
.derive_suggestion = if you meant to clone `{$orig_ty}`, implement `Clone` for it
618+
.derive_suggestion = if you meant to clone `{$orig_ty}`, implement `Clone` for `{$non_clone_ty}`
619619
620620
lint_only_cast_u8_to_char = only `u8` can be cast into `char`
621621
.suggestion = use a `char` literal instead

compiler/rustc_lint/src/lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1346,6 +1346,7 @@ pub(crate) struct NoopMethodCallDiag<'a> {
13461346
pub method: Symbol,
13471347
pub orig_ty: Ty<'a>,
13481348
pub trait_: Symbol,
1349+
pub non_clone_ty: Ty<'a>,
13491350
#[suggestion(code = "", applicability = "machine-applicable")]
13501351
pub label: Span,
13511352
#[suggestion(

compiler/rustc_lint/src/noop_method_call.rs

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use rustc_hir::def::DefKind;
22
use rustc_hir::{Expr, ExprKind};
33
use rustc_middle::ty;
4+
use rustc_middle::ty::ClauseKind;
45
use rustc_middle::ty::adjustment::Adjust;
56
use rustc_session::{declare_lint, declare_lint_pass};
67
use rustc_span::sym;
@@ -124,14 +125,48 @@ impl<'tcx> LateLintPass<'tcx> for NoopMethodCall {
124125
let orig_ty = expr_ty.peel_refs();
125126

126127
if receiver_ty == expr_ty {
128+
let mut non_clone_ty = orig_ty;
129+
127130
let suggest_derive = match orig_ty.kind() {
128-
ty::Adt(def, _) => Some(cx.tcx.def_span(def.did()).shrink_to_lo()),
131+
ty::Adt(def, args) => {
132+
if def.did().is_local() {
133+
Some(cx.tcx.def_span(def.did()).shrink_to_lo())
134+
} else if let Some(trait_impl_id) = cx.tcx.impl_of_method(i.def_id()) {
135+
// If the type is generic over `T`, implements `Clone` if `T` does
136+
// and the concrete `T` is local, suggest deriving `Clone` for `T` rather than the type.
137+
let predicates = cx.tcx.predicates_of(trait_impl_id);
138+
139+
if predicates.predicates.into_iter().all(|&(predicate, _)| {
140+
let ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder()
141+
else {
142+
return true;
143+
};
144+
cx.tcx.is_diagnostic_item(sym::Clone, trait_predicate.trait_ref.def_id)
145+
}) {
146+
args.iter().find_map(|arg| {
147+
let ty = arg.as_type()?;
148+
let did = ty.ty_adt_def()?.did();
149+
if did.is_local() {
150+
non_clone_ty = ty;
151+
Some(cx.tcx.def_span(did))
152+
} else {
153+
None
154+
}
155+
})
156+
} else {
157+
None
158+
}
159+
} else {
160+
None
161+
}
162+
}
129163
_ => None,
130164
};
131165
cx.emit_span_lint(NOOP_METHOD_CALL, span, NoopMethodCallDiag {
132166
method: call.ident.name,
133167
orig_ty,
134168
trait_,
169+
non_clone_ty,
135170
label: span,
136171
suggest_derive,
137172
});
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
pub struct NotClone;
2+
3+
pub struct IsClone;
4+
5+
impl Clone for IsClone {
6+
fn clone(&self) -> Self {
7+
Self
8+
}
9+
}
10+
11+
pub struct ConditionalClone<T>(T);
12+
13+
impl<T: Clone> Clone for ConditionalClone<T> {
14+
fn clone(&self) -> Self {
15+
Self(self.0.clone())
16+
}
17+
}
18+
19+
pub struct DifferentlyConditionalClone<T>(T);
20+
21+
impl<T: Default> Clone for DifferentlyConditionalClone<T> {
22+
fn clone(&self) -> Self {
23+
Self(T::default())
24+
}
25+
}

tests/ui/lint/noop-method-call.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
//@ check-pass
2+
//@ aux-build:non_clone_types.rs
23

34
#![feature(rustc_attrs)]
45
#![allow(unused)]
56

7+
extern crate non_clone_types;
8+
use non_clone_types::*;
9+
610
use std::borrow::Borrow;
711
use std::ops::Deref;
812

@@ -61,3 +65,27 @@ impl Clone for DiagnosticClone {
6165
fn with_other_diagnostic_item(x: DiagnosticClone) {
6266
x.clone();
6367
}
68+
69+
fn with_foreign_type(v: &NotClone) {
70+
v.clone();
71+
//~^ WARN call to `.clone()` on a reference in this situation does nothing
72+
}
73+
74+
fn with_foreign_generic_type(v: &ConditionalClone<PlainType<u32>>) {
75+
v.clone();
76+
//~^ WARN call to `.clone()` on a reference in this situation does nothing
77+
}
78+
79+
fn with_only_foreign_types_1(v: &ConditionalClone<NotClone>) {
80+
v.clone();
81+
//~^ WARN call to `.clone()` on a reference in this situation does nothing
82+
}
83+
84+
fn with_only_foreign_types_2(v: &ConditionalClone<IsClone>) {
85+
v.clone();
86+
}
87+
88+
fn different_impl_bound(v: &DifferentlyConditionalClone<PlainType<u8>>) {
89+
v.clone();
90+
//~^ WARN call to `.clone()` on a reference in this situation does nothing
91+
}

tests/ui/lint/noop-method-call.stderr

Lines changed: 65 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
warning: call to `.clone()` on a reference in this situation does nothing
2-
--> $DIR/noop-method-call.rs:15:25
2+
--> $DIR/noop-method-call.rs:19:25
33
|
44
LL | let _ = &mut encoded.clone();
55
| ^^^^^^^^ help: remove this redundant call
@@ -8,15 +8,15 @@ LL | let _ = &mut encoded.clone();
88
= note: `#[warn(noop_method_call)]` on by default
99

1010
warning: call to `.clone()` on a reference in this situation does nothing
11-
--> $DIR/noop-method-call.rs:17:21
11+
--> $DIR/noop-method-call.rs:21:21
1212
|
1313
LL | let _ = &encoded.clone();
1414
| ^^^^^^^^ help: remove this redundant call
1515
|
1616
= note: the type `[u8]` does not implement `Clone`, so calling `clone` on `&[u8]` copies the reference, which does not do anything and can be removed
1717

1818
warning: call to `.clone()` on a reference in this situation does nothing
19-
--> $DIR/noop-method-call.rs:23:71
19+
--> $DIR/noop-method-call.rs:27:71
2020
|
2121
LL | let non_clone_type_ref_clone: &PlainType<u32> = non_clone_type_ref.clone();
2222
| ^^^^^^^^
@@ -27,14 +27,14 @@ help: remove this redundant call
2727
LL - let non_clone_type_ref_clone: &PlainType<u32> = non_clone_type_ref.clone();
2828
LL + let non_clone_type_ref_clone: &PlainType<u32> = non_clone_type_ref;
2929
|
30-
help: if you meant to clone `PlainType<u32>`, implement `Clone` for it
30+
help: if you meant to clone `PlainType<u32>`, implement `Clone` for `PlainType<u32>`
3131
|
3232
LL + #[derive(Clone)]
3333
LL | struct PlainType<T>(T);
3434
|
3535

3636
warning: call to `.deref()` on a reference in this situation does nothing
37-
--> $DIR/noop-method-call.rs:31:63
37+
--> $DIR/noop-method-call.rs:35:63
3838
|
3939
LL | let non_deref_type_deref: &PlainType<u32> = non_deref_type.deref();
4040
| ^^^^^^^^
@@ -45,14 +45,14 @@ help: remove this redundant call
4545
LL - let non_deref_type_deref: &PlainType<u32> = non_deref_type.deref();
4646
LL + let non_deref_type_deref: &PlainType<u32> = non_deref_type;
4747
|
48-
help: if you meant to clone `PlainType<u32>`, implement `Clone` for it
48+
help: if you meant to clone `PlainType<u32>`, implement `Clone` for `PlainType<u32>`
4949
|
5050
LL + #[derive(Clone)]
5151
LL | struct PlainType<T>(T);
5252
|
5353

5454
warning: call to `.borrow()` on a reference in this situation does nothing
55-
--> $DIR/noop-method-call.rs:35:66
55+
--> $DIR/noop-method-call.rs:39:66
5656
|
5757
LL | let non_borrow_type_borrow: &PlainType<u32> = non_borrow_type.borrow();
5858
| ^^^^^^^^^
@@ -63,14 +63,14 @@ help: remove this redundant call
6363
LL - let non_borrow_type_borrow: &PlainType<u32> = non_borrow_type.borrow();
6464
LL + let non_borrow_type_borrow: &PlainType<u32> = non_borrow_type;
6565
|
66-
help: if you meant to clone `PlainType<u32>`, implement `Clone` for it
66+
help: if you meant to clone `PlainType<u32>`, implement `Clone` for `PlainType<u32>`
6767
|
6868
LL + #[derive(Clone)]
6969
LL | struct PlainType<T>(T);
7070
|
7171

7272
warning: call to `.clone()` on a reference in this situation does nothing
73-
--> $DIR/noop-method-call.rs:44:19
73+
--> $DIR/noop-method-call.rs:48:19
7474
|
7575
LL | non_clone_type.clone();
7676
| ^^^^^^^^
@@ -81,14 +81,14 @@ help: remove this redundant call
8181
LL - non_clone_type.clone();
8282
LL + non_clone_type;
8383
|
84-
help: if you meant to clone `PlainType<T>`, implement `Clone` for it
84+
help: if you meant to clone `PlainType<T>`, implement `Clone` for `PlainType<T>`
8585
|
8686
LL + #[derive(Clone)]
8787
LL | struct PlainType<T>(T);
8888
|
8989

9090
warning: call to `.clone()` on a reference in this situation does nothing
91-
--> $DIR/noop-method-call.rs:49:19
91+
--> $DIR/noop-method-call.rs:53:19
9292
|
9393
LL | non_clone_type.clone();
9494
| ^^^^^^^^
@@ -99,11 +99,63 @@ help: remove this redundant call
9999
LL - non_clone_type.clone();
100100
LL + non_clone_type;
101101
|
102-
help: if you meant to clone `PlainType<u32>`, implement `Clone` for it
102+
help: if you meant to clone `PlainType<u32>`, implement `Clone` for `PlainType<u32>`
103103
|
104104
LL + #[derive(Clone)]
105105
LL | struct PlainType<T>(T);
106106
|
107107

108-
warning: 7 warnings emitted
108+
warning: call to `.clone()` on a reference in this situation does nothing
109+
--> $DIR/noop-method-call.rs:70:6
110+
|
111+
LL | v.clone();
112+
| ^^^^^^^^ help: remove this redundant call
113+
|
114+
= note: the type `non_clone_types::NotClone` does not implement `Clone`, so calling `clone` on `&non_clone_types::NotClone` copies the reference, which does not do anything and can be removed
115+
116+
warning: call to `.clone()` on a reference in this situation does nothing
117+
--> $DIR/noop-method-call.rs:75:6
118+
|
119+
LL | v.clone();
120+
| ^^^^^^^^
121+
|
122+
= note: the type `non_clone_types::ConditionalClone<PlainType<u32>>` does not implement `Clone`, so calling `clone` on `&non_clone_types::ConditionalClone<PlainType<u32>>` copies the reference, which does not do anything and can be removed
123+
help: remove this redundant call
124+
|
125+
LL - v.clone();
126+
LL + v;
127+
|
128+
help: if you meant to clone `non_clone_types::ConditionalClone<PlainType<u32>>`, implement `Clone` for `PlainType<u32>`
129+
|
130+
LL + #[derive(Clone)]
131+
LL | struct PlainType<T>(T);
132+
|
133+
134+
warning: call to `.clone()` on a reference in this situation does nothing
135+
--> $DIR/noop-method-call.rs:80:6
136+
|
137+
LL | v.clone();
138+
| ^^^^^^^^ help: remove this redundant call
139+
|
140+
= note: the type `non_clone_types::ConditionalClone<non_clone_types::NotClone>` does not implement `Clone`, so calling `clone` on `&non_clone_types::ConditionalClone<non_clone_types::NotClone>` copies the reference, which does not do anything and can be removed
141+
142+
warning: call to `.clone()` on a reference in this situation does nothing
143+
--> $DIR/noop-method-call.rs:89:6
144+
|
145+
LL | v.clone();
146+
| ^^^^^^^^
147+
|
148+
= note: the type `non_clone_types::DifferentlyConditionalClone<PlainType<u8>>` does not implement `Clone`, so calling `clone` on `&non_clone_types::DifferentlyConditionalClone<PlainType<u8>>` copies the reference, which does not do anything and can be removed
149+
help: remove this redundant call
150+
|
151+
LL - v.clone();
152+
LL + v;
153+
|
154+
help: if you meant to clone `non_clone_types::DifferentlyConditionalClone<PlainType<u8>>`, implement `Clone` for `PlainType<u8>`
155+
|
156+
LL + #[derive(Clone)]
157+
LL | struct PlainType<T>(T);
158+
|
159+
160+
warning: 11 warnings emitted
109161

0 commit comments

Comments
 (0)