@@ -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
@@ -559,14 +597,20 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
559597 }
560598
561599 #[ instrument( level = "trace" , skip( self ) ) ]
562- fn lint_single_let ( & mut self , let_span : Span , else_span : Option < Span > ) {
600+ fn lint_single_let (
601+ & mut self ,
602+ let_span : Span ,
603+ else_keyword_span : Option < Span > ,
604+ let_else_span : Option < LetElseReplacementSuggestion > ,
605+ ) {
563606 report_irrefutable_let_patterns (
564607 self . tcx ,
565608 self . hir_source ,
566609 self . let_source ,
567610 1 ,
568611 let_span,
569- else_span,
612+ else_keyword_span,
613+ let_else_span,
570614 ) ;
571615 }
572616
@@ -862,7 +906,8 @@ fn report_irrefutable_let_patterns(
862906 source : LetSource ,
863907 count : usize ,
864908 span : Span ,
865- else_span : Option < Span > ,
909+ else_keyword_span : Option < Span > ,
910+ let_else_span : Option < LetElseReplacementSuggestion > ,
866911) {
867912 macro_rules! emit_diag {
868913 ( $lint: tt) => { {
@@ -875,11 +920,23 @@ fn report_irrefutable_let_patterns(
875920 LetSource :: IfLet | LetSource :: ElseIfLet => emit_diag ! ( IrrefutableLetPatternsIfLet ) ,
876921 LetSource :: IfLetGuard => emit_diag ! ( IrrefutableLetPatternsIfLetGuard ) ,
877922 LetSource :: LetElse => {
923+ let spans = match else_keyword_span {
924+ Some ( else_keyword_span) => {
925+ let mut spans = MultiSpan :: from_span ( else_keyword_span) ;
926+ spans. push_span_label (
927+ span,
928+ msg ! ( "assigning to binding pattern will always succeed" ) ,
929+ ) ;
930+ spans
931+ }
932+ None => span. into ( ) ,
933+ } ;
934+
878935 tcx. emit_node_span_lint (
879936 IRREFUTABLE_LET_PATTERNS ,
880937 id,
881- span ,
882- IrrefutableLetPatternsLetElse { count , else_span } ,
938+ spans ,
939+ IrrefutableLetPatternsLetElse { be_replaced : let_else_span } ,
883940 ) ;
884941 }
885942 LetSource :: WhileLet => emit_diag ! ( IrrefutableLetPatternsWhileLet ) ,
0 commit comments