1
1
use clippy_utils:: diagnostics:: span_lint_and_help;
2
+ use clippy_utils:: eager_or_lazy:: switch_to_eager_eval;
2
3
use clippy_utils:: source:: snippet_with_macro_callsite;
3
4
use clippy_utils:: { contains_return, higher, is_else_clause, is_lang_ctor, meets_msrv, msrvs, peel_blocks} ;
4
- use if_chain:: if_chain;
5
5
use rustc_hir:: LangItem :: { OptionNone , OptionSome } ;
6
6
use rustc_hir:: { Expr , ExprKind , Stmt , StmtKind } ;
7
7
use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
@@ -11,10 +11,12 @@ use rustc_session::{declare_tool_lint, impl_lint_pass};
11
11
12
12
declare_clippy_lint ! {
13
13
/// ### What it does
14
- /// Checks for if-else that could be written to `bool::then`.
14
+ /// Checks for if-else that could be written using either `bool::then` or `bool::then_some `.
15
15
///
16
16
/// ### Why is this bad?
17
- /// Looks a little redundant. Using `bool::then` helps it have less lines of code.
17
+ /// Looks a little redundant. Using `bool::then` is more concise and incurs no loss of clarity.
18
+ /// For simple calculations and known values, use `bool::then_some`, which is eagerly evaluated
19
+ /// in comparison to `bool::then`.
18
20
///
19
21
/// ### Example
20
22
/// ```rust
@@ -39,7 +41,7 @@ declare_clippy_lint! {
39
41
#[ clippy:: version = "1.53.0" ]
40
42
pub IF_THEN_SOME_ELSE_NONE ,
41
43
restriction,
42
- "Finds if-else that could be written using `bool::then`"
44
+ "Finds if-else that could be written using either `bool::then` or `bool::then_some `"
43
45
}
44
46
45
47
pub struct IfThenSomeElseNone {
@@ -56,7 +58,7 @@ impl IfThenSomeElseNone {
56
58
impl_lint_pass ! ( IfThenSomeElseNone => [ IF_THEN_SOME_ELSE_NONE ] ) ;
57
59
58
60
impl < ' tcx > LateLintPass < ' tcx > for IfThenSomeElseNone {
59
- fn check_expr ( & mut self , cx : & LateContext < ' _ > , expr : & ' tcx Expr < ' _ > ) {
61
+ fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > ) {
60
62
if !meets_msrv ( self . msrv , msrvs:: BOOL_THEN ) {
61
63
return ;
62
64
}
@@ -70,43 +72,47 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
70
72
return ;
71
73
}
72
74
73
- if_chain ! {
74
- if let Some ( higher:: If { cond, then, r#else: Some ( els) } ) = higher:: If :: hir( expr) ;
75
- if let ExprKind :: Block ( then_block, _) = then. kind;
76
- if let Some ( then_expr) = then_block. expr;
77
- if let ExprKind :: Call ( then_call, [ then_arg] ) = then_expr. kind;
78
- if let ExprKind :: Path ( ref then_call_qpath) = then_call. kind;
79
- if is_lang_ctor( cx, then_call_qpath, OptionSome ) ;
80
- if let ExprKind :: Path ( ref qpath) = peel_blocks( els) . kind;
81
- if is_lang_ctor( cx, qpath, OptionNone ) ;
82
- if !stmts_contains_early_return( then_block. stmts) ;
83
- then {
84
- let cond_snip = snippet_with_macro_callsite( cx, cond. span, "[condition]" ) ;
85
- let cond_snip = if matches!( cond. kind, ExprKind :: Unary ( _, _) | ExprKind :: Binary ( _, _, _) ) {
86
- format!( "({})" , cond_snip)
87
- } else {
88
- cond_snip. into_owned( )
89
- } ;
90
- let arg_snip = snippet_with_macro_callsite( cx, then_arg. span, "" ) ;
91
- let closure_body = if then_block. stmts. is_empty( ) {
92
- arg_snip. into_owned( )
93
- } else {
94
- format!( "{{ /* snippet */ {} }}" , arg_snip)
95
- } ;
96
- let help = format!(
97
- "consider using `bool::then` like: `{}.then(|| {})`" ,
98
- cond_snip,
99
- closure_body,
100
- ) ;
101
- span_lint_and_help(
102
- cx,
103
- IF_THEN_SOME_ELSE_NONE ,
104
- expr. span,
105
- "this could be simplified with `bool::then`" ,
106
- None ,
107
- & help,
108
- ) ;
109
- }
75
+ if let Some ( higher:: If { cond, then, r#else : Some ( els) } ) = higher:: If :: hir ( expr)
76
+ && let ExprKind :: Block ( then_block, _) = then. kind
77
+ && let Some ( then_expr) = then_block. expr
78
+ && let ExprKind :: Call ( then_call, [ then_arg] ) = then_expr. kind
79
+ && let ExprKind :: Path ( ref then_call_qpath) = then_call. kind
80
+ && is_lang_ctor ( cx, then_call_qpath, OptionSome )
81
+ && let ExprKind :: Path ( ref qpath) = peel_blocks ( els) . kind
82
+ && is_lang_ctor ( cx, qpath, OptionNone )
83
+ && !stmts_contains_early_return ( then_block. stmts )
84
+ {
85
+ let cond_snip = snippet_with_macro_callsite ( cx, cond. span , "[condition]" ) ;
86
+ let cond_snip = if matches ! ( cond. kind, ExprKind :: Unary ( _, _) | ExprKind :: Binary ( _, _, _) ) {
87
+ format ! ( "({})" , cond_snip)
88
+ } else {
89
+ cond_snip. into_owned ( )
90
+ } ;
91
+ let arg_snip = snippet_with_macro_callsite ( cx, then_arg. span , "" ) ;
92
+ let mut method_body = if then_block. stmts . is_empty ( ) {
93
+ arg_snip. into_owned ( )
94
+ } else {
95
+ format ! ( "{{ /* snippet */ {} }}" , arg_snip)
96
+ } ;
97
+ let method_name = if switch_to_eager_eval ( cx, expr) && meets_msrv ( self . msrv , msrvs:: BOOL_THEN_SOME ) {
98
+ "then_some"
99
+ } else {
100
+ method_body. insert_str ( 0 , "|| " ) ;
101
+ "then"
102
+ } ;
103
+
104
+ let help = format ! (
105
+ "consider using `bool::{}` like: `{}.{}({})`" ,
106
+ method_name, cond_snip, method_name, method_body,
107
+ ) ;
108
+ span_lint_and_help (
109
+ cx,
110
+ IF_THEN_SOME_ELSE_NONE ,
111
+ expr. span ,
112
+ & format ! ( "this could be simplified with `bool::{}`" , method_name) ,
113
+ None ,
114
+ & help,
115
+ ) ;
110
116
}
111
117
}
112
118
0 commit comments