@@ -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,24 +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
38
+ && let Some ( binding) = get_pat_binding ( cx, scrutinee, outer_arm)
37
39
{
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 ,
44
+ } ;
38
45
emit_redundant_guards (
39
46
cx,
40
47
outer_arm,
41
48
if_expr. span ,
42
- scrutinee ,
43
- arm . pat . span ,
49
+ pat_span ,
50
+ & binding ,
44
51
arm. guard ,
45
52
) ;
46
53
}
47
54
// `Some(x) if let Some(2) = x`
48
- else if let Guard :: IfLet ( let_expr) = guard {
55
+ else if let Guard :: IfLet ( let_expr) = guard
56
+ && let Some ( binding) = get_pat_binding ( cx, let_expr. init , outer_arm)
57
+ {
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 ,
62
+ } ;
49
63
emit_redundant_guards (
50
64
cx,
51
65
outer_arm,
52
66
let_expr. span ,
53
- let_expr . init ,
54
- let_expr . pat . span ,
67
+ pat_span ,
68
+ & binding ,
55
69
None ,
56
70
) ;
57
71
}
@@ -67,43 +81,63 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>]) {
67
81
//
68
82
// This isn't necessary in the other two checks, as they must be a pattern already.
69
83
&& cx. typeck_results ( ) . expr_ty ( local) == cx. typeck_results ( ) . expr_ty ( pat)
84
+ && let Some ( binding) = get_pat_binding ( cx, local, outer_arm)
70
85
{
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 ,
90
+ } ;
71
91
emit_redundant_guards (
72
92
cx,
73
93
outer_arm,
74
94
if_expr. span ,
75
- local ,
76
- pat . span ,
95
+ pat_span ,
96
+ & binding ,
77
97
None ,
78
98
) ;
79
99
}
80
100
}
81
101
}
82
102
83
- fn get_pat_binding < ' tcx > ( cx : & LateContext < ' tcx > , guard_expr : & Expr < ' _ > , outer_arm : & Arm < ' tcx > ) -> Option < ( Span , bool ) > {
103
+ struct PatBindingInfo {
104
+ span : Span ,
105
+ byref_ident : Option < Ident > ,
106
+ is_field : bool ,
107
+ }
108
+
109
+ fn get_pat_binding < ' tcx > (
110
+ cx : & LateContext < ' tcx > ,
111
+ guard_expr : & Expr < ' _ > ,
112
+ outer_arm : & Arm < ' tcx > ,
113
+ ) -> Option < PatBindingInfo > {
84
114
if let Some ( local) = path_to_local ( guard_expr) && !is_local_used ( cx, outer_arm. body , local) {
85
115
let mut span = None ;
116
+ let mut byref_ident = None ;
86
117
let mut multiple_bindings = false ;
87
118
// `each_binding` gives the `HirId` of the `Pat` itself, not the binding
88
119
outer_arm. pat . walk ( |pat| {
89
- if let PatKind :: Binding ( _ , hir_id, _ , _) = pat. kind
120
+ if let PatKind :: Binding ( bind_annot , hir_id, ident , _) = pat. kind
90
121
&& hir_id == local
91
- && span. replace ( pat. span ) . is_some ( )
92
122
{
93
- multiple_bindings = true ;
94
- return false ;
123
+ if matches ! ( bind_annot. 0 , rustc_ast:: ByRef :: Yes ) {
124
+ let _ = byref_ident. insert ( ident) ;
125
+ }
126
+ // the second call of `replace()` returns a `Some(span)`, meaning a multi-binding pattern
127
+ if span. replace ( pat. span ) . is_some ( ) {
128
+ multiple_bindings = true ;
129
+ return false ;
130
+ }
95
131
}
96
-
97
132
true
98
133
} ) ;
99
134
100
135
// Ignore bindings from or patterns, like `First(x) | Second(x, _) | Third(x, _, _)`
101
136
if !multiple_bindings {
102
- return span. map ( |span| {
103
- (
104
- span,
105
- !matches ! ( cx. tcx. hir( ) . get_parent( local) , Node :: PatField ( _) ) ,
106
- )
137
+ return span. map ( |span| PatBindingInfo {
138
+ span,
139
+ byref_ident,
140
+ is_field : matches ! ( cx. tcx. hir( ) . get_parent( local) , Node :: PatField ( _) ) ,
107
141
} ) ;
108
142
}
109
143
}
@@ -115,14 +149,11 @@ fn emit_redundant_guards<'tcx>(
115
149
cx : & LateContext < ' tcx > ,
116
150
outer_arm : & Arm < ' tcx > ,
117
151
guard_span : Span ,
118
- local : & Expr < ' _ > ,
119
152
pat_span : Span ,
153
+ pat_binding : & PatBindingInfo ,
120
154
inner_guard : Option < Guard < ' _ > > ,
121
155
) {
122
156
let mut app = Applicability :: MaybeIncorrect ;
123
- let Some ( ( pat_binding, can_use_shorthand) ) = get_pat_binding ( cx, local, outer_arm) else {
124
- return ;
125
- } ;
126
157
127
158
span_lint_and_then (
128
159
cx,
@@ -131,14 +162,21 @@ fn emit_redundant_guards<'tcx>(
131
162
"redundant guard" ,
132
163
|diag| {
133
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
+ } ;
134
176
diag. multipart_suggestion_verbose (
135
177
"try" ,
136
178
vec ! [
137
- if can_use_shorthand {
138
- ( pat_binding, binding_replacement. into_owned( ) )
139
- } else {
140
- ( pat_binding. shrink_to_hi( ) , format!( ": {binding_replacement}" ) )
141
- } ,
179
+ suggestion_span,
142
180
(
143
181
guard_span. source_callsite( ) . with_lo( outer_arm. pat. span. hi( ) ) ,
144
182
inner_guard. map_or_else( String :: new, |guard| {
0 commit comments