@@ -77,53 +77,54 @@ impl<'tcx> LateLintPass<'tcx> for ManualLetElse {
77
77
local. els . is_none ( ) &&
78
78
local. ty . is_none ( ) &&
79
79
init. span . ctxt ( ) == stmt. span . ctxt ( ) &&
80
- let Some ( if_let_or_match) = IfLetOrMatch :: parse ( cx, init) {
81
- match if_let_or_match {
82
- IfLetOrMatch :: IfLet ( if_let_expr, let_pat, if_then, if_else) => if_chain ! {
83
- if expr_is_simple_identity( let_pat, if_then) ;
84
- if let Some ( if_else) = if_else;
85
- if expr_diverges( cx, if_else) ;
86
- then {
87
- emit_manual_let_else( cx, stmt. span, if_let_expr, local. pat, let_pat, if_else) ;
88
- }
89
- } ,
90
- IfLetOrMatch :: Match ( match_expr, arms, source) => {
91
- if self . matches_behaviour == MatchLintBehaviour :: Never {
92
- return ;
93
- }
94
- if source != MatchSource :: Normal {
95
- return ;
96
- }
97
- // Any other number than two arms doesn't (necessarily)
98
- // have a trivial mapping to let else.
99
- if arms. len ( ) != 2 {
100
- return ;
101
- }
102
- // Guards don't give us an easy mapping either
103
- if arms. iter ( ) . any ( |arm| arm. guard . is_some ( ) ) {
104
- return ;
105
- }
106
- let check_types = self . matches_behaviour == MatchLintBehaviour :: WellKnownTypes ;
107
- let diverging_arm_opt = arms
108
- . iter ( )
109
- . enumerate ( )
110
- . find ( |( _, arm) | expr_diverges ( cx, arm. body ) && pat_allowed_for_else ( cx, arm. pat , check_types) ) ;
111
- let Some ( ( idx, diverging_arm) ) = diverging_arm_opt else { return ; } ;
112
- // If the non-diverging arm is the first one, its pattern can be reused in a let/else statement.
113
- // However, if it arrives in second position, its pattern may cover some cases already covered
114
- // by the diverging one.
115
- // TODO: accept the non-diverging arm as a second position if patterns are disjointed.
116
- if idx == 0 {
117
- return ;
118
- }
119
- let pat_arm = & arms[ 1 - idx] ;
120
- if !expr_is_simple_identity ( pat_arm. pat , pat_arm. body ) {
121
- return ;
122
- }
80
+ let Some ( if_let_or_match) = IfLetOrMatch :: parse ( cx, init)
81
+ {
82
+ match if_let_or_match {
83
+ IfLetOrMatch :: IfLet ( if_let_expr, let_pat, if_then, if_else) => if_chain ! {
84
+ if expr_is_simple_identity( let_pat, if_then) ;
85
+ if let Some ( if_else) = if_else;
86
+ if expr_diverges( cx, if_else) ;
87
+ then {
88
+ emit_manual_let_else( cx, stmt. span, if_let_expr, local. pat, let_pat, if_else) ;
89
+ }
90
+ } ,
91
+ IfLetOrMatch :: Match ( match_expr, arms, source) => {
92
+ if self . matches_behaviour == MatchLintBehaviour :: Never {
93
+ return ;
94
+ }
95
+ if source != MatchSource :: Normal {
96
+ return ;
97
+ }
98
+ // Any other number than two arms doesn't (necessarily)
99
+ // have a trivial mapping to let else.
100
+ if arms. len ( ) != 2 {
101
+ return ;
102
+ }
103
+ // Guards don't give us an easy mapping either
104
+ if arms. iter ( ) . any ( |arm| arm. guard . is_some ( ) ) {
105
+ return ;
106
+ }
107
+ let check_types = self . matches_behaviour == MatchLintBehaviour :: WellKnownTypes ;
108
+ let diverging_arm_opt = arms
109
+ . iter ( )
110
+ . enumerate ( )
111
+ . find ( |( _, arm) | expr_diverges ( cx, arm. body ) && pat_allowed_for_else ( cx, arm. pat , check_types) ) ;
112
+ let Some ( ( idx, diverging_arm) ) = diverging_arm_opt else { return ; } ;
113
+ // If the non-diverging arm is the first one, its pattern can be reused in a let/else statement.
114
+ // However, if it arrives in second position, its pattern may cover some cases already covered
115
+ // by the diverging one.
116
+ // TODO: accept the non-diverging arm as a second position if patterns are disjointed.
117
+ if idx == 0 {
118
+ return ;
119
+ }
120
+ let pat_arm = & arms[ 1 - idx] ;
121
+ if !expr_is_simple_identity ( pat_arm. pat , pat_arm. body ) {
122
+ return ;
123
+ }
123
124
124
- emit_manual_let_else ( cx, stmt. span , match_expr, local. pat , pat_arm. pat , diverging_arm. body ) ;
125
- } ,
126
- }
125
+ emit_manual_let_else ( cx, stmt. span , match_expr, local. pat , pat_arm. pat , diverging_arm. body ) ;
126
+ } ,
127
+ }
127
128
} ;
128
129
}
129
130
@@ -145,10 +146,9 @@ fn emit_manual_let_else(
145
146
"this could be rewritten as `let...else`" ,
146
147
|diag| {
147
148
// This is far from perfect, for example there needs to be:
148
- // * mut additions for the bindings
149
- // * renamings of the bindings for `PatKind::Or`
149
+ // * tracking for multi-binding cases: let (foo, bar) = if let (Some(foo), Ok(bar)) = ...
150
+ // * renamings of the bindings for many `PatKind`s like structs, slices, etc.
150
151
// * unused binding collision detection with existing ones
151
- // * putting patterns with at the top level | inside ()
152
152
// for this to be machine applicable.
153
153
let mut app = Applicability :: HasPlaceholders ;
154
154
let ( sn_expr, _) = snippet_with_context ( cx, expr. span , span. ctxt ( ) , "" , & mut app) ;
@@ -159,28 +159,62 @@ fn emit_manual_let_else(
159
159
} else {
160
160
format ! ( "{{ {sn_else} }}" )
161
161
} ;
162
- let sn_bl = match pat. kind {
163
- PatKind :: Or ( ..) => {
164
- let ( sn_pat, _) = snippet_with_context ( cx, pat. span , span. ctxt ( ) , "" , & mut app) ;
165
- format ! ( "({sn_pat})" )
166
- } ,
167
- // Replace the variable name iff `TupleStruct` has one argument like `Variant(v)`.
168
- PatKind :: TupleStruct ( ref w, args, ..) if args. len ( ) == 1 => {
169
- let sn_wrapper = cx. sess ( ) . source_map ( ) . span_to_snippet ( w. span ( ) ) . unwrap_or_default ( ) ;
170
- let ( sn_inner, _) = snippet_with_context ( cx, local. span , span. ctxt ( ) , "" , & mut app) ;
171
- format ! ( "{sn_wrapper}({sn_inner})" )
172
- } ,
173
- _ => {
174
- let ( sn_pat, _) = snippet_with_context ( cx, pat. span , span. ctxt ( ) , "" , & mut app) ;
175
- sn_pat. into_owned ( )
176
- } ,
177
- } ;
162
+ let sn_bl = replace_in_pattern ( cx, span, local, pat, & mut app) ;
178
163
let sugg = format ! ( "let {sn_bl} = {sn_expr} else {else_bl};" ) ;
179
164
diag. span_suggestion ( span, "consider writing" , sugg, app) ;
180
165
} ,
181
166
) ;
182
167
}
183
168
169
+ // replaces the locals in the pattern
170
+ fn replace_in_pattern (
171
+ cx : & LateContext < ' _ > ,
172
+ span : Span ,
173
+ local : & Pat < ' _ > ,
174
+ pat : & Pat < ' _ > ,
175
+ app : & mut Applicability ,
176
+ ) -> String {
177
+ let mut bindings_count = 0 ;
178
+ pat. each_binding_or_first ( & mut |_, _, _, _| bindings_count += 1 ) ;
179
+ // If the pattern creates multiple bindings, exit early,
180
+ // as otherwise we might paste the pattern to the positions of multiple bindings.
181
+ if bindings_count > 1 {
182
+ let ( sn_pat, _) = snippet_with_context ( cx, pat. span , span. ctxt ( ) , "" , app) ;
183
+ return sn_pat. into_owned ( ) ;
184
+ }
185
+
186
+ match pat. kind {
187
+ PatKind :: Binding ( ..) => {
188
+ let ( sn_bdg, _) = snippet_with_context ( cx, local. span , span. ctxt ( ) , "" , app) ;
189
+ return sn_bdg. to_string ( ) ;
190
+ } ,
191
+ PatKind :: Or ( pats) => {
192
+ let patterns = pats
193
+ . iter ( )
194
+ . map ( |pat| replace_in_pattern ( cx, span, local, pat, app) )
195
+ . collect :: < Vec < _ > > ( ) ;
196
+ let or_pat = patterns. join ( " | " ) ;
197
+ return format ! ( "({or_pat})" ) ;
198
+ } ,
199
+ // Replace the variable name iff `TupleStruct` has one argument like `Variant(v)`.
200
+ PatKind :: TupleStruct ( ref w, args, dot_dot_pos) => {
201
+ let mut args = args
202
+ . iter ( )
203
+ . map ( |pat| replace_in_pattern ( cx, span, local, pat, app) )
204
+ . collect :: < Vec < _ > > ( ) ;
205
+ if let Some ( pos) = dot_dot_pos. as_opt_usize ( ) {
206
+ args. insert ( pos, ".." . to_owned ( ) ) ;
207
+ }
208
+ let args = args. join ( ", " ) ;
209
+ let sn_wrapper = cx. sess ( ) . source_map ( ) . span_to_snippet ( w. span ( ) ) . unwrap_or_default ( ) ;
210
+ return format ! ( "{sn_wrapper}({args})" ) ;
211
+ } ,
212
+ _ => { } ,
213
+ }
214
+ let ( sn_pat, _) = snippet_with_context ( cx, pat. span , span. ctxt ( ) , "" , app) ;
215
+ sn_pat. into_owned ( )
216
+ }
217
+
184
218
/// Check whether an expression is divergent. May give false negatives.
185
219
fn expr_diverges ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) -> bool {
186
220
struct V < ' cx , ' tcx > {
0 commit comments