Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 60 additions & 8 deletions clippy_lints/src/matches/redundant_pattern_match.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand All @@ -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};
Expand Down Expand Up @@ -117,14 +118,65 @@ fn try_get_generic_ty(ty: Ty<'_>, index: usize) -> Option<Ty<'_>> {
}
}

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<'_>,
op_ty: Ty<'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 {
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -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
Expand All @@ -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,
}
}
Expand Down
37 changes: 37 additions & 0 deletions tests/ui/redundant_pattern_matching_option.fixed
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
37 changes: 37 additions & 0 deletions tests/ui/redundant_pattern_matching_option.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
32 changes: 31 additions & 1 deletion tests/ui/redundant_pattern_matching_option.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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