11use clippy_utils:: diagnostics:: span_lint_and_help;
2+ use clippy_utils:: eager_or_lazy:: switch_to_eager_eval;
23use clippy_utils:: source:: snippet_with_macro_callsite;
34use clippy_utils:: { contains_return, higher, is_else_clause, is_lang_ctor, meets_msrv, msrvs, peel_blocks} ;
4- use if_chain:: if_chain;
55use rustc_hir:: LangItem :: { OptionNone , OptionSome } ;
66use rustc_hir:: { Expr , ExprKind , Stmt , StmtKind } ;
77use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
@@ -11,10 +11,12 @@ use rustc_session::{declare_tool_lint, impl_lint_pass};
1111
1212declare_clippy_lint ! {
1313 /// ### 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 `.
1515 ///
1616 /// ### 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`.
1820 ///
1921 /// ### Example
2022 /// ```rust
@@ -39,7 +41,7 @@ declare_clippy_lint! {
3941 #[ clippy:: version = "1.53.0" ]
4042 pub IF_THEN_SOME_ELSE_NONE ,
4143 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 `"
4345}
4446
4547pub struct IfThenSomeElseNone {
@@ -56,7 +58,7 @@ impl IfThenSomeElseNone {
5658impl_lint_pass ! ( IfThenSomeElseNone => [ IF_THEN_SOME_ELSE_NONE ] ) ;
5759
5860impl < ' 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 > ) {
6062 if !meets_msrv ( self . msrv , msrvs:: BOOL_THEN ) {
6163 return ;
6264 }
@@ -70,43 +72,47 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
7072 return ;
7173 }
7274
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+ ) ;
110116 }
111117 }
112118
0 commit comments