1
1
use super :: { contains_return, BIND_INSTEAD_OF_MAP } ;
2
2
use clippy_utils:: diagnostics:: { multispan_sugg_with_applicability, span_lint_and_sugg, span_lint_and_then} ;
3
3
use clippy_utils:: source:: { snippet, snippet_with_macro_callsite} ;
4
- use clippy_utils:: ty:: match_type;
5
- use clippy_utils:: { in_macro, match_qpath, method_calls, paths, remove_blocks, visitors:: find_all_ret_expressions} ;
4
+ use clippy_utils:: { in_macro, remove_blocks, visitors:: find_all_ret_expressions} ;
6
5
use if_chain:: if_chain;
7
6
use rustc_errors:: Applicability ;
8
7
use rustc_hir as hir;
8
+ use rustc_hir:: def:: { CtorKind , CtorOf , DefKind , Res } ;
9
+ use rustc_hir:: { LangItem , QPath } ;
9
10
use rustc_lint:: LateContext ;
11
+ use rustc_middle:: ty:: DefIdTree ;
10
12
use rustc_span:: Span ;
11
13
12
14
pub ( crate ) struct OptionAndThenSome ;
13
15
14
16
impl BindInsteadOfMap for OptionAndThenSome {
15
- const TYPE_NAME : & ' static str = "Option" ;
16
- const TYPE_QPATH : & ' static [ & ' static str ] = & paths:: OPTION ;
17
-
17
+ const VARIANT_LANG_ITEM : LangItem = LangItem :: OptionSome ;
18
18
const BAD_METHOD_NAME : & ' static str = "and_then" ;
19
- const BAD_VARIANT_NAME : & ' static str = "Some" ;
20
- const BAD_VARIANT_QPATH : & ' static [ & ' static str ] = & paths:: OPTION_SOME ;
21
-
22
19
const GOOD_METHOD_NAME : & ' static str = "map" ;
23
20
}
24
21
25
22
pub ( crate ) struct ResultAndThenOk ;
26
23
27
24
impl BindInsteadOfMap for ResultAndThenOk {
28
- const TYPE_NAME : & ' static str = "Result" ;
29
- const TYPE_QPATH : & ' static [ & ' static str ] = & paths:: RESULT ;
30
-
25
+ const VARIANT_LANG_ITEM : LangItem = LangItem :: ResultOk ;
31
26
const BAD_METHOD_NAME : & ' static str = "and_then" ;
32
- const BAD_VARIANT_NAME : & ' static str = "Ok" ;
33
- const BAD_VARIANT_QPATH : & ' static [ & ' static str ] = & paths:: RESULT_OK ;
34
-
35
27
const GOOD_METHOD_NAME : & ' static str = "map" ;
36
28
}
37
29
38
30
pub ( crate ) struct ResultOrElseErrInfo ;
39
31
40
32
impl BindInsteadOfMap for ResultOrElseErrInfo {
41
- const TYPE_NAME : & ' static str = "Result" ;
42
- const TYPE_QPATH : & ' static [ & ' static str ] = & paths:: RESULT ;
43
-
33
+ const VARIANT_LANG_ITEM : LangItem = LangItem :: ResultErr ;
44
34
const BAD_METHOD_NAME : & ' static str = "or_else" ;
45
- const BAD_VARIANT_NAME : & ' static str = "Err" ;
46
- const BAD_VARIANT_QPATH : & ' static [ & ' static str ] = & paths:: RESULT_ERR ;
47
-
48
35
const GOOD_METHOD_NAME : & ' static str = "map_err" ;
49
36
}
50
37
51
38
pub ( crate ) trait BindInsteadOfMap {
52
- const TYPE_NAME : & ' static str ;
53
- const TYPE_QPATH : & ' static [ & ' static str ] ;
54
-
39
+ const VARIANT_LANG_ITEM : LangItem ;
55
40
const BAD_METHOD_NAME : & ' static str ;
56
- const BAD_VARIANT_NAME : & ' static str ;
57
- const BAD_VARIANT_QPATH : & ' static [ & ' static str ] ;
58
-
59
41
const GOOD_METHOD_NAME : & ' static str ;
60
42
61
- fn no_op_msg ( ) -> String {
62
- format ! (
43
+ fn no_op_msg ( cx : & LateContext < ' _ > ) -> Option < String > {
44
+ let variant_id = cx. tcx . lang_items ( ) . require ( Self :: VARIANT_LANG_ITEM ) . ok ( ) ?;
45
+ let item_id = cx. tcx . parent ( variant_id) ?;
46
+ Some ( format ! (
63
47
"using `{}.{}({})`, which is a no-op" ,
64
- Self :: TYPE_NAME ,
48
+ cx . tcx . item_name ( item_id ) ,
65
49
Self :: BAD_METHOD_NAME ,
66
- Self :: BAD_VARIANT_NAME
67
- )
50
+ cx . tcx . item_name ( variant_id ) ,
51
+ ) )
68
52
}
69
53
70
- fn lint_msg ( ) -> String {
71
- format ! (
54
+ fn lint_msg ( cx : & LateContext < ' _ > ) -> Option < String > {
55
+ let variant_id = cx. tcx . lang_items ( ) . require ( Self :: VARIANT_LANG_ITEM ) . ok ( ) ?;
56
+ let item_id = cx. tcx . parent ( variant_id) ?;
57
+ Some ( format ! (
72
58
"using `{}.{}(|x| {}(y))`, which is more succinctly expressed as `{}(|x| y)`" ,
73
- Self :: TYPE_NAME ,
59
+ cx . tcx . item_name ( item_id ) ,
74
60
Self :: BAD_METHOD_NAME ,
75
- Self :: BAD_VARIANT_NAME ,
61
+ cx . tcx . item_name ( variant_id ) ,
76
62
Self :: GOOD_METHOD_NAME
77
- )
63
+ ) )
78
64
}
79
65
80
66
fn lint_closure_autofixable (
@@ -85,17 +71,12 @@ pub(crate) trait BindInsteadOfMap {
85
71
closure_args_span : Span ,
86
72
) -> bool {
87
73
if_chain ! {
88
- if let hir:: ExprKind :: Call ( ref some_expr, ref some_args) = closure_expr. kind;
89
- if let hir:: ExprKind :: Path ( ref qpath) = some_expr. kind;
90
- if match_qpath( qpath, Self :: BAD_VARIANT_QPATH ) ;
91
- if some_args. len( ) == 1 ;
74
+ if let hir:: ExprKind :: Call ( ref some_expr, [ inner_expr] ) = closure_expr. kind;
75
+ if let hir:: ExprKind :: Path ( QPath :: Resolved ( _, path) ) = some_expr. kind;
76
+ if Self :: is_variant( cx, path. res) ;
77
+ if !contains_return( inner_expr) ;
78
+ if let Some ( msg) = Self :: lint_msg( cx) ;
92
79
then {
93
- let inner_expr = & some_args[ 0 ] ;
94
-
95
- if contains_return( inner_expr) {
96
- return false ;
97
- }
98
-
99
80
let some_inner_snip = if inner_expr. span. from_expansion( ) {
100
81
snippet_with_macro_callsite( cx, inner_expr. span, "_" )
101
82
} else {
@@ -109,7 +90,7 @@ pub(crate) trait BindInsteadOfMap {
109
90
cx,
110
91
BIND_INSTEAD_OF_MAP ,
111
92
expr. span,
112
- Self :: lint_msg ( ) . as_ref ( ) ,
93
+ & msg ,
113
94
"try this" ,
114
95
note,
115
96
Applicability :: MachineApplicable ,
@@ -126,41 +107,46 @@ pub(crate) trait BindInsteadOfMap {
126
107
let can_sugg: bool = find_all_ret_expressions ( cx, closure_expr, |ret_expr| {
127
108
if_chain ! {
128
109
if !in_macro( ret_expr. span) ;
129
- if let hir:: ExprKind :: Call ( ref func_path, ref args) = ret_expr. kind;
130
- if let hir:: ExprKind :: Path ( ref qpath) = func_path. kind;
131
- if match_qpath( qpath, Self :: BAD_VARIANT_QPATH ) ;
132
- if args. len( ) == 1 ;
133
- if !contains_return( & args[ 0 ] ) ;
110
+ if let hir:: ExprKind :: Call ( ref func_path, [ arg] ) = ret_expr. kind;
111
+ if let hir:: ExprKind :: Path ( QPath :: Resolved ( _, path) ) = func_path. kind;
112
+ if Self :: is_variant( cx, path. res) ;
113
+ if !contains_return( arg) ;
134
114
then {
135
- suggs. push( ( ret_expr. span, args [ 0 ] . span. source_callsite( ) ) ) ;
115
+ suggs. push( ( ret_expr. span, arg . span. source_callsite( ) ) ) ;
136
116
true
137
117
} else {
138
118
false
139
119
}
140
120
}
141
121
} ) ;
142
-
143
- if can_sugg {
144
- span_lint_and_then ( cx, BIND_INSTEAD_OF_MAP , expr. span , Self :: lint_msg ( ) . as_ref ( ) , |diag| {
145
- multispan_sugg_with_applicability (
146
- diag,
147
- "try this" ,
148
- Applicability :: MachineApplicable ,
149
- std:: iter:: once ( ( * method_calls ( expr, 1 ) . 2 . get ( 0 ) . unwrap ( ) , Self :: GOOD_METHOD_NAME . into ( ) ) ) . chain (
150
- suggs
151
- . into_iter ( )
152
- . map ( |( span1, span2) | ( span1, snippet ( cx, span2, "_" ) . into ( ) ) ) ,
153
- ) ,
154
- )
155
- } ) ;
156
- }
157
- can_sugg
122
+ let ( span, msg) = if_chain ! {
123
+ if can_sugg;
124
+ if let hir:: ExprKind :: MethodCall ( _, span, ..) = expr. kind;
125
+ if let Some ( msg) = Self :: lint_msg( cx) ;
126
+ then { ( span, msg) } else { return false ; }
127
+ } ;
128
+ span_lint_and_then ( cx, BIND_INSTEAD_OF_MAP , expr. span , & msg, |diag| {
129
+ multispan_sugg_with_applicability (
130
+ diag,
131
+ "try this" ,
132
+ Applicability :: MachineApplicable ,
133
+ std:: iter:: once ( ( span, Self :: GOOD_METHOD_NAME . into ( ) ) ) . chain (
134
+ suggs
135
+ . into_iter ( )
136
+ . map ( |( span1, span2) | ( span1, snippet ( cx, span2, "_" ) . into ( ) ) ) ,
137
+ ) ,
138
+ )
139
+ } ) ;
140
+ true
158
141
}
159
142
160
143
/// Lint use of `_.and_then(|x| Some(y))` for `Option`s
161
144
fn check ( cx : & LateContext < ' _ > , expr : & hir:: Expr < ' _ > , recv : & hir:: Expr < ' _ > , arg : & hir:: Expr < ' _ > ) -> bool {
162
- if !match_type ( cx, cx. typeck_results ( ) . expr_ty ( recv) , Self :: TYPE_QPATH ) {
163
- return false ;
145
+ if_chain ! {
146
+ if let Some ( adt) = cx. typeck_results( ) . expr_ty( recv) . ty_adt_def( ) ;
147
+ if let Ok ( vid) = cx. tcx. lang_items( ) . require( Self :: VARIANT_LANG_ITEM ) ;
148
+ if Some ( adt. did) == cx. tcx. parent( vid) ;
149
+ then { } else { return false ; }
164
150
}
165
151
166
152
match arg. kind {
@@ -175,19 +161,30 @@ pub(crate) trait BindInsteadOfMap {
175
161
}
176
162
} ,
177
163
// `_.and_then(Some)` case, which is no-op.
178
- hir:: ExprKind :: Path ( ref qpath) if match_qpath ( qpath, Self :: BAD_VARIANT_QPATH ) => {
179
- span_lint_and_sugg (
180
- cx,
181
- BIND_INSTEAD_OF_MAP ,
182
- expr. span ,
183
- Self :: no_op_msg ( ) . as_ref ( ) ,
184
- "use the expression directly" ,
185
- snippet ( cx, recv. span , ".." ) . into ( ) ,
186
- Applicability :: MachineApplicable ,
187
- ) ;
164
+ hir:: ExprKind :: Path ( QPath :: Resolved ( _, path) ) if Self :: is_variant ( cx, path. res ) => {
165
+ if let Some ( msg) = Self :: no_op_msg ( cx) {
166
+ span_lint_and_sugg (
167
+ cx,
168
+ BIND_INSTEAD_OF_MAP ,
169
+ expr. span ,
170
+ & msg,
171
+ "use the expression directly" ,
172
+ snippet ( cx, recv. span , ".." ) . into ( ) ,
173
+ Applicability :: MachineApplicable ,
174
+ ) ;
175
+ }
188
176
true
189
177
} ,
190
178
_ => false ,
191
179
}
192
180
}
181
+
182
+ fn is_variant ( cx : & LateContext < ' _ > , res : Res ) -> bool {
183
+ if let Res :: Def ( DefKind :: Ctor ( CtorOf :: Variant , CtorKind :: Fn ) , id) = res {
184
+ if let Ok ( variant_id) = cx. tcx . lang_items ( ) . require ( Self :: VARIANT_LANG_ITEM ) {
185
+ return cx. tcx . parent ( id) == Some ( variant_id) ;
186
+ }
187
+ }
188
+ false
189
+ }
193
190
}
0 commit comments