@@ -6,6 +6,66 @@ use rustc_lint::LateContext;
66
77use super :: UNNECESSARY_LAZY_EVALUATION ;
88
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+
969/// lint use of `<fn>_else(simple closure)` for `Option`s and `Result`s that can be
1070/// replaced with `<fn>(return value of simple closure)`
1171pub ( super ) fn lint < ' tcx > (
@@ -18,96 +78,34 @@ pub(super) fn lint<'tcx>(
1878 let is_option = is_type_diagnostic_item ( cx, cx. typeck_results ( ) . expr_ty ( & args[ 0 ] ) , sym ! ( option_type) ) ;
1979 let is_result = is_type_diagnostic_item ( cx, cx. typeck_results ( ) . expr_ty ( & args[ 0 ] ) , sym ! ( result_type) ) ;
2080
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 ;
4086
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`"
5590 } 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+ } ;
6593
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+ }
111109 }
112110 }
113111}
0 commit comments