@@ -56,7 +56,7 @@ fn create_e0004(
56
56
struct_span_err ! ( sess, sp, E0004 , "{}" , & error_message)
57
57
}
58
58
59
- #[ derive( Copy , Clone , PartialEq ) ]
59
+ #[ derive( Debug , Copy , Clone , PartialEq ) ]
60
60
enum RefutableFlag {
61
61
Irrefutable ,
62
62
Refutable ,
@@ -151,18 +151,22 @@ impl<'thir, 'tcx> Visitor<'thir, 'tcx> for MatchVisitor<'thir, '_, 'tcx> {
151
151
} ;
152
152
self . check_match ( scrutinee, arms, source, ex. span ) ;
153
153
}
154
- ExprKind :: Let { box ref pat, expr } if ! matches ! ( self . let_source , LetSource :: None ) => {
154
+ ExprKind :: Let { box ref pat, expr } => {
155
155
self . check_let ( pat, Some ( expr) , ex. span ) ;
156
156
}
157
157
ExprKind :: LogicalOp { op : LogicalOp :: And , .. }
158
158
if !matches ! ( self . let_source, LetSource :: None ) =>
159
159
{
160
- self . check_let_chain ( ex) ;
160
+ let mut chain_refutabilities = Vec :: new ( ) ;
161
+ let Ok ( ( ) ) = self . visit_land ( ex, & mut chain_refutabilities) else { return } ;
162
+ // If at least one of the operands is a `let ... = ...`.
163
+ if chain_refutabilities. iter ( ) . any ( |x| x. is_some ( ) ) {
164
+ self . check_let_chain ( chain_refutabilities, ex. span ) ;
165
+ }
166
+ return ;
161
167
}
162
168
_ => { }
163
169
} ;
164
- // If we got e.g. `let pat1 = x1 && let pat2 = x2` above, we will now traverse the two
165
- // `let`s. In order not to check them twice we set `LetSource::None`.
166
170
self . with_let_source ( LetSource :: None , |this| visit:: walk_expr ( this, ex) ) ;
167
171
}
168
172
@@ -212,6 +216,57 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
212
216
}
213
217
}
214
218
219
+ /// Visit a nested chain of `&&`. This must call `visit_expr` on the expressions we are not
220
+ /// handling ourselves.
221
+ fn visit_land (
222
+ & mut self ,
223
+ ex : & Expr < ' tcx > ,
224
+ accumulator : & mut Vec < Option < ( Span , RefutableFlag ) > > ,
225
+ ) -> Result < ( ) , ErrorGuaranteed > {
226
+ match ex. kind {
227
+ ExprKind :: Scope { value, lint_level, .. } => self . with_lint_level ( lint_level, |this| {
228
+ this. visit_land ( & this. thir [ value] , accumulator)
229
+ } ) ,
230
+ ExprKind :: LogicalOp { op : LogicalOp :: And , lhs, rhs } => {
231
+ let res_lhs = self . visit_land ( & self . thir [ lhs] , accumulator) ;
232
+ let res_rhs = self . visit_land_rhs ( & self . thir [ rhs] ) ?;
233
+ accumulator. push ( res_rhs) ;
234
+ res_lhs
235
+ }
236
+ _ => {
237
+ let res = self . visit_land_rhs ( ex) ?;
238
+ accumulator. push ( res) ;
239
+ Ok ( ( ) )
240
+ }
241
+ }
242
+ }
243
+
244
+ /// Visit the right-hand-side of a `&&`. Used for if-let chains. Returns `Some` if the
245
+ /// expression was ultimately a `let ... = ...`, and `None` if it was a normal boolean
246
+ /// expression. This must call `visit_expr` on the expressions we are not handling ourselves.
247
+ fn visit_land_rhs (
248
+ & mut self ,
249
+ ex : & Expr < ' tcx > ,
250
+ ) -> Result < Option < ( Span , RefutableFlag ) > , ErrorGuaranteed > {
251
+ match ex. kind {
252
+ ExprKind :: Scope { value, lint_level, .. } => {
253
+ self . with_lint_level ( lint_level, |this| this. visit_land_rhs ( & this. thir [ value] ) )
254
+ }
255
+ ExprKind :: Let { box ref pat, expr } => {
256
+ self . with_let_source ( LetSource :: None , |this| {
257
+ this. visit_expr ( & this. thir ( ) [ expr] ) ;
258
+ } ) ;
259
+ Ok ( Some ( ( ex. span , self . is_let_irrefutable ( pat) ?) ) )
260
+ }
261
+ _ => {
262
+ self . with_let_source ( LetSource :: None , |this| {
263
+ this. visit_expr ( ex) ;
264
+ } ) ;
265
+ Ok ( None )
266
+ }
267
+ }
268
+ }
269
+
215
270
fn lower_pattern (
216
271
& mut self ,
217
272
cx : & MatchCheckCtxt < ' p , ' tcx > ,
@@ -249,8 +304,8 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
249
304
if let LetSource :: PlainLet = self . let_source {
250
305
self . check_binding_is_irrefutable ( pat, "local binding" , Some ( span) )
251
306
} else {
252
- let Ok ( irrefutable ) = self . is_let_irrefutable ( pat) else { return } ;
253
- if irrefutable {
307
+ let Ok ( refutability ) = self . is_let_irrefutable ( pat) else { return } ;
308
+ if matches ! ( refutability , Irrefutable ) {
254
309
report_irrefutable_let_patterns (
255
310
self . tcx ,
256
311
self . lint_level ,
@@ -321,81 +376,27 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
321
376
}
322
377
323
378
#[ instrument( level = "trace" , skip( self ) ) ]
324
- fn check_let_chain ( & mut self , expr : & Expr < ' tcx > ) {
379
+ fn check_let_chain (
380
+ & mut self ,
381
+ chain_refutabilities : Vec < Option < ( Span , RefutableFlag ) > > ,
382
+ whole_chain_span : Span ,
383
+ ) {
325
384
assert ! ( self . let_source != LetSource :: None ) ;
326
- let top_expr_span = expr. span ;
327
-
328
- // Lint level enclosing `next_expr`.
329
- let mut next_expr_lint_level = self . lint_level ;
330
-
331
- // Obtain the refutabilities of all exprs in the chain,
332
- // and record chain members that aren't let exprs.
333
- let mut chain_refutabilities = Vec :: new ( ) ;
334
-
335
- let mut got_error = false ;
336
- let mut next_expr = Some ( expr) ;
337
- while let Some ( mut expr) = next_expr {
338
- while let ExprKind :: Scope { value, lint_level, .. } = expr. kind {
339
- if let LintLevel :: Explicit ( hir_id) = lint_level {
340
- next_expr_lint_level = hir_id
341
- }
342
- expr = & self . thir [ value] ;
343
- }
344
- if let ExprKind :: LogicalOp { op : LogicalOp :: And , lhs, rhs } = expr. kind {
345
- expr = & self . thir [ rhs] ;
346
- // Let chains recurse on the left, so we recurse into the lhs.
347
- next_expr = Some ( & self . thir [ lhs] ) ;
348
- } else {
349
- next_expr = None ;
350
- }
351
-
352
- // Lint level enclosing `expr`.
353
- let mut expr_lint_level = next_expr_lint_level;
354
- // Fast-forward through scopes.
355
- while let ExprKind :: Scope { value, lint_level, .. } = expr. kind {
356
- if let LintLevel :: Explicit ( hir_id) = lint_level {
357
- expr_lint_level = hir_id
358
- }
359
- expr = & self . thir [ value] ;
360
- }
361
- let value = match expr. kind {
362
- ExprKind :: Let { box ref pat, expr : _ } => {
363
- self . with_lint_level ( LintLevel :: Explicit ( expr_lint_level) , |this| {
364
- match this. is_let_irrefutable ( pat) {
365
- Ok ( irrefutable) => Some ( ( expr. span , !irrefutable) ) ,
366
- Err ( _) => {
367
- got_error = true ;
368
- None
369
- }
370
- }
371
- } )
372
- }
373
- _ => None ,
374
- } ;
375
- chain_refutabilities. push ( value) ;
376
- }
377
- debug ! ( ?chain_refutabilities) ;
378
- chain_refutabilities. reverse ( ) ;
379
-
380
- if got_error {
381
- return ;
382
- }
383
385
384
- // Emit the actual warnings.
385
- if chain_refutabilities. iter ( ) . all ( |r| matches ! ( * r, Some ( ( _, false ) ) ) ) {
386
+ if chain_refutabilities. iter ( ) . all ( |r| matches ! ( * r, Some ( ( _, Irrefutable ) ) ) ) {
386
387
// The entire chain is made up of irrefutable `let` statements
387
388
report_irrefutable_let_patterns (
388
389
self . tcx ,
389
390
self . lint_level ,
390
391
self . let_source ,
391
392
chain_refutabilities. len ( ) ,
392
- top_expr_span ,
393
+ whole_chain_span ,
393
394
) ;
394
395
return ;
395
396
}
396
397
397
398
if let Some ( until) =
398
- chain_refutabilities. iter ( ) . position ( |r| !matches ! ( * r, Some ( ( _, false ) ) ) )
399
+ chain_refutabilities. iter ( ) . position ( |r| !matches ! ( * r, Some ( ( _, Irrefutable ) ) ) )
399
400
&& until > 0
400
401
{
401
402
// The chain has a non-zero prefix of irrefutable `let` statements.
@@ -423,7 +424,7 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
423
424
}
424
425
425
426
if let Some ( from) =
426
- chain_refutabilities. iter ( ) . rposition ( |r| !matches ! ( * r, Some ( ( _, false ) ) ) )
427
+ chain_refutabilities. iter ( ) . rposition ( |r| !matches ! ( * r, Some ( ( _, Irrefutable ) ) ) )
427
428
&& from != ( chain_refutabilities. len ( ) - 1 )
428
429
{
429
430
// The chain has a non-empty suffix of irrefutable `let` statements
@@ -453,14 +454,14 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
453
454
Ok ( ( cx, report) )
454
455
}
455
456
456
- fn is_let_irrefutable ( & mut self , pat : & Pat < ' tcx > ) -> Result < bool , ErrorGuaranteed > {
457
+ fn is_let_irrefutable ( & mut self , pat : & Pat < ' tcx > ) -> Result < RefutableFlag , ErrorGuaranteed > {
457
458
let ( cx, report) = self . analyze_binding ( pat, Refutable ) ?;
458
- // Report if the pattern is unreachable, which can only occur when the type is
459
- // uninhabited. This also reports unreachable sub-patterns.
459
+ // Report if the pattern is unreachable, which can only occur when the type is uninhabited.
460
+ // This also reports unreachable sub-patterns.
460
461
report_arm_reachability ( & cx, & report) ;
461
- // If the list of witnesses is empty, the match is exhaustive,
462
- // i.e. the `if let` pattern is irrefutable.
463
- Ok ( report. non_exhaustiveness_witnesses . is_empty ( ) )
462
+ // If the list of witnesses is empty, the match is exhaustive, i.e. the `if let` pattern is
463
+ // irrefutable.
464
+ Ok ( if report. non_exhaustiveness_witnesses . is_empty ( ) { Irrefutable } else { Refutable } )
464
465
}
465
466
466
467
#[ instrument( level = "trace" , skip( self ) ) ]
0 commit comments