Skip to content

Commit 66afdd1

Browse files
committed
Enhance collapsible_match for adjusted bindings
1 parent 2d509f8 commit 66afdd1

File tree

3 files changed

+85
-4
lines changed

3 files changed

+85
-4
lines changed

clippy_lints/src/collapsible_match.rs

+19-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate::utils::visitors::LocalUsedVisitor;
22
use crate::utils::{span_lint_and_then, SpanlessEq};
33
use if_chain::if_chain;
44
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
5-
use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, QPath, StmtKind};
5+
use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, QPath, StmtKind, UnOp};
66
use rustc_lint::{LateContext, LateLintPass};
77
use rustc_middle::ty::{DefIdTree, TyCtxt};
88
use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -72,8 +72,7 @@ fn check_arm(arm: &Arm<'_>, wild_outer_arm: &Arm<'_>, cx: &LateContext<'_>) {
7272
if arms_inner.iter().all(|arm| arm.guard.is_none());
7373
// match expression must be a local binding
7474
// match <local> { .. }
75-
if let ExprKind::Path(QPath::Resolved(None, path)) = expr_in.kind;
76-
if let Res::Local(binding_id) = path.res;
75+
if let Some(binding_id) = addr_adjusted_binding(expr_in, cx);
7776
// one of the branches must be "wild-like"
7877
if let Some(wild_inner_arm_idx) = arms_inner.iter().rposition(|arm_inner| arm_is_wild_like(arm_inner, cx.tcx));
7978
let (wild_inner_arm, non_wild_inner_arm) =
@@ -175,3 +174,20 @@ fn is_none_ctor(res: Res, tcx: TyCtxt<'_>) -> bool {
175174
}
176175
false
177176
}
177+
178+
/// Retrieves a binding ID with optional `&` and/or `*` operators removed. (e.g. `&**foo`)
179+
/// Returns `None` if a non-reference type is de-referenced.
180+
/// For example, if `Vec` is de-referenced to a slice, `None` is returned.
181+
fn addr_adjusted_binding(mut expr: &Expr<'_>, cx: &LateContext<'_>) -> Option<HirId> {
182+
loop {
183+
match expr.kind {
184+
ExprKind::AddrOf(_, _, e) => expr = e,
185+
ExprKind::Path(QPath::Resolved(None, path)) => match path.res {
186+
Res::Local(binding_id) => break Some(binding_id),
187+
_ => break None,
188+
},
189+
ExprKind::Unary(UnOp::UnDeref, e) if cx.typeck_results().expr_ty(e).is_ref() => expr = e,
190+
_ => break None,
191+
}
192+
}
193+
}

tests/ui/collapsible_match2.rs

+29
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,35 @@ fn lint_cases(opt_opt: Option<Option<u32>>, res_opt: Result<Option<u32>, String>
4040
// there is still a better way to write this.
4141
mac!(res_opt => Ok(val), val => Some(n), foo(n));
4242
}
43+
44+
// deref reference value
45+
match Some(&[1]) {
46+
Some(s) => match *s {
47+
[n] => foo(n),
48+
_ => (),
49+
},
50+
_ => (),
51+
}
52+
53+
// ref pattern and deref
54+
match Some(&[1]) {
55+
Some(ref s) => match &*s {
56+
[n] => foo(n),
57+
_ => (),
58+
},
59+
_ => (),
60+
}
61+
}
62+
63+
fn no_lint() {
64+
// deref inner value (cannot pattern match with Vec)
65+
match Some(vec![1]) {
66+
Some(s) => match *s {
67+
[n] => foo(n),
68+
_ => (),
69+
},
70+
_ => (),
71+
}
4372
}
4473

4574
fn make<T>() -> T {

tests/ui/collapsible_match2.stderr

+37-1
Original file line numberDiff line numberDiff line change
@@ -57,5 +57,41 @@ LL | mac!(res_opt => Ok(val), val => Some(n), foo(n));
5757
| Replace this binding
5858
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
5959

60-
error: aborting due to 3 previous errors
60+
error: Unnecessary nested match
61+
--> $DIR/collapsible_match2.rs:46:20
62+
|
63+
LL | Some(s) => match *s {
64+
| ____________________^
65+
LL | | [n] => foo(n),
66+
LL | | _ => (),
67+
LL | | },
68+
| |_________^
69+
|
70+
help: The outer pattern can be modified to include the inner pattern.
71+
--> $DIR/collapsible_match2.rs:46:14
72+
|
73+
LL | Some(s) => match *s {
74+
| ^ Replace this binding
75+
LL | [n] => foo(n),
76+
| ^^^ with this pattern
77+
78+
error: Unnecessary nested match
79+
--> $DIR/collapsible_match2.rs:55:24
80+
|
81+
LL | Some(ref s) => match &*s {
82+
| ________________________^
83+
LL | | [n] => foo(n),
84+
LL | | _ => (),
85+
LL | | },
86+
| |_________^
87+
|
88+
help: The outer pattern can be modified to include the inner pattern.
89+
--> $DIR/collapsible_match2.rs:55:14
90+
|
91+
LL | Some(ref s) => match &*s {
92+
| ^^^^^ Replace this binding
93+
LL | [n] => foo(n),
94+
| ^^^ with this pattern
95+
96+
error: aborting due to 5 previous errors
6197

0 commit comments

Comments
 (0)