@@ -56,7 +56,7 @@ fn create_e0004(
5656 struct_span_err ! ( sess, sp, E0004 , "{}" , & error_message)
5757}
5858
59- #[ derive( Copy , Clone , PartialEq ) ]
59+ #[ derive( Debug , Copy , Clone , PartialEq ) ]
6060enum RefutableFlag {
6161 Irrefutable ,
6262 Refutable ,
@@ -151,18 +151,22 @@ impl<'thir, 'tcx> Visitor<'thir, 'tcx> for MatchVisitor<'thir, '_, 'tcx> {
151151 } ;
152152 self . check_match ( scrutinee, arms, source, ex. span ) ;
153153 }
154- ExprKind :: Let { box ref pat, expr } if ! matches ! ( self . let_source , LetSource :: None ) => {
154+ ExprKind :: Let { box ref pat, expr } => {
155155 self . check_let ( pat, Some ( expr) , ex. span ) ;
156156 }
157157 ExprKind :: LogicalOp { op : LogicalOp :: And , .. }
158158 if !matches ! ( self . let_source, LetSource :: None ) =>
159159 {
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 ;
161167 }
162168 _ => { }
163169 } ;
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`.
166170 self . with_let_source ( LetSource :: None , |this| visit:: walk_expr ( this, ex) ) ;
167171 }
168172
@@ -212,6 +216,58 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
212216 }
213217 }
214218
219+ /// Visit a nested chain of `&&`. Used for if-let chains. This must call `visit_expr` on the
220+ /// subexpressions we are not 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+ // We recurse into the lhs only, because `&&` chains associate to the left.
232+ let res_lhs = self . visit_land ( & self . thir [ lhs] , accumulator) ;
233+ let res_rhs = self . visit_land_rhs ( & self . thir [ rhs] ) ?;
234+ accumulator. push ( res_rhs) ;
235+ res_lhs
236+ }
237+ _ => {
238+ let res = self . visit_land_rhs ( ex) ?;
239+ accumulator. push ( res) ;
240+ Ok ( ( ) )
241+ }
242+ }
243+ }
244+
245+ /// Visit the right-hand-side of a `&&`. Used for if-let chains. Returns `Some` if the
246+ /// expression was ultimately a `let ... = ...`, and `None` if it was a normal boolean
247+ /// expression. This must call `visit_expr` on the subexpressions we are not handling ourselves.
248+ fn visit_land_rhs (
249+ & mut self ,
250+ ex : & Expr < ' tcx > ,
251+ ) -> Result < Option < ( Span , RefutableFlag ) > , ErrorGuaranteed > {
252+ match ex. kind {
253+ ExprKind :: Scope { value, lint_level, .. } => {
254+ self . with_lint_level ( lint_level, |this| this. visit_land_rhs ( & this. thir [ value] ) )
255+ }
256+ ExprKind :: Let { box ref pat, expr } => {
257+ self . with_let_source ( LetSource :: None , |this| {
258+ this. visit_expr ( & this. thir ( ) [ expr] ) ;
259+ } ) ;
260+ Ok ( Some ( ( ex. span , self . is_let_irrefutable ( pat) ?) ) )
261+ }
262+ _ => {
263+ self . with_let_source ( LetSource :: None , |this| {
264+ this. visit_expr ( ex) ;
265+ } ) ;
266+ Ok ( None )
267+ }
268+ }
269+ }
270+
215271 fn lower_pattern (
216272 & mut self ,
217273 cx : & MatchCheckCtxt < ' p , ' tcx > ,
@@ -249,8 +305,8 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
249305 if let LetSource :: PlainLet = self . let_source {
250306 self . check_binding_is_irrefutable ( pat, "local binding" , Some ( span) )
251307 } else {
252- let Ok ( irrefutable ) = self . is_let_irrefutable ( pat) else { return } ;
253- if irrefutable {
308+ let Ok ( refutability ) = self . is_let_irrefutable ( pat) else { return } ;
309+ if matches ! ( refutability , Irrefutable ) {
254310 report_irrefutable_let_patterns (
255311 self . tcx ,
256312 self . lint_level ,
@@ -321,81 +377,27 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
321377 }
322378
323379 #[ instrument( level = "trace" , skip( self ) ) ]
324- fn check_let_chain ( & mut self , expr : & Expr < ' tcx > ) {
380+ fn check_let_chain (
381+ & mut self ,
382+ chain_refutabilities : Vec < Option < ( Span , RefutableFlag ) > > ,
383+ whole_chain_span : Span ,
384+ ) {
325385 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- }
383386
384- // Emit the actual warnings.
385- if chain_refutabilities. iter ( ) . all ( |r| matches ! ( * r, Some ( ( _, false ) ) ) ) {
387+ if chain_refutabilities. iter ( ) . all ( |r| matches ! ( * r, Some ( ( _, Irrefutable ) ) ) ) {
386388 // The entire chain is made up of irrefutable `let` statements
387389 report_irrefutable_let_patterns (
388390 self . tcx ,
389391 self . lint_level ,
390392 self . let_source ,
391393 chain_refutabilities. len ( ) ,
392- top_expr_span ,
394+ whole_chain_span ,
393395 ) ;
394396 return ;
395397 }
396398
397399 if let Some ( until) =
398- chain_refutabilities. iter ( ) . position ( |r| !matches ! ( * r, Some ( ( _, false ) ) ) )
400+ chain_refutabilities. iter ( ) . position ( |r| !matches ! ( * r, Some ( ( _, Irrefutable ) ) ) )
399401 && until > 0
400402 {
401403 // The chain has a non-zero prefix of irrefutable `let` statements.
@@ -423,7 +425,7 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
423425 }
424426
425427 if let Some ( from) =
426- chain_refutabilities. iter ( ) . rposition ( |r| !matches ! ( * r, Some ( ( _, false ) ) ) )
428+ chain_refutabilities. iter ( ) . rposition ( |r| !matches ! ( * r, Some ( ( _, Irrefutable ) ) ) )
427429 && from != ( chain_refutabilities. len ( ) - 1 )
428430 {
429431 // The chain has a non-empty suffix of irrefutable `let` statements
@@ -453,14 +455,14 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
453455 Ok ( ( cx, report) )
454456 }
455457
456- fn is_let_irrefutable ( & mut self , pat : & Pat < ' tcx > ) -> Result < bool , ErrorGuaranteed > {
458+ fn is_let_irrefutable ( & mut self , pat : & Pat < ' tcx > ) -> Result < RefutableFlag , ErrorGuaranteed > {
457459 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.
460+ // Report if the pattern is unreachable, which can only occur when the type is uninhabited.
461+ // This also reports unreachable sub-patterns.
460462 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 ( ) )
463+ // If the list of witnesses is empty, the match is exhaustive, i.e. the `if let` pattern is
464+ // irrefutable.
465+ Ok ( if report. non_exhaustiveness_witnesses . is_empty ( ) { Irrefutable } else { Refutable } )
464466 }
465467
466468 #[ instrument( level = "trace" , skip( self ) ) ]
0 commit comments