diff --git a/clippy_lints/src/matches/redundant_pattern_match.rs b/clippy_lints/src/matches/redundant_pattern_match.rs index bc3783750e5c..1c007b9b0d4a 100644 --- a/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/clippy_lints/src/matches/redundant_pattern_match.rs @@ -1,3 +1,4 @@ +#![allow(clippy::enum_glob_use)] use super::REDUNDANT_PATTERN_MATCHING; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; @@ -10,7 +11,7 @@ use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::LangItem::{self, OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk}; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{Arm, Expr, ExprKind, Node, Pat, PatExpr, PatExprKind, PatKind, QPath, UnOp}; +use rustc_hir::{Arm, Expr, ExprKind, Node, Pat, PatExpr, PatExprKind, PatKind, Path, QPath, UnOp}; use rustc_lint::LateContext; use rustc_middle::ty::{self, GenericArgKind, Ty}; use rustc_span::{Span, Symbol}; @@ -117,6 +118,57 @@ fn try_get_generic_ty(ty: Ty<'_>, index: usize) -> Option> { } } +fn is_infalliable(pat: &Pat<'_>, cx: &LateContext<'_>) -> bool { + use PatKind::*; + pat.walk_short(|pat| match pat.kind { + Expr(PatExpr { + hir_id: _, + span: _, + kind: + PatExprKind::Path(QPath::Resolved( + _, + Path { + res: Res::Def(DefKind::Struct | DefKind::Variant | DefKind::Enum | DefKind::Ctor(..), did), + .. + }, + )), + }) + | TupleStruct( + QPath::Resolved( + _, + Path { + res: Res::Def(DefKind::Struct | DefKind::Variant | DefKind::Enum | DefKind::Ctor(..), did), + .. + }, + ), + _, + _, + ) + | Struct( + QPath::Resolved( + _, + Path { + res: Res::Def(DefKind::Struct | DefKind::Variant | DefKind::Enum | DefKind::Ctor(..), did), + .. + }, + ), + _, + _, + ) => { + let mut did = *did; + while ![DefKind::Enum, DefKind::Struct].contains(&cx.tcx.def_kind(did)) { + did = did.opt_parent(&cx.tcx).unwrap_or(did); + } + let adt_def = cx.tcx.adt_def(did); + (adt_def.is_enum() && adt_def.variants().len() == 1) || adt_def.is_struct() + }, + Expr(..) | Range(..) | Binding(..) | Err(_) | Guard(..) => false, + // for slices we have to account for the length, because thats what distinguishes each slice + Slice(s1, _dotdotlike, s2) => s1.is_empty() && s2.is_empty(), + + _ => true, + }) +} fn find_method_and_type<'tcx>( cx: &LateContext<'tcx>, check_pat: &Pat<'_>, @@ -124,7 +176,7 @@ fn find_method_and_type<'tcx>( ) -> Option<(&'static str, Ty<'tcx>)> { match check_pat.kind { PatKind::TupleStruct(ref qpath, args, rest) => { - let is_wildcard = matches!(args.first().map(|p| &p.kind), Some(PatKind::Wild)); + let is_wildcard = args.first().is_some_and(|pat| is_infalliable(pat, cx)); let is_rest = matches!((args, rest.as_opt_usize()), ([], Some(_))); if is_wildcard || is_rest { @@ -324,7 +376,7 @@ fn found_good_method<'tcx>( ) -> Option<(&'static str, Option<&'tcx Expr<'tcx>>)> { match (&arms[0].pat.kind, &arms[1].pat.kind) { (PatKind::TupleStruct(path_left, [pattern_left], _), PatKind::TupleStruct(path_right, [pattern_right], _)) => { - if let (PatKind::Wild, PatKind::Wild) = (&pattern_left.kind, &pattern_right.kind) { + if is_infalliable(pattern_left, cx) && is_infalliable(pattern_right, cx) { find_good_method_for_match( cx, arms, @@ -365,7 +417,7 @@ fn found_good_method<'tcx>( }), PatKind::TupleStruct(path_right, [pattern], _), ) => { - if let PatKind::Wild = pattern.kind { + if is_infalliable(pattern, cx) { find_good_method_for_match( cx, arms, @@ -392,8 +444,8 @@ fn found_good_method<'tcx>( None } }, - (PatKind::TupleStruct(path_left, [pattern], _), PatKind::Wild) => { - if let PatKind::Wild = pattern.kind { + (PatKind::TupleStruct(path_left, [pattern], _), _) if is_infalliable(arms[1].pat, cx) => { + if is_infalliable(pattern, cx) { get_good_method(cx, arms, path_left) } else { None @@ -404,8 +456,8 @@ fn found_good_method<'tcx>( kind: PatExprKind::Path(path_left), .. }), - PatKind::Wild, - ) => get_good_method(cx, arms, path_left), + _, + ) if is_infalliable(arms[1].pat, cx) => get_good_method(cx, arms, path_left), _ => None, } } diff --git a/tests/ui/redundant_pattern_matching_option.fixed b/tests/ui/redundant_pattern_matching_option.fixed index 08903ef7fdda..f0a128df0ba5 100644 --- a/tests/ui/redundant_pattern_matching_option.fixed +++ b/tests/ui/redundant_pattern_matching_option.fixed @@ -195,3 +195,40 @@ fn issue16045() { } } } + +fn issue16235() { + #![allow(clippy::disallowed_names)] + enum Baz { + Qux, + } + + enum Ban { + Foo, + Bar, + } + struct Quux { + #[allow(dead_code)] + corge: bool, + } + + let foo = Some((2, 4)); + let bar = Some(Baz::Qux); + let grault = Some(Quux { corge: true }); + let ban = Some(Ban::Foo); + + if foo.is_some() {} + //~^ redundant_pattern_matching + if bar.is_some() {} + //~^ redundant_pattern_matching + if grault.is_some() {} + //~^ redundant_pattern_matching + if let Some(Ban::Bar) = ban {} +} + +fn slices() { + if let Some([_, _]) = Some([1, 3]) {} + if Some([1, 3]).is_some() {} + //~^ redundant_pattern_matching + if Some(&[1, 3] as &[i32]).is_some() {} + //~^ redundant_pattern_matching +} diff --git a/tests/ui/redundant_pattern_matching_option.rs b/tests/ui/redundant_pattern_matching_option.rs index 95eff3f9ebf9..564e78d131af 100644 --- a/tests/ui/redundant_pattern_matching_option.rs +++ b/tests/ui/redundant_pattern_matching_option.rs @@ -231,3 +231,40 @@ fn issue16045() { } } } + +fn issue16235() { + #![allow(clippy::disallowed_names)] + enum Baz { + Qux, + } + + enum Ban { + Foo, + Bar, + } + struct Quux { + #[allow(dead_code)] + corge: bool, + } + + let foo = Some((2, 4)); + let bar = Some(Baz::Qux); + let grault = Some(Quux { corge: true }); + let ban = Some(Ban::Foo); + + if let Some((_, _)) = foo {} + //~^ redundant_pattern_matching + if let Some(Baz::Qux) = bar {} + //~^ redundant_pattern_matching + if let Some(Quux { corge: _ }) = grault {} + //~^ redundant_pattern_matching + if let Some(Ban::Bar) = ban {} +} + +fn slices() { + if let Some([_, _]) = Some([1, 3]) {} + if let Some([..]) = Some([1, 3]) {} + //~^ redundant_pattern_matching + if let Some([]) = Some(&[1, 3] as &[i32]) {} + //~^ redundant_pattern_matching +} diff --git a/tests/ui/redundant_pattern_matching_option.stderr b/tests/ui/redundant_pattern_matching_option.stderr index 6fd0c5a6f859..6ceea27c5065 100644 --- a/tests/ui/redundant_pattern_matching_option.stderr +++ b/tests/ui/redundant_pattern_matching_option.stderr @@ -236,5 +236,35 @@ error: redundant pattern matching, consider using `is_some()` LL | if let Some(_) = x.await { | -------^^^^^^^---------- help: try: `if x.await.is_some()` -error: aborting due to 33 previous errors +error: redundant pattern matching, consider using `is_some()` + --> tests/ui/redundant_pattern_matching_option.rs:255:12 + | +LL | if let Some((_, _)) = foo {} + | -------^^^^^^^^^^^^------ help: try: `if foo.is_some()` + +error: redundant pattern matching, consider using `is_some()` + --> tests/ui/redundant_pattern_matching_option.rs:257:12 + | +LL | if let Some(Baz::Qux) = bar {} + | -------^^^^^^^^^^^^^^------ help: try: `if bar.is_some()` + +error: redundant pattern matching, consider using `is_some()` + --> tests/ui/redundant_pattern_matching_option.rs:259:12 + | +LL | if let Some(Quux { corge: _ }) = grault {} + | -------^^^^^^^^^^^^^^^^^^^^^^^--------- help: try: `if grault.is_some()` + +error: redundant pattern matching, consider using `is_some()` + --> tests/ui/redundant_pattern_matching_option.rs:266:12 + | +LL | if let Some([..]) = Some([1, 3]) {} + | -------^^^^^^^^^^--------------- help: try: `if Some([1, 3]).is_some()` + +error: redundant pattern matching, consider using `is_some()` + --> tests/ui/redundant_pattern_matching_option.rs:268:12 + | +LL | if let Some([]) = Some(&[1, 3] as &[i32]) {} + | -------^^^^^^^^-------------------------- help: try: `if Some(&[1, 3] as &[i32]).is_some()` + +error: aborting due to 38 previous errors