1
1
use clippy_utils:: diagnostics:: span_lint_and_then;
2
2
use clippy_utils:: higher:: IfLetOrMatch ;
3
+ use clippy_utils:: source:: snippet;
3
4
use clippy_utils:: visitors:: is_local_used;
4
5
use clippy_utils:: {
5
6
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>(
63
64
if !pat_contains_or( inner_then_pat) ;
64
65
// the binding must come from the pattern of the containing match arm
65
66
// ..<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) ;
67
69
// the "else" branches must be equal
68
70
if match ( outer_else_body, inner_else_body) {
69
71
( None , None ) => true ,
@@ -88,6 +90,13 @@ fn check_arm<'tcx>(
88
90
if matches!( inner, IfLetOrMatch :: Match ( ..) ) { "match" } else { "if let" } ,
89
91
if outer_is_match { "match" } else { "if let" } ,
90
92
) ;
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
+ } ;
91
100
span_lint_and_then(
92
101
cx,
93
102
COLLAPSIBLE_MATCH ,
@@ -96,7 +105,7 @@ fn check_arm<'tcx>(
96
105
|diag| {
97
106
let mut help_span = MultiSpan :: from_spans( vec![ binding_span, inner_then_pat. span] ) ;
98
107
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}" ) ) ;
100
109
diag. span_help( help_span, "the outer pattern can be modified to include the inner pattern" ) ;
101
110
} ,
102
111
) ;
@@ -117,8 +126,9 @@ fn arm_is_wild_like(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
117
126
}
118
127
}
119
128
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 ) {
121
130
let mut span = None ;
131
+ let mut is_innermost_parent_pat_struct = false ;
122
132
pat. walk_short ( |p| match & p. kind {
123
133
// ignore OR patterns
124
134
PatKind :: Or ( _) => false ,
@@ -129,9 +139,12 @@ fn find_pat_binding(pat: &Pat<'_>, hir_id: HirId) -> Option<Span> {
129
139
}
130
140
!found
131
141
} ,
132
- _ => true ,
142
+ _ => {
143
+ is_innermost_parent_pat_struct = matches ! ( p. kind, PatKind :: Struct ( ..) ) ;
144
+ true
145
+ } ,
133
146
} ) ;
134
- span
147
+ ( span, is_innermost_parent_pat_struct )
135
148
}
136
149
137
150
fn pat_contains_or ( pat : & Pat < ' _ > ) -> bool {
0 commit comments