1
+ use crate :: rustc:: hir;
2
+ use crate :: rustc:: lint:: { LateContext , LateLintPass , LintArray , LintPass } ;
3
+ use crate :: rustc:: { declare_tool_lint, lint_array} ;
4
+ use crate :: syntax:: source_map:: Span ;
5
+ use crate :: utils:: paths;
6
+ use crate :: utils:: {
7
+ in_macro, match_trait_method, match_type,
8
+ remove_blocks, snippet,
9
+ span_lint_and_sugg,
10
+ } ;
11
+ use if_chain:: if_chain;
12
+ use crate :: syntax:: ast:: Ident ;
13
+
14
+ #[ derive( Clone ) ]
15
+ pub struct Pass ;
16
+
17
+ /// **What it does:** Checks for usage of `iterator.map(|x| x.clone())` and suggests
18
+ /// `iterator.cloned()` instead
19
+ ///
20
+ /// **Why is this bad?** Readability, this can be written more concisely
21
+ ///
22
+ /// **Known problems:** None.
23
+ ///
24
+ /// **Example:**
25
+ ///
26
+ /// ```rust
27
+ /// let x = vec![42, 43];
28
+ /// let y = x.iter();
29
+ /// let z = y.map(|i| *i);
30
+ /// ```
31
+ ///
32
+ /// The correct use would be:
33
+ ///
34
+ /// ```rust
35
+ /// let x = vec![42, 43];
36
+ /// let y = x.iter();
37
+ /// let z = y.cloned();
38
+ /// ```
39
+ declare_clippy_lint ! {
40
+ pub MAP_CLONE ,
41
+ style,
42
+ "using `iterator.map(|x| x.clone())`, or dereferencing closures for `Copy` types"
43
+ }
44
+
45
+ impl LintPass for Pass {
46
+ fn get_lints ( & self ) -> LintArray {
47
+ lint_array ! ( MAP_CLONE )
48
+ }
49
+ }
50
+
51
+ impl < ' a , ' tcx > LateLintPass < ' a , ' tcx > for Pass {
52
+ fn check_expr ( & mut self , cx : & LateContext < ' _ , ' _ > , e : & hir:: Expr ) {
53
+ if in_macro ( e. span ) {
54
+ return ;
55
+ }
56
+
57
+ if_chain ! {
58
+ if let hir:: ExprKind :: MethodCall ( ref method, _, ref args) = e. node;
59
+ if args. len( ) == 2 ;
60
+ if method. ident. as_str( ) == "map" ;
61
+ let ty = cx. tables. expr_ty( & args[ 0 ] ) ;
62
+ if match_type( cx, ty, & paths:: OPTION ) || match_trait_method( cx, e, & paths:: ITERATOR ) ;
63
+ if let hir:: ExprKind :: Closure ( _, _, body_id, _, _) = args[ 1 ] . node;
64
+ let closure_body = cx. tcx. hir. body( body_id) ;
65
+ let closure_expr = remove_blocks( & closure_body. value) ;
66
+ then {
67
+ match closure_body. arguments[ 0 ] . pat. node {
68
+ hir:: PatKind :: Ref ( ref inner, _) => if let hir:: PatKind :: Binding ( hir:: BindingAnnotation :: Unannotated , _, name, None ) = inner. node {
69
+ lint( cx, e. span, args[ 0 ] . span, name, closure_expr) ;
70
+ } ,
71
+ hir:: PatKind :: Binding ( hir:: BindingAnnotation :: Unannotated , _, name, None ) => match closure_expr. node {
72
+ hir:: ExprKind :: Unary ( hir:: UnOp :: UnDeref , ref inner) => lint( cx, e. span, args[ 0 ] . span, name, inner) ,
73
+ hir:: ExprKind :: MethodCall ( ref method, _, ref obj) => if method. ident. as_str( ) == "clone" {
74
+ if match_trait_method( cx, closure_expr, & paths:: CLONE_TRAIT ) {
75
+ lint( cx, e. span, args[ 0 ] . span, name, & obj[ 0 ] ) ;
76
+ }
77
+ }
78
+ _ => { } ,
79
+ } ,
80
+ _ => { } ,
81
+ }
82
+ }
83
+ }
84
+ }
85
+ }
86
+
87
+ fn lint ( cx : & LateContext < ' _ , ' _ > , replace : Span , root : Span , name : Ident , path : & hir:: Expr ) {
88
+ if let hir:: ExprKind :: Path ( hir:: QPath :: Resolved ( None , ref path) ) = path. node {
89
+ if path. segments . len ( ) == 1 && path. segments [ 0 ] . ident == name {
90
+ span_lint_and_sugg (
91
+ cx,
92
+ MAP_CLONE ,
93
+ replace,
94
+ "You are using an explicit closure for cloning elements" ,
95
+ "Consider calling the dedicated `cloned` method" ,
96
+ format ! ( "{}.cloned()" , snippet( cx, root, ".." ) ) ,
97
+ )
98
+ }
99
+ }
100
+ }
0 commit comments