Skip to content

Commit bf634cb

Browse files
committed
add equatable-pattern config
1 parent edfd2b9 commit bf634cb

File tree

11 files changed

+492
-188
lines changed

11 files changed

+492
-188
lines changed

clippy_lints/src/equatable_if_let.rs

+33-12
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,11 @@ use rustc_hir::{
99
};
1010
use rustc_lint::{LateContext, LateLintPass, Lint};
1111
use rustc_middle::ty::{Adt, Ty};
12-
use rustc_session::{declare_lint_pass, declare_tool_lint};
12+
use rustc_session::{declare_tool_lint, impl_lint_pass};
1313
use rustc_span::Span;
1414

15+
use crate::utils::conf::EquatablePatternLevel;
16+
1517
declare_clippy_lint! {
1618
/// ### What it does
1719
/// Checks for `if let <pat> = <expr>` (and `while let` and similars) that can be expressed
@@ -64,7 +66,17 @@ declare_clippy_lint! {
6466
"using `matches!` instead of equality"
6567
}
6668

67-
declare_lint_pass!(PatternEquality => [EQUATABLE_IF_LET, EQUATABLE_MATCHES]);
69+
pub struct PatternEquality {
70+
level: EquatablePatternLevel,
71+
}
72+
73+
impl PatternEquality {
74+
pub fn new(level: EquatablePatternLevel) -> PatternEquality {
75+
PatternEquality { level }
76+
}
77+
}
78+
79+
impl_lint_pass!(PatternEquality => [EQUATABLE_IF_LET, EQUATABLE_MATCHES]);
6880

6981
fn equatable_pattern(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
7082
fn array_rec(cx: &LateContext<'_>, pats: &[Pat<'_>]) -> bool {
@@ -179,21 +191,30 @@ fn pat_to_string(cx: &LateContext<'tcx>, app: &mut Applicability, pat: &Pat<'_>,
179191
Some(r)
180192
}
181193

182-
fn emit_lint(cx: &LateContext<'tcx>, pat: &Pat<'_>, exp: &Expr<'_>, span: Span, lint: &'static Lint) {
194+
fn level_contains(level: EquatablePatternLevel, pat: &Pat<'_>) -> bool {
195+
match level {
196+
EquatablePatternLevel::Primitive => matches!(pat.kind, PatKind::Lit(_)),
197+
EquatablePatternLevel::Simple => matches!(pat.kind, PatKind::Lit(_) | PatKind::Path(_)),
198+
EquatablePatternLevel::All => true,
199+
}
200+
}
201+
202+
fn emit_lint(
203+
cx: &LateContext<'tcx>,
204+
pat: &Pat<'_>,
205+
exp: &Expr<'_>,
206+
span: Span,
207+
lint: &'static Lint,
208+
level: EquatablePatternLevel,
209+
) {
183210
if_chain! {
184211
if equatable_pattern(cx, pat);
212+
if level_contains(level, pat);
185213
let exp_ty = cx.typeck_results().expr_ty(exp);
186214
if is_partial_eq(cx, exp_ty, exp_ty);
187215
let mut app = Applicability::MachineApplicable;
188216
if let Some(pat_str) = pat_to_string(cx, &mut app, pat, exp_ty);
189217
then {
190-
/*let pat_str = match pat.kind {
191-
PatKind::Struct(..) => format!(
192-
"({})",
193-
snippet_with_applicability(cx, pat.span, "..", &mut applicability),
194-
),
195-
_ => snippet_with_applicability(cx, pat.span, "..", &mut applicability).to_string(),
196-
};*/
197218
let exp_str = snippet_with_applicability(cx, exp.span, "..", &mut app);
198219
span_lint_and_sugg(
199220
cx,
@@ -215,15 +236,15 @@ fn emit_lint(cx: &LateContext<'tcx>, pat: &Pat<'_>, exp: &Expr<'_>, span: Span,
215236
impl<'tcx> LateLintPass<'tcx> for PatternEquality {
216237
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
217238
if let ExprKind::Let(pat, exp, _) = expr.kind {
218-
emit_lint(cx, pat, exp, expr.span, EQUATABLE_IF_LET);
239+
emit_lint(cx, pat, exp, expr.span, EQUATABLE_IF_LET, self.level);
219240
}
220241
if let Some(MatchesExpn {
221242
call_site,
222243
arm: Arm { pat, guard: None, .. },
223244
exp,
224245
}) = MatchesExpn::parse(expr)
225246
{
226-
emit_lint(cx, pat, exp, call_site, EQUATABLE_MATCHES);
247+
emit_lint(cx, pat, exp, call_site, EQUATABLE_MATCHES, self.level);
227248
}
228249
}
229250
}

clippy_lints/src/lib.rs

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

clippy_lints/src/utils/conf.rs

+11
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@ pub struct Rename {
1515
pub rename: String,
1616
}
1717

18+
#[derive(Clone, Copy, Debug, Deserialize)]
19+
pub enum EquatablePatternLevel {
20+
Primitive,
21+
Simple,
22+
All,
23+
}
24+
1825
/// A single disallowed method, used by the `DISALLOWED_METHOD` lint.
1926
#[derive(Clone, Debug, Deserialize)]
2027
#[serde(untagged)]
@@ -288,6 +295,10 @@ define_Conf! {
288295
///
289296
/// Whether to apply the raw pointer heuristic to determine if a type is `Send`.
290297
(enable_raw_pointer_heuristic_for_send: bool = true),
298+
/// Lint: EQUATABLE_IF_LET, EQUATABLE_MATCHES
299+
///
300+
/// Whether to detect pattern as a value for using with equality.
301+
(equatable_pattern: crate::utils::conf::EquatablePatternLevel = crate::utils::conf::EquatablePatternLevel::Simple),
291302
}
292303

293304
/// Search for the configuration file.
+130
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
// run-rustfix
2+
3+
#![allow(unused_variables, dead_code, clippy::redundant_pattern_matching, clippy::op_ref)]
4+
#![warn(clippy::equatable_if_let, clippy::equatable_matches)]
5+
6+
use std::cmp::Ordering;
7+
8+
#[derive(PartialEq)]
9+
enum Enum {
10+
TupleVariant(i32, u64),
11+
RecordVariant { a: i64, b: u32 },
12+
UnitVariant,
13+
Recursive(Struct),
14+
}
15+
16+
#[derive(PartialEq)]
17+
struct Struct {
18+
a: i32,
19+
b: bool,
20+
}
21+
22+
#[derive(Clone, Copy)]
23+
enum NotPartialEq {
24+
A,
25+
B,
26+
}
27+
28+
#[derive(Clone, Copy)]
29+
enum NotStructuralEq {
30+
A,
31+
B,
32+
}
33+
34+
impl PartialEq for NotStructuralEq {
35+
fn eq(&self, _: &NotStructuralEq) -> bool {
36+
false
37+
}
38+
}
39+
40+
#[derive(PartialEq)]
41+
enum Generic<A, B> {
42+
VA(A),
43+
VB(B),
44+
VC,
45+
}
46+
47+
#[derive(PartialEq)]
48+
struct Generic2<A, B> {
49+
a: A,
50+
b: B,
51+
}
52+
53+
fn main() {
54+
let a = 2;
55+
let b = 3;
56+
let c = Some(2);
57+
let d = Struct { a: 2, b: false };
58+
let e = Enum::UnitVariant;
59+
let f = NotPartialEq::A;
60+
let g = NotStructuralEq::A;
61+
let h: Generic<Enum, NotPartialEq> = Generic::VC;
62+
let i: Generic<Enum, NotStructuralEq> = Generic::VC;
63+
let j = vec![1, 2, 3, 4];
64+
let k = Some(&false);
65+
let l = Generic2 {
66+
a: Generic2 { a: "xxxx", b: 3 },
67+
b: Generic2 {
68+
a: &Enum::UnitVariant,
69+
b: false,
70+
},
71+
};
72+
let m = Generic2 { a: 3, b: 5 };
73+
let n = Some("xxxx");
74+
let mut o = j.iter();
75+
76+
// true
77+
78+
if a == 2 {}
79+
if a.cmp(&b) == Ordering::Greater {}
80+
if c == Some(2) {}
81+
if d == (Struct { a: 2, b: false }) {}
82+
if e == Enum::TupleVariant(32, 64) {}
83+
if e == (Enum::RecordVariant { a: 64, b: 32 }) {}
84+
if e == Enum::UnitVariant {}
85+
if (e, &d) == (Enum::UnitVariant, &Struct { a: 2, b: false }) {}
86+
if Some(g) == None {}
87+
if i == Generic::VA(Enum::UnitVariant) {}
88+
if i == Generic::VC {}
89+
if j[1..3] == [7, 5] {}
90+
if j[..] == [1, 2, 3, 4] {}
91+
if k == Some(&true) {}
92+
if k == Some(&false) {}
93+
if &k == &Some(&true) {}
94+
if &&k == &&Some(&false) {}
95+
if k == None {}
96+
if l == (Generic2 { a: Generic2 { a: "yyy", b: 3 }, b: Generic2 { a: &Enum::UnitVariant, b: false } })
97+
{}
98+
if m == (Generic2 { a: 3, b: 5 }) {}
99+
if n == Some("yyy") {}
100+
101+
let _ = c == Some(2);
102+
103+
while o.next() == Some(&2) {}
104+
105+
// false
106+
107+
if let 2 | 3 = a {}
108+
if let x @ 2 = a {}
109+
if let Some(3 | 4) = c {}
110+
if let Struct { a, b: false } = d {}
111+
if let Struct { a: 2, b: x } = d {}
112+
if let NotPartialEq::A = f {}
113+
if let NotStructuralEq::A = g {}
114+
if let Some(NotPartialEq::A) = Some(f) {}
115+
if let None = Some(f) {}
116+
if let Some(NotStructuralEq::A) = Some(g) {}
117+
if let Generic::VA(Enum::UnitVariant) = h {}
118+
if let Generic::VB(NotPartialEq::A) = h {}
119+
if let Generic::VC = h {}
120+
if let Generic::VB(NotStructuralEq::A) = i {}
121+
if let [7, _] = j[2..] {}
122+
if let [1, 2 | 5, 3, 4] = j[..] {}
123+
if let [2, ..] = j[..] {}
124+
125+
let _ = matches!(c, Some(x));
126+
let _ = matches!(c, Some(x) if x == 2);
127+
let _ = matches!(c, Some(2) if 3 > 5);
128+
129+
while let Some(4 | 7) = o.next() {}
130+
}
+136
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
// run-rustfix
2+
3+
#![allow(unused_variables, dead_code, clippy::redundant_pattern_matching, clippy::op_ref)]
4+
#![warn(clippy::equatable_if_let, clippy::equatable_matches)]
5+
6+
use std::cmp::Ordering;
7+
8+
#[derive(PartialEq)]
9+
enum Enum {
10+
TupleVariant(i32, u64),
11+
RecordVariant { a: i64, b: u32 },
12+
UnitVariant,
13+
Recursive(Struct),
14+
}
15+
16+
#[derive(PartialEq)]
17+
struct Struct {
18+
a: i32,
19+
b: bool,
20+
}
21+
22+
#[derive(Clone, Copy)]
23+
enum NotPartialEq {
24+
A,
25+
B,
26+
}
27+
28+
#[derive(Clone, Copy)]
29+
enum NotStructuralEq {
30+
A,
31+
B,
32+
}
33+
34+
impl PartialEq for NotStructuralEq {
35+
fn eq(&self, _: &NotStructuralEq) -> bool {
36+
false
37+
}
38+
}
39+
40+
#[derive(PartialEq)]
41+
enum Generic<A, B> {
42+
VA(A),
43+
VB(B),
44+
VC,
45+
}
46+
47+
#[derive(PartialEq)]
48+
struct Generic2<A, B> {
49+
a: A,
50+
b: B,
51+
}
52+
53+
fn main() {
54+
let a = 2;
55+
let b = 3;
56+
let c = Some(2);
57+
let d = Struct { a: 2, b: false };
58+
let e = Enum::UnitVariant;
59+
let f = NotPartialEq::A;
60+
let g = NotStructuralEq::A;
61+
let h: Generic<Enum, NotPartialEq> = Generic::VC;
62+
let i: Generic<Enum, NotStructuralEq> = Generic::VC;
63+
let j = vec![1, 2, 3, 4];
64+
let k = Some(&false);
65+
let l = Generic2 {
66+
a: Generic2 { a: "xxxx", b: 3 },
67+
b: Generic2 {
68+
a: &Enum::UnitVariant,
69+
b: false,
70+
},
71+
};
72+
let m = Generic2 { a: 3, b: 5 };
73+
let n = Some("xxxx");
74+
let mut o = j.iter();
75+
76+
// true
77+
78+
if let 2 = a {}
79+
if let Ordering::Greater = a.cmp(&b) {}
80+
if let Some(2) = c {}
81+
if let Struct { a: 2, b: false } = d {}
82+
if let Enum::TupleVariant(32, 64) = e {}
83+
if let Enum::RecordVariant { a: 64, b: 32 } = e {}
84+
if let Enum::UnitVariant = e {}
85+
if let (Enum::UnitVariant, &Struct { a: 2, b: false }) = (e, &d) {}
86+
if let None = Some(g) {}
87+
if let Generic::VA(Enum::UnitVariant) = i {}
88+
if let Generic::VC = i {}
89+
if let [7, 5] = j[1..3] {}
90+
if let [1, 2, 3, 4] = j[..] {}
91+
if let Some(true) = k {}
92+
if let Some(&false) = k {}
93+
if let Some(true) = &k {}
94+
if let Some(false) = &&k {}
95+
if let None = k {}
96+
if let Generic2 {
97+
a: Generic2 { a: "yyy", b: 3 },
98+
b: Generic2 {
99+
a: Enum::UnitVariant,
100+
b: false,
101+
},
102+
} = l
103+
{}
104+
if let Generic2 { a: 3, b: 5 } = m {}
105+
if let Some("yyy") = n {}
106+
107+
let _ = matches!(c, Some(2));
108+
109+
while let Some(2) = o.next() {}
110+
111+
// false
112+
113+
if let 2 | 3 = a {}
114+
if let x @ 2 = a {}
115+
if let Some(3 | 4) = c {}
116+
if let Struct { a, b: false } = d {}
117+
if let Struct { a: 2, b: x } = d {}
118+
if let NotPartialEq::A = f {}
119+
if let NotStructuralEq::A = g {}
120+
if let Some(NotPartialEq::A) = Some(f) {}
121+
if let None = Some(f) {}
122+
if let Some(NotStructuralEq::A) = Some(g) {}
123+
if let Generic::VA(Enum::UnitVariant) = h {}
124+
if let Generic::VB(NotPartialEq::A) = h {}
125+
if let Generic::VC = h {}
126+
if let Generic::VB(NotStructuralEq::A) = i {}
127+
if let [7, _] = j[2..] {}
128+
if let [1, 2 | 5, 3, 4] = j[..] {}
129+
if let [2, ..] = j[..] {}
130+
131+
let _ = matches!(c, Some(x));
132+
let _ = matches!(c, Some(x) if x == 2);
133+
let _ = matches!(c, Some(2) if 3 > 5);
134+
135+
while let Some(4 | 7) = o.next() {}
136+
}

0 commit comments

Comments
 (0)