1
1
use super :: implicit_clone:: is_clone_like;
2
+ use super :: unnecessary_iter_cloned:: { self , is_into_iter} ;
2
3
use clippy_utils:: diagnostics:: span_lint_and_sugg;
3
4
use clippy_utils:: source:: snippet_opt;
4
- use clippy_utils:: ty:: { implements_trait, is_copy, peel_mid_ty_refs} ;
5
- use clippy_utils:: { get_parent_expr, is_diag_item_method, is_diag_trait_item} ;
5
+ use clippy_utils:: ty:: { get_associated_type , get_iterator_item_ty , implements_trait, is_copy, peel_mid_ty_refs} ;
6
+ use clippy_utils:: { fn_def_id , get_parent_expr, is_diag_item_method, is_diag_trait_item} ;
6
7
use rustc_errors:: Applicability ;
7
8
use rustc_hir:: { def_id:: DefId , BorrowKind , Expr , ExprKind } ;
8
9
use rustc_lint:: LateContext ;
@@ -18,17 +19,23 @@ use super::UNNECESSARY_TO_OWNED;
18
19
pub fn check ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > , method_name : Symbol , args : & ' tcx [ Expr < ' tcx > ] ) {
19
20
if_chain ! {
20
21
if let Some ( method_def_id) = cx. typeck_results( ) . type_dependent_def_id( expr. hir_id) ;
21
- if is_to_owned_like( cx, method_name, method_def_id) ;
22
22
if let [ receiver] = args;
23
23
then {
24
- // At this point, we know the call is of a `to_owned`-like function. The functions
25
- // `check_addr_of_expr` and `check_call_arg` determine whether the call is unnecessary
26
- // based on its context, that is, whether it is a referent in an `AddrOf` expression or
27
- // an argument in a function call.
28
- if check_addr_of_expr( cx, expr, method_name, method_def_id, receiver) {
29
- return ;
24
+ if is_cloned_or_copied( cx, method_name, method_def_id) {
25
+ unnecessary_iter_cloned:: check( cx, expr, method_name, receiver) ;
26
+ } else if is_to_owned_like( cx, method_name, method_def_id) {
27
+ // At this point, we know the call is of a `to_owned`-like function. The functions
28
+ // `check_addr_of_expr` and `check_call_arg` determine whether the call is unnecessary
29
+ // based on its context, that is, whether it is a referent in an `AddrOf` expression, an
30
+ // argument in a `into_iter` call, or an argument in the call of some other function.
31
+ if check_addr_of_expr( cx, expr, method_name, method_def_id, receiver) {
32
+ return ;
33
+ }
34
+ if check_into_iter_call_arg( cx, expr, method_name, receiver) {
35
+ return ;
36
+ }
37
+ check_other_call_arg( cx, expr, method_name, receiver) ;
30
38
}
31
- check_call_arg( cx, expr, method_name, receiver) ;
32
39
}
33
40
}
34
41
}
@@ -116,29 +123,34 @@ fn check_addr_of_expr(
116
123
return true ;
117
124
}
118
125
}
119
- if implements_deref_trait( cx, receiver_ty, target_ty) {
120
- if n_receiver_refs > 0 {
121
- span_lint_and_sugg(
122
- cx,
123
- UNNECESSARY_TO_OWNED ,
124
- parent. span,
125
- & format!( "unnecessary use of `{}`" , method_name) ,
126
- "use" ,
127
- receiver_snippet,
128
- Applicability :: MachineApplicable ,
129
- ) ;
130
- } else {
131
- span_lint_and_sugg(
132
- cx,
133
- UNNECESSARY_TO_OWNED ,
134
- expr. span. with_lo( receiver. span. hi( ) ) ,
135
- & format!( "unnecessary use of `{}`" , method_name) ,
136
- "remove this" ,
137
- String :: new( ) ,
138
- Applicability :: MachineApplicable ,
139
- ) ;
126
+ if_chain! {
127
+ if let Some ( deref_trait_id) = cx. tcx. get_diagnostic_item( sym:: Deref ) ;
128
+ if implements_trait( cx, receiver_ty, deref_trait_id, & [ ] ) ;
129
+ if get_associated_type( cx, receiver_ty, deref_trait_id, "Target" ) == Some ( target_ty) ;
130
+ then {
131
+ if n_receiver_refs > 0 {
132
+ span_lint_and_sugg(
133
+ cx,
134
+ UNNECESSARY_TO_OWNED ,
135
+ parent. span,
136
+ & format!( "unnecessary use of `{}`" , method_name) ,
137
+ "use" ,
138
+ receiver_snippet,
139
+ Applicability :: MachineApplicable ,
140
+ ) ;
141
+ } else {
142
+ span_lint_and_sugg(
143
+ cx,
144
+ UNNECESSARY_TO_OWNED ,
145
+ expr. span. with_lo( receiver. span. hi( ) ) ,
146
+ & format!( "unnecessary use of `{}`" , method_name) ,
147
+ "remove this" ,
148
+ String :: new( ) ,
149
+ Applicability :: MachineApplicable ,
150
+ ) ;
151
+ }
152
+ return true ;
140
153
}
141
- return true ;
142
154
}
143
155
if_chain! {
144
156
if let Some ( as_ref_trait_id) = cx. tcx. get_diagnostic_item( sym:: AsRef ) ;
@@ -161,9 +173,55 @@ fn check_addr_of_expr(
161
173
false
162
174
}
163
175
176
+ /// Checks whether `expr` is an argument in an `into_iter` call and, if so, determines whether its
177
+ /// call of a `to_owned`-like function is unnecessary.
178
+ fn check_into_iter_call_arg (
179
+ cx : & LateContext < ' tcx > ,
180
+ expr : & ' tcx Expr < ' tcx > ,
181
+ method_name : Symbol ,
182
+ receiver : & ' tcx Expr < ' tcx > ,
183
+ ) -> bool {
184
+ if_chain ! {
185
+ if let Some ( parent) = get_parent_expr( cx, expr) ;
186
+ if let Some ( callee_def_id) = fn_def_id( cx, parent) ;
187
+ if is_into_iter( cx, callee_def_id) ;
188
+ if let Some ( iterator_trait_id) = cx. tcx. get_diagnostic_item( sym:: Iterator ) ;
189
+ let parent_ty = cx. typeck_results( ) . expr_ty( parent) ;
190
+ if implements_trait( cx, parent_ty, iterator_trait_id, & [ ] ) ;
191
+ if let Some ( item_ty) = get_iterator_item_ty( cx, parent_ty) ;
192
+ if let Some ( receiver_snippet) = snippet_opt( cx, receiver. span) ;
193
+ then {
194
+ if unnecessary_iter_cloned:: check_for_loop_iter( cx, parent, method_name, receiver) {
195
+ return true ;
196
+ }
197
+ let cloned_or_copied = if is_copy( cx, item_ty) {
198
+ "copied"
199
+ } else {
200
+ "cloned"
201
+ } ;
202
+ span_lint_and_sugg(
203
+ cx,
204
+ UNNECESSARY_TO_OWNED ,
205
+ parent. span,
206
+ & format!( "unnecessary use of `{}`" , method_name) ,
207
+ "use" ,
208
+ format!( "{}.iter().{}()" , receiver_snippet, cloned_or_copied) ,
209
+ Applicability :: MachineApplicable ,
210
+ ) ;
211
+ return true ;
212
+ }
213
+ }
214
+ false
215
+ }
216
+
164
217
/// Checks whether `expr` is an argument in a function call and, if so, determines whether its call
165
218
/// of a `to_owned`-like function is unnecessary.
166
- fn check_call_arg ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > , method_name : Symbol , receiver : & ' tcx Expr < ' tcx > ) {
219
+ fn check_other_call_arg (
220
+ cx : & LateContext < ' tcx > ,
221
+ expr : & ' tcx Expr < ' tcx > ,
222
+ method_name : Symbol ,
223
+ receiver : & ' tcx Expr < ' tcx > ,
224
+ ) -> bool {
167
225
if_chain ! {
168
226
if let Some ( ( maybe_call, maybe_arg) ) = skip_addr_of_ancestors( cx, expr) ;
169
227
if let Some ( ( callee_def_id, call_substs, call_args) ) = get_callee_substs_and_args( cx, maybe_call) ;
@@ -186,7 +244,8 @@ fn check_call_arg(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: S
186
244
if let [ projection_predicate] = projection_predicates[ ..] {
187
245
let normalized_ty =
188
246
cx. tcx. subst_and_normalize_erasing_regions( call_substs, cx. param_env, projection_predicate. ty) ;
189
- implements_deref_trait( cx, receiver_ty, normalized_ty)
247
+ implements_trait( cx, receiver_ty, deref_trait_id, & [ ] )
248
+ && get_associated_type( cx, receiver_ty, deref_trait_id, "Target" ) == Some ( normalized_ty)
190
249
} else {
191
250
false
192
251
}
@@ -215,8 +274,10 @@ fn check_call_arg(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: S
215
274
format!( "{:&>width$}{}" , "" , receiver_snippet, width = n_refs) ,
216
275
Applicability :: MachineApplicable ,
217
276
) ;
277
+ return true ;
218
278
}
219
279
}
280
+ false
220
281
}
221
282
222
283
/// Walks an expression's ancestors until it finds a non-`AddrOf` expression. Returns the first such
@@ -315,22 +376,10 @@ fn compose_substs(cx: &LateContext<'tcx>, left: &[GenericArg<'tcx>], right: Subs
315
376
. collect ( )
316
377
}
317
378
318
- /// Helper function to check whether a type implements the `Deref` trait.
319
- fn implements_deref_trait ( cx : & LateContext < ' tcx > , ty : Ty < ' tcx > , deref_target_ty : Ty < ' tcx > ) -> bool {
320
- if_chain ! {
321
- if let Some ( deref_trait_id) = cx. tcx. get_diagnostic_item( sym:: Deref ) ;
322
- if implements_trait( cx, ty, deref_trait_id, & [ ] ) ;
323
- if let Some ( deref_target_id) = cx. tcx. lang_items( ) . deref_target( ) ;
324
- let substs = cx. tcx. mk_substs_trait( ty, & [ ] ) ;
325
- let projection_ty = cx. tcx. mk_projection( deref_target_id, substs) ;
326
- let normalized_ty = cx. tcx. normalize_erasing_regions( cx. param_env, projection_ty) ;
327
- if normalized_ty == deref_target_ty;
328
- then {
329
- true
330
- } else {
331
- false
332
- }
333
- }
379
+ /// Returns true if the named method is `Iterator::cloned` or `Iterator::copied`.
380
+ fn is_cloned_or_copied ( cx : & LateContext < ' _ > , method_name : Symbol , method_def_id : DefId ) -> bool {
381
+ ( method_name. as_str ( ) == "cloned" || method_name. as_str ( ) == "copied" )
382
+ && is_diag_trait_item ( cx, method_def_id, sym:: Iterator )
334
383
}
335
384
336
385
/// Returns true if the named method can be used to convert the receiver to its "owned"
0 commit comments