diff --git a/CHANGELOG.md b/CHANGELOG.md index c78aa5908de9..5448682e37e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2843,6 +2843,7 @@ Released 2018-09-13 [`enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_variant_names [`eq_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#eq_op [`equatable_if_let`]: https://rust-lang.github.io/rust-clippy/master/index.html#equatable_if_let +[`equatable_matches`]: https://rust-lang.github.io/rust-clippy/master/index.html#equatable_matches [`erasing_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#erasing_op [`eval_order_dependence`]: https://rust-lang.github.io/rust-clippy/master/index.html#eval_order_dependence [`excessive_precision`]: https://rust-lang.github.io/rust-clippy/master/index.html#excessive_precision diff --git a/clippy_lints/src/casts/cast_lossless.rs b/clippy_lints/src/casts/cast_lossless.rs index 869deecfbd53..7b3322741fa7 100644 --- a/clippy_lints/src/casts/cast_lossless.rs +++ b/clippy_lints/src/casts/cast_lossless.rs @@ -65,7 +65,7 @@ fn should_lint(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to (true, false) => { let from_nbits = utils::int_ty_to_nbits(cast_from, cx.tcx); - let to_nbits = if let ty::Float(FloatTy::F32) = cast_to.kind() { + let to_nbits = if cast_to.kind() == &ty::Float(FloatTy::F32) { 32 } else { 64 @@ -73,9 +73,7 @@ fn should_lint(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to from_nbits < to_nbits }, - (_, _) => { - matches!(cast_from.kind(), ty::Float(FloatTy::F32)) && matches!(cast_to.kind(), ty::Float(FloatTy::F64)) - }, + (_, _) => cast_from.kind() == &ty::Float(FloatTy::F32) && cast_to.kind() == &ty::Float(FloatTy::F64), } } diff --git a/clippy_lints/src/casts/cast_possible_truncation.rs b/clippy_lints/src/casts/cast_possible_truncation.rs index 2ae7d16e00bb..59caa808c7e5 100644 --- a/clippy_lints/src/casts/cast_possible_truncation.rs +++ b/clippy_lints/src/casts/cast_possible_truncation.rs @@ -48,9 +48,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, }, (_, _) => { - if matches!(cast_from.kind(), &ty::Float(FloatTy::F64)) - && matches!(cast_to.kind(), &ty::Float(FloatTy::F32)) - { + if cast_from.kind() == &ty::Float(FloatTy::F64) && cast_to.kind() == &ty::Float(FloatTy::F32) { "casting `f64` to `f32` may truncate the value".to_string() } else { return; diff --git a/clippy_lints/src/equatable_if_let.rs b/clippy_lints/src/equatable_if_let.rs index e8b1d6f6edaa..5a3ac643c8b7 100644 --- a/clippy_lints/src/equatable_if_let.rs +++ b/clippy_lints/src/equatable_if_let.rs @@ -1,16 +1,23 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_with_context; +use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::ty::implements_trait; +use clippy_utils::{diagnostics::span_lint_and_sugg, higher::MatchesExpn}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, Pat, PatKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::Ty; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_hir::{ + def::{DefKind, Res}, + Arm, Expr, ExprKind, Pat, PatKind, QPath, +}; +use rustc_lint::{LateContext, LateLintPass, Lint}; +use rustc_middle::ty::{Adt, Ty}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::{Span, SyntaxContext}; + +use crate::utils::conf::EquatablePatternLevel; declare_clippy_lint! { /// ### What it does - /// Checks for pattern matchings that can be expressed using equality. + /// Checks for `if let = ` (and `while let` and similars) that can be expressed + /// using `if == `. /// /// ### Why is this bad? /// @@ -33,68 +40,225 @@ declare_clippy_lint! { /// } /// ``` pub EQUATABLE_IF_LET, - nursery, - "using pattern matching instead of equality" + pedantic, + "using if let instead of if with a equality condition" } -declare_lint_pass!(PatternEquality => [EQUATABLE_IF_LET]); +declare_clippy_lint! { + /// ### What it does + /// Checks for `matches!(, )` that can be expressed + /// using ` == `. + /// + /// ### Why is this bad? + /// + /// It is less concise and less clear. + /// + /// ### Example + /// ```rust,ignore + /// let condition = matches!(x, Some(2)); + /// ``` + /// Should be written + /// ```rust,ignore + /// let condition = x == Some(2); + /// ``` + pub EQUATABLE_MATCHES, + pedantic, + "using `matches!` instead of equality" +} + +pub struct PatternEquality { + level: EquatablePatternLevel, +} -/// detects if pattern matches just one thing -fn unary_pattern(pat: &Pat<'_>) -> bool { - fn array_rec(pats: &[Pat<'_>]) -> bool { - pats.iter().all(unary_pattern) +impl PatternEquality { + pub fn new(level: EquatablePatternLevel) -> PatternEquality { + PatternEquality { level } } - match &pat.kind { - PatKind::Slice(_, _, _) | PatKind::Range(_, _, _) | PatKind::Binding(..) | PatKind::Wild | PatKind::Or(_) => { +} + +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 { + pats.iter().all(|x| equatable_pattern(cx, x)) + } + fn is_derived(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { + let ty = cx.typeck_results().pat_ty(pat); + if let Some(def_id) = cx.tcx.lang_items().structural_peq_trait() { + implements_trait(cx, ty, def_id, &[ty.into()]) + } else { false + } + } + match &pat.kind { + PatKind::Slice(a, None, []) => array_rec(cx, a), + PatKind::Struct(_, a, etc) => !etc && is_derived(cx, pat) && a.iter().all(|x| equatable_pattern(cx, x.pat)), + PatKind::Tuple(a, etc) => !etc.is_some() && array_rec(cx, a), + PatKind::TupleStruct(_, a, etc) => !etc.is_some() && is_derived(cx, pat) && array_rec(cx, a), + PatKind::Ref(x, _) | PatKind::Box(x) => equatable_pattern(cx, x), + PatKind::Path(QPath::Resolved(_, b)) => match b.res { + Res::Def(DefKind::Const, _) => true, + _ => is_derived(cx, pat), }, - PatKind::Struct(_, a, etc) => !etc && a.iter().all(|x| unary_pattern(x.pat)), - PatKind::Tuple(a, etc) | PatKind::TupleStruct(_, a, etc) => !etc.is_some() && array_rec(a), - PatKind::Ref(x, _) | PatKind::Box(x) => unary_pattern(x), - PatKind::Path(_) | PatKind::Lit(_) => true, + PatKind::Path(_) => is_derived(cx, pat), + PatKind::Lit(_) => true, + PatKind::Slice(..) | PatKind::Range(..) | PatKind::Binding(..) | PatKind::Wild | PatKind::Or(_) => false, } } -fn is_structural_partial_eq(cx: &LateContext<'tcx>, ty: Ty<'tcx>, other: Ty<'tcx>) -> bool { +fn is_partial_eq(cx: &LateContext<'tcx>, t1: Ty<'tcx>, t2: Ty<'tcx>) -> bool { if let Some(def_id) = cx.tcx.lang_items().eq_trait() { - implements_trait(cx, ty, def_id, &[other.into()]) + implements_trait(cx, t1, def_id, &[t2.into()]) } else { false } } +fn pat_to_string( + cx: &LateContext<'tcx>, + app: &mut Applicability, + pat: &Pat<'_>, + goal: Ty<'_>, + ctxt: SyntaxContext, +) -> Option { + fn inner( + cx: &LateContext<'tcx>, + app: &mut Applicability, + pat: &Pat<'_>, + goal: Ty<'_>, + r: &mut String, + ctxt: SyntaxContext, + ) -> bool { + let ty = cx.typeck_results().pat_ty(pat); + if ty == goal { + match &pat.kind { + PatKind::TupleStruct(q, ..) | PatKind::Struct(q, ..) => { + let (adt_def, generic_args) = if let Adt(x, y) = ty.kind() { + (x, y) + } else { + return false; // shouldn't happen + }; + let path = if let QPath::Resolved(.., p) = q { + p + } else { + return false; // give up + }; + let var = adt_def.variant_of_res(path.res); + match &pat.kind { + PatKind::TupleStruct(_, params, _) => { + *r += &*snippet_with_applicability(cx, path.span, "..", app); + *r += "("; + for (i, (p, f)) in params.iter().zip(var.fields.iter()).enumerate() { + if i != 0 { + *r += ", "; + } + inner(cx, app, p, f.ty(cx.tcx, generic_args), r, ctxt); + } + *r += ")"; + }, + PatKind::Struct(_, fields, _) => { + *r += &*snippet_with_applicability(cx, path.span, "..", app); + *r += " { "; + for (i, p) in fields.iter().enumerate() { + if i != 0 { + *r += ", "; + } + *r += &*snippet_with_applicability(cx, p.ident.span, "..", app); + *r += ": "; + if let Some(x) = var.fields.iter().find(|f| f.ident == p.ident) { + inner(cx, app, p.pat, x.ty(cx.tcx, generic_args), r, ctxt); + } else { + return false; // won't happen + } + } + *r += " }"; + }, + _ => return false, // won't happen + } + }, + _ => { + *r += &*snippet_with_context(cx, pat.span, ctxt, "..", app).0; + }, + } + return true; + } + if goal.is_ref() { + if let Some(tam) = goal.builtin_deref(true) { + *r += "&"; + return inner(cx, app, pat, tam.ty, r, ctxt); + } + } + false + } + let mut r = "".to_string(); + if let PatKind::Struct(..) = pat.kind { + r += "("; + } + let success = inner(cx, app, pat, goal, &mut r, ctxt); + if let PatKind::Struct(..) = pat.kind { + r += ")"; + } + if !success { + return None; + } + Some(r) +} + +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<'_>, + ctxt: SyntaxContext, + 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, ctxt); + then { + let exp_str = snippet_with_context(cx, exp.span, ctxt, "..", &mut app).0; + span_lint_and_sugg( + cx, + lint, + span, + "this pattern matching can be expressed using equality", + "try", + format!( + "{} == {}", + exp_str, + pat_str, + ), + app, + ); + } + } +} + impl<'tcx> LateLintPass<'tcx> for PatternEquality { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if_chain! { - if let ExprKind::Let(pat, exp, _) = expr.kind; - if unary_pattern(pat); - let exp_ty = cx.typeck_results().expr_ty(exp); - let pat_ty = cx.typeck_results().pat_ty(pat); - if is_structural_partial_eq(cx, exp_ty, pat_ty); - then { - - let mut applicability = Applicability::MachineApplicable; - let pat_str = match pat.kind { - PatKind::Struct(..) => format!( - "({})", - snippet_with_context(cx, pat.span, expr.span.ctxt(), "..", &mut applicability).0, - ), - _ => snippet_with_context(cx, pat.span, expr.span.ctxt(), "..", &mut applicability).0.to_string(), - }; - span_lint_and_sugg( - cx, - EQUATABLE_IF_LET, - expr.span, - "this pattern matching can be expressed using equality", - "try", - format!( - "{} == {}", - snippet_with_context(cx, exp.span, expr.span.ctxt(), "..", &mut applicability).0, - pat_str, - ), - applicability, - ); - } + if let ExprKind::Let(pat, exp, _) = expr.kind { + emit_lint(cx, pat, exp, expr.span.ctxt(), 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, expr.span.ctxt(), call_site, EQUATABLE_MATCHES, self.level); } } } diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 9b2107915746..1c2467e878bb 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -118,6 +118,7 @@ store.register_lints(&[ eq_op::EQ_OP, eq_op::OP_REF, equatable_if_let::EQUATABLE_IF_LET, + equatable_if_let::EQUATABLE_MATCHES, erasing_op::ERASING_OP, escape::BOXED_LOCAL, eta_reduction::REDUNDANT_CLOSURE, diff --git a/clippy_lints/src/lib.register_nursery.rs b/clippy_lints/src/lib.register_nursery.rs index 44c75a11eec0..03ecb90f25e8 100644 --- a/clippy_lints/src/lib.register_nursery.rs +++ b/clippy_lints/src/lib.register_nursery.rs @@ -8,7 +8,6 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ LintId::of(copies::BRANCHES_SHARING_CODE), LintId::of(disallowed_method::DISALLOWED_METHOD), LintId::of(disallowed_type::DISALLOWED_TYPE), - LintId::of(equatable_if_let::EQUATABLE_IF_LET), LintId::of(fallible_impl_from::FALLIBLE_IMPL_FROM), LintId::of(floating_point_arithmetic::IMPRECISE_FLOPS), LintId::of(floating_point_arithmetic::SUBOPTIMAL_FLOPS), diff --git a/clippy_lints/src/lib.register_pedantic.rs b/clippy_lints/src/lib.register_pedantic.rs index 63ab7f1ca6f6..e3d2358cfe3b 100644 --- a/clippy_lints/src/lib.register_pedantic.rs +++ b/clippy_lints/src/lib.register_pedantic.rs @@ -28,6 +28,8 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(doc::MISSING_PANICS_DOC), LintId::of(empty_enum::EMPTY_ENUM), LintId::of(enum_variants::MODULE_NAME_REPETITIONS), + LintId::of(equatable_if_let::EQUATABLE_IF_LET), + LintId::of(equatable_if_let::EQUATABLE_MATCHES), LintId::of(eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS), LintId::of(excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS), LintId::of(excessive_bools::STRUCT_EXCESSIVE_BOOLS), diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 7174d0a082e0..a8624f6a381c 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -722,7 +722,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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(if_not_else::IfNotElse)); - 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)); diff --git a/clippy_lints/src/manual_async_fn.rs b/clippy_lints/src/manual_async_fn.rs index b632af455f85..14b6c36627e7 100644 --- a/clippy_lints/src/manual_async_fn.rs +++ b/clippy_lints/src/manual_async_fn.rs @@ -165,7 +165,7 @@ fn captures_all_lifetimes(inputs: &[Ty<'_>], output_lifetimes: &[LifetimeName]) // - There's only one output lifetime bound using `+ '_` // - All input lifetimes are explicitly bound to the output input_lifetimes.is_empty() - || (output_lifetimes.len() == 1 && matches!(output_lifetimes[0], LifetimeName::Underscore)) + || (output_lifetimes.len() == 1 && output_lifetimes[0] == LifetimeName::Underscore) || input_lifetimes .iter() .all(|in_lt| output_lifetimes.iter().any(|out_lt| in_lt == out_lt)) diff --git a/clippy_lints/src/manual_map.rs b/clippy_lints/src/manual_map.rs index 96df3d0a490f..be193875dbab 100644 --- a/clippy_lints/src/manual_map.rs +++ b/clippy_lints/src/manual_map.rs @@ -170,7 +170,7 @@ impl LateLintPass<'_> for ManualMap { } // `ref` and `ref mut` annotations were handled earlier. - let annotation = if matches!(annotation, BindingAnnotation::Mutable) { + let annotation = if annotation == BindingAnnotation::Mutable { "mut " } else { "" diff --git a/clippy_lints/src/map_clone.rs b/clippy_lints/src/map_clone.rs index 7db5c7e52ea4..3e7ab0e2aa58 100644 --- a/clippy_lints/src/map_clone.rs +++ b/clippy_lints/src/map_clone.rs @@ -88,7 +88,7 @@ impl<'tcx> LateLintPass<'tcx> for MapClone { then { let obj_ty = cx.typeck_results().expr_ty(obj); if let ty::Ref(_, ty, mutability) = obj_ty.kind() { - if matches!(mutability, Mutability::Not) { + if mutability == &Mutability::Not { let copy = is_copy(cx, ty); lint(cx, e.span, args[0].span, copy); } diff --git a/clippy_lints/src/match_str_case_mismatch.rs b/clippy_lints/src/match_str_case_mismatch.rs index f501593c5187..8e1c7cade27d 100644 --- a/clippy_lints/src/match_str_case_mismatch.rs +++ b/clippy_lints/src/match_str_case_mismatch.rs @@ -60,7 +60,7 @@ impl LateLintPass<'_> for MatchStrCaseMismatch { if !in_external_macro(cx.tcx.sess, expr.span); if let ExprKind::Match(match_expr, arms, MatchSource::Normal) = expr.kind; if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(match_expr).kind(); - if let ty::Str = ty.kind(); + if *ty.kind() == ty::Str; then { let mut visitor = MatchExprVisitor { cx, diff --git a/clippy_lints/src/methods/inefficient_to_string.rs b/clippy_lints/src/methods/inefficient_to_string.rs index c0f66feb48ae..8085f2e29d9f 100644 --- a/clippy_lints/src/methods/inefficient_to_string.rs +++ b/clippy_lints/src/methods/inefficient_to_string.rs @@ -51,7 +51,7 @@ pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, method_name: Sy /// Returns whether `ty` specializes `ToString`. /// Currently, these are `str`, `String`, and `Cow<'_, str>`. fn specializes_tostring(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { - if let ty::Str = ty.kind() { + if ty.kind() == &ty::Str { return true; } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 26c29fbb289c..e3f7e17f7dcb 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2455,7 +2455,7 @@ impl OutType { fn is_bool(ty: &hir::Ty<'_>) -> bool { if let hir::TyKind::Path(QPath::Resolved(_, path)) = ty.kind { - matches!(path.res, Res::PrimTy(PrimTy::Bool)) + path.res == Res::PrimTy(PrimTy::Bool) } else { false } diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index fe9ffde0d337..7d45d2d6e00e 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -51,8 +51,8 @@ pub(super) fn check<'tcx>( let path = last_path_segment(qpath).ident.name; // needs to target Default::default in particular or be *::new and have a Default impl // available - if (matches!(path, kw::Default) && is_default_default()) - || (matches!(path, sym::new) && implements_default(arg, default_trait_id)); + if (path == kw::Default && is_default_default()) + || (path == sym::new && implements_default(arg, default_trait_id)); then { let mut applicability = Applicability::MachineApplicable; diff --git a/clippy_lints/src/multiple_crate_versions.rs b/clippy_lints/src/multiple_crate_versions.rs index 816b2f275fb5..4c0f35cd5348 100644 --- a/clippy_lints/src/multiple_crate_versions.rs +++ b/clippy_lints/src/multiple_crate_versions.rs @@ -84,13 +84,9 @@ impl LateLintPass<'_> for MultipleCrateVersions { fn is_normal_dep(nodes: &[Node], local_id: &PackageId, dep_id: &PackageId) -> bool { fn depends_on(node: &Node, dep_id: &PackageId) -> bool { - node.deps.iter().any(|dep| { - dep.pkg == *dep_id - && dep - .dep_kinds - .iter() - .any(|info| matches!(info.kind, DependencyKind::Normal)) - }) + node.deps + .iter() + .any(|dep| dep.pkg == *dep_id && dep.dep_kinds.iter().any(|info| info.kind == DependencyKind::Normal)) } nodes diff --git a/clippy_lints/src/needless_borrow.rs b/clippy_lints/src/needless_borrow.rs index f1be90c44f98..c0d6d53e3dd4 100644 --- a/clippy_lints/src/needless_borrow.rs +++ b/clippy_lints/src/needless_borrow.rs @@ -120,7 +120,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrow { }, ] = *adj3 { - let help_msg_ty = if matches!(mutability, Mutability::Not) { + let help_msg_ty = if mutability == Mutability::Not { format!("&{}", ty) } else { format!("&mut {}", ty) diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index f7711b6fe947..a5951fa83974 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -323,7 +323,7 @@ fn find_stmt_assigns_to<'tcx>( Some(base_local_and_movability(cx, mir, *place)) }, (false, mir::Rvalue::Ref(_, _, place)) => { - if let [mir::ProjectionElem::Deref] = place.as_ref().projection { + if place.as_ref().projection == [mir::ProjectionElem::Deref] { Some(base_local_and_movability(cx, mir, *place)) } else { None @@ -355,7 +355,7 @@ fn base_local_and_movability<'tcx>( let PlaceRef { local, mut projection } = place.as_ref(); while let [base @ .., elem] = projection { projection = base; - deref |= matches!(elem, mir::ProjectionElem::Deref); + deref |= elem == &mir::ProjectionElem::Deref; field |= matches!(elem, mir::ProjectionElem::Field(..)) && has_drop(cx, mir::Place::ty_from(local, projection, &mir.local_decls, cx.tcx).ty); slice |= matches!(elem, mir::ProjectionElem::Index(..)) diff --git a/clippy_lints/src/same_name_method.rs b/clippy_lints/src/same_name_method.rs index 737ff634e449..9ab0f1893f42 100644 --- a/clippy_lints/src/same_name_method.rs +++ b/clippy_lints/src/same_name_method.rs @@ -83,9 +83,7 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod { cx.tcx .associated_items(did) .in_definition_order() - .filter(|assoc_item| { - matches!(assoc_item.kind, AssocKind::Fn) - }) + .filter(|assoc_item| assoc_item.kind == AssocKind::Fn) .map(|assoc_item| assoc_item.ident.name) .collect() }else{ diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 6435107b8b46..2c0c070e2deb 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -178,7 +178,7 @@ impl<'tcx> LateLintPass<'tcx> for StringAdd { }, ExprKind::Index(target, _idx) => { let e_ty = cx.typeck_results().expr_ty(target).peel_refs(); - if matches!(e_ty.kind(), ty::Str) || is_type_diagnostic_item(cx, e_ty, sym::String) { + if *e_ty.kind() == ty::Str || is_type_diagnostic_item(cx, e_ty, sym::String) { span_lint( cx, STRING_SLICE, diff --git a/clippy_lints/src/transmute/transmute_ref_to_ref.rs b/clippy_lints/src/transmute/transmute_ref_to_ref.rs index d105e37abf9c..50eca0a9cc21 100644 --- a/clippy_lints/src/transmute/transmute_ref_to_ref.rs +++ b/clippy_lints/src/transmute/transmute_ref_to_ref.rs @@ -23,7 +23,7 @@ pub(super) fn check<'tcx>( if let (ty::Ref(_, ty_from, from_mutbl), ty::Ref(_, ty_to, to_mutbl)) = (&from_ty.kind(), &to_ty.kind()) { if_chain! { if let (&ty::Slice(slice_ty), &ty::Str) = (&ty_from.kind(), &ty_to.kind()); - if let ty::Uint(ty::UintTy::U8) = slice_ty.kind(); + if slice_ty.kind() == &ty::Uint(ty::UintTy::U8); if from_mutbl == to_mutbl; then { let postfix = if *from_mutbl == Mutability::Mut { diff --git a/clippy_lints/src/unused_async.rs b/clippy_lints/src/unused_async.rs index f4808682b692..fe16217b8099 100644 --- a/clippy_lints/src/unused_async.rs +++ b/clippy_lints/src/unused_async.rs @@ -67,7 +67,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync { hir_id: HirId, ) { if let FnKind::ItemFn(_, _, FnHeader { asyncness, .. }, _) = &fn_kind { - if matches!(asyncness, IsAsync::Async) { + if asyncness == &IsAsync::Async { let mut visitor = AsyncFnVisitor { cx, found_await: false }; walk_fn(&mut visitor, fn_kind, fn_decl, body.id(), span, hir_id); if !visitor.found_await { diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 122a5ce3fc8f..b7add48e7b7b 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -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)] @@ -296,6 +303,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 + /// + /// Specifies which kind of patterns should be linted. Possible values are "All, "Simple", and "Primitive". + (equatable_pattern: crate::utils::conf::EquatablePatternLevel = crate::utils::conf::EquatablePatternLevel::Simple), } /// Search for the configuration file. diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 824ec53ab9c7..f5c123cb902e 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -536,7 +536,7 @@ impl<'tcx> LateLintPass<'tcx> for OuterExpnDataPass { let method_names: Vec = method_names.iter().map(|s| s.as_str()).collect(); let method_names: Vec<&str> = method_names.iter().map(|s| &**s).collect(); if_chain! { - if let ["expn_data", "outer_expn"] = method_names.as_slice(); + if method_names.as_slice() == ["expn_data", "outer_expn"]; let args = arg_lists[1]; if args.len() == 1; let self_arg = &args[0]; diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 85d1f65c51f0..890ff0e6d506 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -564,13 +564,13 @@ impl Write { LitKind::StrRaw(_) | LitKind::ByteStrRaw(_) if matches!(fmtstr.style, StrStyle::Raw(_)) => { lit.token.symbol.as_str().replace("{", "{{").replace("}", "}}") }, - LitKind::Str | LitKind::ByteStr if matches!(fmtstr.style, StrStyle::Cooked) => { + LitKind::Str | LitKind::ByteStr if fmtstr.style == StrStyle::Cooked => { lit.token.symbol.as_str().replace("{", "{{").replace("}", "}}") }, LitKind::StrRaw(_) | LitKind::Str | LitKind::ByteStrRaw(_) | LitKind::ByteStr => continue, LitKind::Byte | LitKind::Char => match &*lit.token.symbol.as_str() { - "\"" if matches!(fmtstr.style, StrStyle::Cooked) => "\\\"", - "\"" if matches!(fmtstr.style, StrStyle::Raw(0)) => continue, + "\"" if fmtstr.style == StrStyle::Cooked => "\\\"", + "\"" if fmtstr.style == StrStyle::Raw(0) => continue, "\\\\" if matches!(fmtstr.style, StrStyle::Raw(_)) => "\\", "\\'" => "'", "{" => "{{", diff --git a/clippy_utils/src/ast_utils.rs b/clippy_utils/src/ast_utils.rs index 2fa98831c774..e536f2a90a89 100644 --- a/clippy_utils/src/ast_utils.rs +++ b/clippy_utils/src/ast_utils.rs @@ -278,7 +278,7 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { }, (Trait(box TraitKind(la, lu, lg, lb, li)), Trait(box TraitKind(ra, ru, rg, rb, ri))) => { la == ra - && matches!(lu, Unsafe::No) == matches!(ru, Unsafe::No) + && matches!(lu, Unsafe::Yes(_)) == matches!(ru, Unsafe::Yes(_)) && eq_generics(lg, rg) && over(lb, rb, eq_generic_bound) && over(li, ri, |l, r| eq_item(l, r, eq_assoc_item_kind)) @@ -306,10 +306,10 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { items: ri, }), ) => { - matches!(lu, Unsafe::No) == matches!(ru, Unsafe::No) - && matches!(lp, ImplPolarity::Positive) == matches!(rp, ImplPolarity::Positive) + matches!(lu, Unsafe::Yes(_)) == matches!(ru, Unsafe::Yes(_)) + && matches!(lp, ImplPolarity::Negative(_)) == matches!(rp, ImplPolarity::Negative(_)) && eq_defaultness(*ld, *rd) - && matches!(lc, ast::Const::No) == matches!(rc, ast::Const::No) + && matches!(lc, ast::Const::Yes(_)) == matches!(rc, ast::Const::Yes(_)) && eq_generics(lg, rg) && both(lot, rot, |l, r| eq_path(&l.path, &r.path)) && eq_ty(lst, rst) @@ -388,9 +388,9 @@ pub fn eq_fn_sig(l: &FnSig, r: &FnSig) -> bool { } pub fn eq_fn_header(l: &FnHeader, r: &FnHeader) -> bool { - matches!(l.unsafety, Unsafe::No) == matches!(r.unsafety, Unsafe::No) + matches!(l.unsafety, Unsafe::Yes(_)) == matches!(r.unsafety, Unsafe::Yes(_)) && l.asyncness.is_async() == r.asyncness.is_async() - && matches!(l.constness, Const::No) == matches!(r.constness, Const::No) + && matches!(l.constness, Const::Yes(_)) == matches!(r.constness, Const::Yes(_)) && eq_ext(&l.ext, &r.ext) } diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index 7cbd43e6266e..cf7f49f8e12f 100644 --- a/clippy_utils/src/higher.rs +++ b/clippy_utils/src/higher.rs @@ -470,6 +470,42 @@ pub fn extract_assert_macro_args<'tcx>(e: &'tcx Expr<'tcx>) -> Option { + /// Span of `matches!(..)` + pub call_site: Span, + /// Second parameter of `matches!` + pub arm: &'tcx Arm<'tcx>, + /// First parameter of `matches!` + pub exp: &'tcx Expr<'tcx>, +} + +impl MatchesExpn<'tcx> { + /// Parses an expanded `matches!` invocation + pub fn parse(expr: &'tcx Expr<'tcx>) -> Option { + if_chain! { + if let ExprKind::Match(exp, [arm_true, arm_false], _) = expr.kind; + if let ExprKind::Lit(lit_true) = &arm_true.body.kind; + if lit_true.node == LitKind::Bool(true); + if let ExprKind::Lit(lit_false) = &arm_false.body.kind; + if lit_false.node == LitKind::Bool(false); + // there is no sym::matches ?! + //if let ExpnKind::Macro(_, sym::matches) = expn_data.kind; + then { + let expn_data = expr.span.ctxt().outer_expn_data(); + Some(MatchesExpn { + call_site: expn_data.call_site, + arm: arm_true, + exp, + }) + } else { + None + } + } + } +} + /// A parsed `format!` expansion pub struct FormatExpn<'tcx> { /// Span of `format!(..)` diff --git a/tests/lint_message_convention.rs b/tests/lint_message_convention.rs index b4d94dc983fe..c17d73acc35e 100644 --- a/tests/lint_message_convention.rs +++ b/tests/lint_message_convention.rs @@ -84,7 +84,7 @@ fn lint_message_convention() { .expect("failed to read dir") .map(|direntry| direntry.unwrap().path()) }) - .filter(|file| matches!(file.extension().map(OsStr::to_str), Some(Some("stderr")))); + .filter(|file| file.extension().map(OsStr::to_str) == Some(Some("stderr"))); // get all files that have any "bad lines" in them let bad_tests: Vec = tests diff --git a/tests/ui-internal/if_chain_style.rs b/tests/ui-internal/if_chain_style.rs index 8e871707aa8f..1bd8c4d61a10 100644 --- a/tests/ui-internal/if_chain_style.rs +++ b/tests/ui-internal/if_chain_style.rs @@ -1,5 +1,5 @@ #![warn(clippy::if_chain_style)] -#![allow(clippy::no_effect)] +#![allow(clippy::no_effect, clippy::equatable_if_let)] extern crate if_chain; diff --git a/tests/ui-toml/equatable_pattern/all.fixed b/tests/ui-toml/equatable_pattern/all.fixed new file mode 100644 index 000000000000..11eb5a7c720d --- /dev/null +++ b/tests/ui-toml/equatable_pattern/all.fixed @@ -0,0 +1,121 @@ +// run-rustfix + +#![allow(unused_variables, dead_code, clippy::redundant_pattern_matching, clippy::op_ref)] +#![warn(clippy::equatable_if_let, clippy::equatable_matches)] + +#[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 { + VA(A), + VB(B), + VC, +} + +#[derive(PartialEq)] +struct Generic2 { + 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 = Generic::VC; + let i: Generic = 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 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, &d) == (Enum::UnitVariant, &Struct { a: 2, b: false }) {} + if i == Generic::VA(Enum::UnitVariant) {} + 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 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() {} +} diff --git a/tests/ui-toml/equatable_pattern/all.rs b/tests/ui-toml/equatable_pattern/all.rs new file mode 100644 index 000000000000..23cc3a32dd51 --- /dev/null +++ b/tests/ui-toml/equatable_pattern/all.rs @@ -0,0 +1,127 @@ +// run-rustfix + +#![allow(unused_variables, dead_code, clippy::redundant_pattern_matching, clippy::op_ref)] +#![warn(clippy::equatable_if_let, clippy::equatable_matches)] + +#[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 { + VA(A), + VB(B), + VC, +} + +#[derive(PartialEq)] +struct Generic2 { + 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 = Generic::VC; + let i: Generic = 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 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, &Struct { a: 2, b: false }) = (e, &d) {} + if let Generic::VA(Enum::UnitVariant) = 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 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() {} +} diff --git a/tests/ui-toml/equatable_pattern/all.stderr b/tests/ui-toml/equatable_pattern/all.stderr new file mode 100644 index 000000000000..086b572a1a97 --- /dev/null +++ b/tests/ui-toml/equatable_pattern/all.stderr @@ -0,0 +1,115 @@ +error: this pattern matching can be expressed using equality + --> $DIR/all.rs:76:8 + | +LL | if let Some(2) = c {} + | ^^^^^^^^^^^^^^^ help: try: `c == Some(2)` + | + = note: `-D clippy::equatable-if-let` implied by `-D warnings` + +error: this pattern matching can be expressed using equality + --> $DIR/all.rs:77:8 + | +LL | if let Struct { a: 2, b: false } = d {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `d == (Struct { a: 2, b: false })` + +error: this pattern matching can be expressed using equality + --> $DIR/all.rs:78:8 + | +LL | if let Enum::TupleVariant(32, 64) = e {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == Enum::TupleVariant(32, 64)` + +error: this pattern matching can be expressed using equality + --> $DIR/all.rs:79:8 + | +LL | if let Enum::RecordVariant { a: 64, b: 32 } = e {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == (Enum::RecordVariant { a: 64, b: 32 })` + +error: this pattern matching can be expressed using equality + --> $DIR/all.rs:80:8 + | +LL | if let (Enum::UnitVariant, &Struct { a: 2, b: false }) = (e, &d) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(e, &d) == (Enum::UnitVariant, &Struct { a: 2, b: false })` + +error: this pattern matching can be expressed using equality + --> $DIR/all.rs:81:8 + | +LL | if let Generic::VA(Enum::UnitVariant) = i {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i == Generic::VA(Enum::UnitVariant)` + +error: this pattern matching can be expressed using equality + --> $DIR/all.rs:82:8 + | +LL | if let [7, 5] = j[1..3] {} + | ^^^^^^^^^^^^^^^^^^^^ help: try: `j[1..3] == [7, 5]` + +error: this pattern matching can be expressed using equality + --> $DIR/all.rs:83:8 + | +LL | if let [1, 2, 3, 4] = j[..] {} + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `j[..] == [1, 2, 3, 4]` + +error: this pattern matching can be expressed using equality + --> $DIR/all.rs:84:8 + | +LL | if let Some(true) = k {} + | ^^^^^^^^^^^^^^^^^^ help: try: `k == Some(&true)` + +error: this pattern matching can be expressed using equality + --> $DIR/all.rs:85:8 + | +LL | if let Some(&false) = k {} + | ^^^^^^^^^^^^^^^^^^^^ help: try: `k == Some(&false)` + +error: this pattern matching can be expressed using equality + --> $DIR/all.rs:86:8 + | +LL | if let Some(true) = &k {} + | ^^^^^^^^^^^^^^^^^^^ help: try: `&k == &Some(&true)` + +error: this pattern matching can be expressed using equality + --> $DIR/all.rs:87:8 + | +LL | if let Some(false) = &&k {} + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `&&k == &&Some(&false)` + +error: this pattern matching can be expressed using equality + --> $DIR/all.rs:88:8 + | +LL | if let Generic2 { + | ________^ +LL | | a: Generic2 { a: "yyy", b: 3 }, +LL | | b: Generic2 { +LL | | a: Enum::UnitVariant, +LL | | b: false, +LL | | }, +LL | | } = l + | |_________^ help: try: `l == (Generic2 { a: Generic2 { a: "yyy", b: 3 }, b: Generic2 { a: &Enum::UnitVariant, b: false } })` + +error: this pattern matching can be expressed using equality + --> $DIR/all.rs:96:8 + | +LL | if let Generic2 { a: 3, b: 5 } = m {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `m == (Generic2 { a: 3, b: 5 })` + +error: this pattern matching can be expressed using equality + --> $DIR/all.rs:97:8 + | +LL | if let Some("yyy") = n {} + | ^^^^^^^^^^^^^^^^^^^ help: try: `n == Some("yyy")` + +error: this pattern matching can be expressed using equality + --> $DIR/all.rs:98:13 + | +LL | let _ = matches!(c, Some(2)); + | ^^^^^^^^^^^^^^^^^^^^ help: try: `c == Some(2)` + | + = note: `-D clippy::equatable-matches` implied by `-D warnings` + +error: this pattern matching can be expressed using equality + --> $DIR/all.rs:100:11 + | +LL | while let Some(2) = o.next() {} + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `o.next() == Some(&2)` + +error: aborting due to 17 previous errors + diff --git a/tests/ui-toml/equatable_pattern/clippy.toml b/tests/ui-toml/equatable_pattern/clippy.toml new file mode 100644 index 000000000000..5d792c8393a0 --- /dev/null +++ b/tests/ui-toml/equatable_pattern/clippy.toml @@ -0,0 +1 @@ +equatable-pattern = "All" diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 97bab1308aa5..1859a2fa1168 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -1,4 +1,4 @@ -error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `avoid-breaking-exported-api`, `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `disallowed-types`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `standard-macro-braces`, `enforced-import-renames`, `allowed-scripts`, `enable-raw-pointer-heuristic-for-send`, `third-party` at line 5 column 1 +error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `avoid-breaking-exported-api`, `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `disallowed-types`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `standard-macro-braces`, `enforced-import-renames`, `allowed-scripts`, `enable-raw-pointer-heuristic-for-send`, `equatable-pattern`, `third-party` at line 5 column 1 error: aborting due to previous error diff --git a/tests/ui/equatable_if_let.fixed b/tests/ui/equatable_if_let.fixed index 88918d9671e4..2e47624756df 100644 --- a/tests/ui/equatable_if_let.fixed +++ b/tests/ui/equatable_if_let.fixed @@ -1,7 +1,7 @@ // run-rustfix -#![allow(unused_variables, dead_code)] -#![warn(clippy::equatable_if_let)] +#![allow(unused_variables, dead_code, clippy::redundant_pattern_matching, clippy::op_ref)] +#![warn(clippy::equatable_if_let, clippy::equatable_matches)] use std::cmp::Ordering; @@ -19,11 +19,13 @@ struct Struct { b: bool, } +#[derive(Clone, Copy)] enum NotPartialEq { A, B, } +#[derive(Clone, Copy)] enum NotStructuralEq { A, B, @@ -35,6 +37,30 @@ impl PartialEq for NotStructuralEq { } } +#[derive(PartialEq)] +enum Generic { + VA(A), + VB(B), + VC, +} + +#[derive(PartialEq)] +struct Generic2 { + a: A, + b: B, +} + +fn macro_pattern() { + macro_rules! m1 { + (x) => { + "abc" + }; + } + if "abc" == m1!(x) { + println!("OK"); + } +} + fn main() { let a = 2; let b = 3; @@ -43,36 +69,42 @@ fn main() { let e = Enum::UnitVariant; let f = NotPartialEq::A; let g = NotStructuralEq::A; + let h: Generic = Generic::VC; + let i: Generic = 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 "world" == "hello" {} 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::VC {} + if k == None {} + + let _ = b == 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 g == NotStructuralEq::A {} - if let Some(NotPartialEq::A) = Some(f) {} - if Some(g) == Some(NotStructuralEq::A) {} + 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 Some("yyy") = n {} - macro_rules! m1 { - (x) => { - "abc" - }; - } - if "abc" == m1!(x) { - println!("OK"); - } + let _ = matches!(c, Some(2)); + + while let Some(4 | 7) = o.next() {} } diff --git a/tests/ui/equatable_if_let.rs b/tests/ui/equatable_if_let.rs index 9a7ab75ef450..5c7bcf27846d 100644 --- a/tests/ui/equatable_if_let.rs +++ b/tests/ui/equatable_if_let.rs @@ -1,7 +1,7 @@ // run-rustfix -#![allow(unused_variables, dead_code)] -#![warn(clippy::equatable_if_let)] +#![allow(unused_variables, dead_code, clippy::redundant_pattern_matching, clippy::op_ref)] +#![warn(clippy::equatable_if_let, clippy::equatable_matches)] use std::cmp::Ordering; @@ -19,11 +19,13 @@ struct Struct { b: bool, } +#[derive(Clone, Copy)] enum NotPartialEq { A, B, } +#[derive(Clone, Copy)] enum NotStructuralEq { A, B, @@ -35,6 +37,30 @@ impl PartialEq for NotStructuralEq { } } +#[derive(PartialEq)] +enum Generic { + VA(A), + VB(B), + VC, +} + +#[derive(PartialEq)] +struct Generic2 { + a: A, + b: B, +} + +fn macro_pattern() { + macro_rules! m1 { + (x) => { + "abc" + }; + } + if let m1!(x) = "abc" { + println!("OK"); + } +} + fn main() { let a = 2; let b = 3; @@ -43,36 +69,42 @@ fn main() { let e = Enum::UnitVariant; let f = NotPartialEq::A; let g = NotStructuralEq::A; + let h: Generic = Generic::VC; + let i: Generic = 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 "hello" = "world" {} if let Ordering::Greater = a.cmp(&b) {} + if let Enum::UnitVariant = e {} + if let None = Some(g) {} + if let Generic::VC = i {} + if let None = k {} + + let _ = matches!(b, 2); + + // false + 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) {} - - // false + if let Some("yyy") = n {} - 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 Some(NotStructuralEq::A) = Some(g) {} + let _ = matches!(c, Some(2)); - macro_rules! m1 { - (x) => { - "abc" - }; - } - if let m1!(x) = "abc" { - println!("OK"); - } + while let Some(4 | 7) = o.next() {} } diff --git a/tests/ui/equatable_if_let.stderr b/tests/ui/equatable_if_let.stderr index 760ff88f448f..64e58e36c293 100644 --- a/tests/ui/equatable_if_let.stderr +++ b/tests/ui/equatable_if_let.stderr @@ -1,70 +1,60 @@ error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:49:8 + --> $DIR/equatable_if_let.rs:59:8 | -LL | if let 2 = a {} - | ^^^^^^^^^ help: try: `a == 2` +LL | if let m1!(x) = "abc" { + | ^^^^^^^^^^^^^^^^^^ help: try: `"abc" == m1!(x)` | = note: `-D clippy::equatable-if-let` implied by `-D warnings` error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:50:8 - | -LL | if let Ordering::Greater = a.cmp(&b) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `a.cmp(&b) == Ordering::Greater` - -error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:51:8 + --> $DIR/equatable_if_let.rs:89:8 | -LL | if let Some(2) = c {} - | ^^^^^^^^^^^^^^^ help: try: `c == Some(2)` - -error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:52:8 - | -LL | if let Struct { a: 2, b: false } = d {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `d == (Struct { a: 2, b: false })` +LL | if let 2 = a {} + | ^^^^^^^^^ help: try: `a == 2` error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:53:8 + --> $DIR/equatable_if_let.rs:90:8 | -LL | if let Enum::TupleVariant(32, 64) = e {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == Enum::TupleVariant(32, 64)` +LL | if let "hello" = "world" {} + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `"world" == "hello"` error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:54:8 + --> $DIR/equatable_if_let.rs:91:8 | -LL | if let Enum::RecordVariant { a: 64, b: 32 } = e {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == (Enum::RecordVariant { a: 64, b: 32 })` +LL | if let Ordering::Greater = a.cmp(&b) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `a.cmp(&b) == Ordering::Greater` error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:55:8 + --> $DIR/equatable_if_let.rs:92:8 | LL | if let Enum::UnitVariant = e {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == Enum::UnitVariant` error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:56:8 + --> $DIR/equatable_if_let.rs:93:8 | -LL | if let (Enum::UnitVariant, &Struct { a: 2, b: false }) = (e, &d) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(e, &d) == (Enum::UnitVariant, &Struct { a: 2, b: false })` +LL | if let None = Some(g) {} + | ^^^^^^^^^^^^^^^^^^ help: try: `Some(g) == None` error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:66:8 + --> $DIR/equatable_if_let.rs:94:8 | -LL | if let NotStructuralEq::A = g {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `g == NotStructuralEq::A` +LL | if let Generic::VC = i {} + | ^^^^^^^^^^^^^^^^^^^ help: try: `i == Generic::VC` error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:68:8 + --> $DIR/equatable_if_let.rs:95:8 | -LL | if let Some(NotStructuralEq::A) = Some(g) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(g) == Some(NotStructuralEq::A)` +LL | if let None = k {} + | ^^^^^^^^^^^^ help: try: `k == None` error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:75:8 + --> $DIR/equatable_if_let.rs:97:13 | -LL | if let m1!(x) = "abc" { - | ^^^^^^^^^^^^^^^^^^ help: try: `"abc" == m1!(x)` +LL | let _ = matches!(b, 2); + | ^^^^^^^^^^^^^^ help: try: `b == 2` + | + = note: `-D clippy::equatable-matches` implied by `-D warnings` -error: aborting due to 11 previous errors +error: aborting due to 9 previous errors