@@ -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,57 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
212216 }
213217 }
214218
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+
215270 fn lower_pattern (
216271 & mut self ,
217272 cx : & MatchCheckCtxt < ' p , ' tcx > ,
@@ -249,8 +304,8 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
249304 if let LetSource :: PlainLet = self . let_source {
250305 self . check_binding_is_irrefutable ( pat, "local binding" , Some ( span) )
251306 } 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 ) {
254309 report_irrefutable_let_patterns (
255310 self . tcx ,
256311 self . lint_level ,
@@ -321,81 +376,27 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
321376 }
322377
323378 #[ 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+ ) {
325384 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- }
383385
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 ) ) ) ) {
386387 // The entire chain is made up of irrefutable `let` statements
387388 report_irrefutable_let_patterns (
388389 self . tcx ,
389390 self . lint_level ,
390391 self . let_source ,
391392 chain_refutabilities. len ( ) ,
392- top_expr_span ,
393+ whole_chain_span ,
393394 ) ;
394395 return ;
395396 }
396397
397398 if let Some ( until) =
398- chain_refutabilities. iter ( ) . position ( |r| !matches ! ( * r, Some ( ( _, false ) ) ) )
399+ chain_refutabilities. iter ( ) . position ( |r| !matches ! ( * r, Some ( ( _, Irrefutable ) ) ) )
399400 && until > 0
400401 {
401402 // The chain has a non-zero prefix of irrefutable `let` statements.
@@ -423,7 +424,7 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
423424 }
424425
425426 if let Some ( from) =
426- chain_refutabilities. iter ( ) . rposition ( |r| !matches ! ( * r, Some ( ( _, false ) ) ) )
427+ chain_refutabilities. iter ( ) . rposition ( |r| !matches ! ( * r, Some ( ( _, Irrefutable ) ) ) )
427428 && from != ( chain_refutabilities. len ( ) - 1 )
428429 {
429430 // The chain has a non-empty suffix of irrefutable `let` statements
@@ -453,14 +454,14 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
453454 Ok ( ( cx, report) )
454455 }
455456
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 > {
457458 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.
460461 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 } )
464465 }
465466
466467 #[ instrument( level = "trace" , skip( self ) ) ]
0 commit comments