11use clippy_utils:: diagnostics:: span_lint_and_then;
22use clippy_utils:: higher:: IfLetOrMatch ;
3+ use clippy_utils:: source:: snippet;
34use clippy_utils:: visitors:: is_local_used;
45use clippy_utils:: {
56 is_res_lang_ctor, is_unit_expr, path_to_local, peel_blocks_with_stmt, peel_ref_operators, SpanlessEq ,
@@ -63,7 +64,8 @@ fn check_arm<'tcx>(
6364 if !pat_contains_or( inner_then_pat) ;
6465 // the binding must come from the pattern of the containing match arm
6566 // ..<local>.. => match <local> { .. }
66- if let Some ( binding_span) = find_pat_binding( outer_pat, binding_id) ;
67+ if let ( Some ( binding_span) , is_innermost_parent_pat_struct)
68+ = find_pat_binding_and_is_innermost_parent_pat_struct( outer_pat, binding_id) ;
6769 // the "else" branches must be equal
6870 if match ( outer_else_body, inner_else_body) {
6971 ( None , None ) => true ,
@@ -88,6 +90,13 @@ fn check_arm<'tcx>(
8890 if matches!( inner, IfLetOrMatch :: Match ( ..) ) { "match" } else { "if let" } ,
8991 if outer_is_match { "match" } else { "if let" } ,
9092 ) ;
93+ // collapsing patterns need an explicit field name in struct pattern matching
94+ // ex: Struct {x: Some(1)}
95+ let replace_msg = if is_innermost_parent_pat_struct {
96+ format!( ", prefixed by {}:" , snippet( cx, binding_span, "their field name" ) )
97+ } else {
98+ String :: new( )
99+ } ;
91100 span_lint_and_then(
92101 cx,
93102 COLLAPSIBLE_MATCH ,
@@ -96,7 +105,7 @@ fn check_arm<'tcx>(
96105 |diag| {
97106 let mut help_span = MultiSpan :: from_spans( vec![ binding_span, inner_then_pat. span] ) ;
98107 help_span. push_span_label( binding_span, "replace this binding" ) ;
99- help_span. push_span_label( inner_then_pat. span, "with this pattern" ) ;
108+ help_span. push_span_label( inner_then_pat. span, format! ( "with this pattern{replace_msg}" ) ) ;
100109 diag. span_help( help_span, "the outer pattern can be modified to include the inner pattern" ) ;
101110 } ,
102111 ) ;
@@ -117,8 +126,9 @@ fn arm_is_wild_like(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
117126 }
118127}
119128
120- fn find_pat_binding ( pat : & Pat < ' _ > , hir_id : HirId ) -> Option < Span > {
129+ fn find_pat_binding_and_is_innermost_parent_pat_struct ( pat : & Pat < ' _ > , hir_id : HirId ) -> ( Option < Span > , bool ) {
121130 let mut span = None ;
131+ let mut is_innermost_parent_pat_struct = false ;
122132 pat. walk_short ( |p| match & p. kind {
123133 // ignore OR patterns
124134 PatKind :: Or ( _) => false ,
@@ -129,9 +139,12 @@ fn find_pat_binding(pat: &Pat<'_>, hir_id: HirId) -> Option<Span> {
129139 }
130140 !found
131141 } ,
132- _ => true ,
142+ _ => {
143+ is_innermost_parent_pat_struct = matches ! ( p. kind, PatKind :: Struct ( ..) ) ;
144+ true
145+ } ,
133146 } ) ;
134- span
147+ ( span, is_innermost_parent_pat_struct )
135148}
136149
137150fn pat_contains_or ( pat : & Pat < ' _ > ) -> bool {
0 commit comments