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