@@ -70,38 +70,68 @@ declare_clippy_lint! {
7070
7171declare_lint_pass ! ( OptionIfLetElse => [ OPTION_IF_LET_ELSE ] ) ;
7272
73- /// If this expression is the option if let/else construct we're
74- /// detecting, then this function returns Some(Option<_> compared,
75- /// expression if Option is Some , expression if Option is None).
76- /// Otherwise, it returns None.
77- fn detect_option_if_let_else < ' a > ( cx : & LateContext < ' _ , ' _ > , expr : & ' a Expr < ' _ > ) -> Option < ( String , String , String ) > {
73+ /// If this expression is the option if let/else construct we're detecting, then
74+ /// this function returns Some(Option<_> tested against, the method to call on
75+ /// the object (map_or or map_or_else) , expression if Option is Some,
76+ /// expression if Option is None). Otherwise, it returns None.
77+ fn detect_option_if_let_else < ' a > ( cx : & LateContext < ' _ , ' _ > , expr : & ' a Expr < ' _ > ) -> Option < ( String , String , String , String ) > {
7878 if_chain ! {
7979 if let ExprKind :: Match ( let_body, arms, MatchSource :: IfLetDesugar { contains_else_clause: true } ) = & expr. kind;
8080 if arms. len( ) == 2 ;
8181 if match_type( cx, & cx. tables. expr_ty( let_body) , & paths:: OPTION ) ;
8282 if let PatKind :: TupleStruct ( path, & [ inner_pat] , _) = & arms[ 0 ] . pat. kind;
8383 if let PatKind :: Binding ( _, _, id, _) = & inner_pat. kind;
8484 then {
85- let ( mut some_body, mut none_body) = if match_qpath( path, & paths:: OPTION_SOME ) {
85+ let ( some_body, none_body) = if match_qpath( path, & paths:: OPTION_SOME ) {
8686 ( arms[ 0 ] . body, arms[ 1 ] . body)
8787 } else {
8888 ( arms[ 1 ] . body, arms[ 0 ] . body)
8989 } ;
90- some_body = if let ExprKind :: Block ( Block { stmts: & [ ] , expr: Some ( expr) , .. } , _) = & some_body. kind {
91- expr
90+ let some_body = if let ExprKind :: Block ( Block { stmts: statements, expr: Some ( expr) , .. } , _) = & some_body. kind {
91+ if let & [ ] = & statements {
92+ expr
93+ } else {
94+ // Don't trigger if there is a return, break, or continue inside
95+ if statements. iter( ) . any( |statement| {
96+ match & statement {
97+ Stmt { kind: StmtKind :: Semi ( Expr { kind: ExprKind :: Ret ( ..) , ..} ) , .. } => true ,
98+ Stmt { kind: StmtKind :: Semi ( Expr { kind: ExprKind :: Break ( ..) , ..} ) , .. } => true ,
99+ Stmt { kind: StmtKind :: Semi ( Expr { kind: ExprKind :: Continue ( ..) , ..} ) , .. } => true ,
100+ _ => false ,
101+ }
102+ } ) {
103+ return None ;
104+ }
105+ some_body
106+ }
92107 } else {
93- some_body
108+ return None ;
94109 } ;
95- none_body = if let ExprKind :: Block ( Block { stmts: & [ ] , expr: Some ( expr) , .. } , _) = & none_body. kind {
96- expr
110+ let ( none_body, method_sugg) = if let ExprKind :: Block ( Block { stmts: statements, expr: Some ( expr) , .. } , _) = & none_body. kind {
111+ if let & [ ] = & statements {
112+ ( expr, "map_or" )
113+ } else {
114+ if statements. iter( ) . any( |statement| {
115+ match & statement {
116+ Stmt { kind: StmtKind :: Semi ( Expr { kind: ExprKind :: Ret ( ..) , ..} ) , .. } => true ,
117+ Stmt { kind: StmtKind :: Semi ( Expr { kind: ExprKind :: Break ( ..) , ..} ) , .. } => true ,
118+ Stmt { kind: StmtKind :: Semi ( Expr { kind: ExprKind :: Continue ( ..) , ..} ) , .. } => true ,
119+ _ => false ,
120+ }
121+ } ) {
122+ return None ;
123+ }
124+ ( & none_body, "map_or_else" )
125+ }
97126 } else {
98- none_body
127+ return None ;
99128 } ;
100129 let capture_name = id. name. to_ident_string( ) ;
101130 Some ( (
102131 format!( "{}" , Sugg :: hir( cx, let_body, ".." ) ) ,
132+ format!( "{}" , method_sugg) ,
103133 format!( "|{}| {}" , capture_name, Sugg :: hir( cx, some_body, ".." ) ) ,
104- format!( "{}" , Sugg :: hir( cx, none_body, ".." ) )
134+ format!( "{}{}" , if method_sugg == "map_or" { "" } else { "||" } , Sugg :: hir( cx, none_body, ".." ) )
105135 ) )
106136 } else {
107137 None
@@ -111,15 +141,15 @@ fn detect_option_if_let_else<'a>(cx: &LateContext<'_, '_>, expr: &'a Expr<'_>) -
111141
112142impl < ' a , ' tcx > LateLintPass < ' a , ' tcx > for OptionIfLetElse {
113143 fn check_expr ( & mut self , cx : & LateContext < ' a , ' tcx > , expr : & ' tcx Expr < ' _ > ) {
114- if let Some ( ( option, map, else_func) ) = detect_option_if_let_else ( cx, expr) {
144+ if let Some ( ( option, method , map, else_func) ) = detect_option_if_let_else ( cx, expr) {
115145 span_lint_and_sugg (
116146 cx,
117147 OPTION_IF_LET_ELSE ,
118148 expr. span ,
119- "use Option::map_or instead of an if let/else" ,
149+ format ! ( "use Option::{} instead of an if let/else" , method ) . as_str ( ) ,
120150 "try" ,
121- format ! ( "{}.map_or ({}, {})" , option, else_func, map) ,
122- Applicability :: MaybeIncorrect ,
151+ format ! ( "{}.{} ({}, {})" , option, method , else_func, map) ,
152+ Applicability :: MachineApplicable ,
123153 ) ;
124154 }
125155 }
0 commit comments