@@ -168,7 +168,7 @@ impl<'p, 'tcx> Visitor<'p, 'tcx> for MatchVisitor<'p, 'tcx> {
168168 let Ok ( ( ) ) = self . visit_land ( ex, & mut chain_refutabilities) else { return } ;
169169 // Lint only single irrefutable let binding.
170170 if let [ Some ( ( _, Irrefutable ) ) ] = chain_refutabilities[ ..] {
171- self . lint_single_let ( ex. span , None ) ;
171+ self . lint_single_let ( ex. span , None , None ) ;
172172 }
173173 return ;
174174 }
@@ -438,7 +438,45 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
438438 if let LetSource :: PlainLet = self . let_source {
439439 self . check_binding_is_irrefutable ( pat, "local binding" , scrut, Some ( span) ) ;
440440 } else if let Ok ( Irrefutable ) = self . is_let_irrefutable ( pat, scrut) {
441- self . lint_single_let ( span, else_span) ;
441+ if span. from_expansion ( ) {
442+ self . lint_single_let ( span, None , None ) ;
443+ return ;
444+ }
445+ let let_else_span = self . check_irrefutable_option_some ( pat, scrut, span) ;
446+
447+ let sm = self . tcx . sess . source_map ( ) ;
448+ let next_token_start = sm. span_extend_while_whitespace ( span. clone ( ) ) . hi ( ) ;
449+ let line_span = sm. span_extend_to_line ( span. clone ( ) ) . with_lo ( next_token_start) ;
450+ let else_keyword_span = sm. span_until_whitespace ( line_span) ;
451+ self . lint_single_let ( span, Some ( else_keyword_span) , let_else_span) ;
452+ }
453+ }
454+
455+ /// Check case `let x = Some(y);`, user likely intended to destructure `Option`
456+ fn check_irrefutable_option_some (
457+ & self ,
458+ pat : & ' p Pat < ' tcx > ,
459+ initializer : Option < & Expr < ' tcx > > ,
460+ span : Span ,
461+ ) -> Option < LetElseReplacementSuggestion > {
462+ if let sm = self . tcx . sess . source_map ( )
463+ && let Some ( initializer) = initializer
464+ && let Some ( s_ty) = initializer. ty . ty_adt_def ( )
465+ && self . tcx . is_diagnostic_item ( rustc_span:: sym:: Option , s_ty. did ( ) )
466+ && let ExprKind :: Scope { value, .. } = initializer. kind
467+ && let initializer_expr = & self . thir [ value]
468+ && let ExprKind :: Adt ( box AdtExpr { fields, .. } ) = & initializer_expr. kind
469+ && let Some ( field) = fields. first ( )
470+ && let inner = & self . thir [ field. expr ]
471+ && let Some ( inner_ty) = inner. ty . ty_adt_def ( )
472+ && self . tcx . is_diagnostic_item ( rustc_span:: sym:: Option , inner_ty. did ( ) )
473+ && let Ok ( rhs) = sm. span_to_snippet ( inner. span )
474+ && let Ok ( lhs) = sm. span_to_snippet ( pat. span )
475+ {
476+ let lhs = format ! ( "Some({})" , lhs) ;
477+ Some ( LetElseReplacementSuggestion { span, lhs, rhs } )
478+ } else {
479+ None
442480 }
443481 }
444482
@@ -546,14 +584,20 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
546584 }
547585
548586 #[ instrument( level = "trace" , skip( self ) ) ]
549- fn lint_single_let ( & mut self , let_span : Span , else_span : Option < Span > ) {
587+ fn lint_single_let (
588+ & mut self ,
589+ let_span : Span ,
590+ else_keyword_span : Option < Span > ,
591+ let_else_span : Option < LetElseReplacementSuggestion > ,
592+ ) {
550593 report_irrefutable_let_patterns (
551594 self . tcx ,
552595 self . hir_source ,
553596 self . let_source ,
554597 1 ,
555598 let_span,
556- else_span,
599+ else_keyword_span,
600+ let_else_span,
557601 ) ;
558602 }
559603
@@ -849,7 +893,8 @@ fn report_irrefutable_let_patterns(
849893 source : LetSource ,
850894 count : usize ,
851895 span : Span ,
852- else_span : Option < Span > ,
896+ else_keyword_span : Option < Span > ,
897+ let_else_span : Option < LetElseReplacementSuggestion > ,
853898) {
854899 macro_rules! emit_diag {
855900 ( $lint: tt) => { {
@@ -862,11 +907,23 @@ fn report_irrefutable_let_patterns(
862907 LetSource :: IfLet | LetSource :: ElseIfLet => emit_diag ! ( IrrefutableLetPatternsIfLet ) ,
863908 LetSource :: IfLetGuard => emit_diag ! ( IrrefutableLetPatternsIfLetGuard ) ,
864909 LetSource :: LetElse => {
910+ let spans = match else_keyword_span {
911+ Some ( else_keyword_span) => {
912+ let mut spans = MultiSpan :: from_span ( else_keyword_span) ;
913+ spans. push_span_label (
914+ span,
915+ msg ! ( "assigning to binding pattern will always succeed" ) ,
916+ ) ;
917+ spans
918+ }
919+ None => span. into ( ) ,
920+ } ;
921+
865922 tcx. emit_node_span_lint (
866923 IRREFUTABLE_LET_PATTERNS ,
867924 id,
868- span ,
869- IrrefutableLetPatternsLetElse { count , else_span } ,
925+ spans ,
926+ IrrefutableLetPatternsLetElse { be_replaced : let_else_span } ,
870927 ) ;
871928 }
872929 LetSource :: WhileLet => emit_diag ! ( IrrefutableLetPatternsWhileLet ) ,
0 commit comments