Skip to content

Commit 8d3bbb0

Browse files
committed
handle the byref binding in the struct pattern
1 parent 7f87020 commit 8d3bbb0

File tree

3 files changed

+90
-53
lines changed

3 files changed

+90
-53
lines changed

clippy_lints/src/matches/redundant_guards.rs

Lines changed: 50 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@ use clippy_utils::diagnostics::span_lint_and_then;
22
use clippy_utils::path_to_local;
33
use clippy_utils::source::snippet_with_applicability;
44
use clippy_utils::visitors::{for_each_expr, is_local_used};
5-
use rustc_ast::LitKind;
5+
use rustc_ast::{BorrowKind, LitKind};
66
use rustc_errors::Applicability;
77
use rustc_hir::def::{DefKind, Res};
88
use rustc_hir::{Arm, BinOpKind, Expr, ExprKind, Guard, MatchSource, Node, Pat, PatKind};
99
use rustc_lint::LateContext;
10+
use rustc_span::symbol::Ident;
1011
use rustc_span::Span;
1112
use std::ops::ControlFlow;
1213

@@ -34,45 +35,37 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>]) {
3435
],
3536
MatchSource::Normal,
3637
) = 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)
3839
{
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,
4544
};
46-
4745
emit_redundant_guards(
4846
cx,
4947
outer_arm,
5048
if_expr.span,
5149
pat_span,
52-
binding_span,
53-
is_field,
50+
&binding,
5451
arm.guard,
5552
);
5653
}
5754
// `Some(x) if let Some(2) = x`
5855
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)
6057
{
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,
6762
};
68-
6963
emit_redundant_guards(
7064
cx,
7165
outer_arm,
7266
let_expr.span,
7367
pat_span,
74-
binding_span,
75-
is_field,
68+
&binding,
7669
None,
7770
);
7871
}
@@ -88,61 +81,63 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>]) {
8881
//
8982
// This isn't necessary in the other two checks, as they must be a pattern already.
9083
&& 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)
9285
{
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,
9990
};
100-
10191
emit_redundant_guards(
10292
cx,
10393
outer_arm,
10494
if_expr.span,
10595
pat_span,
106-
binding_span,
107-
is_field,
96+
&binding,
10897
None,
10998
);
11099
}
111100
}
112101
}
113102

103+
struct PatBindingInfo {
104+
span: Span,
105+
byref_ident: Option<Ident>,
106+
is_field: bool,
107+
}
108+
114109
fn get_pat_binding<'tcx>(
115110
cx: &LateContext<'tcx>,
116111
guard_expr: &Expr<'_>,
117112
outer_arm: &Arm<'tcx>,
118-
) -> Option<(Span, bool, bool)> {
113+
) -> Option<PatBindingInfo> {
119114
if let Some(local) = path_to_local(guard_expr) && !is_local_used(cx, outer_arm.body, local) {
120115
let mut span = None;
116+
let mut byref_ident = None;
121117
let mut multiple_bindings = false;
122-
let mut is_byref = false;
123118
// `each_binding` gives the `HirId` of the `Pat` itself, not the binding
124119
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
126121
&& hir_id == local
127122
{
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
129127
if span.replace(pat.span).is_some() {
130128
multiple_bindings = true;
131129
return false;
132130
}
133131
}
134-
135132
true
136133
});
137134

138135
// Ignore bindings from or patterns, like `First(x) | Second(x, _) | Third(x, _, _)`
139136
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(_)),
146141
});
147142
}
148143
}
@@ -155,8 +150,7 @@ fn emit_redundant_guards<'tcx>(
155150
outer_arm: &Arm<'tcx>,
156151
guard_span: Span,
157152
pat_span: Span,
158-
binding_span: Span,
159-
field_binding: bool,
153+
pat_binding: &PatBindingInfo,
160154
inner_guard: Option<Guard<'_>>,
161155
) {
162156
let mut app = Applicability::MaybeIncorrect;
@@ -168,14 +162,21 @@ fn emit_redundant_guards<'tcx>(
168162
"redundant guard",
169163
|diag| {
170164
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+
};
171176
diag.multipart_suggestion_verbose(
172177
"try",
173178
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,
179180
(
180181
guard_span.source_callsite().with_lo(outer_arm.pat.span.hi()),
181182
inner_guard.map_or_else(String::new, |guard| {

tests/ui/redundant_guards.fixed

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -177,9 +177,9 @@ mod issue11465 {
177177
};
178178
match struct_b {
179179
B { ref b, .. } if b == "bar" => {},
180-
B { ref c, .. } if c == &1 => {},
181-
B { ref c, .. } if let &1 = c => {},
182-
B { ref c, .. } if matches!(c, &1) => {},
180+
B { c: 1, .. } => {},
181+
B { c: 1, .. } => {},
182+
B { c: 1, .. } => {},
183183
_ => {},
184184
}
185185
}

tests/ui/redundant_guards.stderr

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,5 +130,41 @@ LL - Some(ref x) if matches!(x, &3) => {},
130130
LL + Some(3) => {},
131131
|
132132

133-
error: aborting due to 11 previous errors
133+
error: redundant guard
134+
--> $DIR/redundant_guards.rs:180:32
135+
|
136+
LL | B { ref c, .. } if c == &1 => {},
137+
| ^^^^^^^
138+
|
139+
help: try
140+
|
141+
LL - B { ref c, .. } if c == &1 => {},
142+
LL + B { c: 1, .. } => {},
143+
|
144+
145+
error: redundant guard
146+
--> $DIR/redundant_guards.rs:181:32
147+
|
148+
LL | B { ref c, .. } if let &1 = c => {},
149+
| ^^^^^^^^^^
150+
|
151+
help: try
152+
|
153+
LL - B { ref c, .. } if let &1 = c => {},
154+
LL + B { c: 1, .. } => {},
155+
|
156+
157+
error: redundant guard
158+
--> $DIR/redundant_guards.rs:182:32
159+
|
160+
LL | B { ref c, .. } if matches!(c, &1) => {},
161+
| ^^^^^^^^^^^^^^^
162+
|
163+
help: try
164+
|
165+
LL - B { ref c, .. } if matches!(c, &1) => {},
166+
LL + B { c: 1, .. } => {},
167+
|
168+
169+
error: aborting due to 14 previous errors
134170

0 commit comments

Comments
 (0)