11use super :: { contains_return, BIND_INSTEAD_OF_MAP } ;
22use clippy_utils:: diagnostics:: { multispan_sugg_with_applicability, span_lint_and_sugg, span_lint_and_then} ;
33use 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} ;
65use if_chain:: if_chain;
76use rustc_errors:: Applicability ;
87use rustc_hir as hir;
8+ use rustc_hir:: def:: { CtorKind , CtorOf , DefKind , Res } ;
9+ use rustc_hir:: { LangItem , QPath } ;
910use rustc_lint:: LateContext ;
11+ use rustc_middle:: ty:: DefIdTree ;
1012use rustc_span:: Span ;
1113
1214pub ( crate ) struct OptionAndThenSome ;
1315
1416impl 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 ;
1818 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-
2219 const GOOD_METHOD_NAME : & ' static str = "map" ;
2320}
2421
2522pub ( crate ) struct ResultAndThenOk ;
2623
2724impl 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 ;
3126 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-
3527 const GOOD_METHOD_NAME : & ' static str = "map" ;
3628}
3729
3830pub ( crate ) struct ResultOrElseErrInfo ;
3931
4032impl 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 ;
4434 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-
4835 const GOOD_METHOD_NAME : & ' static str = "map_err" ;
4936}
5037
5138pub ( crate ) trait BindInsteadOfMap {
52- const TYPE_NAME : & ' static str ;
53- const TYPE_QPATH : & ' static [ & ' static str ] ;
54-
39+ const VARIANT_LANG_ITEM : LangItem ;
5540 const BAD_METHOD_NAME : & ' static str ;
56- const BAD_VARIANT_NAME : & ' static str ;
57- const BAD_VARIANT_QPATH : & ' static [ & ' static str ] ;
58-
5941 const GOOD_METHOD_NAME : & ' static str ;
6042
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 ! (
6347 "using `{}.{}({})`, which is a no-op" ,
64- Self :: TYPE_NAME ,
48+ cx . tcx . item_name ( item_id ) ,
6549 Self :: BAD_METHOD_NAME ,
66- Self :: BAD_VARIANT_NAME
67- )
50+ cx . tcx . item_name ( variant_id ) ,
51+ ) )
6852 }
6953
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 ! (
7258 "using `{}.{}(|x| {}(y))`, which is more succinctly expressed as `{}(|x| y)`" ,
73- Self :: TYPE_NAME ,
59+ cx . tcx . item_name ( item_id ) ,
7460 Self :: BAD_METHOD_NAME ,
75- Self :: BAD_VARIANT_NAME ,
61+ cx . tcx . item_name ( variant_id ) ,
7662 Self :: GOOD_METHOD_NAME
77- )
63+ ) )
7864 }
7965
8066 fn lint_closure_autofixable (
@@ -85,17 +71,12 @@ pub(crate) trait BindInsteadOfMap {
8571 closure_args_span : Span ,
8672 ) -> bool {
8773 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) ;
9279 then {
93- let inner_expr = & some_args[ 0 ] ;
94-
95- if contains_return( inner_expr) {
96- return false ;
97- }
98-
9980 let some_inner_snip = if inner_expr. span. from_expansion( ) {
10081 snippet_with_macro_callsite( cx, inner_expr. span, "_" )
10182 } else {
@@ -109,7 +90,7 @@ pub(crate) trait BindInsteadOfMap {
10990 cx,
11091 BIND_INSTEAD_OF_MAP ,
11192 expr. span,
112- Self :: lint_msg ( ) . as_ref ( ) ,
93+ & msg ,
11394 "try this" ,
11495 note,
11596 Applicability :: MachineApplicable ,
@@ -126,41 +107,46 @@ pub(crate) trait BindInsteadOfMap {
126107 let can_sugg: bool = find_all_ret_expressions ( cx, closure_expr, |ret_expr| {
127108 if_chain ! {
128109 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) ;
134114 then {
135- suggs. push( ( ret_expr. span, args [ 0 ] . span. source_callsite( ) ) ) ;
115+ suggs. push( ( ret_expr. span, arg . span. source_callsite( ) ) ) ;
136116 true
137117 } else {
138118 false
139119 }
140120 }
141121 } ) ;
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
158141 }
159142
160143 /// Lint use of `_.and_then(|x| Some(y))` for `Option`s
161144 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 ; }
164150 }
165151
166152 match arg. kind {
@@ -175,19 +161,30 @@ pub(crate) trait BindInsteadOfMap {
175161 }
176162 } ,
177163 // `_.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+ }
188176 true
189177 } ,
190178 _ => false ,
191179 }
192180 }
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+ }
193190}
0 commit comments