@@ -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,41 @@ 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+ let let_else_span = self . check_irrefutable_option_some ( pat, scrut, span) ;
442+
443+ let sm = self . tcx . sess . source_map ( ) ;
444+ let next_token_start = sm. span_extend_while_whitespace ( span. clone ( ) ) . hi ( ) ;
445+ let line_span = sm. span_extend_to_line ( span. clone ( ) ) . with_lo ( next_token_start) ;
446+ let else_keyword_span = sm. span_until_whitespace ( line_span) ;
447+ self . lint_single_let ( pat. span , Some ( else_keyword_span) , let_else_span) ;
448+ }
449+ }
450+
451+ /// Check case `let x = Some(y);`, user likely intended to destructure `Option`
452+ fn check_irrefutable_option_some (
453+ & self ,
454+ pat : & ' p Pat < ' tcx > ,
455+ initializer : Option < & Expr < ' tcx > > ,
456+ span : Span ,
457+ ) -> Option < BeReplacedLetElse > {
458+ if let sm = self . tcx . sess . source_map ( )
459+ && let Some ( initializer) = initializer
460+ && let Some ( s_ty) = initializer. ty . ty_adt_def ( )
461+ && self . tcx . is_diagnostic_item ( rustc_span:: sym:: Option , s_ty. did ( ) )
462+ && let ExprKind :: Scope { value, .. } = initializer. kind
463+ && let initializer_expr = & self . thir [ value]
464+ && let ExprKind :: Adt ( box AdtExpr { fields, .. } ) = & initializer_expr. kind
465+ && let Some ( field) = fields. first ( )
466+ && let inner = & self . thir [ field. expr ]
467+ && let Some ( inner_ty) = inner. ty . ty_adt_def ( )
468+ && self . tcx . is_diagnostic_item ( rustc_span:: sym:: Option , inner_ty. did ( ) )
469+ && let Ok ( rhs) = sm. span_to_snippet ( inner. span )
470+ && let Ok ( lhs) = sm. span_to_snippet ( pat. span )
471+ {
472+ let lhs = format ! ( "Some({})" , lhs) ;
473+ Some ( BeReplacedLetElse { span, lhs, rhs } )
474+ } else {
475+ None
442476 }
443477 }
444478
@@ -546,14 +580,20 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
546580 }
547581
548582 #[ instrument( level = "trace" , skip( self ) ) ]
549- fn lint_single_let ( & mut self , let_span : Span , else_span : Option < Span > ) {
583+ fn lint_single_let (
584+ & mut self ,
585+ let_span : Span ,
586+ else_keyword_span : Option < Span > ,
587+ let_else_span : Option < BeReplacedLetElse > ,
588+ ) {
550589 report_irrefutable_let_patterns (
551590 self . tcx ,
552591 self . hir_source ,
553592 self . let_source ,
554593 1 ,
555594 let_span,
556- else_span,
595+ else_keyword_span,
596+ let_else_span,
557597 ) ;
558598 }
559599
@@ -849,7 +889,8 @@ fn report_irrefutable_let_patterns(
849889 source : LetSource ,
850890 count : usize ,
851891 span : Span ,
852- else_span : Option < Span > ,
892+ else_keyword_span : Option < Span > ,
893+ let_else_span : Option < BeReplacedLetElse > ,
853894) {
854895 macro_rules! emit_diag {
855896 ( $lint: tt) => { {
@@ -862,11 +903,23 @@ fn report_irrefutable_let_patterns(
862903 LetSource :: IfLet | LetSource :: ElseIfLet => emit_diag ! ( IrrefutableLetPatternsIfLet ) ,
863904 LetSource :: IfLetGuard => emit_diag ! ( IrrefutableLetPatternsIfLetGuard ) ,
864905 LetSource :: LetElse => {
906+ let spans = match else_keyword_span {
907+ Some ( else_keyword_span) => {
908+ let mut spans = MultiSpan :: from_span ( else_keyword_span) ;
909+ spans. push_span_label (
910+ span,
911+ msg ! ( "assigning to binding pattern will always succeed" ) ,
912+ ) ;
913+ spans
914+ }
915+ None => span. into ( ) ,
916+ } ;
917+
865918 tcx. emit_node_span_lint (
866919 IRREFUTABLE_LET_PATTERNS ,
867920 id,
868- span ,
869- IrrefutableLetPatternsLetElse { count , else_span } ,
921+ spans ,
922+ IrrefutableLetPatternsLetElse { be_replaced : let_else_span } ,
870923 ) ;
871924 }
872925 LetSource :: WhileLet => emit_diag ! ( IrrefutableLetPatternsWhileLet ) ,
0 commit comments