@@ -2,7 +2,7 @@ use clippy_config::msrvs::{self, Msrv};
22use clippy_utils:: diagnostics:: span_lint_and_sugg;
33use clippy_utils:: source:: snippet_with_applicability;
44use clippy_utils:: ty:: { is_copy, is_type_diagnostic_item} ;
5- use clippy_utils:: { is_diag_trait_item, peel_blocks} ;
5+ use clippy_utils:: { is_diag_trait_item, match_def_path , paths , peel_blocks} ;
66use rustc_errors:: Applicability ;
77use rustc_hir as hir;
88use rustc_lint:: LateContext ;
@@ -19,50 +19,63 @@ pub(super) fn check(cx: &LateContext<'_>, e: &hir::Expr<'_>, recv: &hir::Expr<'_
1919 && ( cx. tcx . impl_of_method ( method_id) . map_or ( false , |id| {
2020 is_type_diagnostic_item ( cx, cx. tcx . type_of ( id) . instantiate_identity ( ) , sym:: Option )
2121 } ) || is_diag_trait_item ( cx, method_id, sym:: Iterator ) )
22- && let hir:: ExprKind :: Closure ( & hir:: Closure { body, .. } ) = arg. kind
2322 {
24- let closure_body = cx. tcx . hir ( ) . body ( body) ;
25- let closure_expr = peel_blocks ( closure_body. value ) ;
26- match closure_body. params [ 0 ] . pat . kind {
27- hir:: PatKind :: Ref ( inner, hir:: Mutability :: Not ) => {
28- if let hir:: PatKind :: Binding ( hir:: BindingAnnotation :: NONE , .., name, None ) = inner. kind {
29- if ident_eq ( name, closure_expr) {
30- lint_explicit_closure ( cx, e. span , recv. span , true , msrv) ;
31- }
32- }
33- } ,
34- hir:: PatKind :: Binding ( hir:: BindingAnnotation :: NONE , .., name, None ) => {
35- match closure_expr. kind {
36- hir:: ExprKind :: Unary ( hir:: UnOp :: Deref , inner) => {
37- if ident_eq ( name, inner) {
38- if let ty:: Ref ( .., Mutability :: Not ) = cx. typeck_results ( ) . expr_ty ( inner) . kind ( ) {
23+ match arg. kind {
24+ hir:: ExprKind :: Closure ( & hir:: Closure { body, .. } ) => {
25+ let closure_body = cx. tcx . hir ( ) . body ( body) ;
26+ let closure_expr = peel_blocks ( closure_body. value ) ;
27+ match closure_body. params [ 0 ] . pat . kind {
28+ hir:: PatKind :: Ref ( inner, hir:: Mutability :: Not ) => {
29+ if let hir:: PatKind :: Binding ( hir:: BindingAnnotation :: NONE , .., name, None ) = inner. kind {
30+ if ident_eq ( name, closure_expr) {
3931 lint_explicit_closure ( cx, e. span , recv. span , true , msrv) ;
4032 }
4133 }
4234 } ,
43- hir:: ExprKind :: MethodCall ( method, obj, [ ] , _) => {
44- if ident_eq ( name, obj) && method. ident . name == sym:: clone
45- && let Some ( fn_id) = cx. typeck_results ( ) . type_dependent_def_id ( closure_expr. hir_id )
46- && let Some ( trait_id) = cx. tcx . trait_of_item ( fn_id)
47- && cx. tcx . lang_items ( ) . clone_trait ( ) . map_or ( false , |id| id == trait_id)
48- // no autoderefs
49- && !cx. typeck_results ( ) . expr_adjustments ( obj) . iter ( )
50- . any ( |a| matches ! ( a. kind, Adjust :: Deref ( Some ( ..) ) ) )
51- {
52- let obj_ty = cx. typeck_results ( ) . expr_ty ( obj) ;
53- if let ty:: Ref ( _, ty, mutability) = obj_ty. kind ( ) {
54- if matches ! ( mutability, Mutability :: Not ) {
55- let copy = is_copy ( cx, * ty) ;
56- lint_explicit_closure ( cx, e. span , recv. span , copy, msrv) ;
35+ hir:: PatKind :: Binding ( hir:: BindingAnnotation :: NONE , .., name, None ) => {
36+ match closure_expr. kind {
37+ hir:: ExprKind :: Unary ( hir:: UnOp :: Deref , inner) => {
38+ if ident_eq ( name, inner) {
39+ if let ty:: Ref ( .., Mutability :: Not ) = cx. typeck_results ( ) . expr_ty ( inner) . kind ( ) {
40+ lint_explicit_closure ( cx, e. span , recv. span , true , msrv) ;
41+ }
5742 }
58- } else {
59- lint_needless_cloning ( cx, e. span , recv. span ) ;
60- }
43+ } ,
44+ hir:: ExprKind :: MethodCall ( method, obj, [ ] , _) => {
45+ if ident_eq ( name, obj) && method. ident . name == sym:: clone
46+ && let Some ( fn_id) = cx. typeck_results ( ) . type_dependent_def_id ( closure_expr. hir_id )
47+ && let Some ( trait_id) = cx. tcx . trait_of_item ( fn_id)
48+ && cx. tcx . lang_items ( ) . clone_trait ( ) . map_or ( false , |id| id == trait_id)
49+ // no autoderefs
50+ && !cx. typeck_results ( ) . expr_adjustments ( obj) . iter ( )
51+ . any ( |a| matches ! ( a. kind, Adjust :: Deref ( Some ( ..) ) ) )
52+ {
53+ let obj_ty = cx. typeck_results ( ) . expr_ty ( obj) ;
54+ if let ty:: Ref ( _, ty, mutability) = obj_ty. kind ( ) {
55+ if matches ! ( mutability, Mutability :: Not ) {
56+ let copy = is_copy ( cx, * ty) ;
57+ lint_explicit_closure ( cx, e. span , recv. span , copy, msrv) ;
58+ }
59+ } else {
60+ lint_needless_cloning ( cx, e. span , recv. span ) ;
61+ }
62+ }
63+ } ,
64+ _ => { } ,
6165 }
6266 } ,
6367 _ => { } ,
6468 }
6569 } ,
70+ hir:: ExprKind :: Path ( qpath) => {
71+ if let Some ( path_def_id) = cx. qpath_res ( & qpath, arg. hir_id ) . opt_def_id ( )
72+ && match_def_path ( cx, path_def_id, & paths:: CLONE_TRAIT_METHOD )
73+ {
74+ // FIXME: It would be better to infer the type to check if it's copyable or not
75+ // to suggest to use `.copied()` instead of `.cloned()` where applicable.
76+ lint_path ( cx, e. span , recv. span ) ;
77+ }
78+ } ,
6679 _ => { } ,
6780 }
6881 }
@@ -88,6 +101,23 @@ fn lint_needless_cloning(cx: &LateContext<'_>, root: Span, receiver: Span) {
88101 ) ;
89102}
90103
104+ fn lint_path ( cx : & LateContext < ' _ > , replace : Span , root : Span ) {
105+ let mut applicability = Applicability :: MachineApplicable ;
106+
107+ span_lint_and_sugg (
108+ cx,
109+ MAP_CLONE ,
110+ replace,
111+ "you are explicitly cloning with `.map()`" ,
112+ "consider calling the dedicated `cloned` method" ,
113+ format ! (
114+ "{}.cloned()" ,
115+ snippet_with_applicability( cx, root, ".." , & mut applicability) ,
116+ ) ,
117+ applicability,
118+ ) ;
119+ }
120+
91121fn lint_explicit_closure ( cx : & LateContext < ' _ > , replace : Span , root : Span , is_copy : bool , msrv : & Msrv ) {
92122 let mut applicability = Applicability :: MachineApplicable ;
93123
0 commit comments