11use super :: implicit_clone:: is_clone_like;
2+ use super :: unnecessary_iter_cloned:: { self , is_into_iter} ;
23use clippy_utils:: diagnostics:: span_lint_and_sugg;
34use 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} ;
67use rustc_errors:: Applicability ;
78use rustc_hir:: { def_id:: DefId , BorrowKind , Expr , ExprKind } ;
89use rustc_lint:: LateContext ;
@@ -18,17 +19,23 @@ use super::UNNECESSARY_TO_OWNED;
1819pub fn check ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > , method_name : Symbol , args : & ' tcx [ Expr < ' tcx > ] ) {
1920 if_chain ! {
2021 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) ;
2222 if let [ receiver] = args;
2323 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) ;
3038 }
31- check_call_arg( cx, expr, method_name, receiver) ;
3239 }
3340 }
3441}
@@ -116,29 +123,34 @@ fn check_addr_of_expr(
116123 return true ;
117124 }
118125 }
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 ;
140153 }
141- return true ;
142154 }
143155 if_chain! {
144156 if let Some ( as_ref_trait_id) = cx. tcx. get_diagnostic_item( sym:: AsRef ) ;
@@ -161,9 +173,55 @@ fn check_addr_of_expr(
161173 false
162174}
163175
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+
164217/// Checks whether `expr` is an argument in a function call and, if so, determines whether its call
165218/// 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 {
167225 if_chain ! {
168226 if let Some ( ( maybe_call, maybe_arg) ) = skip_addr_of_ancestors( cx, expr) ;
169227 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
186244 if let [ projection_predicate] = projection_predicates[ ..] {
187245 let normalized_ty =
188246 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)
190249 } else {
191250 false
192251 }
@@ -215,8 +274,10 @@ fn check_call_arg(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: S
215274 format!( "{:&>width$}{}" , "" , receiver_snippet, width = n_refs) ,
216275 Applicability :: MachineApplicable ,
217276 ) ;
277+ return true ;
218278 }
219279 }
280+ false
220281}
221282
222283/// 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
315376 . collect ( )
316377}
317378
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 )
334383}
335384
336385/// Returns true if the named method can be used to convert the receiver to its "owned"
0 commit comments