@@ -70,38 +70,68 @@ declare_clippy_lint! {
70
70
71
71
declare_lint_pass ! ( OptionIfLetElse => [ OPTION_IF_LET_ELSE ] ) ;
72
72
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 ) > {
78
78
if_chain ! {
79
79
if let ExprKind :: Match ( let_body, arms, MatchSource :: IfLetDesugar { contains_else_clause: true } ) = & expr. kind;
80
80
if arms. len( ) == 2 ;
81
81
if match_type( cx, & cx. tables. expr_ty( let_body) , & paths:: OPTION ) ;
82
82
if let PatKind :: TupleStruct ( path, & [ inner_pat] , _) = & arms[ 0 ] . pat. kind;
83
83
if let PatKind :: Binding ( _, _, id, _) = & inner_pat. kind;
84
84
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 ) {
86
86
( arms[ 0 ] . body, arms[ 1 ] . body)
87
87
} else {
88
88
( arms[ 1 ] . body, arms[ 0 ] . body)
89
89
} ;
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
+ }
92
107
} else {
93
- some_body
108
+ return None ;
94
109
} ;
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
+ }
97
126
} else {
98
- none_body
127
+ return None ;
99
128
} ;
100
129
let capture_name = id. name. to_ident_string( ) ;
101
130
Some ( (
102
131
format!( "{}" , Sugg :: hir( cx, let_body, ".." ) ) ,
132
+ format!( "{}" , method_sugg) ,
103
133
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, ".." ) )
105
135
) )
106
136
} else {
107
137
None
@@ -111,15 +141,15 @@ fn detect_option_if_let_else<'a>(cx: &LateContext<'_, '_>, expr: &'a Expr<'_>) -
111
141
112
142
impl < ' a , ' tcx > LateLintPass < ' a , ' tcx > for OptionIfLetElse {
113
143
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) {
115
145
span_lint_and_sugg (
116
146
cx,
117
147
OPTION_IF_LET_ELSE ,
118
148
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 ( ) ,
120
150
"try" ,
121
- format ! ( "{}.map_or ({}, {})" , option, else_func, map) ,
122
- Applicability :: MaybeIncorrect ,
151
+ format ! ( "{}.{} ({}, {})" , option, method , else_func, map) ,
152
+ Applicability :: MachineApplicable ,
123
153
) ;
124
154
}
125
155
}
0 commit comments