1
1
use clippy_utils:: diagnostics:: span_lint_and_then;
2
+ use clippy_utils:: path_to_local;
2
3
use clippy_utils:: source:: snippet_with_applicability;
3
- use clippy_utils:: visitors:: for_each_expr;
4
+ use clippy_utils:: visitors:: { for_each_expr, is_local_used } ;
4
5
use rustc_errors:: Applicability ;
5
6
use rustc_hir:: def:: { DefKind , Res } ;
6
- use rustc_hir:: { Arm , BinOpKind , Expr , ExprKind , Guard , HirId , MatchSource , Node , Pat , PatField , PatKind } ;
7
+ use rustc_hir:: { Arm , BinOpKind , Expr , ExprKind , Guard , MatchSource , Node , Pat , PatKind } ;
7
8
use rustc_lint:: LateContext ;
8
- use rustc_span:: symbol:: Ident ;
9
9
use rustc_span:: Span ;
10
- use std:: borrow:: Cow ;
11
10
use std:: ops:: ControlFlow ;
12
11
13
- use super :: REDUNDANT_GUARD ;
12
+ use super :: REDUNDANT_GUARDS ;
14
13
15
- pub ( super ) fn check ( cx : & LateContext < ' _ > , arms : & [ Arm < ' _ > ] ) {
14
+ pub ( super ) fn check < ' tcx > ( cx : & LateContext < ' tcx > , arms : & ' tcx [ Arm < ' tcx > ] ) {
16
15
for outer_arm in arms {
17
16
let mut app = Applicability :: MaybeIncorrect ;
18
17
let Some ( guard) = outer_arm. guard else {
@@ -35,14 +34,14 @@ pub(super) fn check(cx: &LateContext<'_>, arms: &[Arm<'_>]) {
35
34
] ,
36
35
MatchSource :: Normal ,
37
36
) = if_expr. kind
38
- && let Some ( ( pat_binding, field_name ) ) = get_pat_binding ( cx, scrutinee, outer_arm)
37
+ && let Some ( ( pat_binding, can_use_shorthand ) ) = get_pat_binding ( cx, scrutinee, outer_arm)
39
38
{
40
- emit_redundant_guard (
39
+ emit_redundant_guards (
41
40
cx,
42
41
outer_arm,
43
42
if_expr. span ,
44
43
pat_binding,
45
- field_name ,
44
+ can_use_shorthand ,
46
45
arm. pat . span ,
47
46
arm. guard ,
48
47
& mut app,
@@ -51,14 +50,14 @@ pub(super) fn check(cx: &LateContext<'_>, arms: &[Arm<'_>]) {
51
50
// `Some(x) if let Some(2) = x`
52
51
else if let Guard :: IfLet ( let_expr) = guard
53
52
&& let pat = let_expr. pat
54
- && let Some ( ( pat_binding, field_name ) ) = get_pat_binding ( cx, let_expr. init , outer_arm)
53
+ && let Some ( ( pat_binding, can_use_shorthand ) ) = get_pat_binding ( cx, let_expr. init , outer_arm)
55
54
{
56
- emit_redundant_guard (
55
+ emit_redundant_guards (
57
56
cx,
58
57
outer_arm,
59
58
let_expr. span ,
60
59
pat_binding,
61
- field_name ,
60
+ can_use_shorthand ,
62
61
pat. span ,
63
62
None ,
64
63
& mut app,
@@ -68,7 +67,7 @@ pub(super) fn check(cx: &LateContext<'_>, arms: &[Arm<'_>]) {
68
67
else if let Guard :: If ( if_expr) = guard
69
68
&& let ExprKind :: Binary ( bin_op, local, pat) = if_expr. kind
70
69
&& matches ! ( bin_op. node, BinOpKind :: Eq )
71
- && let Some ( ( pat_binding, field_name ) ) = get_pat_binding ( cx, local, outer_arm)
70
+ && let Some ( ( pat_binding, can_use_shorthand ) ) = get_pat_binding ( cx, local, outer_arm)
72
71
&& expr_can_be_pat ( cx, pat)
73
72
// Ensure they have the same type. If they don't, we'd need deref coercion which isn't
74
73
// possible (currently) in a pattern. In some cases, you can use something like
@@ -78,12 +77,12 @@ pub(super) fn check(cx: &LateContext<'_>, arms: &[Arm<'_>]) {
78
77
// This isn't necessary in the other two checks, as they must be a pattern already.
79
78
&& cx. typeck_results ( ) . expr_ty ( local) == cx. typeck_results ( ) . expr_ty ( pat)
80
79
{
81
- emit_redundant_guard (
80
+ emit_redundant_guards (
82
81
cx,
83
82
outer_arm,
84
83
if_expr. span ,
85
84
pat_binding,
86
- field_name ,
85
+ can_use_shorthand ,
87
86
pat. span ,
88
87
None ,
89
88
& mut app,
@@ -92,103 +91,75 @@ pub(super) fn check(cx: &LateContext<'_>, arms: &[Arm<'_>]) {
92
91
}
93
92
}
94
93
95
- fn remove_binding (
96
- cx : & LateContext < ' _ > ,
97
- pat : & Pat < ' _ > ,
98
- span : Span ,
99
- app : & mut Applicability ,
100
- ) -> ( Cow < ' static , str > , Cow < ' static , str > ) {
101
- let pat_start = snippet_with_applicability ( cx, pat. span . with_hi ( span. lo ( ) ) , "<start>" , app) ;
102
- let pat_end = snippet_with_applicability ( cx, pat. span . with_lo ( span. hi ( ) ) , "<end>" , app) ;
103
- ( pat_start, pat_end)
104
- }
105
-
106
- fn get_pat_binding ( cx : & LateContext < ' _ > , guard_expr : & Expr < ' _ > , outer_arm : & Arm < ' _ > ) -> Option < ( Span , Option < Ident > ) > {
107
- if let ExprKind :: Path ( qpath) = guard_expr. kind
108
- && let res = cx. qpath_res ( & qpath, guard_expr. hir_id )
109
- && let Res :: Local ( hir_id) = res
110
- && local_came_from_and_not_used ( cx, & res, outer_arm. pat . hir_id , outer_arm. body )
111
- {
112
- return cx. tcx . hir ( ) . res_span ( res) . map ( |span| {
113
- (
114
- span,
115
- match cx. tcx . hir ( ) . get_parent ( hir_id) {
116
- Node :: PatField ( PatField { ident, .. } ) => Some ( * ident) ,
117
- _ => None ,
118
- } ,
119
- )
94
+ fn get_pat_binding < ' tcx > ( cx : & LateContext < ' tcx > , guard_expr : & Expr < ' _ > , outer_arm : & Arm < ' tcx > ) -> Option < ( Span , bool ) > {
95
+ if let Some ( local) = path_to_local ( guard_expr) && !is_local_used ( cx, outer_arm. body , local) {
96
+ let mut span = None ;
97
+ // FIXME: hir_id == local is only true for the first or, this will always be false
98
+ let mut multiple_bindings = false ;
99
+ outer_arm. pat . each_binding ( |_, hir_id, binding_span, _| {
100
+ if hir_id == local && span. replace ( binding_span) . is_some ( ) {
101
+ multiple_bindings = true ;
102
+ }
120
103
} ) ;
104
+
105
+ if !multiple_bindings {
106
+ return span. map ( |span| {
107
+ (
108
+ span,
109
+ !matches ! ( cx. tcx. hir( ) . get_parent( local) , Node :: PatField ( _) ) ,
110
+ )
111
+ } ) ;
112
+ }
121
113
}
122
114
123
115
None
124
116
}
125
117
126
- fn get_guard ( cx : & LateContext < ' _ > , guard : Option < Guard < ' _ > > , app : & mut Applicability ) -> Cow < ' static , str > {
118
+ fn get_guard ( cx : & LateContext < ' _ > , guard : Option < Guard < ' _ > > , app : & mut Applicability ) -> String {
127
119
guard. map_or_else (
128
- || Cow :: Borrowed ( "" ) ,
120
+ || String :: new ( ) ,
129
121
|guard| {
130
122
let ( prefix, span) = match guard {
131
123
Guard :: If ( e) => ( "if" , e. span ) ,
132
124
Guard :: IfLet ( l) => ( "if let" , l. span ) ,
133
125
} ;
134
126
135
- Cow :: Owned ( format ! (
136
- " {prefix} {}" ,
137
- snippet_with_applicability( cx, span, "<guard>" , app) ,
138
- ) )
127
+ format ! ( " {prefix} {}" , snippet_with_applicability( cx, span, "<guard>" , app) )
139
128
} ,
140
129
)
141
130
}
142
131
143
- fn local_came_from_and_not_used ( cx : & LateContext < ' _ > , local : & Res , target : HirId , body : & Expr < ' _ > ) -> bool {
144
- if let Res :: Local ( local) = local
145
- && cx. tcx . hir ( ) . parent_iter ( * local) . any ( |( p, _) | p == target)
146
- && for_each_expr ( body, |expr| {
147
- if let ExprKind :: Path ( qpath) = expr. kind
148
- && let Res :: Local ( local_ref) = cx. qpath_res ( & qpath, expr. hir_id )
149
- && * local == local_ref
150
- {
151
- return ControlFlow :: Break ( ( ) ) ;
152
- }
153
-
154
- ControlFlow :: Continue ( ( ) )
155
- } )
156
- . is_none ( )
157
- {
158
- return true ;
159
- }
160
-
161
- false
162
- }
163
-
164
132
#[ expect( clippy:: too_many_arguments) ]
165
- fn emit_redundant_guard (
133
+ fn emit_redundant_guards (
166
134
cx : & LateContext < ' _ > ,
167
135
outer_arm : & Arm < ' _ > ,
168
136
guard_span : Span ,
169
137
pat_binding : Span ,
170
- field_name : Option < Ident > ,
138
+ can_use_shorthand : bool ,
171
139
pat_span : Span ,
172
140
inner_guard : Option < Guard < ' _ > > ,
173
141
app : & mut Applicability ,
174
142
) {
175
- let ( pat_start, pat_end) = remove_binding ( cx, outer_arm. pat , pat_binding, app) ;
176
- let binding_replacement = snippet_with_applicability ( cx, pat_span, "<binding_repl>" , app) ;
177
-
178
143
span_lint_and_then (
179
144
cx,
180
- REDUNDANT_GUARD ,
145
+ REDUNDANT_GUARDS ,
181
146
guard_span. source_callsite ( ) ,
182
147
"redundant guard" ,
183
148
|diag| {
184
- diag . span_suggestion (
185
- outer_arm . span . with_hi ( guard_span . source_callsite ( ) . hi ( ) ) ,
149
+ let binding_replacement = snippet_with_applicability ( cx , pat_span , "<binding_repl>" , app ) ;
150
+ diag . multipart_suggestion_verbose (
186
151
"try" ,
187
- format ! (
188
- "{pat_start}{}{binding_replacement}{pat_end}{}" ,
189
- field_name. map_or_else( || Cow :: Borrowed ( "" ) , |i| format!( "{}: " , i. as_str( ) ) . into( ) ) ,
190
- get_guard( cx, inner_guard, app) ,
191
- ) ,
152
+ vec ! [
153
+ if can_use_shorthand {
154
+ ( pat_binding, binding_replacement. into_owned( ) )
155
+ } else {
156
+ ( pat_binding. shrink_to_hi( ) , format!( ": {binding_replacement}" ) )
157
+ } ,
158
+ (
159
+ guard_span. source_callsite( ) . with_lo( outer_arm. pat. span. hi( ) ) ,
160
+ get_guard( cx, inner_guard, app) ,
161
+ ) ,
162
+ ] ,
192
163
* app,
193
164
) ;
194
165
} ,
0 commit comments