Skip to content

Commit

Permalink
add equatable-pattern config
Browse files Browse the repository at this point in the history
  • Loading branch information
HKalbasi committed Oct 10, 2021
1 parent edfd2b9 commit c9531d0
Show file tree
Hide file tree
Showing 10 changed files with 491 additions and 187 deletions.
45 changes: 33 additions & 12 deletions clippy_lints/src/equatable_if_let.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ use rustc_hir::{
};
use rustc_lint::{LateContext, LateLintPass, Lint};
use rustc_middle::ty::{Adt, Ty};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::Span;

use crate::utils::conf::EquatablePatternLevel;

declare_clippy_lint! {
/// ### What it does
/// Checks for `if let <pat> = <expr>` (and `while let` and similars) that can be expressed
Expand Down Expand Up @@ -64,7 +66,17 @@ declare_clippy_lint! {
"using `matches!` instead of equality"
}

declare_lint_pass!(PatternEquality => [EQUATABLE_IF_LET, EQUATABLE_MATCHES]);
pub struct PatternEquality {
level: EquatablePatternLevel,
}

impl PatternEquality {
pub fn new(level: EquatablePatternLevel) -> PatternEquality {
PatternEquality { level }
}
}

impl_lint_pass!(PatternEquality => [EQUATABLE_IF_LET, EQUATABLE_MATCHES]);

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

fn emit_lint(cx: &LateContext<'tcx>, pat: &Pat<'_>, exp: &Expr<'_>, span: Span, lint: &'static Lint) {
fn level_contains(level: EquatablePatternLevel, pat: &Pat<'_>) -> bool {
match level {
EquatablePatternLevel::Primitive => matches!(pat.kind, PatKind::Lit(_)),
EquatablePatternLevel::Simple => matches!(pat.kind, PatKind::Lit(_) | PatKind::Path(_)),
EquatablePatternLevel::All => true,
}
}

fn emit_lint(
cx: &LateContext<'tcx>,
pat: &Pat<'_>,
exp: &Expr<'_>,
span: Span,
lint: &'static Lint,
level: EquatablePatternLevel,
) {
if_chain! {
if equatable_pattern(cx, pat);
if level_contains(level, pat);
let exp_ty = cx.typeck_results().expr_ty(exp);
if is_partial_eq(cx, exp_ty, exp_ty);
let mut app = Applicability::MachineApplicable;
if let Some(pat_str) = pat_to_string(cx, &mut app, pat, exp_ty);
then {
/*let pat_str = match pat.kind {
PatKind::Struct(..) => format!(
"({})",
snippet_with_applicability(cx, pat.span, "..", &mut applicability),
),
_ => snippet_with_applicability(cx, pat.span, "..", &mut applicability).to_string(),
};*/
let exp_str = snippet_with_applicability(cx, exp.span, "..", &mut app);
span_lint_and_sugg(
cx,
Expand All @@ -215,15 +236,15 @@ fn emit_lint(cx: &LateContext<'tcx>, pat: &Pat<'_>, exp: &Expr<'_>, span: Span,
impl<'tcx> LateLintPass<'tcx> for PatternEquality {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
if let ExprKind::Let(pat, exp, _) = expr.kind {
emit_lint(cx, pat, exp, expr.span, EQUATABLE_IF_LET);
emit_lint(cx, pat, exp, expr.span, EQUATABLE_IF_LET, self.level);
}
if let Some(MatchesExpn {
call_site,
arm: Arm { pat, guard: None, .. },
exp,
}) = MatchesExpn::parse(expr)
{
emit_lint(cx, pat, exp, call_site, EQUATABLE_MATCHES);
emit_lint(cx, pat, exp, call_site, EQUATABLE_MATCHES, self.level);
}
}
}
3 changes: 2 additions & 1 deletion clippy_lints/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -721,7 +721,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|| Box::new(option_if_let_else::OptionIfLetElse));
store.register_late_pass(|| Box::new(future_not_send::FutureNotSend));
store.register_late_pass(|| Box::new(if_let_mutex::IfLetMutex));
store.register_late_pass(|| Box::new(equatable_if_let::PatternEquality));
let equatable_pattern = conf.equatable_pattern;
store.register_late_pass(move || Box::new(equatable_if_let::PatternEquality::new(equatable_pattern)));
store.register_late_pass(|| Box::new(mut_mutex_lock::MutMutexLock));
store.register_late_pass(|| Box::new(match_on_vec_items::MatchOnVecItems));
store.register_late_pass(|| Box::new(manual_async_fn::ManualAsyncFn));
Expand Down
11 changes: 11 additions & 0 deletions clippy_lints/src/utils/conf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ pub struct Rename {
pub rename: String,
}

#[derive(Clone, Copy, Debug, Deserialize)]
pub enum EquatablePatternLevel {
Primitive,
Simple,
All,
}

/// A single disallowed method, used by the `DISALLOWED_METHOD` lint.
#[derive(Clone, Debug, Deserialize)]
#[serde(untagged)]
Expand Down Expand Up @@ -288,6 +295,10 @@ define_Conf! {
///
/// Whether to apply the raw pointer heuristic to determine if a type is `Send`.
(enable_raw_pointer_heuristic_for_send: bool = true),
/// Lint: EQUATABLE_IF_LET, EQUATABLE_MATCHES
///
/// Whether to detect pattern as a value for using with equality.
(equatable_pattern: crate::utils::conf::EquatablePatternLevel = crate::utils::conf::EquatablePatternLevel::Simple),
}

/// Search for the configuration file.
Expand Down
130 changes: 130 additions & 0 deletions tests/ui-toml/equatable_pattern/all.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
// run-rustfix

#![allow(unused_variables, dead_code, clippy::redundant_pattern_matching, clippy::op_ref)]
#![warn(clippy::equatable_if_let, clippy::equatable_matches)]

use std::cmp::Ordering;

#[derive(PartialEq)]
enum Enum {
TupleVariant(i32, u64),
RecordVariant { a: i64, b: u32 },
UnitVariant,
Recursive(Struct),
}

#[derive(PartialEq)]
struct Struct {
a: i32,
b: bool,
}

#[derive(Clone, Copy)]
enum NotPartialEq {
A,
B,
}

#[derive(Clone, Copy)]
enum NotStructuralEq {
A,
B,
}

impl PartialEq for NotStructuralEq {
fn eq(&self, _: &NotStructuralEq) -> bool {
false
}
}

#[derive(PartialEq)]
enum Generic<A, B> {
VA(A),
VB(B),
VC,
}

#[derive(PartialEq)]
struct Generic2<A, B> {
a: A,
b: B,
}

fn main() {
let a = 2;
let b = 3;
let c = Some(2);
let d = Struct { a: 2, b: false };
let e = Enum::UnitVariant;
let f = NotPartialEq::A;
let g = NotStructuralEq::A;
let h: Generic<Enum, NotPartialEq> = Generic::VC;
let i: Generic<Enum, NotStructuralEq> = Generic::VC;
let j = vec![1, 2, 3, 4];
let k = Some(&false);
let l = Generic2 {
a: Generic2 { a: "xxxx", b: 3 },
b: Generic2 {
a: &Enum::UnitVariant,
b: false,
},
};
let m = Generic2 { a: 3, b: 5 };
let n = Some("xxxx");
let mut o = j.iter();

// true

if a == 2 {}
if a.cmp(&b) == Ordering::Greater {}
if c == Some(2) {}
if d == (Struct { a: 2, b: false }) {}
if e == Enum::TupleVariant(32, 64) {}
if e == (Enum::RecordVariant { a: 64, b: 32 }) {}
if e == Enum::UnitVariant {}
if (e, &d) == (Enum::UnitVariant, &Struct { a: 2, b: false }) {}
if Some(g) == None {}
if i == Generic::VA(Enum::UnitVariant) {}
if i == Generic::VC {}
if j[1..3] == [7, 5] {}
if j[..] == [1, 2, 3, 4] {}
if k == Some(&true) {}
if k == Some(&false) {}
if &k == &Some(&true) {}
if &&k == &&Some(&false) {}
if k == None {}
if l == (Generic2 { a: Generic2 { a: "yyy", b: 3 }, b: Generic2 { a: &Enum::UnitVariant, b: false } })
{}
if m == (Generic2 { a: 3, b: 5 }) {}
if n == Some("yyy") {}

let _ = c == Some(2);

while o.next() == Some(&2) {}

// false

if let 2 | 3 = a {}
if let x @ 2 = a {}
if let Some(3 | 4) = c {}
if let Struct { a, b: false } = d {}
if let Struct { a: 2, b: x } = d {}
if let NotPartialEq::A = f {}
if let NotStructuralEq::A = g {}
if let Some(NotPartialEq::A) = Some(f) {}
if let None = Some(f) {}
if let Some(NotStructuralEq::A) = Some(g) {}
if let Generic::VA(Enum::UnitVariant) = h {}
if let Generic::VB(NotPartialEq::A) = h {}
if let Generic::VC = h {}
if let Generic::VB(NotStructuralEq::A) = i {}
if let [7, _] = j[2..] {}
if let [1, 2 | 5, 3, 4] = j[..] {}
if let [2, ..] = j[..] {}

let _ = matches!(c, Some(x));
let _ = matches!(c, Some(x) if x == 2);
let _ = matches!(c, Some(2) if 3 > 5);

while let Some(4 | 7) = o.next() {}
}
136 changes: 136 additions & 0 deletions tests/ui-toml/equatable_pattern/all.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
// run-rustfix

#![allow(unused_variables, dead_code, clippy::redundant_pattern_matching, clippy::op_ref)]
#![warn(clippy::equatable_if_let, clippy::equatable_matches)]

use std::cmp::Ordering;

#[derive(PartialEq)]
enum Enum {
TupleVariant(i32, u64),
RecordVariant { a: i64, b: u32 },
UnitVariant,
Recursive(Struct),
}

#[derive(PartialEq)]
struct Struct {
a: i32,
b: bool,
}

#[derive(Clone, Copy)]
enum NotPartialEq {
A,
B,
}

#[derive(Clone, Copy)]
enum NotStructuralEq {
A,
B,
}

impl PartialEq for NotStructuralEq {
fn eq(&self, _: &NotStructuralEq) -> bool {
false
}
}

#[derive(PartialEq)]
enum Generic<A, B> {
VA(A),
VB(B),
VC,
}

#[derive(PartialEq)]
struct Generic2<A, B> {
a: A,
b: B,
}

fn main() {
let a = 2;
let b = 3;
let c = Some(2);
let d = Struct { a: 2, b: false };
let e = Enum::UnitVariant;
let f = NotPartialEq::A;
let g = NotStructuralEq::A;
let h: Generic<Enum, NotPartialEq> = Generic::VC;
let i: Generic<Enum, NotStructuralEq> = Generic::VC;
let j = vec![1, 2, 3, 4];
let k = Some(&false);
let l = Generic2 {
a: Generic2 { a: "xxxx", b: 3 },
b: Generic2 {
a: &Enum::UnitVariant,
b: false,
},
};
let m = Generic2 { a: 3, b: 5 };
let n = Some("xxxx");
let mut o = j.iter();

// true

if let 2 = a {}
if let Ordering::Greater = a.cmp(&b) {}
if let Some(2) = c {}
if let Struct { a: 2, b: false } = d {}
if let Enum::TupleVariant(32, 64) = e {}
if let Enum::RecordVariant { a: 64, b: 32 } = e {}
if let Enum::UnitVariant = e {}
if let (Enum::UnitVariant, &Struct { a: 2, b: false }) = (e, &d) {}
if let None = Some(g) {}
if let Generic::VA(Enum::UnitVariant) = i {}
if let Generic::VC = i {}
if let [7, 5] = j[1..3] {}
if let [1, 2, 3, 4] = j[..] {}
if let Some(true) = k {}
if let Some(&false) = k {}
if let Some(true) = &k {}
if let Some(false) = &&k {}
if let None = k {}
if let Generic2 {
a: Generic2 { a: "yyy", b: 3 },
b: Generic2 {
a: Enum::UnitVariant,
b: false,
},
} = l
{}
if let Generic2 { a: 3, b: 5 } = m {}
if let Some("yyy") = n {}

let _ = matches!(c, Some(2));

while let Some(2) = o.next() {}

// false

if let 2 | 3 = a {}
if let x @ 2 = a {}
if let Some(3 | 4) = c {}
if let Struct { a, b: false } = d {}
if let Struct { a: 2, b: x } = d {}
if let NotPartialEq::A = f {}
if let NotStructuralEq::A = g {}
if let Some(NotPartialEq::A) = Some(f) {}
if let None = Some(f) {}
if let Some(NotStructuralEq::A) = Some(g) {}
if let Generic::VA(Enum::UnitVariant) = h {}
if let Generic::VB(NotPartialEq::A) = h {}
if let Generic::VC = h {}
if let Generic::VB(NotStructuralEq::A) = i {}
if let [7, _] = j[2..] {}
if let [1, 2 | 5, 3, 4] = j[..] {}
if let [2, ..] = j[..] {}

let _ = matches!(c, Some(x));
let _ = matches!(c, Some(x) if x == 2);
let _ = matches!(c, Some(2) if 3 > 5);

while let Some(4 | 7) = o.next() {}
}
Loading

0 comments on commit c9531d0

Please sign in to comment.