@@ -2,7 +2,7 @@ use clippy_config::msrvs::{self, Msrv};
2
2
use clippy_utils:: diagnostics:: span_lint_and_sugg;
3
3
use clippy_utils:: source:: snippet_with_applicability;
4
4
use 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} ;
6
6
use rustc_errors:: Applicability ;
7
7
use rustc_hir as hir;
8
8
use rustc_lint:: LateContext ;
@@ -19,50 +19,63 @@ pub(super) fn check(cx: &LateContext<'_>, e: &hir::Expr<'_>, recv: &hir::Expr<'_
19
19
&& ( cx. tcx . impl_of_method ( method_id) . map_or ( false , |id| {
20
20
is_type_diagnostic_item ( cx, cx. tcx . type_of ( id) . instantiate_identity ( ) , sym:: Option )
21
21
} ) || is_diag_trait_item ( cx, method_id, sym:: Iterator ) )
22
- && let hir:: ExprKind :: Closure ( & hir:: Closure { body, .. } ) = arg. kind
23
22
{
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) {
39
31
lint_explicit_closure ( cx, e. span , recv. span , true , msrv) ;
40
32
}
41
33
}
42
34
} ,
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
+ }
57
42
}
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
+ _ => { } ,
61
65
}
62
66
} ,
63
67
_ => { } ,
64
68
}
65
69
} ,
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
+ } ,
66
79
_ => { } ,
67
80
}
68
81
}
@@ -88,6 +101,23 @@ fn lint_needless_cloning(cx: &LateContext<'_>, root: Span, receiver: Span) {
88
101
) ;
89
102
}
90
103
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
+
91
121
fn lint_explicit_closure ( cx : & LateContext < ' _ > , replace : Span , root : Span , is_copy : bool , msrv : & Msrv ) {
92
122
let mut applicability = Applicability :: MachineApplicable ;
93
123
0 commit comments