@@ -6,7 +6,7 @@ use rustc_errors::codes::*;
66use rustc_errors:: { Applicability , ErrorGuaranteed , MultiSpan , struct_span_code_err} ;
77use rustc_hir:: def:: * ;
88use rustc_hir:: def_id:: LocalDefId ;
9- use rustc_hir:: { self as hir, BindingMode , ByRef , HirId , MatchSource } ;
9+ use rustc_hir:: { self as hir, BindingMode , ByRef , HirId , LocalSource , MatchSource , Node } ;
1010use rustc_infer:: infer:: TyCtxtInferExt ;
1111use rustc_lint:: Level ;
1212use rustc_middle:: bug;
@@ -32,6 +32,7 @@ use crate::errors::*;
3232use crate :: fluent_generated as fluent;
3333
3434pub ( crate ) fn check_match ( tcx : TyCtxt < ' _ > , def_id : LocalDefId ) -> Result < ( ) , ErrorGuaranteed > {
35+ //println!("check_match: {:?}, span: {:?}", def_id, tcx.def_span(def_id));
3536 let typeck_results = tcx. typeck ( def_id) ;
3637 let ( thir, expr) = tcx. thir_body ( def_id) ?;
3738 let thir = thir. borrow ( ) ;
@@ -62,7 +63,7 @@ pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), Err
6263
6364 for param in thir. params . iter ( ) {
6465 if let Some ( box ref pattern) = param. pat {
65- visitor. check_binding_is_irrefutable ( pattern, origin, None , None ) ;
66+ visitor. check_binding_is_irrefutable ( pattern, origin, None , None , None ) ;
6667 }
6768 }
6869 visitor. error
@@ -160,8 +161,8 @@ impl<'p, 'tcx> Visitor<'p, 'tcx> for MatchVisitor<'p, 'tcx> {
160161 } => {
161162 self . check_match ( scrutinee, arms, MatchSource :: Normal , span) ;
162163 }
163- ExprKind :: Let { box ref pat, expr } => {
164- self . check_let ( pat, Some ( expr) , ex. span ) ;
164+ ExprKind :: Let { box ref pat, expr, pat_hir_id } => {
165+ self . check_let ( pat, Some ( expr) , ex. span , pat_hir_id ) ;
165166 }
166167 ExprKind :: LogicalOp { op : LogicalOp :: And , .. }
167168 if !matches ! ( self . let_source, LetSource :: None ) =>
@@ -182,13 +183,19 @@ impl<'p, 'tcx> Visitor<'p, 'tcx> for MatchVisitor<'p, 'tcx> {
182183 fn visit_stmt ( & mut self , stmt : & ' p Stmt < ' tcx > ) {
183184 match stmt. kind {
184185 StmtKind :: Let {
185- box ref pattern, initializer, else_block, lint_level, span, ..
186+ box ref pattern,
187+ initializer,
188+ else_block,
189+ lint_level,
190+ span,
191+ pat_hir_id,
192+ ..
186193 } => {
187194 self . with_lint_level ( lint_level, |this| {
188195 let let_source =
189196 if else_block. is_some ( ) { LetSource :: LetElse } else { LetSource :: PlainLet } ;
190197 this. with_let_source ( let_source, |this| {
191- this. check_let ( pattern, initializer, span)
198+ this. check_let ( pattern, initializer, span, pat_hir_id )
192199 } ) ;
193200 visit:: walk_stmt ( this, stmt) ;
194201 } ) ;
@@ -262,7 +269,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
262269 ExprKind :: Scope { value, lint_level, .. } => {
263270 self . with_lint_level ( lint_level, |this| this. visit_land_rhs ( & this. thir [ value] ) )
264271 }
265- ExprKind :: Let { box ref pat, expr } => {
272+ ExprKind :: Let { box ref pat, expr, pat_hir_id : _ } => {
266273 let expr = & self . thir ( ) [ expr] ;
267274 self . with_let_source ( LetSource :: None , |this| {
268275 this. visit_expr ( expr) ;
@@ -439,11 +446,23 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
439446 }
440447
441448 #[ instrument( level = "trace" , skip( self ) ) ]
442- fn check_let ( & mut self , pat : & ' p Pat < ' tcx > , scrutinee : Option < ExprId > , span : Span ) {
449+ fn check_let (
450+ & mut self ,
451+ pat : & ' p Pat < ' tcx > ,
452+ scrutinee : Option < ExprId > ,
453+ span : Span ,
454+ pat_hir_id : HirId ,
455+ ) {
443456 assert ! ( self . let_source != LetSource :: None ) ;
444457 let scrut = scrutinee. map ( |id| & self . thir [ id] ) ;
445458 if let LetSource :: PlainLet = self . let_source {
446- self . check_binding_is_irrefutable ( pat, "local binding" , scrut, Some ( span) )
459+ self . check_binding_is_irrefutable (
460+ pat,
461+ "local binding" ,
462+ scrut,
463+ Some ( span) ,
464+ Some ( pat_hir_id) ,
465+ )
447466 } else {
448467 let Ok ( refutability) = self . is_let_irrefutable ( pat, scrut) else { return } ;
449468 if matches ! ( refutability, Irrefutable ) {
@@ -519,6 +538,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
519538 "`for` loop binding" ,
520539 None ,
521540 None ,
541+ None ,
522542 ) ;
523543 } else {
524544 // span after scrutinee, or after `.match`. That is, the braces, arms,
@@ -665,6 +685,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
665685 origin : & str ,
666686 scrut : Option < & Expr < ' tcx > > ,
667687 sp : Option < Span > ,
688+ pat_hir_id : Option < HirId > ,
668689 ) {
669690 let pattern_ty = pat. ty ;
670691
@@ -715,6 +736,9 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
715736 && self . tcx . sess . source_map ( ) . is_span_accessible ( span)
716737 && interpreted_as_const. is_none ( )
717738 && scrut. is_some ( )
739+ // we only suggest `let else` when the pattern is not desugared from an assignment
740+ && let Some ( pat_hir_id) = pat_hir_id
741+ && !self . is_from_destructing_assignment ( pat_hir_id)
718742 {
719743 let mut bindings = vec ! [ ] ;
720744 pat. each_binding ( |name, _, _, _| bindings. push ( name) ) ;
@@ -768,6 +792,20 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
768792 adt_defined_here,
769793 } ) ) ;
770794 }
795+
796+ /// Check if `pat` is desugared into a `let`, i.e., `LocalSource::AssignDesugar`
797+ /// rather than a real `let` the user wrote.
798+ /// This helps suppress suggestions for `let` statements/exprs when
799+ /// we're dealing with an assignment like `Some(x) = rhs`.
800+ fn is_from_destructing_assignment ( & self , pat_hir_id : HirId ) -> bool {
801+ // pat_hir_id is the hir_id of the pattern in the `let` statement/expr.
802+ for ( _, node) in self . tcx . hir_parent_iter ( pat_hir_id) {
803+ if let Node :: LetStmt ( let_stmt) = node {
804+ return matches ! ( let_stmt. source, LocalSource :: AssignDesugar ( _) ) ;
805+ }
806+ }
807+ false
808+ }
771809}
772810
773811/// Check if a by-value binding is by-value. That is, check if the binding's type is not `Copy`.
0 commit comments