From 3ec903fed7f4e1b0da6ccaef61ed0b4c23c0b54e Mon Sep 17 00:00:00 2001 From: Georgiy Komarov Date: Sat, 5 Feb 2022 11:35:06 +0300 Subject: [PATCH 01/19] single_match: Lint structs with the same name These changes allow `single_match` lint to suggest the simplification of `match` constructions for structs with the same name that forms an exhaustive match. For example: ```rust // lint: S(_, _) forms an exhaustive match, so it could be removed match s { S(42, _a) => {}, S(_, _) => {}, } ``` See: https://github.com/rust-lang/rust-clippy/pull/8322#issuecomment-1025316474 --- clippy_lints/src/matches.rs | 219 ++++++++++++++++++++--------------- tests/ui/single_match.rs | 35 +++--- tests/ui/single_match.stderr | 20 +++- 3 files changed, 157 insertions(+), 117 deletions(-) diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index e0cbadeb6455..bda7b3c93b60 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -5,7 +5,7 @@ use clippy_utils::diagnostics::{ use clippy_utils::macros::{is_panic, root_macro_call}; use clippy_utils::source::{expr_block, indent_of, snippet, snippet_block, snippet_opt, snippet_with_applicability}; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type, peel_mid_ty_refs}; +use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, peel_mid_ty_refs}; use clippy_utils::visitors::is_local_used; use clippy_utils::{ get_parent_expr, is_lang_ctor, is_lint_allowed, is_refutable, is_unit_expr, is_wild, meets_msrv, msrvs, @@ -13,7 +13,7 @@ use clippy_utils::{ strip_pat_refs, }; use clippy_utils::{higher, peel_blocks_with_stmt}; -use clippy_utils::{paths, search_same, SpanlessEq, SpanlessHash}; +use clippy_utils::{search_same, SpanlessEq, SpanlessHash}; use core::iter::{once, ExactSizeIterator}; use if_chain::if_chain; use rustc_ast::ast::{Attribute, LitKind}; @@ -838,8 +838,27 @@ fn check_single_match_opt_like<'a>( ty: Ty<'a>, els: Option<&Expr<'_>>, ) { - // list of candidate `Enum`s we know will never get any more members - let candidates = &[ + if check_exhaustive(cx, arms[0].pat, arms[1].pat, ty) { + report_single_match_single_pattern(cx, ex, arms, expr, els); + } +} + +fn qpath_to_string(qpath: &QPath<'_>) -> String { + rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| { + s.print_qpath(qpath, false); + }) +} + +mod known_enums { + use super::{contains_only_wilds, contains_single_binding, qpath_to_string}; + use clippy_utils::paths; + use clippy_utils::ty::match_type; + use rustc_hir::{Pat, PatKind}; + use rustc_lint::LateContext; + use rustc_middle::ty::Ty; + + /// List of candidate `Enum`s we know will never get any more members. + const CANDIDATES: &[(&[&str; 3], &str)] = &[ (&paths::COW, "Borrowed"), (&paths::COW, "Cow::Borrowed"), (&paths::COW, "Cow::Owned"), @@ -849,63 +868,119 @@ fn check_single_match_opt_like<'a>( (&paths::RESULT, "Ok"), ]; - // We want to suggest to exclude an arm that contains only wildcards or forms the exhaustive - // match with the second branch, without enum variants in matches. - if !contains_only_wilds(arms[1].pat) && !form_exhaustive_matches(arms[0].pat, arms[1].pat) { - return; - } - - let mut paths_and_types = Vec::new(); - if !collect_pat_paths(&mut paths_and_types, cx, arms[1].pat, ty) { - return; - } - - let in_candidate_enum = |path_info: &(String, &TyS<'_>)| -> bool { - let (path, ty) = path_info; - for &(ty_path, pat_path) in candidates { + pub fn contains(cx: &LateContext<'_>, path: &str, ty: Ty<'_>) -> bool { + for &(ty_path, pat_path) in CANDIDATES { if path == pat_path && match_type(cx, ty, ty_path) { return true; } } false - }; - if paths_and_types.iter().all(in_candidate_enum) { - report_single_match_single_pattern(cx, ex, arms, expr, els); + } + + /// Returns true if the given pattern contains only one of `CANDIDATES` enums that could be + /// simplified by the `matches` lint. + pub fn is_known_enum(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { + match pat.kind { + PatKind::Path(ref path) => contains(cx, &qpath_to_string(path), cx.typeck_results().pat_ty(pat)), + PatKind::TupleStruct(ref path, ..) => { + let ty = cx.typeck_results().pat_ty(pat); + let path = &qpath_to_string(path); + (contains_only_wilds(pat) || contains_single_binding(pat)) && contains(cx, path, ty) + }, + _ => false, + } } } -/// Collects paths and their types from the given patterns. Returns true if the given pattern could -/// be simplified, false otherwise. -fn collect_pat_paths<'a>(acc: &mut Vec<(String, Ty<'a>)>, cx: &LateContext<'a>, pat: &Pat<'_>, ty: Ty<'a>) -> bool { - match pat.kind { - PatKind::Wild => true, - PatKind::Tuple(inner, _) => inner.iter().all(|p| { - let p_ty = cx.typeck_results().pat_ty(p); - collect_pat_paths(acc, cx, p, p_ty) - }), - PatKind::TupleStruct(ref path, ..) => { - let path = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| { - s.print_qpath(path, false); - }); - acc.push((path, ty)); - true +/// Returns true if the given arms of the pattern forms exhaustive match that can be simplified to +/// `if let` construction. +fn check_exhaustive<'a>(cx: &LateContext<'a>, left: &Pat<'_>, right: &Pat<'_>, ty: Ty<'a>) -> bool { + match (&left.kind, &right.kind) { + (PatKind::Wild, _) | (_, PatKind::Wild) => true, + (PatKind::Tuple(left_in, left_pos), PatKind::Tuple(right_in, right_pos)) => { + check_exhaustive_tuples(cx, left_in, left_pos, right_in, right_pos) }, - PatKind::Binding(BindingAnnotation::Unannotated, .., ident, None) => { - acc.push((ident.to_string(), ty)); - true + ( + PatKind::TupleStruct(left_qpath, left_in, left_pos), + PatKind::TupleStruct(right_qpath, right_in, right_pos), + ) => { + let are_structs_with_the_same_name = + || -> bool { cx.qpath_res(left_qpath, left.hir_id) == cx.qpath_res(right_qpath, right.hir_id) }; + let are_known_enums = + || -> bool { known_enums::is_known_enum(cx, left) && known_enums::is_known_enum(cx, right) }; + if are_structs_with_the_same_name() || are_known_enums() { + return check_exhaustive_tuples(cx, left_in, left_pos, right_in, right_pos); + } + false }, - PatKind::Path(ref path) => { - let path = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| { - s.print_qpath(path, false); - }); - acc.push((path, ty)); - true + (PatKind::Binding(BindingAnnotation::Unannotated, .., ident, None), _) if contains_only_wilds(right) => { + known_enums::contains(cx, &ident.to_string(), ty) + }, + (PatKind::Path(ref path), _) if contains_only_wilds(right) => { + known_enums::contains(cx, &qpath_to_string(path), ty) }, _ => false, } } -/// Returns true if the given arm of pattern matching contains wildcard patterns. +/// Returns true if two tuples that contain patterns `left_in` and `right_in` and `..` operators at +/// positions `left_pos` and `right_pos` form exhaustive match. This means, that two elements of +/// these tuples located at the same positions must have at least one wildcard (`_`) pattern. +fn check_exhaustive_tuples<'a>( + cx: &LateContext<'_>, + left_in: &'a [Pat<'a>], + left_pos: &Option, + right_in: &'a [Pat<'a>], + right_pos: &Option, +) -> bool { + // We don't actually know the position and the presence of the `..` (dotdot) operator + // in the arms, so we need to evaluate the correct offsets here in order to iterate in + // both arms at the same time. + let len = max( + left_in.len() + { + if left_pos.is_some() { 1 } else { 0 } + }, + right_in.len() + { + if right_pos.is_some() { 1 } else { 0 } + }, + ); + let mut left_pos = left_pos.unwrap_or(usize::MAX); + let mut right_pos = right_pos.unwrap_or(usize::MAX); + let mut left_dot_space = 0; + let mut right_dot_space = 0; + for i in 0..len { + let mut found_dotdot = false; + if i == left_pos { + left_dot_space += 1; + if left_dot_space < len - left_in.len() { + left_pos += 1; + } + found_dotdot = true; + } + if i == right_pos { + right_dot_space += 1; + if right_dot_space < len - right_in.len() { + right_pos += 1; + } + found_dotdot = true; + } + if found_dotdot { + continue; + } + + let left_pat = &left_in[i - left_dot_space]; + let right_pat = &right_in[i - right_dot_space]; + if !(contains_only_wilds(left_pat) + || contains_only_wilds(right_pat) + || known_enums::is_known_enum(cx, left_pat) && known_enums::is_known_enum(cx, right_pat)) + { + return false; + } + } + true +} + +/// Returns true if the given arm of pattern matching contains only wildcard patterns. fn contains_only_wilds(pat: &Pat<'_>) -> bool { match pat.kind { PatKind::Wild => true, @@ -914,54 +989,10 @@ fn contains_only_wilds(pat: &Pat<'_>) -> bool { } } -/// Returns true if the given patterns forms only exhaustive matches that don't contain enum -/// patterns without a wildcard. -fn form_exhaustive_matches(left: &Pat<'_>, right: &Pat<'_>) -> bool { - match (&left.kind, &right.kind) { - (PatKind::Wild, _) | (_, PatKind::Wild) => true, - (PatKind::Tuple(left_in, left_pos), PatKind::Tuple(right_in, right_pos)) => { - // We don't actually know the position and the presence of the `..` (dotdot) operator - // in the arms, so we need to evaluate the correct offsets here in order to iterate in - // both arms at the same time. - let len = max( - left_in.len() + { - if left_pos.is_some() { 1 } else { 0 } - }, - right_in.len() + { - if right_pos.is_some() { 1 } else { 0 } - }, - ); - let mut left_pos = left_pos.unwrap_or(usize::MAX); - let mut right_pos = right_pos.unwrap_or(usize::MAX); - let mut left_dot_space = 0; - let mut right_dot_space = 0; - for i in 0..len { - let mut found_dotdot = false; - if i == left_pos { - left_dot_space += 1; - if left_dot_space < len - left_in.len() { - left_pos += 1; - } - found_dotdot = true; - } - if i == right_pos { - right_dot_space += 1; - if right_dot_space < len - right_in.len() { - right_pos += 1; - } - found_dotdot = true; - } - if found_dotdot { - continue; - } - if !contains_only_wilds(&left_in[i - left_dot_space]) - && !contains_only_wilds(&right_in[i - right_dot_space]) - { - return false; - } - } - true - }, +fn contains_single_binding(pat: &Pat<'_>) -> bool { + match pat.kind { + PatKind::Binding(BindingAnnotation::Unannotated, .., None) => true, + PatKind::TupleStruct(_, inner, ..) => inner.iter().all(contains_single_binding), _ => false, } } diff --git a/tests/ui/single_match.rs b/tests/ui/single_match.rs index bd3718880463..2e56e1f2d061 100644 --- a/tests/ui/single_match.rs +++ b/tests/ui/single_match.rs @@ -197,30 +197,21 @@ fn ranges() { } } -fn skip_type_aliases() { - enum OptionEx { - Some(i32), - None, - } - enum ResultEx { - Err(i32), - Ok(i32), - } +fn tuple_structs() { + struct S(i32, i32); + let s = S(1, 2); - use OptionEx::{None, Some}; - use ResultEx::{Err, Ok}; - - // don't lint - match Err(42) { - Ok(_) => dummy(), - Err(_) => (), - }; + // lint: S(_, _) forms an exhaustive match, so it could be removed + match s { + S(42, _a) => {}, + S(_, _) => {}, + } - // don't lint - match Some(1i32) { - Some(_) => dummy(), - None => (), - }; + // lint: S(..) forms an exhaustive match, so it could be removed + match s { + S(42, _a) => {}, + S(..) => {}, + } } macro_rules! single_match { diff --git a/tests/ui/single_match.stderr b/tests/ui/single_match.stderr index 318faf257175..95742c0b2d62 100644 --- a/tests/ui/single_match.stderr +++ b/tests/ui/single_match.stderr @@ -146,5 +146,23 @@ LL | | (..) => {}, LL | | } | |_____^ help: try this: `if let (.., Some(E::V), _) = (Some(42), Some(E::V), Some(42)) {}` -error: aborting due to 15 previous errors +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> $DIR/single_match.rs:205:5 + | +LL | / match s { +LL | | S(42, _a) => {}, +LL | | S(_, _) => {}, +LL | | } + | |_____^ help: try this: `if let S(42, _a) = s {}` + +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> $DIR/single_match.rs:211:5 + | +LL | / match s { +LL | | S(42, _a) => {}, +LL | | S(..) => {}, +LL | | } + | |_____^ help: try this: `if let S(42, _a) = s {}` + +error: aborting due to 17 previous errors From 1907ff501fa9613324e384ac27184a68340cb4d7 Mon Sep 17 00:00:00 2001 From: Georgiy Komarov Date: Sun, 6 Feb 2022 11:51:57 +0300 Subject: [PATCH 02/19] matches: Simplify code; get rid of `known_enums` module --- clippy_lints/src/matches.rs | 73 +++++++++++-------------------------- 1 file changed, 21 insertions(+), 52 deletions(-) diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index bda7b3c93b60..92e29b7d1fdc 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -3,9 +3,10 @@ use clippy_utils::diagnostics::{ multispan_sugg, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, }; use clippy_utils::macros::{is_panic, root_macro_call}; +use clippy_utils::paths; use clippy_utils::source::{expr_block, indent_of, snippet, snippet_block, snippet_opt, snippet_with_applicability}; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, peel_mid_ty_refs}; +use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type, peel_mid_ty_refs}; use clippy_utils::visitors::is_local_used; use clippy_utils::{ get_parent_expr, is_lang_ctor, is_lint_allowed, is_refutable, is_unit_expr, is_wild, meets_msrv, msrvs, @@ -843,52 +844,21 @@ fn check_single_match_opt_like<'a>( } } -fn qpath_to_string(qpath: &QPath<'_>) -> String { - rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| { - s.print_qpath(qpath, false); - }) +/// Resturns true if the given type is one of the standard `Enum`s we know will never get any more +/// members. +fn is_known_enum(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { + const CANDIDATES: &[&[&str; 3]] = &[&paths::COW, &paths::OPTION, &paths::RESULT]; + CANDIDATES.iter().any(|&ty_path| match_type(cx, ty, ty_path)) } -mod known_enums { - use super::{contains_only_wilds, contains_single_binding, qpath_to_string}; - use clippy_utils::paths; - use clippy_utils::ty::match_type; - use rustc_hir::{Pat, PatKind}; - use rustc_lint::LateContext; - use rustc_middle::ty::Ty; - - /// List of candidate `Enum`s we know will never get any more members. - const CANDIDATES: &[(&[&str; 3], &str)] = &[ - (&paths::COW, "Borrowed"), - (&paths::COW, "Cow::Borrowed"), - (&paths::COW, "Cow::Owned"), - (&paths::COW, "Owned"), - (&paths::OPTION, "None"), - (&paths::RESULT, "Err"), - (&paths::RESULT, "Ok"), - ]; - - pub fn contains(cx: &LateContext<'_>, path: &str, ty: Ty<'_>) -> bool { - for &(ty_path, pat_path) in CANDIDATES { - if path == pat_path && match_type(cx, ty, ty_path) { - return true; - } - } - false - } - - /// Returns true if the given pattern contains only one of `CANDIDATES` enums that could be - /// simplified by the `matches` lint. - pub fn is_known_enum(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { - match pat.kind { - PatKind::Path(ref path) => contains(cx, &qpath_to_string(path), cx.typeck_results().pat_ty(pat)), - PatKind::TupleStruct(ref path, ..) => { - let ty = cx.typeck_results().pat_ty(pat); - let path = &qpath_to_string(path); - (contains_only_wilds(pat) || contains_single_binding(pat)) && contains(cx, path, ty) - }, - _ => false, - } +fn contains_only_known_enums(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { + match pat.kind { + PatKind::Path(..) => is_known_enum(cx, cx.typeck_results().pat_ty(pat)), + PatKind::TupleStruct(..) => { + let ty = cx.typeck_results().pat_ty(pat); + (contains_only_wilds(pat) || contains_single_binding(pat)) && is_known_enum(cx, ty) + }, + _ => false, } } @@ -907,17 +877,16 @@ fn check_exhaustive<'a>(cx: &LateContext<'a>, left: &Pat<'_>, right: &Pat<'_>, t let are_structs_with_the_same_name = || -> bool { cx.qpath_res(left_qpath, left.hir_id) == cx.qpath_res(right_qpath, right.hir_id) }; let are_known_enums = - || -> bool { known_enums::is_known_enum(cx, left) && known_enums::is_known_enum(cx, right) }; + || -> bool { contains_only_known_enums(cx, left) && contains_only_known_enums(cx, right) }; if are_structs_with_the_same_name() || are_known_enums() { return check_exhaustive_tuples(cx, left_in, left_pos, right_in, right_pos); } false }, - (PatKind::Binding(BindingAnnotation::Unannotated, .., ident, None), _) if contains_only_wilds(right) => { - known_enums::contains(cx, &ident.to_string(), ty) - }, - (PatKind::Path(ref path), _) if contains_only_wilds(right) => { - known_enums::contains(cx, &qpath_to_string(path), ty) + (PatKind::Binding(BindingAnnotation::Unannotated, .., None), _) | (PatKind::Path(_), _) + if contains_only_wilds(right) => + { + is_known_enum(cx, ty) }, _ => false, } @@ -972,7 +941,7 @@ fn check_exhaustive_tuples<'a>( let right_pat = &right_in[i - right_dot_space]; if !(contains_only_wilds(left_pat) || contains_only_wilds(right_pat) - || known_enums::is_known_enum(cx, left_pat) && known_enums::is_known_enum(cx, right_pat)) + || contains_only_known_enums(cx, left_pat) && contains_only_known_enums(cx, right_pat)) { return false; } From 22a44b7c0b22d3ea8ee6d318fe3a5b1a64ab6318 Mon Sep 17 00:00:00 2001 From: Georgiy Komarov Date: Sun, 6 Feb 2022 11:54:43 +0300 Subject: [PATCH 03/19] matches: Clarify the comment. NFC. --- clippy_lints/src/matches.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 92e29b7d1fdc..e095f8602f14 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -862,8 +862,7 @@ fn contains_only_known_enums(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { } } -/// Returns true if the given arms of the pattern forms exhaustive match that can be simplified to -/// `if let` construction. +/// Returns true if the patterns exhaustively match an enum. fn check_exhaustive<'a>(cx: &LateContext<'a>, left: &Pat<'_>, right: &Pat<'_>, ty: Ty<'a>) -> bool { match (&left.kind, &right.kind) { (PatKind::Wild, _) | (_, PatKind::Wild) => true, From 2d1546bb4c15255499b775effd240c49ba186918 Mon Sep 17 00:00:00 2001 From: Georgiy Komarov Date: Sun, 6 Feb 2022 12:17:01 +0300 Subject: [PATCH 04/19] matches: Fix clippy warning --- clippy_lints/src/matches.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index e095f8602f14..4da300ad6faa 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -882,7 +882,7 @@ fn check_exhaustive<'a>(cx: &LateContext<'a>, left: &Pat<'_>, right: &Pat<'_>, t } false }, - (PatKind::Binding(BindingAnnotation::Unannotated, .., None), _) | (PatKind::Path(_), _) + (PatKind::Binding(BindingAnnotation::Unannotated, .., None) | PatKind::Path(_), _) if contains_only_wilds(right) => { is_known_enum(cx, ty) From cd3857ad98bc788c8ab05b5de40267161fa99531 Mon Sep 17 00:00:00 2001 From: Georgiy Komarov Date: Sun, 6 Feb 2022 13:16:55 +0300 Subject: [PATCH 05/19] matches: Allow lint for Bindings w/ annotations --- clippy_lints/src/matches.rs | 2 +- tests/ui/single_match.rs | 25 +++++++++++++++++++++ tests/ui/single_match.stderr | 42 +++++++++++++++++++++++++++++++++--- 3 files changed, 65 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 4da300ad6faa..9a8be872a0a2 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -882,7 +882,7 @@ fn check_exhaustive<'a>(cx: &LateContext<'a>, left: &Pat<'_>, right: &Pat<'_>, t } false }, - (PatKind::Binding(BindingAnnotation::Unannotated, .., None) | PatKind::Path(_), _) + (PatKind::Binding(.., None) | PatKind::Path(_), _) if contains_only_wilds(right) => { is_known_enum(cx, ty) diff --git a/tests/ui/single_match.rs b/tests/ui/single_match.rs index 2e56e1f2d061..d6e13ff5efb2 100644 --- a/tests/ui/single_match.rs +++ b/tests/ui/single_match.rs @@ -197,6 +197,31 @@ fn ranges() { } } +fn annotated_bindings() { + struct S(i32); + let x = Some(S(1i32)); + + match Some(S(1i32)) { + Some(a) => {}, + _ => () + } + + match x { + Some(ref a) => {}, + _ => () + } + + match x { + Some(mut a) => {}, + _ => () + } + + match Some(S(1i32)) { + Some(ref mut a) => {}, + _ => () + } +} + fn tuple_structs() { struct S(i32, i32); let s = S(1, 2); diff --git a/tests/ui/single_match.stderr b/tests/ui/single_match.stderr index 95742c0b2d62..f7298b1c32c8 100644 --- a/tests/ui/single_match.stderr +++ b/tests/ui/single_match.stderr @@ -147,7 +147,43 @@ LL | | } | |_____^ help: try this: `if let (.., Some(E::V), _) = (Some(42), Some(E::V), Some(42)) {}` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:205:5 + --> $DIR/single_match.rs:204:5 + | +LL | / match Some(S(1i32)) { +LL | | Some(a) => {}, +LL | | _ => () +LL | | } + | |_____^ help: try this: `if let Some(a) = Some(S(1i32)) {}` + +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> $DIR/single_match.rs:209:5 + | +LL | / match x { +LL | | Some(ref a) => {}, +LL | | _ => () +LL | | } + | |_____^ help: try this: `if let Some(ref a) = x {}` + +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> $DIR/single_match.rs:214:5 + | +LL | / match x { +LL | | Some(mut a) => {}, +LL | | _ => () +LL | | } + | |_____^ help: try this: `if let Some(mut a) = x {}` + +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> $DIR/single_match.rs:219:5 + | +LL | / match Some(S(1i32)) { +LL | | Some(ref mut a) => {}, +LL | | _ => () +LL | | } + | |_____^ help: try this: `if let Some(ref mut a) = Some(S(1i32)) {}` + +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> $DIR/single_match.rs:230:5 | LL | / match s { LL | | S(42, _a) => {}, @@ -156,7 +192,7 @@ LL | | } | |_____^ help: try this: `if let S(42, _a) = s {}` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:211:5 + --> $DIR/single_match.rs:236:5 | LL | / match s { LL | | S(42, _a) => {}, @@ -164,5 +200,5 @@ LL | | S(..) => {}, LL | | } | |_____^ help: try this: `if let S(42, _a) = s {}` -error: aborting due to 17 previous errors +error: aborting due to 21 previous errors From 4873ca193ddfef1558fa173302fe53975f2b4ff2 Mon Sep 17 00:00:00 2001 From: Georgiy Komarov Date: Sun, 6 Feb 2022 14:44:42 +0300 Subject: [PATCH 06/19] matches: Peel subpatterns in bindings --- clippy_lints/src/matches.rs | 19 ++++++++++++------- tests/ui/patterns.stderr | 13 ++++++++++++- tests/ui/single_match.rs | 17 +++++++++++++---- tests/ui/single_match.stderr | 23 ++++++++++++++++------- 4 files changed, 53 insertions(+), 19 deletions(-) diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 9a8be872a0a2..e912d030e440 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -862,8 +862,17 @@ fn contains_only_known_enums(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { } } +fn peel_subpatterns<'a>(pat: &'a Pat<'a>) -> &'a Pat<'a> { + if let PatKind::Binding(.., Some(sub)) = pat.kind { + return peel_subpatterns(sub); + } + pat +} + /// Returns true if the patterns exhaustively match an enum. fn check_exhaustive<'a>(cx: &LateContext<'a>, left: &Pat<'_>, right: &Pat<'_>, ty: Ty<'a>) -> bool { + let left = peel_subpatterns(left); + let right = peel_subpatterns(right); match (&left.kind, &right.kind) { (PatKind::Wild, _) | (_, PatKind::Wild) => true, (PatKind::Tuple(left_in, left_pos), PatKind::Tuple(right_in, right_pos)) => { @@ -876,17 +885,13 @@ fn check_exhaustive<'a>(cx: &LateContext<'a>, left: &Pat<'_>, right: &Pat<'_>, t let are_structs_with_the_same_name = || -> bool { cx.qpath_res(left_qpath, left.hir_id) == cx.qpath_res(right_qpath, right.hir_id) }; let are_known_enums = - || -> bool { contains_only_known_enums(cx, left) && contains_only_known_enums(cx, right) }; + || -> bool { contains_only_known_enums(cx, &left) && contains_only_known_enums(cx, &right) }; if are_structs_with_the_same_name() || are_known_enums() { return check_exhaustive_tuples(cx, left_in, left_pos, right_in, right_pos); } false }, - (PatKind::Binding(.., None) | PatKind::Path(_), _) - if contains_only_wilds(right) => - { - is_known_enum(cx, ty) - }, + (PatKind::Binding(.., None) | PatKind::Path(_), _) if contains_only_wilds(&right) => is_known_enum(cx, ty), _ => false, } } @@ -959,7 +964,7 @@ fn contains_only_wilds(pat: &Pat<'_>) -> bool { fn contains_single_binding(pat: &Pat<'_>) -> bool { match pat.kind { - PatKind::Binding(BindingAnnotation::Unannotated, .., None) => true, + PatKind::Binding(.., None) => true, PatKind::TupleStruct(_, inner, ..) => inner.iter().all(contains_single_binding), _ => false, } diff --git a/tests/ui/patterns.stderr b/tests/ui/patterns.stderr index af067580688b..bfad5941337c 100644 --- a/tests/ui/patterns.stderr +++ b/tests/ui/patterns.stderr @@ -18,5 +18,16 @@ error: the `x @ _` pattern can be written as just `x` LL | ref x @ _ => println!("vec: {:?}", x), | ^^^^^^^^^ help: try: `ref x` -error: aborting due to 3 previous errors +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> $DIR/patterns.rs:8:5 + | +LL | / match v { +LL | | Some(x) => (), +LL | | y @ _ => (), +LL | | } + | |_____^ help: try this: `if let Some(x) = v { () }` + | + = note: `-D clippy::single-match` implied by `-D warnings` + +error: aborting due to 4 previous errors diff --git a/tests/ui/single_match.rs b/tests/ui/single_match.rs index d6e13ff5efb2..9ac0b1830333 100644 --- a/tests/ui/single_match.rs +++ b/tests/ui/single_match.rs @@ -203,22 +203,31 @@ fn annotated_bindings() { match Some(S(1i32)) { Some(a) => {}, - _ => () + _ => (), } match x { Some(ref a) => {}, - _ => () + _ => (), } match x { Some(mut a) => {}, - _ => () + _ => (), } match Some(S(1i32)) { Some(ref mut a) => {}, - _ => () + _ => (), + } +} + +fn binding_subpatterns() { + struct S(i32); + let x = S(1); + match x { + S(aa @ 42) => {}, + S(_) => {}, } } diff --git a/tests/ui/single_match.stderr b/tests/ui/single_match.stderr index f7298b1c32c8..839b8e5c3898 100644 --- a/tests/ui/single_match.stderr +++ b/tests/ui/single_match.stderr @@ -151,7 +151,7 @@ error: you seem to be trying to use `match` for destructuring a single pattern. | LL | / match Some(S(1i32)) { LL | | Some(a) => {}, -LL | | _ => () +LL | | _ => (), LL | | } | |_____^ help: try this: `if let Some(a) = Some(S(1i32)) {}` @@ -160,7 +160,7 @@ error: you seem to be trying to use `match` for destructuring a single pattern. | LL | / match x { LL | | Some(ref a) => {}, -LL | | _ => () +LL | | _ => (), LL | | } | |_____^ help: try this: `if let Some(ref a) = x {}` @@ -169,7 +169,7 @@ error: you seem to be trying to use `match` for destructuring a single pattern. | LL | / match x { LL | | Some(mut a) => {}, -LL | | _ => () +LL | | _ => (), LL | | } | |_____^ help: try this: `if let Some(mut a) = x {}` @@ -178,12 +178,21 @@ error: you seem to be trying to use `match` for destructuring a single pattern. | LL | / match Some(S(1i32)) { LL | | Some(ref mut a) => {}, -LL | | _ => () +LL | | _ => (), LL | | } | |_____^ help: try this: `if let Some(ref mut a) = Some(S(1i32)) {}` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:230:5 + --> $DIR/single_match.rs:228:5 + | +LL | / match x { +LL | | S(aa @ 42) => {}, +LL | | S(_) => {}, +LL | | } + | |_____^ help: try this: `if let S(aa @ 42) = x {}` + +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> $DIR/single_match.rs:239:5 | LL | / match s { LL | | S(42, _a) => {}, @@ -192,7 +201,7 @@ LL | | } | |_____^ help: try this: `if let S(42, _a) = s {}` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:236:5 + --> $DIR/single_match.rs:245:5 | LL | / match s { LL | | S(42, _a) => {}, @@ -200,5 +209,5 @@ LL | | S(..) => {}, LL | | } | |_____^ help: try this: `if let S(42, _a) = s {}` -error: aborting due to 21 previous errors +error: aborting due to 22 previous errors From 5fc50fe3b4c691df36f6402c96a4a7f86c3be5d8 Mon Sep 17 00:00:00 2001 From: Georgiy Komarov Date: Mon, 7 Feb 2022 19:50:00 +0300 Subject: [PATCH 07/19] matches: Clarify comment --- clippy_lints/src/matches.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index e912d030e440..a2560c1c2f5c 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -869,7 +869,7 @@ fn peel_subpatterns<'a>(pat: &'a Pat<'a>) -> &'a Pat<'a> { pat } -/// Returns true if the patterns exhaustively match an enum. +/// Returns false if the patterns exhaustively match an enum. fn check_exhaustive<'a>(cx: &LateContext<'a>, left: &Pat<'_>, right: &Pat<'_>, ty: Ty<'a>) -> bool { let left = peel_subpatterns(left); let right = peel_subpatterns(right); From 31613894455de48b1859c9346a36c478b738d701 Mon Sep 17 00:00:00 2001 From: Georgiy Komarov Date: Tue, 8 Feb 2022 06:44:38 +0300 Subject: [PATCH 08/19] matches: Support structs --- clippy_lints/src/matches.rs | 41 ++++++++++++++++++++++++++++++------ tests/ui/single_match.rs | 28 ++++++++++++++++++++++++ tests/ui/single_match.stderr | 20 +++++++++++++++++- 3 files changed, 81 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index a2560c1c2f5c..a155baf9fe41 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -23,7 +23,7 @@ use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::{ self as hir, Arm, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, Guard, HirId, Local, MatchSource, - Mutability, Node, Pat, PatKind, PathSegment, QPath, RangeEnd, TyKind, + Mutability, Node, Pat, PatField, PatKind, PathSegment, QPath, RangeEnd, TyKind, }; use rustc_hir::{HirIdMap, HirIdSet}; use rustc_lint::{LateContext, LateLintPass}; @@ -875,6 +875,9 @@ fn check_exhaustive<'a>(cx: &LateContext<'a>, left: &Pat<'_>, right: &Pat<'_>, t let right = peel_subpatterns(right); match (&left.kind, &right.kind) { (PatKind::Wild, _) | (_, PatKind::Wild) => true, + (PatKind::Struct(_, left_fields, _), PatKind::Struct(_, right_fields, _)) => { + check_exhaustive_structs(cx, left_fields, right_fields) + }, (PatKind::Tuple(left_in, left_pos), PatKind::Tuple(right_in, right_pos)) => { check_exhaustive_tuples(cx, left_in, left_pos, right_in, right_pos) }, @@ -896,6 +899,12 @@ fn check_exhaustive<'a>(cx: &LateContext<'a>, left: &Pat<'_>, right: &Pat<'_>, t } } +fn could_be_simplified(cx: &LateContext<'_>, lpat: &Pat<'_>, rpat: &Pat<'_>) -> bool { + contains_only_wilds(lpat) + || contains_only_wilds(rpat) + || contains_only_known_enums(cx, lpat) && contains_only_known_enums(cx, rpat) +} + /// Returns true if two tuples that contain patterns `left_in` and `right_in` and `..` operators at /// positions `left_pos` and `right_pos` form exhaustive match. This means, that two elements of /// these tuples located at the same positions must have at least one wildcard (`_`) pattern. @@ -941,18 +950,36 @@ fn check_exhaustive_tuples<'a>( continue; } - let left_pat = &left_in[i - left_dot_space]; - let right_pat = &right_in[i - right_dot_space]; - if !(contains_only_wilds(left_pat) - || contains_only_wilds(right_pat) - || contains_only_known_enums(cx, left_pat) && contains_only_known_enums(cx, right_pat)) - { + if !could_be_simplified(cx, &left_in[i - left_dot_space], &right_in[i - right_dot_space]) { return false; } } true } +fn check_exhaustive_structs<'a>( + cx: &LateContext<'_>, + left_fields: &'a [PatField<'a>], + right_fields: &'a [PatField<'a>], +) -> bool { + // TODO: use `min` + for i in 0..max(left_fields.len(), right_fields.len()) { + let get_pat = |fields: &'a [PatField<'a>]| { + if fields.len() > i { + Some(fields.get(i).unwrap().pat) + } else { + None + } + }; + if let (Some(lpat), Some(rpat)) = (get_pat(left_fields), get_pat(right_fields)) { + if !could_be_simplified(cx, &lpat, &rpat) { + return false; + } + } + } + true +} + /// Returns true if the given arm of pattern matching contains only wildcard patterns. fn contains_only_wilds(pat: &Pat<'_>) -> bool { match pat.kind { diff --git a/tests/ui/single_match.rs b/tests/ui/single_match.rs index 9ac0b1830333..1e5f0a0c3960 100644 --- a/tests/ui/single_match.rs +++ b/tests/ui/single_match.rs @@ -248,6 +248,34 @@ fn tuple_structs() { } } +fn structs() { + struct S { + x: i32, + y: i32, + } + let s = S { x: 1, y: 2 }; + + // lint + match s { + S { x: 42, .. } => {}, + S { .. } => {}, + } + + // lint + match s { + S { x: _x, y: 42 } => {}, + S { .. } => {}, + } + + // don't lint + match s { + S { x: i32::MIN, y: _y } => {}, + S { + x: i32::MIN..=i32::MAX, .. + } => {}, + } +} + macro_rules! single_match { ($num:literal) => { match $num { diff --git a/tests/ui/single_match.stderr b/tests/ui/single_match.stderr index 839b8e5c3898..8f6c952ad676 100644 --- a/tests/ui/single_match.stderr +++ b/tests/ui/single_match.stderr @@ -209,5 +209,23 @@ LL | | S(..) => {}, LL | | } | |_____^ help: try this: `if let S(42, _a) = s {}` -error: aborting due to 22 previous errors +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> $DIR/single_match.rs:259:5 + | +LL | / match s { +LL | | S { x: 42, .. } => {}, +LL | | S { .. } => {}, +LL | | } + | |_____^ help: try this: `if let S { x: 42, .. } = s {}` + +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> $DIR/single_match.rs:265:5 + | +LL | / match s { +LL | | S { x: _x, y: 42 } => {}, +LL | | S { .. } => {}, +LL | | } + | |_____^ help: try this: `if let S { x: _x, y: 42 } = s {}` + +error: aborting due to 24 previous errors From 159037a32e469765e4767968b0045af9acc925d7 Mon Sep 17 00:00:00 2001 From: Georgiy Komarov Date: Tue, 8 Feb 2022 07:06:00 +0300 Subject: [PATCH 09/19] single_match: Update test --- tests/ui/unneeded_field_pattern.stderr | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/tests/ui/unneeded_field_pattern.stderr b/tests/ui/unneeded_field_pattern.stderr index b8d3c2945322..18f5ea26c970 100644 --- a/tests/ui/unneeded_field_pattern.stderr +++ b/tests/ui/unneeded_field_pattern.stderr @@ -15,5 +15,26 @@ LL | Foo { a: _, b: _, c: _ } => {}, | = help: try with `Foo { .. }` instead -error: aborting due to 2 previous errors +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> $DIR/unneeded_field_pattern.rs:13:5 + | +LL | / match f { +LL | | Foo { a: _, b: 0, .. } => {}, +LL | | +LL | | Foo { a: _, b: _, c: _ } => {}, +LL | | } + | |_____^ help: try this: `if let Foo { a: _, b: 0, .. } = f {}` + | + = note: `-D clippy::single-match` implied by `-D warnings` + +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> $DIR/unneeded_field_pattern.rs:18:5 + | +LL | / match f { +LL | | Foo { b: 0, .. } => {}, // should be OK +LL | | Foo { .. } => {}, // and the Force might be with this one +LL | | } + | |_____^ help: try this: `if let Foo { b: 0, .. } = f {}` + +error: aborting due to 4 previous errors From 060957aedc5b741927091ee60972f0969965f353 Mon Sep 17 00:00:00 2001 From: Georgiy Komarov Date: Tue, 8 Feb 2022 07:09:52 +0300 Subject: [PATCH 10/19] single_match: Simplify code --- clippy_lints/src/matches/single_match.rs | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index 4d52feceb4c7..d8d76e234d4f 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -4,7 +4,7 @@ use clippy_utils::ty::{implements_trait, match_type, peel_mid_ty_refs}; use clippy_utils::{ is_lint_allowed, is_unit_expr, is_wild, paths, peel_blocks, peel_hir_pat_refs, peel_n_hir_expr_refs, }; -use core::cmp::max; +use core::cmp::{max, min}; use rustc_errors::Applicability; use rustc_hir::{Arm, Block, Expr, ExprKind, Pat, PatField, PatKind}; use rustc_lint::LateContext; @@ -263,19 +263,11 @@ fn check_exhaustive_structs<'a>( left_fields: &'a [PatField<'a>], right_fields: &'a [PatField<'a>], ) -> bool { - // TODO: use `min` - for i in 0..max(left_fields.len(), right_fields.len()) { - let get_pat = |fields: &'a [PatField<'a>]| { - if fields.len() > i { - Some(fields.get(i).unwrap().pat) - } else { - None - } - }; - if let (Some(lpat), Some(rpat)) = (get_pat(left_fields), get_pat(right_fields)) { - if !could_be_simplified(cx, &lpat, &rpat) { - return false; - } + for i in 0..min(left_fields.len(), right_fields.len()) { + let lpat = left_fields.get(i).unwrap().pat; + let rpat = right_fields.get(i).unwrap().pat; + if !could_be_simplified(cx, &lpat, &rpat) { + return false; } } true From 7fc3545cd6eae7d94aff3b2e07d3317be8b2cae7 Mon Sep 17 00:00:00 2001 From: Georgiy Komarov Date: Tue, 8 Feb 2022 07:52:39 +0300 Subject: [PATCH 11/19] single_match: Add test to check we don't lint structs with different names --- tests/ui/single_match.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/ui/single_match.rs b/tests/ui/single_match.rs index 1e5f0a0c3960..19720d0d7c2d 100644 --- a/tests/ui/single_match.rs +++ b/tests/ui/single_match.rs @@ -276,6 +276,20 @@ fn structs() { } } +fn lint_only_structs_with_the_same_name() { + enum E { + S1(i32, i32), + S2(i32, i32), + } + let s = E::S1(1, 2); + + // don't lint + match s { + E::S1(..) => {}, + E::S2(..) => {}, + } +} + macro_rules! single_match { ($num:literal) => { match $num { From b6695b0caf1e86242f7bb222216fde1202e17e01 Mon Sep 17 00:00:00 2001 From: Georgiy Komarov Date: Tue, 8 Feb 2022 07:53:38 +0300 Subject: [PATCH 12/19] single_match: Fix naming. NFC. --- clippy_lints/src/matches/single_match.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index d8d76e234d4f..74b11edbb186 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -200,10 +200,10 @@ fn check_exhaustive<'a>(cx: &LateContext<'a>, left: &Pat<'_>, right: &Pat<'_>, t } } -fn could_be_simplified(cx: &LateContext<'_>, lpat: &Pat<'_>, rpat: &Pat<'_>) -> bool { - contains_only_wilds(lpat) - || contains_only_wilds(rpat) - || contains_only_known_enums(cx, lpat) && contains_only_known_enums(cx, rpat) +fn could_be_simplified(cx: &LateContext<'_>, left_pat: &Pat<'_>, right_pat: &Pat<'_>) -> bool { + contains_only_wilds(left_pat) + || contains_only_wilds(right_pat) + || contains_only_known_enums(cx, left_pat) && contains_only_known_enums(cx, right_pat) } /// Returns true if two tuples that contain patterns `left_in` and `right_in` and `..` operators at @@ -264,9 +264,9 @@ fn check_exhaustive_structs<'a>( right_fields: &'a [PatField<'a>], ) -> bool { for i in 0..min(left_fields.len(), right_fields.len()) { - let lpat = left_fields.get(i).unwrap().pat; - let rpat = right_fields.get(i).unwrap().pat; - if !could_be_simplified(cx, &lpat, &rpat) { + let left_pat = left_fields.get(i).unwrap().pat; + let right_pat = right_fields.get(i).unwrap().pat; + if !could_be_simplified(cx, &left_pat, &right_pat) { return false; } } From cd89b1f41bd865b9c8e4a20e27366994edbfa1b6 Mon Sep 17 00:00:00 2001 From: Georgiy Komarov Date: Tue, 8 Feb 2022 12:15:12 +0300 Subject: [PATCH 13/19] single_match: Simplify code --- clippy_lints/src/matches/single_match.rs | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index 74b11edbb186..5b625c0cfba3 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -4,7 +4,7 @@ use clippy_utils::ty::{implements_trait, match_type, peel_mid_ty_refs}; use clippy_utils::{ is_lint_allowed, is_unit_expr, is_wild, paths, peel_blocks, peel_hir_pat_refs, peel_n_hir_expr_refs, }; -use core::cmp::{max, min}; +use core::cmp::max; use rustc_errors::Applicability; use rustc_hir::{Arm, Block, Expr, ExprKind, Pat, PatField, PatKind}; use rustc_lint::LateContext; @@ -263,14 +263,10 @@ fn check_exhaustive_structs<'a>( left_fields: &'a [PatField<'a>], right_fields: &'a [PatField<'a>], ) -> bool { - for i in 0..min(left_fields.len(), right_fields.len()) { - let left_pat = left_fields.get(i).unwrap().pat; - let right_pat = right_fields.get(i).unwrap().pat; - if !could_be_simplified(cx, &left_pat, &right_pat) { - return false; - } - } - true + left_fields + .iter() + .zip(right_fields.iter()) + .all(|(left_field, right_field)| could_be_simplified(cx, left_field.pat, right_field.pat)) } /// Returns true if the given arm of pattern matching contains only wildcard patterns. From 791a74e8512b80ebab40d8a919cda53bad057369 Mon Sep 17 00:00:00 2001 From: Georgiy Komarov Date: Wed, 9 Feb 2022 06:42:54 +0300 Subject: [PATCH 14/19] Add a workaround for the `rustfix` crash --- tests/ui/patterns.fixed | 8 ++++++-- tests/ui/patterns.rs | 8 ++++++-- tests/ui/patterns.stderr | 21 +++++---------------- 3 files changed, 17 insertions(+), 20 deletions(-) diff --git a/tests/ui/patterns.fixed b/tests/ui/patterns.fixed index f22388154499..61cb98edb7dc 100644 --- a/tests/ui/patterns.fixed +++ b/tests/ui/patterns.fixed @@ -2,12 +2,16 @@ #![allow(unused)] #![warn(clippy::all)] +fn dummy() { + dbg!("test"); +} + fn main() { let v = Some(true); let s = [0, 1, 2, 3, 4]; match v { - Some(x) => (), - y => (), + Some(x) => dummy(), + y => dummy(), } match v { Some(x) => (), diff --git a/tests/ui/patterns.rs b/tests/ui/patterns.rs index 5848ecd38d98..cc753f8b5eb9 100644 --- a/tests/ui/patterns.rs +++ b/tests/ui/patterns.rs @@ -2,12 +2,16 @@ #![allow(unused)] #![warn(clippy::all)] +fn dummy() { + dbg!("test"); +} + fn main() { let v = Some(true); let s = [0, 1, 2, 3, 4]; match v { - Some(x) => (), - y @ _ => (), + Some(x) => dummy(), + y @ _ => dummy(), } match v { Some(x) => (), diff --git a/tests/ui/patterns.stderr b/tests/ui/patterns.stderr index bfad5941337c..e414f97a95a3 100644 --- a/tests/ui/patterns.stderr +++ b/tests/ui/patterns.stderr @@ -1,33 +1,22 @@ error: the `y @ _` pattern can be written as just `y` - --> $DIR/patterns.rs:10:9 + --> $DIR/patterns.rs:14:9 | -LL | y @ _ => (), +LL | y @ _ => dummy(), | ^^^^^ help: try: `y` | = note: `-D clippy::redundant-pattern` implied by `-D warnings` error: the `x @ _` pattern can be written as just `x` - --> $DIR/patterns.rs:25:9 + --> $DIR/patterns.rs:29:9 | LL | ref mut x @ _ => { | ^^^^^^^^^^^^^ help: try: `ref mut x` error: the `x @ _` pattern can be written as just `x` - --> $DIR/patterns.rs:33:9 + --> $DIR/patterns.rs:37:9 | LL | ref x @ _ => println!("vec: {:?}", x), | ^^^^^^^^^ help: try: `ref x` -error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/patterns.rs:8:5 - | -LL | / match v { -LL | | Some(x) => (), -LL | | y @ _ => (), -LL | | } - | |_____^ help: try this: `if let Some(x) = v { () }` - | - = note: `-D clippy::single-match` implied by `-D warnings` - -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors From 51045172c1313ed912742a521cd02b639c120fc4 Mon Sep 17 00:00:00 2001 From: Georgiy Komarov Date: Wed, 9 Feb 2022 07:18:21 +0300 Subject: [PATCH 15/19] single_match: Lint Struct patterns with the same type --- clippy_lints/src/matches/single_match.rs | 19 ++++++----------- tests/ui/single_match.rs | 27 ++++++++++++++---------- tests/ui/single_match.stderr | 15 ++++++++++--- 3 files changed, 35 insertions(+), 26 deletions(-) diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index 5b625c0cfba3..155d5ff8aae7 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -182,20 +182,15 @@ fn check_exhaustive<'a>(cx: &LateContext<'a>, left: &Pat<'_>, right: &Pat<'_>, t (PatKind::Tuple(left_in, left_pos), PatKind::Tuple(right_in, right_pos)) => { check_exhaustive_tuples(cx, left_in, left_pos, right_in, right_pos) }, - ( - PatKind::TupleStruct(left_qpath, left_in, left_pos), - PatKind::TupleStruct(right_qpath, right_in, right_pos), - ) => { - let are_structs_with_the_same_name = - || -> bool { cx.qpath_res(left_qpath, left.hir_id) == cx.qpath_res(right_qpath, right.hir_id) }; - let are_known_enums = - || -> bool { contains_only_known_enums(cx, &left) && contains_only_known_enums(cx, &right) }; - if are_structs_with_the_same_name() || are_known_enums() { - return check_exhaustive_tuples(cx, left_in, left_pos, right_in, right_pos); + (PatKind::TupleStruct(_, left_in, left_pos), PatKind::TupleStruct(_, right_in, right_pos)) + if contains_only_wilds(right) => + { + if cx.typeck_results().pat_ty(left) != cx.typeck_results().pat_ty(right) { + return false; } - false + check_exhaustive_tuples(cx, left_in, left_pos, right_in, right_pos) }, - (PatKind::Binding(.., None) | PatKind::Path(_), _) if contains_only_wilds(&right) => is_known_enum(cx, ty), + (PatKind::Binding(.., None) | PatKind::Path(_), _) if contains_only_wilds(right) => is_known_enum(cx, ty), _ => false, } } diff --git a/tests/ui/single_match.rs b/tests/ui/single_match.rs index 19720d0d7c2d..125ab6c34ba8 100644 --- a/tests/ui/single_match.rs +++ b/tests/ui/single_match.rs @@ -246,6 +246,18 @@ fn tuple_structs() { S(42, _a) => {}, S(..) => {}, } + + enum E { + S1(i32, i32), + S2(i32, i32), + } + let s = E::S1(1, 2); + + // lint + match s { + E::S1(..) => {}, + E::S2(..) => {}, + } } fn structs() { @@ -274,19 +286,12 @@ fn structs() { x: i32::MIN..=i32::MAX, .. } => {}, } -} -fn lint_only_structs_with_the_same_name() { - enum E { - S1(i32, i32), - S2(i32, i32), - } - let s = E::S1(1, 2); - - // don't lint + // Don't lint, because we have an annotated binding in the second arm. So we cannot replace it + // with `else` branch, keeping this binding. match s { - E::S1(..) => {}, - E::S2(..) => {}, + S { x: _x, y: _y } => dummy(), + S { x: _x, .. } => dummy(), } } diff --git a/tests/ui/single_match.stderr b/tests/ui/single_match.stderr index 8f6c952ad676..2e3c8df51d18 100644 --- a/tests/ui/single_match.stderr +++ b/tests/ui/single_match.stderr @@ -210,7 +210,16 @@ LL | | } | |_____^ help: try this: `if let S(42, _a) = s {}` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:259:5 + --> $DIR/single_match.rs:257:5 + | +LL | / match s { +LL | | E::S1(..) => {}, +LL | | E::S2(..) => {}, +LL | | } + | |_____^ help: try this: `if let E::S1(..) = s {}` + +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> $DIR/single_match.rs:271:5 | LL | / match s { LL | | S { x: 42, .. } => {}, @@ -219,7 +228,7 @@ LL | | } | |_____^ help: try this: `if let S { x: 42, .. } = s {}` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:265:5 + --> $DIR/single_match.rs:277:5 | LL | / match s { LL | | S { x: _x, y: 42 } => {}, @@ -227,5 +236,5 @@ LL | | S { .. } => {}, LL | | } | |_____^ help: try this: `if let S { x: _x, y: 42 } = s {}` -error: aborting due to 24 previous errors +error: aborting due to 25 previous errors From 4fae6c04b90f780f295afb9151f701b95cab5218 Mon Sep 17 00:00:00 2001 From: Georgiy Komarov Date: Thu, 10 Feb 2022 16:28:13 +0300 Subject: [PATCH 16/19] single_match: Don't lint `TupleStruct`s with the same name --- clippy_lints/src/matches/single_match.rs | 5 +-- tests/ui/single_match.rs | 8 +++-- tests/ui/single_match.stderr | 42 ++---------------------- 3 files changed, 9 insertions(+), 46 deletions(-) diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index 155d5ff8aae7..7c243faecc57 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -183,11 +183,8 @@ fn check_exhaustive<'a>(cx: &LateContext<'a>, left: &Pat<'_>, right: &Pat<'_>, t check_exhaustive_tuples(cx, left_in, left_pos, right_in, right_pos) }, (PatKind::TupleStruct(_, left_in, left_pos), PatKind::TupleStruct(_, right_in, right_pos)) - if contains_only_wilds(right) => + if contains_only_wilds(right) && contains_only_known_enums(cx, left) => { - if cx.typeck_results().pat_ty(left) != cx.typeck_results().pat_ty(right) { - return false; - } check_exhaustive_tuples(cx, left_in, left_pos, right_in, right_pos) }, (PatKind::Binding(.., None) | PatKind::Path(_), _) if contains_only_wilds(right) => is_known_enum(cx, ty), diff --git a/tests/ui/single_match.rs b/tests/ui/single_match.rs index 125ab6c34ba8..3b9c3e5c7f71 100644 --- a/tests/ui/single_match.rs +++ b/tests/ui/single_match.rs @@ -225,6 +225,8 @@ fn annotated_bindings() { fn binding_subpatterns() { struct S(i32); let x = S(1); + + // don't lint match x { S(aa @ 42) => {}, S(_) => {}, @@ -235,13 +237,13 @@ fn tuple_structs() { struct S(i32, i32); let s = S(1, 2); - // lint: S(_, _) forms an exhaustive match, so it could be removed + // dont' lint match s { S(42, _a) => {}, S(_, _) => {}, } - // lint: S(..) forms an exhaustive match, so it could be removed + // don't lint match s { S(42, _a) => {}, S(..) => {}, @@ -253,7 +255,7 @@ fn tuple_structs() { } let s = E::S1(1, 2); - // lint + // don't lint match s { E::S1(..) => {}, E::S2(..) => {}, diff --git a/tests/ui/single_match.stderr b/tests/ui/single_match.stderr index 2e3c8df51d18..049b5b88b288 100644 --- a/tests/ui/single_match.stderr +++ b/tests/ui/single_match.stderr @@ -183,43 +183,7 @@ LL | | } | |_____^ help: try this: `if let Some(ref mut a) = Some(S(1i32)) {}` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:228:5 - | -LL | / match x { -LL | | S(aa @ 42) => {}, -LL | | S(_) => {}, -LL | | } - | |_____^ help: try this: `if let S(aa @ 42) = x {}` - -error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:239:5 - | -LL | / match s { -LL | | S(42, _a) => {}, -LL | | S(_, _) => {}, -LL | | } - | |_____^ help: try this: `if let S(42, _a) = s {}` - -error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:245:5 - | -LL | / match s { -LL | | S(42, _a) => {}, -LL | | S(..) => {}, -LL | | } - | |_____^ help: try this: `if let S(42, _a) = s {}` - -error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:257:5 - | -LL | / match s { -LL | | E::S1(..) => {}, -LL | | E::S2(..) => {}, -LL | | } - | |_____^ help: try this: `if let E::S1(..) = s {}` - -error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:271:5 + --> $DIR/single_match.rs:273:5 | LL | / match s { LL | | S { x: 42, .. } => {}, @@ -228,7 +192,7 @@ LL | | } | |_____^ help: try this: `if let S { x: 42, .. } = s {}` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:277:5 + --> $DIR/single_match.rs:279:5 | LL | / match s { LL | | S { x: _x, y: 42 } => {}, @@ -236,5 +200,5 @@ LL | | S { .. } => {}, LL | | } | |_____^ help: try this: `if let S { x: _x, y: 42 } = s {}` -error: aborting due to 25 previous errors +error: aborting due to 21 previous errors From 6686c723f48f23aa859358c0156d9a5fa29361e1 Mon Sep 17 00:00:00 2001 From: Georgiy Komarov Date: Thu, 10 Feb 2022 16:40:56 +0300 Subject: [PATCH 17/19] single_match: Improve tests --- tests/ui/single_match.rs | 67 ++++++++++++++++++++---------------- tests/ui/single_match.stderr | 38 ++++++++++++++------ 2 files changed, 66 insertions(+), 39 deletions(-) diff --git a/tests/ui/single_match.rs b/tests/ui/single_match.rs index 3b9c3e5c7f71..9eb8cda9e4b1 100644 --- a/tests/ui/single_match.rs +++ b/tests/ui/single_match.rs @@ -159,42 +159,43 @@ fn ranges() { (Some(E::V), _) => {}, (None, _) => {}, } + match (Some(E::V), Some(E::V), Some(E::V)) { + (.., Some(E::V), _) => {}, + (.., None, _) => {}, + } + match (Some(E::V), Some(E::V), Some(E::V)) { + (Some(E::V), ..) => {}, + (None, ..) => {}, + } + match (Some(E::V), Some(E::V), Some(E::V)) { + (_, Some(E::V), ..) => {}, + (_, None, ..) => {}, + } - // lint + // Don't lint, because the second arm contains bindings. So, we cannot keep them in the `else` + // branch. + match (1, 2) { + (1, _b) => (), + (_a, ..) => (), + } + match (1, 2) { + (1, _b) => {}, + (_a, _) => {}, + } + + // Lint cases. match x { (Some(_), _) => {}, (None, _) => {}, } - - // lint match x { (Some(E::V), _) => todo!(), (_, _) => {}, } - - // lint match (Some(42), Some(E::V), Some(42)) { (.., Some(E::V), _) => {}, (..) => {}, } - - // Don't lint, see above. - match (Some(E::V), Some(E::V), Some(E::V)) { - (.., Some(E::V), _) => {}, - (.., None, _) => {}, - } - - // Don't lint, see above. - match (Some(E::V), Some(E::V), Some(E::V)) { - (Some(E::V), ..) => {}, - (None, ..) => {}, - } - - // Don't lint, see above. - match (Some(E::V), Some(E::V), Some(E::V)) { - (_, Some(E::V), ..) => {}, - (_, None, ..) => {}, - } } fn annotated_bindings() { @@ -222,14 +223,22 @@ fn annotated_bindings() { } } +#[allow(clippy::redundant_pattern)] fn binding_subpatterns() { - struct S(i32); - let x = S(1); + // Lint. + match 1 { + _a @ 42 => {}, + _ => {}, + } + match 1 { + _a @ 42 => {}, + _a @ _ => {}, + } - // don't lint - match x { - S(aa @ 42) => {}, - S(_) => {}, + // Don't lint. + match 1 { + _a @ 42 => {}, + _b => {}, } } diff --git a/tests/ui/single_match.stderr b/tests/ui/single_match.stderr index 049b5b88b288..915cc6be4fc8 100644 --- a/tests/ui/single_match.stderr +++ b/tests/ui/single_match.stderr @@ -120,7 +120,7 @@ LL | | }; | |_____^ help: try this: `if let None = x { println!() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:164:5 + --> $DIR/single_match.rs:187:5 | LL | / match x { LL | | (Some(_), _) => {}, @@ -129,7 +129,7 @@ LL | | } | |_____^ help: try this: `if let (Some(_), _) = x {}` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:170:5 + --> $DIR/single_match.rs:191:5 | LL | / match x { LL | | (Some(E::V), _) => todo!(), @@ -138,7 +138,7 @@ LL | | } | |_____^ help: try this: `if let (Some(E::V), _) = x { todo!() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:176:5 + --> $DIR/single_match.rs:195:5 | LL | / match (Some(42), Some(E::V), Some(42)) { LL | | (.., Some(E::V), _) => {}, @@ -147,7 +147,7 @@ LL | | } | |_____^ help: try this: `if let (.., Some(E::V), _) = (Some(42), Some(E::V), Some(42)) {}` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:204:5 + --> $DIR/single_match.rs:205:5 | LL | / match Some(S(1i32)) { LL | | Some(a) => {}, @@ -156,7 +156,7 @@ LL | | } | |_____^ help: try this: `if let Some(a) = Some(S(1i32)) {}` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:209:5 + --> $DIR/single_match.rs:210:5 | LL | / match x { LL | | Some(ref a) => {}, @@ -165,7 +165,7 @@ LL | | } | |_____^ help: try this: `if let Some(ref a) = x {}` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:214:5 + --> $DIR/single_match.rs:215:5 | LL | / match x { LL | | Some(mut a) => {}, @@ -174,7 +174,7 @@ LL | | } | |_____^ help: try this: `if let Some(mut a) = x {}` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:219:5 + --> $DIR/single_match.rs:220:5 | LL | / match Some(S(1i32)) { LL | | Some(ref mut a) => {}, @@ -183,7 +183,25 @@ LL | | } | |_____^ help: try this: `if let Some(ref mut a) = Some(S(1i32)) {}` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:273:5 + --> $DIR/single_match.rs:229:5 + | +LL | / match 1 { +LL | | _a @ 42 => {}, +LL | | _ => {}, +LL | | } + | |_____^ help: try this: `if let _a @ 42 = 1 {}` + +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> $DIR/single_match.rs:233:5 + | +LL | / match 1 { +LL | | _a @ 42 => {}, +LL | | _a @ _ => {}, +LL | | } + | |_____^ help: try this: `if let _a @ 42 = 1 {}` + +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> $DIR/single_match.rs:282:5 | LL | / match s { LL | | S { x: 42, .. } => {}, @@ -192,7 +210,7 @@ LL | | } | |_____^ help: try this: `if let S { x: 42, .. } = s {}` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:279:5 + --> $DIR/single_match.rs:288:5 | LL | / match s { LL | | S { x: _x, y: 42 } => {}, @@ -200,5 +218,5 @@ LL | | S { .. } => {}, LL | | } | |_____^ help: try this: `if let S { x: _x, y: 42 } = s {}` -error: aborting due to 21 previous errors +error: aborting due to 23 previous errors From cc55a8c59ed714fbe661fd5ed8e9411c58100220 Mon Sep 17 00:00:00 2001 From: Georgiy Komarov Date: Thu, 17 Feb 2022 07:13:35 +0300 Subject: [PATCH 18/19] single_match: Handle structs with known enums --- clippy_lints/src/matches/single_match.rs | 2 +- tests/ui/single_match.rs | 10 ++++++++++ tests/ui/single_match.stderr | 11 ++++++++++- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index 7c243faecc57..9b3586ee59d5 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -155,7 +155,7 @@ fn is_known_enum(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { fn contains_only_known_enums(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { match pat.kind { PatKind::Path(..) => is_known_enum(cx, cx.typeck_results().pat_ty(pat)), - PatKind::TupleStruct(..) => { + PatKind::TupleStruct(..) | PatKind::Struct(..) => { let ty = cx.typeck_results().pat_ty(pat); (contains_only_wilds(pat) || contains_single_binding(pat)) && is_known_enum(cx, ty) }, diff --git a/tests/ui/single_match.rs b/tests/ui/single_match.rs index 9eb8cda9e4b1..e1561b5c5b07 100644 --- a/tests/ui/single_match.rs +++ b/tests/ui/single_match.rs @@ -304,6 +304,16 @@ fn structs() { S { x: _x, y: _y } => dummy(), S { x: _x, .. } => dummy(), } + + // Lint, because it contains only known enums. + struct S1 { + a: Option, + }; + let s = S1 { a: Some(33) }; + match s { + S1 { a: Some(..) } => (), + S1 { a: None } => (), + } } macro_rules! single_match { diff --git a/tests/ui/single_match.stderr b/tests/ui/single_match.stderr index 915cc6be4fc8..0be262d7e738 100644 --- a/tests/ui/single_match.stderr +++ b/tests/ui/single_match.stderr @@ -218,5 +218,14 @@ LL | | S { .. } => {}, LL | | } | |_____^ help: try this: `if let S { x: _x, y: 42 } = s {}` -error: aborting due to 23 previous errors +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> $DIR/single_match.rs:313:5 + | +LL | / match s { +LL | | S1 { a: Some(..) } => (), +LL | | S1 { a: None } => (), +LL | | } + | |_____^ help: try this: `if let S1 { a: Some(..) } = s { () }` + +error: aborting due to 24 previous errors From 635bb4da9d5407fe6b6981e037936e2447517753 Mon Sep 17 00:00:00 2001 From: Georgiy Komarov Date: Thu, 17 Feb 2022 07:23:17 +0300 Subject: [PATCH 19/19] single_match: Simplify code --- clippy_lints/src/matches/single_match.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index 9b3586ee59d5..df60401aa8b9 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -1,14 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{expr_block, snippet}; -use clippy_utils::ty::{implements_trait, match_type, peel_mid_ty_refs}; -use clippy_utils::{ - is_lint_allowed, is_unit_expr, is_wild, paths, peel_blocks, peel_hir_pat_refs, peel_n_hir_expr_refs, -}; +use clippy_utils::ty::{implements_trait, peel_mid_ty_refs}; +use clippy_utils::{is_lint_allowed, is_unit_expr, is_wild, peel_blocks, peel_hir_pat_refs, peel_n_hir_expr_refs}; use core::cmp::max; use rustc_errors::Applicability; use rustc_hir::{Arm, Block, Expr, ExprKind, Pat, PatField, PatKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; +use rustc_span::sym; use super::{MATCH_BOOL, SINGLE_MATCH, SINGLE_MATCH_ELSE}; @@ -148,8 +147,13 @@ fn check_opt_like<'a>( /// Resturns true if the given type is one of the standard `Enum`s we know will never get any more /// members. fn is_known_enum(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { - const CANDIDATES: &[&[&str; 3]] = &[&paths::COW, &paths::OPTION, &paths::RESULT]; - CANDIDATES.iter().any(|&ty_path| match_type(cx, ty, ty_path)) + if let Some(adt) = ty.ty_adt_def() { + return matches!( + cx.tcx.get_diagnostic_name(adt.did), + Some(sym::Cow | sym::Option | sym::Result) + ); + } + false } fn contains_only_known_enums(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {