@@ -6,6 +6,66 @@ use rustc_lint::LateContext;
6
6
7
7
use super :: UNNECESSARY_LAZY_EVALUATION ;
8
8
9
+ // Return true if the expression is an accessor of any of the arguments
10
+ fn expr_uses_argument ( expr : & hir:: Expr < ' _ > , params : & [ hir:: Param < ' _ > ] ) -> bool {
11
+ params. iter ( ) . any ( |arg| {
12
+ if_chain ! {
13
+ if let hir:: PatKind :: Binding ( _, _, ident, _) = arg. pat. kind;
14
+ if let hir:: ExprKind :: Path ( hir:: QPath :: Resolved ( _, ref path) ) = expr. kind;
15
+ if let [ p, ..] = path. segments;
16
+ then {
17
+ ident. name == p. ident. name
18
+ } else {
19
+ false
20
+ }
21
+ }
22
+ } )
23
+ }
24
+
25
+ fn match_any_qpath ( path : & hir:: QPath < ' _ > , paths : & [ & [ & str ] ] ) -> bool {
26
+ paths. iter ( ) . any ( |candidate| match_qpath ( path, candidate) )
27
+ }
28
+
29
+ fn can_simplify ( expr : & hir:: Expr < ' _ > , params : & [ hir:: Param < ' _ > ] , variant_calls : bool ) -> bool {
30
+ match expr. kind {
31
+ // Closures returning literals can be unconditionally simplified
32
+ hir:: ExprKind :: Lit ( _) => true ,
33
+
34
+ hir:: ExprKind :: Index ( ref object, ref index) => {
35
+ // arguments are not being indexed into
36
+ if expr_uses_argument ( object, params) {
37
+ false
38
+ } else {
39
+ // arguments are not used as index
40
+ !expr_uses_argument ( index, params)
41
+ }
42
+ } ,
43
+
44
+ // Reading fields can be simplified if the object is not an argument of the closure
45
+ hir:: ExprKind :: Field ( ref object, _) => !expr_uses_argument ( object, params) ,
46
+
47
+ // Paths can be simplified if the root is not the argument, this also covers None
48
+ hir:: ExprKind :: Path ( _) => !expr_uses_argument ( expr, params) ,
49
+
50
+ // Calls to Some, Ok, Err can be considered literals if they don't derive an argument
51
+ hir:: ExprKind :: Call ( ref func, ref args) => if_chain ! {
52
+ if variant_calls; // Disable lint when rules conflict with bind_instead_of_map
53
+ if let hir:: ExprKind :: Path ( ref path) = func. kind;
54
+ if match_any_qpath( path, & [ & [ "Some" ] , & [ "Ok" ] , & [ "Err" ] ] ) ;
55
+ then {
56
+ // Recursively check all arguments
57
+ args. iter( ) . all( |arg| can_simplify( arg, params, variant_calls) )
58
+ } else {
59
+ false
60
+ }
61
+ } ,
62
+
63
+ // For anything more complex than the above, a closure is probably the right solution,
64
+ // or the case is handled by an other lint
65
+ _ => false ,
66
+ }
67
+ }
68
+
9
69
/// lint use of `<fn>_else(simple closure)` for `Option`s and `Result`s that can be
10
70
/// replaced with `<fn>(return value of simple closure)`
11
71
pub ( super ) fn lint < ' tcx > (
@@ -18,96 +78,34 @@ pub(super) fn lint<'tcx>(
18
78
let is_option = is_type_diagnostic_item ( cx, cx. typeck_results ( ) . expr_ty ( & args[ 0 ] ) , sym ! ( option_type) ) ;
19
79
let is_result = is_type_diagnostic_item ( cx, cx. typeck_results ( ) . expr_ty ( & args[ 0 ] ) , sym ! ( result_type) ) ;
20
80
21
- if !is_option && !is_result {
22
- return ;
23
- }
24
-
25
- // Return true if the expression is an accessor of any of the arguments
26
- fn expr_uses_argument ( expr : & hir:: Expr < ' _ > , params : & [ hir:: Param < ' _ > ] ) -> bool {
27
- params. iter ( ) . any ( |arg| {
28
- if_chain ! {
29
- if let hir:: PatKind :: Binding ( _, _, ident, _) = arg. pat. kind;
30
- if let hir:: ExprKind :: Path ( hir:: QPath :: Resolved ( _, ref path) ) = expr. kind;
31
- if let [ p, ..] = path. segments;
32
- then {
33
- ident. name == p. ident. name
34
- } else {
35
- false
36
- }
37
- }
38
- } )
39
- }
81
+ if is_option || is_result {
82
+ if let hir:: ExprKind :: Closure ( _, _, eid, _, _) = args[ 1 ] . kind {
83
+ let body = cx. tcx . hir ( ) . body ( eid) ;
84
+ let ex = & body. value ;
85
+ let params = & body. params ;
40
86
41
- fn match_any_qpath ( path : & hir:: QPath < ' _ > , paths : & [ & [ & str ] ] ) -> bool {
42
- paths. iter ( ) . any ( |candidate| match_qpath ( path, candidate) )
43
- }
44
-
45
- fn can_simplify ( expr : & hir:: Expr < ' _ > , params : & [ hir:: Param < ' _ > ] , variant_calls : bool ) -> bool {
46
- match expr. kind {
47
- // Closures returning literals can be unconditionally simplified
48
- hir:: ExprKind :: Lit ( _) => true ,
49
-
50
- hir:: ExprKind :: Index ( ref object, ref index) => {
51
- // arguments are not being indexed into
52
- if !expr_uses_argument ( object, params) {
53
- // arguments are not used as index
54
- !expr_uses_argument ( index, params)
87
+ if can_simplify ( ex, params, allow_variant_calls) {
88
+ let msg = if is_option {
89
+ "unnecessary closure used to substitute value for `Option::None`"
55
90
} else {
56
- false
57
- }
58
- } ,
59
-
60
- // Reading fields can be simplified if the object is not an argument of the closure
61
- hir:: ExprKind :: Field ( ref object, _) => !expr_uses_argument ( object, params) ,
62
-
63
- // Paths can be simplified if the root is not the argument, this also covers None
64
- hir:: ExprKind :: Path ( _) => !expr_uses_argument ( expr, params) ,
91
+ "unnecessary closure used to substitute value for `Result::Err`"
92
+ } ;
65
93
66
- // Calls to Some, Ok, Err can be considered literals if they don't derive an argument
67
- hir:: ExprKind :: Call ( ref func, ref args) => if_chain ! {
68
- if variant_calls; // Disable lint when rules conflict with bind_instead_of_map
69
- if let hir:: ExprKind :: Path ( ref path) = func. kind;
70
- if match_any_qpath( path, & [ & [ "Some" ] , & [ "Ok" ] , & [ "Err" ] ] ) ;
71
- then {
72
- // Recursively check all arguments
73
- args. iter( ) . all( |arg| can_simplify( arg, params, variant_calls) )
74
- } else {
75
- false
76
- }
77
- } ,
78
-
79
- // For anything more complex than the above, a closure is probably the right solution,
80
- // or the case is handled by an other lint
81
- _ => false ,
82
- }
83
- }
84
-
85
- if let hir:: ExprKind :: Closure ( _, _, eid, _, _) = args[ 1 ] . kind {
86
- let body = cx. tcx . hir ( ) . body ( eid) ;
87
- let ex = & body. value ;
88
- let params = & body. params ;
89
-
90
- if can_simplify ( ex, params, allow_variant_calls) {
91
- let msg = if is_option {
92
- "unnecessary closure used to substitute value for `Option::None`"
93
- } else {
94
- "unnecessary closure used to substitute value for `Result::Err`"
95
- } ;
96
-
97
- span_lint_and_sugg (
98
- cx,
99
- UNNECESSARY_LAZY_EVALUATION ,
100
- expr. span ,
101
- msg,
102
- & format ! ( "Use `{}` instead" , simplify_using) ,
103
- format ! (
104
- "{0}.{1}({2})" ,
105
- snippet( cx, args[ 0 ] . span, ".." ) ,
106
- simplify_using,
107
- snippet( cx, ex. span, ".." ) ,
108
- ) ,
109
- Applicability :: MachineApplicable ,
110
- ) ;
94
+ span_lint_and_sugg (
95
+ cx,
96
+ UNNECESSARY_LAZY_EVALUATION ,
97
+ expr. span ,
98
+ msg,
99
+ & format ! ( "Use `{}` instead" , simplify_using) ,
100
+ format ! (
101
+ "{0}.{1}({2})" ,
102
+ snippet( cx, args[ 0 ] . span, ".." ) ,
103
+ simplify_using,
104
+ snippet( cx, ex. span, ".." ) ,
105
+ ) ,
106
+ Applicability :: MachineApplicable ,
107
+ ) ;
108
+ }
111
109
}
112
110
}
113
111
}
0 commit comments