1
1
// noise reduction, remove before committing!
2
2
#![ allow( unused_variables) ]
3
3
4
+ use clippy_utils:: diagnostics:: span_lint_and_sugg;
5
+ use clippy_utils:: source:: snippet_with_applicability;
6
+ use clippy_utils:: { fn_def_id, match_def_path} ;
4
7
use rustc_errors:: Applicability ;
5
- use rustc_hir:: * ;
6
8
use rustc_hir:: def:: Res ;
9
+ use rustc_hir:: * ;
7
10
use rustc_lint:: { LateContext , LateLintPass } ;
8
11
use rustc_session:: declare_lint_pass;
9
12
use rustc_span:: Span ;
10
- use clippy_utils:: { match_def_path, fn_def_id} ;
11
- use clippy_utils:: diagnostics:: span_lint_and_sugg;
12
- use clippy_utils:: source:: snippet_with_applicability;
13
13
14
14
declare_clippy_lint ! {
15
15
/// ### What it does
16
16
/// Detects usage of `Option::and_then` and `bool::then_some` that could
17
- /// be replaced with `Option::filter`.
18
- ///
17
+ /// be replaced with `Option::filter`.
18
+ ///
19
19
/// ### Why is this bad?
20
- /// Needless complexity, uses recent and uncommon stdlib funtions instead of
21
- /// one older function.
20
+ /// Needless complexity, uses recent and uncommon stdlib funtions instead of
21
+ /// one older function.
22
22
///
23
23
/// ### Example
24
24
/// ```no_run
@@ -39,146 +39,150 @@ declare_clippy_lint! {
39
39
declare_lint_pass ! ( AndThenThenSome => [ AND_THEN_THEN_SOME ] ) ;
40
40
41
41
impl < ' tcx > LateLintPass < ' tcx > for AndThenThenSome {
42
- fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > ) {
43
- match expr. kind {
44
- ExprKind :: MethodCall ( method_name, selfarg, [ arg ] , _span) => {
45
- //(expr);
46
- //let option_id = cx.tcx.get_diagnostic_item(sym::Option);
47
- // TODO: check if type of reciever is diagnostic item Option.
48
- //let tckr = cx.typeck_results();
49
- //let def_id = tckr.type_dependent_def_id(expr.hir_id).unwrap();
50
- //(method_name, selfarg, arg);
51
- if is_and_then ( cx, expr)
52
- {
53
- if let Some ( ( closure_args, predicate) ) = ( then_some_closure_arg ( cx, arg) ) {
54
- //(predicate);
55
- show_sugg ( cx, expr. span , selfarg, closure_args, predicate) ;
56
- }
57
- }
58
- }
59
- ExprKind :: Call ( _func, [ selfarg, arg ] ) => {
60
- if ( is_and_then ( cx, expr) ) {
61
- if let Some ( ( closure_args, predicate) ) = ( then_some_closure_arg ( cx, arg) ) {
62
- //(predicate);
63
- show_sugg ( cx, expr. span , selfarg, closure_args, predicate) ;
64
- }
65
- }
66
- }
67
- // TODO: check for call as associated function
68
- _ => { } ,
69
- }
70
- }
42
+ fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > ) {
43
+ match expr. kind {
44
+ ExprKind :: MethodCall ( method_name, selfarg, [ arg] , _span) => {
45
+ //let option_id = cx.tcx.get_diagnostic_item(sym::Option);
46
+ // TODO: check if type of reciever is diagnostic item Option.
47
+ //let tckr = cx.typeck_results();
48
+ //let def_id = tckr.type_dependent_def_id(expr.hir_id).unwrap();
49
+ //(method_name, selfarg, arg);
50
+ if is_and_then ( cx, expr) {
51
+ if let Some ( ( closure_args, predicate) ) = then_some_closure_arg ( cx, arg) {
52
+ //(predicate);
53
+ show_sugg ( cx, expr. span , selfarg, closure_args, predicate) ;
54
+ }
55
+ }
56
+ } ,
57
+ ExprKind :: Call ( _func, [ selfarg, arg] ) => {
58
+ if is_and_then ( cx, expr) {
59
+ if let Some ( ( closure_args, predicate) ) = then_some_closure_arg ( cx, arg) {
60
+ //(predicate);
61
+ show_sugg ( cx, expr. span , selfarg, closure_args, predicate) ;
62
+ }
63
+ }
64
+ } ,
65
+ // TODO: check for call as associated function
66
+ _ => { } ,
67
+ }
68
+ }
71
69
}
72
70
73
71
// `|v| X.then_some(v)` -> Some((span"|v|", X))
74
- fn then_some_closure_arg < ' tcx > ( cx : & LateContext < ' tcx > , expr : & Expr < ' tcx > )
75
- -> Option < ( Span , & ' tcx Expr < ' tcx > ) >
76
- {
77
- ( expr) ;
78
- match expr. kind {
79
- ExprKind :: Closure ( Closure {
80
- fn_decl : FnDecl { inputs : [ Ty { hir_id : arg_id, ..} ] , .. } ,
81
- body,
82
- ..
83
- } ) => {
84
- if let Node :: Expr ( expr) = ( cx. tcx . hir_node ( body. hir_id ) ) {
85
- //(arg_id);
86
- if let Some ( body) = ( peel_closure_body ( cx, expr, * arg_id) ) {
87
- Some ( ( cx. tcx . hir ( ) . span ( * arg_id) , body) )
88
- } else {
89
- None
90
- }
91
- } else {
92
- None
93
- }
94
- } ,
95
- _ => None ,
96
- }
72
+ fn then_some_closure_arg < ' tcx > ( cx : & LateContext < ' tcx > , expr : & Expr < ' tcx > ) -> Option < ( Span , & ' tcx Expr < ' tcx > ) > {
73
+ match expr. kind {
74
+ ExprKind :: Closure ( Closure {
75
+ fn_decl : FnDecl {
76
+ inputs : [ Ty { hir_id : arg_id, .. } ] ,
77
+ ..
78
+ } ,
79
+ body,
80
+ ..
81
+ } ) => {
82
+ if let Node :: Expr ( expr) = cx. tcx . hir_node ( body. hir_id ) {
83
+ //(arg_id);
84
+ ( peel_closure_body ( cx, expr, * arg_id) ) . map ( |body| ( cx. tcx . hir ( ) . span ( * arg_id) , body) )
85
+ } else {
86
+ None
87
+ }
88
+ } ,
89
+ _ => None ,
90
+ }
97
91
}
98
92
99
- fn peel_closure_body < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > , closure_arg_id : HirId ) -> Option < & ' tcx Expr < ' tcx > > {
100
-
101
- match expr. kind {
102
- // it would be nice if we could lift { x; y.a() } into { x; y }.a()
103
- ExprKind :: Block ( Block { stmts : [ ] , expr : Some ( wrapped_expr) , ..} , _) => {
104
- peel_closure_body ( cx, wrapped_expr, closure_arg_id)
105
- }
106
- ExprKind :: MethodCall ( _path, selfarg, [ arg ] , _span) => {
107
- if is_then_some ( cx, expr) &&
108
- is_local_defined_at ( cx, arg, closure_arg_id)
109
- {
110
- // the argument to then_some is the same as that given to the closure
111
- Some ( selfarg)
112
- } else {
113
- None
114
- }
115
- }
116
- ExprKind :: Call ( func, [ pred, arg ] ) => {
117
- if ( is_then_some ( cx, expr) ) && ( is_local_defined_at ( cx, arg, closure_arg_id) ) {
118
- Some ( pred)
119
-
120
- } else {
121
- None
122
- }
123
- }
124
- _ => {
125
- None
126
- }
127
- }
93
+ fn peel_closure_body < ' tcx > (
94
+ cx : & LateContext < ' tcx > ,
95
+ expr : & ' tcx Expr < ' tcx > ,
96
+ closure_arg_id : HirId ,
97
+ ) -> Option < & ' tcx Expr < ' tcx > > {
98
+ match expr. kind {
99
+ // it would be nice if we could lift { x; y.a() } into { x; y }.a()
100
+ ExprKind :: Block (
101
+ Block {
102
+ stmts : [ ] ,
103
+ expr : Some ( wrapped_expr) ,
104
+ ..
105
+ } ,
106
+ _,
107
+ ) => peel_closure_body ( cx, wrapped_expr, closure_arg_id) ,
108
+ ExprKind :: MethodCall ( _path, selfarg, [ arg] , _span) => {
109
+ if is_then_some ( cx, expr) && is_local_defined_at ( cx, arg, closure_arg_id) {
110
+ // the argument to then_some is the same as that given to the closure
111
+ Some ( selfarg)
112
+ } else {
113
+ None
114
+ }
115
+ } ,
116
+ ExprKind :: Call ( func, [ pred, arg] ) => {
117
+ if ( is_then_some ( cx, expr) ) && ( is_local_defined_at ( cx, arg, closure_arg_id) ) {
118
+ Some ( pred)
119
+ } else {
120
+ None
121
+ }
122
+ } ,
123
+ _ => None ,
124
+ }
128
125
}
129
126
130
- fn is_local_defined_at < ' tcx > ( cx : & LateContext < ' tcx > , local : & Expr < ' _ > , arg_hid : HirId ) -> bool {
131
- match local. kind {
132
- ExprKind :: Path ( QPath :: Resolved ( _, Path { res : Res :: Local ( local_hid) , .. } ) ) => {
133
- // FIXME: this is the best way i could find to compare if
134
- // a local refers to a specific closure argument.
135
- //
136
- // it breaks if the closure argument has an explicitly declared type,
137
- // since the spans only align for TyKind::Infer
138
- if let Node :: Pat ( Pat { span : local_span, .. } ) = ( cx. tcx . hir_node ( * local_hid) ) &&
139
- let Node :: Ty ( Ty { span : arg_span, .. } ) = ( cx. tcx . hir_node ( arg_hid) ) &&
140
- local_span == arg_span
141
- {
142
- true
143
- } else {
144
- false
145
- }
146
- }
147
- // is not local at all, so definitly isn't a local defined at the given position
148
- _ => false ,
149
- }
127
+ fn is_local_defined_at ( cx : & LateContext < ' _ > , local : & Expr < ' _ > , arg_hid : HirId ) -> bool {
128
+ match local. kind {
129
+ ExprKind :: Path ( QPath :: Resolved (
130
+ _,
131
+ Path {
132
+ res : Res :: Local ( local_hid) ,
133
+ ..
134
+ } ,
135
+ ) ) => {
136
+ // FIXME: this is the best way i could find to compare if
137
+ // a local refers to a specific closure argument.
138
+ //
139
+ // it breaks if the closure argument has an explicitly declared type,
140
+ // since the spans only align for TyKind::Infer
141
+ if let Node :: Pat ( Pat { span : local_span, .. } ) = ( cx. tcx . hir_node ( * local_hid) )
142
+ && let Node :: Ty ( Ty { span : arg_span, .. } ) = ( cx. tcx . hir_node ( arg_hid) )
143
+ && local_span == arg_span
144
+ {
145
+ true
146
+ } else {
147
+ false
148
+ }
149
+ } ,
150
+ // is not local at all, so definitly isn't a local defined at the given position
151
+ _ => false ,
152
+ }
150
153
}
151
154
152
155
fn show_sugg ( cx : & LateContext < ' _ > , span : Span , selfarg : & Expr < ' _ > , closure_args : Span , predicate : & Expr < ' _ > ) {
153
- let mut appl = Applicability :: MachineApplicable ;
154
- let sugg = format ! (
155
- "{}.filter(|{}| {})" ,
156
- snippet_with_applicability( cx, selfarg. span, "<OPTION>" , & mut appl) ,
157
- snippet_with_applicability( cx, closure_args, "<ARGS>" , & mut appl) ,
158
- snippet_with_applicability( cx, predicate. span, "<PREDICATE>" , & mut appl) ) ;
159
- span_lint_and_sugg ( cx, AND_THEN_THEN_SOME , span,
160
- "use of `and_then` + `then_some` is equivelent to `filter`" ,
161
- "use `Option::filter` instead" ,
162
- sugg, appl) ;
156
+ let mut appl = Applicability :: MachineApplicable ;
157
+ let sugg = format ! (
158
+ "{}.filter(|{}| {})" ,
159
+ snippet_with_applicability( cx, selfarg. span, "<OPTION>" , & mut appl) ,
160
+ snippet_with_applicability( cx, closure_args, "<ARGS>" , & mut appl) ,
161
+ snippet_with_applicability( cx, predicate. span, "<PREDICATE>" , & mut appl)
162
+ ) ;
163
+ span_lint_and_sugg (
164
+ cx,
165
+ AND_THEN_THEN_SOME ,
166
+ span,
167
+ "use of `and_then` + `then_some` is equivelent to `filter`" ,
168
+ "use `Option::filter` instead" ,
169
+ sugg,
170
+ appl,
171
+ ) ;
163
172
}
164
173
165
174
fn is_then_some ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) -> bool {
166
- if let Some ( def_id) = fn_def_id ( cx, expr) {
167
- ( match_def_path (
168
- cx, ( def_id) ,
169
- & [ "core" , "bool" , "<impl bool>" , "then_some" ] ) )
170
- } else {
171
- ( expr) ;
172
- false
173
- }
175
+ if let Some ( def_id) = fn_def_id ( cx, expr) {
176
+ match_def_path ( cx, def_id, & [ "core" , "bool" , "<impl bool>" , "then_some" ] )
177
+ } else {
178
+ false
179
+ }
174
180
}
175
181
176
182
fn is_and_then ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) -> bool {
177
- if let Some ( def_id) = fn_def_id ( cx, expr) {
178
- ( match_def_path (
179
- cx, ( def_id) ,
180
- & [ "core" , "option" , "Option" , "and_then" ] ) )
181
- } else {
182
- false
183
- }
183
+ if let Some ( def_id) = fn_def_id ( cx, expr) {
184
+ match_def_path ( cx, def_id, & [ "core" , "option" , "Option" , "and_then" ] )
185
+ } else {
186
+ false
187
+ }
184
188
}
0 commit comments