@@ -2,11 +2,12 @@ use clippy_utils::diagnostics::span_lint_and_then;
2
2
use clippy_utils:: path_to_local;
3
3
use clippy_utils:: source:: snippet_with_applicability;
4
4
use clippy_utils:: visitors:: { for_each_expr, is_local_used} ;
5
- use rustc_ast:: LitKind ;
5
+ use rustc_ast:: { BorrowKind , LitKind } ;
6
6
use rustc_errors:: Applicability ;
7
7
use rustc_hir:: def:: { DefKind , Res } ;
8
8
use rustc_hir:: { Arm , BinOpKind , Expr , ExprKind , Guard , MatchSource , Node , Pat , PatKind } ;
9
9
use rustc_lint:: LateContext ;
10
+ use rustc_span:: symbol:: Ident ;
10
11
use rustc_span:: Span ;
11
12
use std:: ops:: ControlFlow ;
12
13
@@ -34,45 +35,37 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>]) {
34
35
] ,
35
36
MatchSource :: Normal ,
36
37
) = if_expr. kind
37
- && let Some ( ( binding_span , is_field , is_byref ) ) = get_pat_binding ( cx, scrutinee, outer_arm)
38
+ && let Some ( binding ) = get_pat_binding ( cx, scrutinee, outer_arm)
38
39
{
39
- if is_field && is_byref { return ; }
40
- let pat_span = if let PatKind :: Ref ( pat, _) = arm. pat . kind {
41
- if is_byref { pat. span } else { continue ; }
42
- } else {
43
- if is_byref { continue ; }
44
- arm. pat . span
40
+ let pat_span = match ( arm. pat . kind , binding. byref_ident ) {
41
+ ( PatKind :: Ref ( pat, _) , Some ( _) ) => pat. span ,
42
+ ( PatKind :: Ref ( ..) , None ) | ( _, Some ( _) ) => continue ,
43
+ _ => arm. pat . span ,
45
44
} ;
46
-
47
45
emit_redundant_guards (
48
46
cx,
49
47
outer_arm,
50
48
if_expr. span ,
51
49
pat_span,
52
- binding_span,
53
- is_field,
50
+ & binding,
54
51
arm. guard ,
55
52
) ;
56
53
}
57
54
// `Some(x) if let Some(2) = x`
58
55
else if let Guard :: IfLet ( let_expr) = guard
59
- && let Some ( ( binding_span , is_field , is_byref ) ) = get_pat_binding ( cx, let_expr. init , outer_arm)
56
+ && let Some ( binding ) = get_pat_binding ( cx, let_expr. init , outer_arm)
60
57
{
61
- if is_field && is_byref { return ; }
62
- let pat_span = if let PatKind :: Ref ( pat, _) = let_expr. pat . kind {
63
- if is_byref && !is_field { pat. span } else { continue ; }
64
- } else {
65
- if is_byref { continue ; }
66
- let_expr. pat . span
58
+ let pat_span = match ( let_expr. pat . kind , binding. byref_ident ) {
59
+ ( PatKind :: Ref ( pat, _) , Some ( _) ) => pat. span ,
60
+ ( PatKind :: Ref ( ..) , None ) | ( _, Some ( _) ) => continue ,
61
+ _ => let_expr. pat . span ,
67
62
} ;
68
-
69
63
emit_redundant_guards (
70
64
cx,
71
65
outer_arm,
72
66
let_expr. span ,
73
67
pat_span,
74
- binding_span,
75
- is_field,
68
+ & binding,
76
69
None ,
77
70
) ;
78
71
}
@@ -88,61 +81,63 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>]) {
88
81
//
89
82
// This isn't necessary in the other two checks, as they must be a pattern already.
90
83
&& cx. typeck_results ( ) . expr_ty ( local) == cx. typeck_results ( ) . expr_ty ( pat)
91
- && let Some ( ( binding_span , is_field , is_byref ) ) = get_pat_binding ( cx, local, outer_arm)
84
+ && let Some ( binding ) = get_pat_binding ( cx, local, outer_arm)
92
85
{
93
- if is_field && is_byref { return ; }
94
- let pat_span = if let ExprKind :: AddrOf ( rustc_ast:: BorrowKind :: Ref , _, expr) = pat. kind {
95
- if is_byref { expr. span } else { continue ; }
96
- } else {
97
- if is_byref { continue ; }
98
- pat. span
86
+ let pat_span = match ( pat. kind , binding. byref_ident ) {
87
+ ( ExprKind :: AddrOf ( BorrowKind :: Ref , _, expr) , Some ( _) ) => expr. span ,
88
+ ( ExprKind :: AddrOf ( ..) , None ) | ( _, Some ( _) ) => continue ,
89
+ _ => pat. span ,
99
90
} ;
100
-
101
91
emit_redundant_guards (
102
92
cx,
103
93
outer_arm,
104
94
if_expr. span ,
105
95
pat_span,
106
- binding_span,
107
- is_field,
96
+ & binding,
108
97
None ,
109
98
) ;
110
99
}
111
100
}
112
101
}
113
102
103
+ struct PatBindingInfo {
104
+ span : Span ,
105
+ byref_ident : Option < Ident > ,
106
+ is_field : bool ,
107
+ }
108
+
114
109
fn get_pat_binding < ' tcx > (
115
110
cx : & LateContext < ' tcx > ,
116
111
guard_expr : & Expr < ' _ > ,
117
112
outer_arm : & Arm < ' tcx > ,
118
- ) -> Option < ( Span , bool , bool ) > {
113
+ ) -> Option < PatBindingInfo > {
119
114
if let Some ( local) = path_to_local ( guard_expr) && !is_local_used ( cx, outer_arm. body , local) {
120
115
let mut span = None ;
116
+ let mut byref_ident = None ;
121
117
let mut multiple_bindings = false ;
122
- let mut is_byref = false ;
123
118
// `each_binding` gives the `HirId` of the `Pat` itself, not the binding
124
119
outer_arm. pat . walk ( |pat| {
125
- if let PatKind :: Binding ( bind_annot, hir_id, _ , _) = pat. kind
120
+ if let PatKind :: Binding ( bind_annot, hir_id, ident , _) = pat. kind
126
121
&& hir_id == local
127
122
{
128
- is_byref = matches ! ( bind_annot. 0 , rustc_ast:: ByRef :: Yes ) ;
123
+ if matches ! ( bind_annot. 0 , rustc_ast:: ByRef :: Yes ) {
124
+ let _ = byref_ident. insert ( ident) ;
125
+ }
126
+ // the second call of `replce()` returns a `Some(span)`, meaning a multi-binding pattern
129
127
if span. replace ( pat. span ) . is_some ( ) {
130
128
multiple_bindings = true ;
131
129
return false ;
132
130
}
133
131
}
134
-
135
132
true
136
133
} ) ;
137
134
138
135
// Ignore bindings from or patterns, like `First(x) | Second(x, _) | Third(x, _, _)`
139
136
if !multiple_bindings {
140
- return span. map ( |span| {
141
- (
142
- span,
143
- matches ! ( cx. tcx. hir( ) . get_parent( local) , Node :: PatField ( _) ) ,
144
- is_byref,
145
- )
137
+ return span. map ( |span| PatBindingInfo {
138
+ span,
139
+ byref_ident,
140
+ is_field : matches ! ( cx. tcx. hir( ) . get_parent( local) , Node :: PatField ( _) ) ,
146
141
} ) ;
147
142
}
148
143
}
@@ -155,8 +150,7 @@ fn emit_redundant_guards<'tcx>(
155
150
outer_arm : & Arm < ' tcx > ,
156
151
guard_span : Span ,
157
152
pat_span : Span ,
158
- binding_span : Span ,
159
- field_binding : bool ,
153
+ pat_binding : & PatBindingInfo ,
160
154
inner_guard : Option < Guard < ' _ > > ,
161
155
) {
162
156
let mut app = Applicability :: MaybeIncorrect ;
@@ -168,14 +162,21 @@ fn emit_redundant_guards<'tcx>(
168
162
"redundant guard" ,
169
163
|diag| {
170
164
let binding_replacement = snippet_with_applicability ( cx, pat_span, "<binding_repl>" , & mut app) ;
165
+ let suggestion_span = match * pat_binding {
166
+ PatBindingInfo {
167
+ span,
168
+ byref_ident : Some ( ident) ,
169
+ is_field : true ,
170
+ } => ( span, format ! ( "{ident}: {binding_replacement}" ) ) ,
171
+ PatBindingInfo {
172
+ span, is_field : true , ..
173
+ } => ( span. shrink_to_hi ( ) , format ! ( ": {binding_replacement}" ) ) ,
174
+ PatBindingInfo { span, .. } => ( span, binding_replacement. into_owned ( ) ) ,
175
+ } ;
171
176
diag. multipart_suggestion_verbose (
172
177
"try" ,
173
178
vec ! [
174
- if field_binding {
175
- ( binding_span. shrink_to_hi( ) , format!( ": {binding_replacement}" ) )
176
- } else {
177
- ( binding_span, binding_replacement. into_owned( ) )
178
- } ,
179
+ suggestion_span,
179
180
(
180
181
guard_span. source_callsite( ) . with_lo( outer_arm. pat. span. hi( ) ) ,
181
182
inner_guard. map_or_else( String :: new, |guard| {
0 commit comments