@@ -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;
@@ -62,7 +62,7 @@ pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), Err
6262
6363 for param in thir. params . iter ( ) {
6464 if let Some ( box ref pattern) = param. pat {
65- visitor. check_binding_is_irrefutable ( pattern, origin, None , None ) ;
65+ visitor. check_binding_is_irrefutable ( pattern, origin, None , None , None ) ;
6666 }
6767 }
6868 visitor. error
@@ -160,8 +160,8 @@ impl<'p, 'tcx> Visitor<'p, 'tcx> for MatchVisitor<'p, 'tcx> {
160160 } => {
161161 self . check_match ( scrutinee, arms, MatchSource :: Normal , span) ;
162162 }
163- ExprKind :: Let { box ref pat, expr } => {
164- self . check_let ( pat, Some ( expr) , ex. span ) ;
163+ ExprKind :: Let { box ref pat, expr, pat_hir_id } => {
164+ self . check_let ( pat, Some ( expr) , ex. span , pat_hir_id ) ;
165165 }
166166 ExprKind :: LogicalOp { op : LogicalOp :: And , .. }
167167 if !matches ! ( self . let_source, LetSource :: None ) =>
@@ -182,13 +182,19 @@ impl<'p, 'tcx> Visitor<'p, 'tcx> for MatchVisitor<'p, 'tcx> {
182182 fn visit_stmt ( & mut self , stmt : & ' p Stmt < ' tcx > ) {
183183 match stmt. kind {
184184 StmtKind :: Let {
185- box ref pattern, initializer, else_block, lint_level, span, ..
185+ box ref pattern,
186+ initializer,
187+ else_block,
188+ lint_level,
189+ span,
190+ pat_hir_id,
191+ ..
186192 } => {
187193 self . with_lint_level ( lint_level, |this| {
188194 let let_source =
189195 if else_block. is_some ( ) { LetSource :: LetElse } else { LetSource :: PlainLet } ;
190196 this. with_let_source ( let_source, |this| {
191- this. check_let ( pattern, initializer, span)
197+ this. check_let ( pattern, initializer, span, pat_hir_id )
192198 } ) ;
193199 visit:: walk_stmt ( this, stmt) ;
194200 } ) ;
@@ -262,7 +268,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
262268 ExprKind :: Scope { value, lint_level, .. } => {
263269 self . with_lint_level ( lint_level, |this| this. visit_land_rhs ( & this. thir [ value] ) )
264270 }
265- ExprKind :: Let { box ref pat, expr } => {
271+ ExprKind :: Let { box ref pat, expr, pat_hir_id : _ } => {
266272 let expr = & self . thir ( ) [ expr] ;
267273 self . with_let_source ( LetSource :: None , |this| {
268274 this. visit_expr ( expr) ;
@@ -439,11 +445,23 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
439445 }
440446
441447 #[ instrument( level = "trace" , skip( self ) ) ]
442- fn check_let ( & mut self , pat : & ' p Pat < ' tcx > , scrutinee : Option < ExprId > , span : Span ) {
448+ fn check_let (
449+ & mut self ,
450+ pat : & ' p Pat < ' tcx > ,
451+ scrutinee : Option < ExprId > ,
452+ span : Span ,
453+ pat_hir_id : HirId ,
454+ ) {
443455 assert ! ( self . let_source != LetSource :: None ) ;
444456 let scrut = scrutinee. map ( |id| & self . thir [ id] ) ;
445457 if let LetSource :: PlainLet = self . let_source {
446- self . check_binding_is_irrefutable ( pat, "local binding" , scrut, Some ( span) )
458+ self . check_binding_is_irrefutable (
459+ pat,
460+ "local binding" ,
461+ scrut,
462+ Some ( span) ,
463+ Some ( pat_hir_id) ,
464+ )
447465 } else {
448466 let Ok ( refutability) = self . is_let_irrefutable ( pat, scrut) else { return } ;
449467 if matches ! ( refutability, Irrefutable ) {
@@ -519,6 +537,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
519537 "`for` loop binding" ,
520538 None ,
521539 None ,
540+ None ,
522541 ) ;
523542 } else {
524543 // span after scrutinee, or after `.match`. That is, the braces, arms,
@@ -665,6 +684,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
665684 origin : & str ,
666685 scrut : Option < & Expr < ' tcx > > ,
667686 sp : Option < Span > ,
687+ pat_hir_id : Option < HirId > ,
668688 ) {
669689 let pattern_ty = pat. ty ;
670690
@@ -715,6 +735,9 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
715735 && self . tcx . sess . source_map ( ) . is_span_accessible ( span)
716736 && interpreted_as_const. is_none ( )
717737 && scrut. is_some ( )
738+ // we only suggest `let else` when the pattern is not desugared from an assignment
739+ && let Some ( pat_hir_id) = pat_hir_id
740+ && !self . is_from_destructing_assignment ( pat_hir_id)
718741 {
719742 let mut bindings = vec ! [ ] ;
720743 pat. each_binding ( |name, _, _, _| bindings. push ( name) ) ;
@@ -768,6 +791,20 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
768791 adt_defined_here,
769792 } ) ) ;
770793 }
794+
795+ /// Check if `pat` is desugared into a `let`, i.e., `LocalSource::AssignDesugar`
796+ /// rather than a real `let` the user wrote.
797+ /// This helps suppress suggestions for `let` statements/exprs when
798+ /// we're dealing with an assignment like `Some(x) = rhs`.
799+ fn is_from_destructing_assignment ( & self , pat_hir_id : HirId ) -> bool {
800+ // pat_hir_id is the hir_id of the pattern in the `let` statement/expr.
801+ for ( _, node) in self . tcx . hir_parent_iter ( pat_hir_id) {
802+ if let Node :: LetStmt ( let_stmt) = node {
803+ return matches ! ( let_stmt. source, LocalSource :: AssignDesugar ( _) ) ;
804+ }
805+ }
806+ false
807+ }
771808}
772809
773810/// Check if a by-value binding is by-value. That is, check if the binding's type is not `Copy`.
0 commit comments