@@ -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
@@ -161,7 +161,7 @@ impl<'p, 'tcx> Visitor<'p, 'tcx> for MatchVisitor<'p, 'tcx> {
161161 self . check_match ( scrutinee, arms, MatchSource :: Normal , span) ;
162162 }
163163 ExprKind :: Let { box ref pat, expr } => {
164- self . check_let ( pat, Some ( expr) , ex. span ) ;
164+ self . check_let ( pat, Some ( expr) , ex. span , None ) ;
165165 }
166166 ExprKind :: LogicalOp { op : LogicalOp :: And , .. }
167167 if !matches ! ( self . let_source, LetSource :: None ) =>
@@ -188,7 +188,12 @@ impl<'p, 'tcx> Visitor<'p, 'tcx> for MatchVisitor<'p, 'tcx> {
188188 let let_source =
189189 if else_block. is_some ( ) { LetSource :: LetElse } else { LetSource :: PlainLet } ;
190190 this. with_let_source ( let_source, |this| {
191- this. check_let ( pattern, initializer, span)
191+ // FIXME(#145548): We get hir_id from the lint_level, replace it if thir has a better way to get it.
192+ let hir_id = match lint_level {
193+ LintLevel :: Explicit ( hir_id) => Some ( hir_id) ,
194+ _ => None ,
195+ } ;
196+ this. check_let ( pattern, initializer, span, hir_id)
192197 } ) ;
193198 visit:: walk_stmt ( this, stmt) ;
194199 } ) ;
@@ -439,11 +444,17 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
439444 }
440445
441446 #[ instrument( level = "trace" , skip( self ) ) ]
442- fn check_let ( & mut self , pat : & ' p Pat < ' tcx > , scrutinee : Option < ExprId > , span : Span ) {
447+ fn check_let (
448+ & mut self ,
449+ pat : & ' p Pat < ' tcx > ,
450+ scrutinee : Option < ExprId > ,
451+ span : Span ,
452+ hir_id : Option < HirId > ,
453+ ) {
443454 assert ! ( self . let_source != LetSource :: None ) ;
444455 let scrut = scrutinee. map ( |id| & self . thir [ id] ) ;
445456 if let LetSource :: PlainLet = self . let_source {
446- self . check_binding_is_irrefutable ( pat, "local binding" , scrut, Some ( span) )
457+ self . check_binding_is_irrefutable ( pat, "local binding" , scrut, Some ( span) , hir_id )
447458 } else {
448459 let Ok ( refutability) = self . is_let_irrefutable ( pat, scrut) else { return } ;
449460 if matches ! ( refutability, Irrefutable ) {
@@ -519,6 +530,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
519530 "`for` loop binding" ,
520531 None ,
521532 None ,
533+ None ,
522534 ) ;
523535 } else {
524536 // span after scrutinee, or after `.match`. That is, the braces, arms,
@@ -665,6 +677,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
665677 origin : & str ,
666678 scrut : Option < & Expr < ' tcx > > ,
667679 sp : Option < Span > ,
680+ hir_id : Option < HirId > ,
668681 ) {
669682 let pattern_ty = pat. ty ;
670683
@@ -715,6 +728,8 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
715728 && self . tcx . sess . source_map ( ) . is_span_accessible ( span)
716729 && interpreted_as_const. is_none ( )
717730 && scrut. is_some ( )
731+ // we only suggest `let else` when the pattern is not desugared from an assignment
732+ && !self . is_from_destructing_assignment ( hir_id)
718733 {
719734 let mut bindings = vec ! [ ] ;
720735 pat. each_binding ( |name, _, _, _| bindings. push ( name) ) ;
@@ -768,6 +783,19 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
768783 adt_defined_here,
769784 } ) ) ;
770785 }
786+
787+ /// Check if hir_id of LetStmt is desugared into a `let`, i.e., `LocalSource::AssignDesugar`
788+ /// rather than a real `let` the user wrote. This helps suppress suggestions for `let` statements
789+ /// when we're dealing with an assignment like `Some(x) = rhs`. See #145548.
790+ fn is_from_destructing_assignment ( & self , hir_id : Option < HirId > ) -> bool {
791+ if let Some ( hir_id) = hir_id {
792+ // pat_hir_id is the hir_id of the pattern in the `let` statement/expr.
793+ if let Node :: LetStmt ( let_stmt) = self . tcx . hir_node ( hir_id) {
794+ return matches ! ( let_stmt. source, LocalSource :: AssignDesugar ( _) ) ;
795+ }
796+ }
797+ false
798+ }
771799}
772800
773801/// Check if a by-value binding is by-value. That is, check if the binding's type is not `Copy`.
0 commit comments