Skip to content

Commit 298c8ed

Browse files
committed
upgrade equatable_if_let to style
1 parent 310ecb0 commit 298c8ed

38 files changed

+875
-179
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -2843,6 +2843,7 @@ Released 2018-09-13
28432843
[`enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_variant_names
28442844
[`eq_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#eq_op
28452845
[`equatable_if_let`]: https://rust-lang.github.io/rust-clippy/master/index.html#equatable_if_let
2846+
[`equatable_matches`]: https://rust-lang.github.io/rust-clippy/master/index.html#equatable_matches
28462847
[`erasing_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#erasing_op
28472848
[`eval_order_dependence`]: https://rust-lang.github.io/rust-clippy/master/index.html#eval_order_dependence
28482849
[`excessive_precision`]: https://rust-lang.github.io/rust-clippy/master/index.html#excessive_precision

clippy_lints/src/casts/cast_lossless.rs

+2-4
Original file line numberDiff line numberDiff line change
@@ -65,17 +65,15 @@ fn should_lint(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to
6565

6666
(true, false) => {
6767
let from_nbits = utils::int_ty_to_nbits(cast_from, cx.tcx);
68-
let to_nbits = if let ty::Float(FloatTy::F32) = cast_to.kind() {
68+
let to_nbits = if cast_to.kind() == &ty::Float(FloatTy::F32) {
6969
32
7070
} else {
7171
64
7272
};
7373
from_nbits < to_nbits
7474
},
7575

76-
(_, _) => {
77-
matches!(cast_from.kind(), ty::Float(FloatTy::F32)) && matches!(cast_to.kind(), ty::Float(FloatTy::F64))
78-
},
76+
(_, _) => cast_from.kind() == &ty::Float(FloatTy::F32) && cast_to.kind() == &ty::Float(FloatTy::F64),
7977
}
8078
}
8179

clippy_lints/src/casts/cast_possible_truncation.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
4848
},
4949

5050
(_, _) => {
51-
if matches!(cast_from.kind(), &ty::Float(FloatTy::F64))
52-
&& matches!(cast_to.kind(), &ty::Float(FloatTy::F32))
53-
{
51+
if cast_from.kind() == &ty::Float(FloatTy::F64) && cast_to.kind() == &ty::Float(FloatTy::F32) {
5452
"casting `f64` to `f32` may truncate the value".to_string()
5553
} else {
5654
return;

clippy_lints/src/equatable_if_let.rs

+216-52
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,23 @@
1-
use clippy_utils::diagnostics::span_lint_and_sugg;
2-
use clippy_utils::source::snippet_with_context;
1+
use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
32
use clippy_utils::ty::implements_trait;
3+
use clippy_utils::{diagnostics::span_lint_and_sugg, higher::MatchesExpn};
44
use if_chain::if_chain;
55
use rustc_errors::Applicability;
6-
use rustc_hir::{Expr, ExprKind, Pat, PatKind};
7-
use rustc_lint::{LateContext, LateLintPass};
8-
use rustc_middle::ty::Ty;
9-
use rustc_session::{declare_lint_pass, declare_tool_lint};
6+
use rustc_hir::{
7+
def::{DefKind, Res},
8+
Arm, Expr, ExprKind, Pat, PatKind, QPath,
9+
};
10+
use rustc_lint::{LateContext, LateLintPass, Lint};
11+
use rustc_middle::ty::{Adt, Ty};
12+
use rustc_session::{declare_tool_lint, impl_lint_pass};
13+
use rustc_span::{Span, SyntaxContext};
14+
15+
use crate::utils::conf::EquatablePatternLevel;
1016

1117
declare_clippy_lint! {
1218
/// ### What it does
13-
/// Checks for pattern matchings that can be expressed using equality.
19+
/// Checks for `if let <pat> = <expr>` (and `while let` and similars) that can be expressed
20+
/// using `if <expr> == <pat>`.
1421
///
1522
/// ### Why is this bad?
1623
///
@@ -33,68 +40,225 @@ declare_clippy_lint! {
3340
/// }
3441
/// ```
3542
pub EQUATABLE_IF_LET,
36-
nursery,
37-
"using pattern matching instead of equality"
43+
style,
44+
"using if let instead of if with a equality condition"
3845
}
3946

40-
declare_lint_pass!(PatternEquality => [EQUATABLE_IF_LET]);
47+
declare_clippy_lint! {
48+
/// ### What it does
49+
/// Checks for `matches!(<expr>, <pat>)` that can be expressed
50+
/// using `<expr> == <pat>`.
51+
///
52+
/// ### Why is this bad?
53+
///
54+
/// It is less concise and less clear.
55+
///
56+
/// ### Example
57+
/// ```rust,ignore
58+
/// let condition = matches!(x, Some(2));
59+
/// ```
60+
/// Should be written
61+
/// ```rust,ignore
62+
/// let condition = x == Some(2);
63+
/// ```
64+
pub EQUATABLE_MATCHES,
65+
pedantic,
66+
"using `matches!` instead of equality"
67+
}
68+
69+
pub struct PatternEquality {
70+
level: EquatablePatternLevel,
71+
}
4172

42-
/// detects if pattern matches just one thing
43-
fn unary_pattern(pat: &Pat<'_>) -> bool {
44-
fn array_rec(pats: &[Pat<'_>]) -> bool {
45-
pats.iter().all(unary_pattern)
73+
impl PatternEquality {
74+
pub fn new(level: EquatablePatternLevel) -> PatternEquality {
75+
PatternEquality { level }
4676
}
47-
match &pat.kind {
48-
PatKind::Slice(_, _, _) | PatKind::Range(_, _, _) | PatKind::Binding(..) | PatKind::Wild | PatKind::Or(_) => {
77+
}
78+
79+
impl_lint_pass!(PatternEquality => [EQUATABLE_IF_LET, EQUATABLE_MATCHES]);
80+
81+
fn equatable_pattern(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
82+
fn array_rec(cx: &LateContext<'_>, pats: &[Pat<'_>]) -> bool {
83+
pats.iter().all(|x| equatable_pattern(cx, x))
84+
}
85+
fn is_derived(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
86+
let ty = cx.typeck_results().pat_ty(pat);
87+
if let Some(def_id) = cx.tcx.lang_items().structural_peq_trait() {
88+
implements_trait(cx, ty, def_id, &[ty.into()])
89+
} else {
4990
false
91+
}
92+
}
93+
match &pat.kind {
94+
PatKind::Slice(a, None, []) => array_rec(cx, a),
95+
PatKind::Struct(_, a, etc) => !etc && is_derived(cx, pat) && a.iter().all(|x| equatable_pattern(cx, x.pat)),
96+
PatKind::Tuple(a, etc) => !etc.is_some() && array_rec(cx, a),
97+
PatKind::TupleStruct(_, a, etc) => !etc.is_some() && is_derived(cx, pat) && array_rec(cx, a),
98+
PatKind::Ref(x, _) | PatKind::Box(x) => equatable_pattern(cx, x),
99+
PatKind::Path(QPath::Resolved(_, b)) => match b.res {
100+
Res::Def(DefKind::Const, _) => true,
101+
_ => is_derived(cx, pat),
50102
},
51-
PatKind::Struct(_, a, etc) => !etc && a.iter().all(|x| unary_pattern(x.pat)),
52-
PatKind::Tuple(a, etc) | PatKind::TupleStruct(_, a, etc) => !etc.is_some() && array_rec(a),
53-
PatKind::Ref(x, _) | PatKind::Box(x) => unary_pattern(x),
54-
PatKind::Path(_) | PatKind::Lit(_) => true,
103+
PatKind::Path(_) => is_derived(cx, pat),
104+
PatKind::Lit(_) => true,
105+
PatKind::Slice(..) | PatKind::Range(..) | PatKind::Binding(..) | PatKind::Wild | PatKind::Or(_) => false,
55106
}
56107
}
57108

58-
fn is_structural_partial_eq(cx: &LateContext<'tcx>, ty: Ty<'tcx>, other: Ty<'tcx>) -> bool {
109+
fn is_partial_eq(cx: &LateContext<'tcx>, t1: Ty<'tcx>, t2: Ty<'tcx>) -> bool {
59110
if let Some(def_id) = cx.tcx.lang_items().eq_trait() {
60-
implements_trait(cx, ty, def_id, &[other.into()])
111+
implements_trait(cx, t1, def_id, &[t2.into()])
61112
} else {
62113
false
63114
}
64115
}
65116

117+
fn pat_to_string(
118+
cx: &LateContext<'tcx>,
119+
app: &mut Applicability,
120+
pat: &Pat<'_>,
121+
goal: Ty<'_>,
122+
ctxt: SyntaxContext,
123+
) -> Option<String> {
124+
fn inner(
125+
cx: &LateContext<'tcx>,
126+
app: &mut Applicability,
127+
pat: &Pat<'_>,
128+
goal: Ty<'_>,
129+
r: &mut String,
130+
ctxt: SyntaxContext,
131+
) -> bool {
132+
let ty = cx.typeck_results().pat_ty(pat);
133+
if ty == goal {
134+
match &pat.kind {
135+
PatKind::TupleStruct(q, ..) | PatKind::Struct(q, ..) => {
136+
let (adt_def, generic_args) = if let Adt(x, y) = ty.kind() {
137+
(x, y)
138+
} else {
139+
return false; // shouldn't happen
140+
};
141+
let path = if let QPath::Resolved(.., p) = q {
142+
p
143+
} else {
144+
return false; // give up
145+
};
146+
let var = adt_def.variant_of_res(path.res);
147+
match &pat.kind {
148+
PatKind::TupleStruct(_, params, _) => {
149+
*r += &*snippet_with_applicability(cx, path.span, "..", app);
150+
*r += "(";
151+
for (i, (p, f)) in params.iter().zip(var.fields.iter()).enumerate() {
152+
if i != 0 {
153+
*r += ", ";
154+
}
155+
inner(cx, app, p, f.ty(cx.tcx, generic_args), r, ctxt);
156+
}
157+
*r += ")";
158+
},
159+
PatKind::Struct(_, fields, _) => {
160+
*r += &*snippet_with_applicability(cx, path.span, "..", app);
161+
*r += " { ";
162+
for (i, p) in fields.iter().enumerate() {
163+
if i != 0 {
164+
*r += ", ";
165+
}
166+
*r += &*snippet_with_applicability(cx, p.ident.span, "..", app);
167+
*r += ": ";
168+
if let Some(x) = var.fields.iter().find(|f| f.ident == p.ident) {
169+
inner(cx, app, p.pat, x.ty(cx.tcx, generic_args), r, ctxt);
170+
} else {
171+
return false; // won't happen
172+
}
173+
}
174+
*r += " }";
175+
},
176+
_ => return false, // won't happen
177+
}
178+
},
179+
_ => {
180+
*r += &*snippet_with_context(cx, pat.span, ctxt, "..", app).0;
181+
},
182+
}
183+
return true;
184+
}
185+
if goal.is_ref() {
186+
if let Some(tam) = goal.builtin_deref(true) {
187+
*r += "&";
188+
return inner(cx, app, pat, tam.ty, r, ctxt);
189+
}
190+
}
191+
false
192+
}
193+
let mut r = "".to_string();
194+
if let PatKind::Struct(..) = pat.kind {
195+
r += "(";
196+
}
197+
let success = inner(cx, app, pat, goal, &mut r, ctxt);
198+
if let PatKind::Struct(..) = pat.kind {
199+
r += ")";
200+
}
201+
if !success {
202+
return None;
203+
}
204+
Some(r)
205+
}
206+
207+
fn level_contains(level: EquatablePatternLevel, pat: &Pat<'_>) -> bool {
208+
match level {
209+
EquatablePatternLevel::Primitive => matches!(pat.kind, PatKind::Lit(_)),
210+
EquatablePatternLevel::Simple => matches!(pat.kind, PatKind::Lit(_) | PatKind::Path(_)),
211+
EquatablePatternLevel::All => true,
212+
}
213+
}
214+
215+
fn emit_lint(
216+
cx: &LateContext<'tcx>,
217+
pat: &Pat<'_>,
218+
exp: &Expr<'_>,
219+
ctxt: SyntaxContext,
220+
span: Span,
221+
lint: &'static Lint,
222+
level: EquatablePatternLevel,
223+
) {
224+
if_chain! {
225+
if equatable_pattern(cx, pat);
226+
if level_contains(level, pat);
227+
let exp_ty = cx.typeck_results().expr_ty(exp);
228+
if is_partial_eq(cx, exp_ty, exp_ty);
229+
let mut app = Applicability::MachineApplicable;
230+
if let Some(pat_str) = pat_to_string(cx, &mut app, pat, exp_ty, ctxt);
231+
then {
232+
let exp_str = snippet_with_context(cx, exp.span, ctxt, "..", &mut app).0;
233+
span_lint_and_sugg(
234+
cx,
235+
lint,
236+
span,
237+
"this pattern matching can be expressed using equality",
238+
"try",
239+
format!(
240+
"{} == {}",
241+
exp_str,
242+
pat_str,
243+
),
244+
app,
245+
);
246+
}
247+
}
248+
}
249+
66250
impl<'tcx> LateLintPass<'tcx> for PatternEquality {
67251
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
68-
if_chain! {
69-
if let ExprKind::Let(pat, exp, _) = expr.kind;
70-
if unary_pattern(pat);
71-
let exp_ty = cx.typeck_results().expr_ty(exp);
72-
let pat_ty = cx.typeck_results().pat_ty(pat);
73-
if is_structural_partial_eq(cx, exp_ty, pat_ty);
74-
then {
75-
76-
let mut applicability = Applicability::MachineApplicable;
77-
let pat_str = match pat.kind {
78-
PatKind::Struct(..) => format!(
79-
"({})",
80-
snippet_with_context(cx, pat.span, expr.span.ctxt(), "..", &mut applicability).0,
81-
),
82-
_ => snippet_with_context(cx, pat.span, expr.span.ctxt(), "..", &mut applicability).0.to_string(),
83-
};
84-
span_lint_and_sugg(
85-
cx,
86-
EQUATABLE_IF_LET,
87-
expr.span,
88-
"this pattern matching can be expressed using equality",
89-
"try",
90-
format!(
91-
"{} == {}",
92-
snippet_with_context(cx, exp.span, expr.span.ctxt(), "..", &mut applicability).0,
93-
pat_str,
94-
),
95-
applicability,
96-
);
97-
}
252+
if let ExprKind::Let(pat, exp, _) = expr.kind {
253+
emit_lint(cx, pat, exp, expr.span.ctxt(), expr.span, EQUATABLE_IF_LET, self.level);
254+
}
255+
if let Some(MatchesExpn {
256+
call_site,
257+
arm: Arm { pat, guard: None, .. },
258+
exp,
259+
}) = MatchesExpn::parse(expr)
260+
{
261+
emit_lint(cx, pat, exp, expr.span.ctxt(), call_site, EQUATABLE_MATCHES, self.level);
98262
}
99263
}
100264
}

clippy_lints/src/lib.register_all.rs

+1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
5151
LintId::of(enum_variants::MODULE_INCEPTION),
5252
LintId::of(eq_op::EQ_OP),
5353
LintId::of(eq_op::OP_REF),
54+
LintId::of(equatable_if_let::EQUATABLE_IF_LET),
5455
LintId::of(erasing_op::ERASING_OP),
5556
LintId::of(escape::BOXED_LOCAL),
5657
LintId::of(eta_reduction::REDUNDANT_CLOSURE),

clippy_lints/src/lib.register_lints.rs

+1
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ store.register_lints(&[
118118
eq_op::EQ_OP,
119119
eq_op::OP_REF,
120120
equatable_if_let::EQUATABLE_IF_LET,
121+
equatable_if_let::EQUATABLE_MATCHES,
121122
erasing_op::ERASING_OP,
122123
escape::BOXED_LOCAL,
123124
eta_reduction::REDUNDANT_CLOSURE,

clippy_lints/src/lib.register_nursery.rs

-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![
88
LintId::of(copies::BRANCHES_SHARING_CODE),
99
LintId::of(disallowed_method::DISALLOWED_METHOD),
1010
LintId::of(disallowed_type::DISALLOWED_TYPE),
11-
LintId::of(equatable_if_let::EQUATABLE_IF_LET),
1211
LintId::of(fallible_impl_from::FALLIBLE_IMPL_FROM),
1312
LintId::of(floating_point_arithmetic::IMPRECISE_FLOPS),
1413
LintId::of(floating_point_arithmetic::SUBOPTIMAL_FLOPS),

clippy_lints/src/lib.register_pedantic.rs

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
2828
LintId::of(doc::MISSING_PANICS_DOC),
2929
LintId::of(empty_enum::EMPTY_ENUM),
3030
LintId::of(enum_variants::MODULE_NAME_REPETITIONS),
31+
LintId::of(equatable_if_let::EQUATABLE_MATCHES),
3132
LintId::of(eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS),
3233
LintId::of(excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS),
3334
LintId::of(excessive_bools::STRUCT_EXCESSIVE_BOOLS),

clippy_lints/src/lib.register_style.rs

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
2020
LintId::of(enum_variants::ENUM_VARIANT_NAMES),
2121
LintId::of(enum_variants::MODULE_INCEPTION),
2222
LintId::of(eq_op::OP_REF),
23+
LintId::of(equatable_if_let::EQUATABLE_IF_LET),
2324
LintId::of(eta_reduction::REDUNDANT_CLOSURE),
2425
LintId::of(float_literal::EXCESSIVE_PRECISION),
2526
LintId::of(from_over_into::FROM_OVER_INTO),

clippy_lints/src/lib.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -722,7 +722,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
722722
store.register_late_pass(|| Box::new(future_not_send::FutureNotSend));
723723
store.register_late_pass(|| Box::new(if_let_mutex::IfLetMutex));
724724
store.register_late_pass(|| Box::new(if_not_else::IfNotElse));
725-
store.register_late_pass(|| Box::new(equatable_if_let::PatternEquality));
725+
let equatable_pattern = conf.equatable_pattern;
726+
store.register_late_pass(move || Box::new(equatable_if_let::PatternEquality::new(equatable_pattern)));
726727
store.register_late_pass(|| Box::new(mut_mutex_lock::MutMutexLock));
727728
store.register_late_pass(|| Box::new(match_on_vec_items::MatchOnVecItems));
728729
store.register_late_pass(|| Box::new(manual_async_fn::ManualAsyncFn));

clippy_lints/src/manual_async_fn.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ fn captures_all_lifetimes(inputs: &[Ty<'_>], output_lifetimes: &[LifetimeName])
165165
// - There's only one output lifetime bound using `+ '_`
166166
// - All input lifetimes are explicitly bound to the output
167167
input_lifetimes.is_empty()
168-
|| (output_lifetimes.len() == 1 && matches!(output_lifetimes[0], LifetimeName::Underscore))
168+
|| (output_lifetimes.len() == 1 && output_lifetimes[0] == LifetimeName::Underscore)
169169
|| input_lifetimes
170170
.iter()
171171
.all(|in_lt| output_lifetimes.iter().any(|out_lt| in_lt == out_lt))

0 commit comments

Comments
 (0)