| //@aux-build:proc_macros.rs |
| #![allow(clippy::derive_partial_eq_without_eq, clippy::needless_ifs)] |
| #![warn(clippy::equatable_if_let)] |
| use proc_macros::{external, inline_macros}; |
| |
| 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, |
| } |
| |
| struct NoPartialEqStruct { |
| a: i32, |
| b: bool, |
| } |
| |
| enum NotPartialEq { |
| A, |
| B, |
| } |
| |
| enum NotStructuralEq { |
| A, |
| B, |
| } |
| |
| impl PartialEq for NotStructuralEq { |
| fn eq(&self, _: &NotStructuralEq) -> bool { |
| false |
| } |
| } |
| |
| 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 = NoPartialEqStruct { a: 2, b: false }; |
| |
| // true |
| |
| if a == 2 {} |
| //~^ equatable_if_let |
| if a.cmp(&b) == Ordering::Greater {} |
| //~^ equatable_if_let |
| if c == Some(2) {} |
| //~^ equatable_if_let |
| if d == (Struct { a: 2, b: false }) {} |
| //~^ equatable_if_let |
| if e == Enum::TupleVariant(32, 64) {} |
| //~^ equatable_if_let |
| if e == (Enum::RecordVariant { a: 64, b: 32 }) {} |
| //~^ equatable_if_let |
| if e == Enum::UnitVariant {} |
| //~^ equatable_if_let |
| if (e, &d) == (Enum::UnitVariant, &Struct { a: 2, b: false }) {} |
| //~^ equatable_if_let |
| |
| // 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 matches!(f, NotPartialEq::A) {} |
| //~^ equatable_if_let |
| if g == NotStructuralEq::A {} |
| //~^ equatable_if_let |
| if matches!(Some(f), Some(NotPartialEq::A)) {} |
| //~^ equatable_if_let |
| if Some(g) == Some(NotStructuralEq::A) {} |
| //~^ equatable_if_let |
| if matches!(h, NoPartialEqStruct { a: 2, b: false }) {} |
| //~^ equatable_if_let |
| } |
| |
| mod issue8710 { |
| fn str_ref(cs: &[char]) { |
| if matches!(cs.iter().next(), Some('i')) { |
| //~^ equatable_if_let |
| } else { |
| todo!(); |
| } |
| } |
| |
| fn i32_ref(cs: &[i32]) { |
| if matches!(cs.iter().next(), Some(1)) { |
| //~^ equatable_if_let |
| } else { |
| todo!(); |
| } |
| } |
| |
| fn enum_ref() { |
| #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] |
| enum MyEnum { |
| A(i32), |
| B, |
| } |
| |
| fn get_enum() -> Option<&'static MyEnum> { |
| todo!() |
| } |
| |
| if matches!(get_enum(), Some(MyEnum::B)) { |
| //~^ equatable_if_let |
| } else { |
| todo!(); |
| } |
| } |
| } |
| |
| #[inline_macros] |
| fn issue14548() { |
| if let inline!("abc") = "abc" { |
| println!("OK"); |
| } |
| |
| let a = 2; |
| external!({ if let 2 = $a {} }); |
| |
| // Don't lint: `==`/`matches!` might be correct for a particular `$($font)|*`, but not in general |
| macro_rules! m1 { |
| ($($font:pat_param)|*) => { |
| if let $($font)|* = "from_expansion" {} |
| } |
| } |
| m1!("foo"); |
| m1!("Sans" | "Serif" | "Sans Mono"); |
| m1!(inline!("foo")); |
| |
| // Don't lint: the suggestion might be correct for a particular `$from_root_ctxt`, but not in |
| // general |
| macro_rules! m2 { |
| ($from_root_ctxt:pat) => { |
| if let $from_root_ctxt = "from_expansion" {} |
| }; |
| } |
| m2!("foo"); |
| m2!("Sans" | "Serif" | "Sans Mono"); |
| m2!(inline!("foo")); |
| |
| // Don't lint: the suggestion might be correct for a particular `$from_root_ctxt`, but not in |
| // general |
| macro_rules! m3 { |
| ($from_root_ctxt:expr) => { |
| if let "from_expansion" = $from_root_ctxt {} |
| }; |
| } |
| m3!("foo"); |
| m3!("foo"); |
| m3!(inline!("foo")); |
| |
| // Don't lint: the suggestion might be correct for a particular `$from_root_ctxt`, but not in |
| // general. Don't get confused by the scrutinee coming from macro invocation |
| macro_rules! m4 { |
| ($from_root_ctxt:pat) => { |
| if let $from_root_ctxt = inline!("from_expansion") {} |
| }; |
| } |
| m4!("foo"); |
| m4!("Sans" | "Serif" | "Sans Mono"); |
| m4!(inline!("foo")); |
| |
| // Don't lint: the suggestion might be correct for a particular `$from_root_ctxt`, but not in |
| // general. Don't get confused by the scrutinee coming from macro invocation |
| macro_rules! m5 { |
| ($from_root_ctxt:expr) => { |
| if let inline!("from_expansion") = $from_root_ctxt {} |
| }; |
| } |
| m5!("foo"); |
| m5!("foo"); |
| m5!(inline!("foo")); |
| |
| // Would be nice to lint: both sides are macro _invocations_, so the suggestion is correct in |
| // general |
| if let inline!("foo") = inline!("bar") {} |
| } |
| |
| // PartialEq is not stable in consts yet |
| fn issue15376() { |
| enum NonConstEq { |
| A, |
| B, |
| } |
| impl PartialEq for NonConstEq { |
| fn eq(&self, _other: &Self) -> bool { |
| true |
| } |
| } |
| |
| const N: NonConstEq = NonConstEq::A; |
| |
| // `impl PartialEq` is not const, suggest `matches!` |
| const _: u32 = if matches!(N, NonConstEq::A) { 0 } else { 1 }; |
| //~^ ERROR: this pattern matching can be expressed using `matches!` |
| const _: u32 = if matches!(Some(N), Some(NonConstEq::A)) { 0 } else { 1 }; |
| //~^ ERROR: this pattern matching can be expressed using `matches!` |
| } |