From 8bcecfff950de9bdcdd5d642523ac337a4c426f8 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 31 Aug 2023 01:24:49 -0400 Subject: [PATCH] Handle or patterns in `single_match` and `single_match_else` --- clippy_lints/src/matches/match_wild_enum.rs | 5 +- clippy_lints/src/matches/overlapping_arms.rs | 22 +- clippy_lints/src/matches/single_match.rs | 339 +++++++++++++----- .../excessive_nesting/excessive_nesting.rs | 20 +- .../excessive_nesting.stderr | 74 ++-- tests/ui/crashes/ice-6254.rs | 3 +- tests/ui/patterns.fixed | 2 +- tests/ui/patterns.rs | 2 +- tests/ui/single_match.fixed | 43 +++ tests/ui/single_match.rs | 49 +++ tests/ui/single_match.stderr | 20 +- tests/ui/unneeded_field_pattern.rs | 2 +- 12 files changed, 421 insertions(+), 160 deletions(-) diff --git a/clippy_lints/src/matches/match_wild_enum.rs b/clippy_lints/src/matches/match_wild_enum.rs index 894b6c42612..91e40e4275c 100644 --- a/clippy_lints/src/matches/match_wild_enum.rs +++ b/clippy_lints/src/matches/match_wild_enum.rs @@ -180,9 +180,8 @@ enum CommonPrefixSearcher<'a> { } impl<'a> CommonPrefixSearcher<'a> { fn with_path(&mut self, path: &'a [PathSegment<'a>]) { - match path { - [path @ .., _] => self.with_prefix(path), - [] => (), + if let [path @ .., _] = path { + self.with_prefix(path); } } diff --git a/clippy_lints/src/matches/overlapping_arms.rs b/clippy_lints/src/matches/overlapping_arms.rs index 45b375dbe3d..7ad66360d8a 100644 --- a/clippy_lints/src/matches/overlapping_arms.rs +++ b/clippy_lints/src/matches/overlapping_arms.rs @@ -33,19 +33,17 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) .filter_map(|arm| { if let Arm { pat, guard: None, .. } = *arm { if let PatKind::Range(ref lhs, ref rhs, range_end) = pat.kind { - let lhs_const = match lhs { - Some(lhs) => constant(cx, cx.typeck_results(), lhs)?, - None => { - let min_val_const = ty.numeric_min_val(cx.tcx)?; - mir_to_const(cx, mir::Const::from_ty_const(min_val_const, ty, cx.tcx))? - }, + let lhs_const = if let Some(lhs) = lhs { + constant(cx, cx.typeck_results(), lhs)? + } else { + let min_val_const = ty.numeric_min_val(cx.tcx)?; + mir_to_const(cx, mir::Const::from_ty_const(min_val_const, ty, cx.tcx))? }; - let rhs_const = match rhs { - Some(rhs) => constant(cx, cx.typeck_results(), rhs)?, - None => { - let max_val_const = ty.numeric_max_val(cx.tcx)?; - mir_to_const(cx, mir::Const::from_ty_const(max_val_const, ty, cx.tcx))? - }, + let rhs_const = if let Some(rhs) = rhs { + constant(cx, cx.typeck_results(), rhs)? + } else { + let max_val_const = ty.numeric_max_val(cx.tcx)?; + mir_to_const(cx, mir::Const::from_ty_const(max_val_const, ty, cx.tcx))? }; let lhs_val = lhs_const.int_value(cx, ty)?; let rhs_val = rhs_const.int_value(cx, ty)?; diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index 8b413ff892c..24dea03601c 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -1,14 +1,17 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{expr_block, snippet, SpanRangeExt}; -use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; +use clippy_utils::ty::implements_trait; use clippy_utils::{ - is_lint_allowed, is_unit_expr, is_wild, peel_blocks, peel_hir_pat_refs, peel_middle_ty_refs, peel_n_hir_expr_refs, + is_lint_allowed, is_unit_expr, peel_blocks, peel_hir_pat_refs, peel_middle_ty_refs, peel_n_hir_expr_refs, }; -use core::cmp::max; +use core::ops::ControlFlow; +use rustc_arena::DroplessArena; use rustc_errors::Applicability; -use rustc_hir::{Arm, BindingMode, Expr, ExprKind, Pat, PatKind}; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::intravisit::{walk_pat, Visitor}; +use rustc_hir::{Arm, Expr, ExprKind, HirId, Pat, PatKind, QPath}; use rustc_lint::LateContext; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, AdtDef, ParamEnv, TyCtxt, TypeckResults, VariantDef}; use rustc_span::{sym, Span}; use super::{MATCH_BOOL, SINGLE_MATCH, SINGLE_MATCH_ELSE}; @@ -29,7 +32,7 @@ fn empty_arm_has_comment(cx: &LateContext<'_>, span: Span) -> bool { } #[rustfmt::skip] -pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) { +pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>], expr: &'tcx Expr<'_>) { if let [arm1, arm2] = arms && arm1.guard.is_none() && arm2.guard.is_none() @@ -52,10 +55,29 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: return; }; - let ty = cx.typeck_results().expr_ty(ex); - if (*ty.kind() != ty::Bool || is_lint_allowed(cx, MATCH_BOOL, ex.hir_id)) - && (is_wild(arm2.pat) || form_exhaustive_matches(cx, ty, arm1.pat, arm2.pat)) - { + let typeck = cx.typeck_results(); + if *typeck.expr_ty(ex).peel_refs().kind() != ty::Bool || is_lint_allowed(cx, MATCH_BOOL, ex.hir_id) { + let mut v = PatVisitor { + typeck, + has_enum: false, + }; + if v.visit_pat(arm2.pat).is_break() { + return; + } + if v.has_enum { + let cx = PatCtxt { + tcx: cx.tcx, + param_env: cx.param_env, + typeck, + arena: DroplessArena::default(), + }; + let mut state = PatState::Other; + if !(state.add_pat(&cx, arm2.pat) || state.add_pat(&cx, arm1.pat)) { + // Don't lint if the pattern contains an enum which doesn't have a wild match. + return; + } + } + report_single_pattern(cx, ex, arm1, expr, els); } } @@ -121,100 +143,227 @@ fn report_single_pattern(cx: &LateContext<'_>, ex: &Expr<'_>, arm: &Arm<'_>, exp span_lint_and_sugg(cx, lint, expr.span, msg, "try", sugg, app); } -/// Returns `true` if all of the types in the pattern are enums which we know -/// won't be expanded in the future -fn pat_in_candidate_enum<'a>(cx: &LateContext<'a>, ty: Ty<'a>, pat: &Pat<'_>) -> bool { - let mut paths_and_types = Vec::new(); - collect_pat_paths(&mut paths_and_types, cx, pat, ty); - paths_and_types.iter().all(|ty| in_candidate_enum(cx, *ty)) +struct PatVisitor<'tcx> { + typeck: &'tcx TypeckResults<'tcx>, + has_enum: bool, } - -/// Returns `true` if the given type is an enum we know won't be expanded in the future -fn in_candidate_enum(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { - // list of candidate `Enum`s we know will never get any more members - let candidates = [sym::Cow, sym::Option, sym::Result]; - - for candidate_ty in candidates { - if is_type_diagnostic_item(cx, ty, candidate_ty) { - return true; +impl<'tcx> Visitor<'tcx> for PatVisitor<'tcx> { + type Result = ControlFlow<()>; + fn visit_pat(&mut self, pat: &'tcx Pat<'_>) -> Self::Result { + if matches!(pat.kind, PatKind::Binding(..)) { + ControlFlow::Break(()) + } else { + self.has_enum |= self.typeck.pat_ty(pat).ty_adt_def().map_or(false, AdtDef::is_enum); + walk_pat(self, pat) } } - false } -/// Collects types from the given pattern -fn collect_pat_paths<'a>(acc: &mut Vec>, cx: &LateContext<'a>, pat: &Pat<'_>, ty: Ty<'a>) { - match pat.kind { - PatKind::Tuple(inner, _) => inner.iter().for_each(|p| { - let p_ty = cx.typeck_results().pat_ty(p); - collect_pat_paths(acc, cx, p, p_ty); - }), - PatKind::TupleStruct(..) | PatKind::Binding(BindingMode::NONE, .., None) | PatKind::Path(_) => { - acc.push(ty); - }, - _ => {}, +/// The context needed to manipulate a `PatState`. +struct PatCtxt<'tcx> { + tcx: TyCtxt<'tcx>, + param_env: ParamEnv<'tcx>, + typeck: &'tcx TypeckResults<'tcx>, + arena: DroplessArena, +} + +/// State for tracking whether a match can become non-exhaustive by adding a variant to a contained +/// enum. +/// +/// This treats certain std enums as if they will never be extended. +enum PatState<'a> { + /// Either a wild match or an uninteresting type. Uninteresting types include: + /// * builtin types (e.g. `i32` or `!`) + /// * A struct/tuple/array containing only uninteresting types. + /// * A std enum containing only uninteresting types. + Wild, + /// A std enum we know won't be extended. Tracks the states of each variant separately. + /// + /// This is not used for `Option` since it uses the current pattern to track it's state. + StdEnum(&'a mut [PatState<'a>]), + /// Either the initial state for a pattern or a non-std enum. There is currently no need to + /// distinguish these cases. + /// + /// For non-std enums there's no need to track the state of sub-patterns as the state of just + /// this pattern on it's own is enough for linting. Consider two cases: + /// * This enum has no wild match. This case alone is enough to determine we can lint. + /// * This enum has a wild match and therefore all sub-patterns also have a wild match. + /// + /// In both cases the sub patterns are not needed to determine whether to lint. + Other, +} +impl<'a> PatState<'a> { + /// Adds a set of patterns as a product type to the current state. Returns whether or not the + /// current state is a wild match after the merge. + fn add_product_pat<'tcx>( + &mut self, + cx: &'a PatCtxt<'tcx>, + pats: impl IntoIterator>, + ) -> bool { + // Ideally this would actually keep track of the state separately for each pattern. Doing so would + // require implementing something similar to exhaustiveness checking which is a significant increase + // in complexity. + // + // For now treat this as a wild match only if all the sub-patterns are wild + let is_wild = pats.into_iter().all(|p| { + let mut state = Self::Other; + state.add_pat(cx, p) + }); + if is_wild { + *self = Self::Wild; + } + is_wild } -} -/// Returns true if the given arm of pattern matching contains wildcard patterns. -fn contains_only_wilds(pat: &Pat<'_>) -> bool { - match pat.kind { - PatKind::Wild => true, - PatKind::Tuple(inner, _) | PatKind::TupleStruct(_, inner, ..) => inner.iter().all(contains_only_wilds), - _ => false, + /// Attempts to get the state for the enum variant, initializing the current state if necessary. + fn get_std_enum_variant<'tcx>( + &mut self, + cx: &'a PatCtxt<'tcx>, + adt: AdtDef<'tcx>, + path: &'tcx QPath<'_>, + hir_id: HirId, + ) -> Option<(&mut Self, &'tcx VariantDef)> { + let states = match self { + Self::Wild => return None, + Self::Other => { + *self = Self::StdEnum(cx.arena.alloc_from_iter((0..adt.variants().len()).map(|_| Self::Other))); + let Self::StdEnum(x) = self else { + unreachable!(); + }; + x + }, + Self::StdEnum(x) => x, + }; + let i = match cx.typeck.qpath_res(path, hir_id) { + Res::Def(DefKind::Ctor(..), id) => adt.variant_index_with_ctor_id(id), + Res::Def(DefKind::Variant, id) => adt.variant_index_with_id(id), + _ => return None, + }; + Some((&mut states[i.as_usize()], adt.variant(i))) } -} -/// Returns true if the given patterns forms only exhaustive matches that don't contain enum -/// patterns without a wildcard. -fn form_exhaustive_matches<'a>(cx: &LateContext<'a>, ty: Ty<'a>, 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 left_pos = left_pos.as_opt_usize(); - let right_pos = right_pos.as_opt_usize(); - let len = max( - left_in.len() + usize::from(left_pos.is_some()), - right_in.len() + usize::from(right_pos.is_some()), - ); - 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; - } - } + fn check_all_wild_enum(&mut self) -> bool { + if let Self::StdEnum(states) = self + && states.iter().all(|s| matches!(s, Self::Wild)) + { + *self = Self::Wild; true - }, - (PatKind::TupleStruct(..), PatKind::Path(_)) => pat_in_candidate_enum(cx, ty, right), - (PatKind::TupleStruct(..), PatKind::TupleStruct(_, inner, _)) => { - pat_in_candidate_enum(cx, ty, right) && inner.iter().all(contains_only_wilds) - }, - _ => false, + } else { + false + } + } + + #[expect(clippy::similar_names)] + fn add_struct_pats<'tcx>( + &mut self, + cx: &'a PatCtxt<'tcx>, + pat: &'tcx Pat<'tcx>, + path: &'tcx QPath<'tcx>, + single_pat: Option<&'tcx Pat<'tcx>>, + pats: impl IntoIterator>, + ) -> bool { + let ty::Adt(adt, _) = *cx.typeck.pat_ty(pat).kind() else { + // Should never happen + *self = Self::Wild; + return true; + }; + if adt.is_struct() { + return if let Some(pat) = single_pat + && adt.non_enum_variant().fields.len() == 1 + { + self.add_pat(cx, pat) + } else { + self.add_product_pat(cx, pats) + }; + } + match cx.tcx.get_diagnostic_name(adt.did()) { + Some(sym::Option) => { + if let Some(pat) = single_pat { + self.add_pat(cx, pat) + } else { + *self = Self::Wild; + true + } + }, + Some(sym::Result | sym::Cow) => { + let Some((state, variant)) = self.get_std_enum_variant(cx, adt, path, pat.hir_id) else { + return matches!(self, Self::Wild); + }; + let is_wild = if let Some(pat) = single_pat + && variant.fields.len() == 1 + { + state.add_pat(cx, pat) + } else { + state.add_product_pat(cx, pats) + }; + is_wild && self.check_all_wild_enum() + }, + _ => matches!(self, Self::Wild), + } + } + + /// Adds the pattern into the current state. Returns whether or not the current state is a wild + /// match after the merge. + #[expect(clippy::similar_names)] + fn add_pat<'tcx>(&mut self, cx: &'a PatCtxt<'tcx>, pat: &'tcx Pat<'_>) -> bool { + match pat.kind { + PatKind::Path(_) + if match *cx.typeck.pat_ty(pat).peel_refs().kind() { + ty::Adt(adt, _) => adt.is_enum() || (adt.is_struct() && !adt.non_enum_variant().fields.is_empty()), + ty::Tuple(tys) => !tys.is_empty(), + ty::Array(_, len) => len.try_eval_target_usize(cx.tcx, cx.param_env) != Some(1), + ty::Slice(..) => true, + _ => false, + } => + { + matches!(self, Self::Wild) + }, + + // Patterns for things which can only contain a single sub-pattern. + PatKind::Binding(_, _, _, Some(pat)) | PatKind::Ref(pat, _) | PatKind::Box(pat) | PatKind::Deref(pat) => { + self.add_pat(cx, pat) + }, + PatKind::Tuple([sub_pat], pos) + if pos.as_opt_usize().is_none() || cx.typeck.pat_ty(pat).tuple_fields().len() == 1 => + { + self.add_pat(cx, sub_pat) + }, + PatKind::Slice([sub_pat], _, []) | PatKind::Slice([], _, [sub_pat]) + if let ty::Array(_, len) = *cx.typeck.pat_ty(pat).kind() + && len.try_eval_target_usize(cx.tcx, cx.param_env) == Some(1) => + { + self.add_pat(cx, sub_pat) + }, + + PatKind::Or(pats) => pats.iter().any(|p| self.add_pat(cx, p)), + PatKind::Tuple(pats, _) => self.add_product_pat(cx, pats), + PatKind::Slice(head, _, tail) => self.add_product_pat(cx, head.iter().chain(tail)), + + PatKind::TupleStruct(ref path, pats, _) => self.add_struct_pats( + cx, + pat, + path, + if let [pat] = pats { Some(pat) } else { None }, + pats.iter(), + ), + PatKind::Struct(ref path, pats, _) => self.add_struct_pats( + cx, + pat, + path, + if let [pat] = pats { Some(pat.pat) } else { None }, + pats.iter().map(|p| p.pat), + ), + + PatKind::Wild + | PatKind::Binding(_, _, _, None) + | PatKind::Lit(_) + | PatKind::Range(..) + | PatKind::Path(_) + | PatKind::Never + | PatKind::Err(_) => { + *self = PatState::Wild; + true + }, + } } } diff --git a/tests/ui-toml/excessive_nesting/excessive_nesting.rs b/tests/ui-toml/excessive_nesting/excessive_nesting.rs index 4375f324aca..858aab528a9 100644 --- a/tests/ui-toml/excessive_nesting/excessive_nesting.rs +++ b/tests/ui-toml/excessive_nesting/excessive_nesting.rs @@ -1,15 +1,19 @@ //@aux-build:../../ui/auxiliary/proc_macros.rs #![rustfmt::skip] #![feature(custom_inner_attributes)] -#![allow(unused)] -#![allow(clippy::let_and_return)] -#![allow(clippy::redundant_closure_call)] -#![allow(clippy::no_effect)] -#![allow(clippy::unnecessary_operation)] -#![allow(clippy::never_loop)] -#![allow(clippy::needless_if)] #![warn(clippy::excessive_nesting)] -#![allow(clippy::collapsible_if, clippy::blocks_in_conditions)] +#![allow( + unused, + clippy::let_and_return, + clippy::redundant_closure_call, + clippy::no_effect, + clippy::unnecessary_operation, + clippy::never_loop, + clippy::needless_if, + clippy::collapsible_if, + clippy::blocks_in_conditions, + clippy::single_match, +)] #[macro_use] extern crate proc_macros; diff --git a/tests/ui-toml/excessive_nesting/excessive_nesting.stderr b/tests/ui-toml/excessive_nesting/excessive_nesting.stderr index dafcd442055..ccdaecdd481 100644 --- a/tests/ui-toml/excessive_nesting/excessive_nesting.stderr +++ b/tests/ui-toml/excessive_nesting/excessive_nesting.stderr @@ -1,5 +1,5 @@ error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:21:25 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:25:25 | LL | let w = { 3 }; | ^^^^^ @@ -9,7 +9,7 @@ LL | let w = { 3 }; = help: to override `-D warnings` add `#[allow(clippy::excessive_nesting)]` error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:67:17 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:71:17 | LL | / impl C { LL | | pub fn c() {} @@ -19,7 +19,7 @@ LL | | } = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:81:25 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:85:25 | LL | let x = { 1 }; // not a warning, but cc is | ^^^^^ @@ -27,7 +27,7 @@ LL | let x = { 1 }; // not a warning, but cc is = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:98:17 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:102:17 | LL | / pub mod e { LL | | pub mod f {} @@ -37,7 +37,7 @@ LL | | } // not here = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:111:18 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:115:18 | LL | a_but_not({{{{{{{{0}}}}}}}}); | ^^^^^^^^^^^ @@ -45,7 +45,7 @@ LL | a_but_not({{{{{{{{0}}}}}}}}); = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:112:12 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:116:12 | LL | a.a({{{{{{{{{0}}}}}}}}}); | ^^^^^^^^^^^^^ @@ -53,7 +53,7 @@ LL | a.a({{{{{{{{{0}}}}}}}}}); = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:113:12 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:117:12 | LL | (0, {{{{{{{1}}}}}}}); | ^^^^^^^^^ @@ -61,7 +61,7 @@ LL | (0, {{{{{{{1}}}}}}}); = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:118:25 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:122:25 | LL | if true { | _________________________^ @@ -74,7 +74,7 @@ LL | | } = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:130:29 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:134:29 | LL | let z = (|| { | _____________________________^ @@ -86,7 +86,7 @@ LL | | })(); = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:149:13 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:153:13 | LL | y += {{{{{5}}}}}; | ^^^^^ @@ -94,7 +94,7 @@ LL | y += {{{{{5}}}}}; = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:150:20 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:154:20 | LL | let z = y + {{{{{{{{{5}}}}}}}}}; | ^^^^^^^^^^^^^ @@ -102,7 +102,7 @@ LL | let z = y + {{{{{{{{{5}}}}}}}}}; = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:151:12 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:155:12 | LL | [0, {{{{{{{{{{0}}}}}}}}}}]; | ^^^^^^^^^^^^^^^ @@ -110,7 +110,7 @@ LL | [0, {{{{{{{{{{0}}}}}}}}}}]; = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:152:25 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:156:25 | LL | let mut xx = [0; {{{{{{{{100}}}}}}}}]; | ^^^^^^^^^^^^^ @@ -118,7 +118,7 @@ LL | let mut xx = [0; {{{{{{{{100}}}}}}}}]; = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:153:11 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:157:11 | LL | xx[{{{{{{{{{{{{{{{{{{{{{{{{3}}}}}}}}}}}}}}}}}}}}}}}}]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -126,7 +126,7 @@ LL | xx[{{{{{{{{{{{{{{{{{{{{{{{{3}}}}}}}}}}}}}}}}}}}}}}}}]; = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:154:13 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:158:13 | LL | &mut {{{{{{{{{{y}}}}}}}}}}; | ^^^^^^^^^^^^^^^ @@ -134,7 +134,7 @@ LL | &mut {{{{{{{{{{y}}}}}}}}}}; = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:156:17 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:160:17 | LL | for i in {{{{xx}}}} {{{{{{{{}}}}}}}} | ^^^^ @@ -142,7 +142,7 @@ LL | for i in {{{{xx}}}} {{{{{{{{}}}}}}}} = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:156:28 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:160:28 | LL | for i in {{{{xx}}}} {{{{{{{{}}}}}}}} | ^^^^^^^^^^ @@ -150,7 +150,7 @@ LL | for i in {{{{xx}}}} {{{{{{{{}}}}}}}} = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:158:28 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:162:28 | LL | while let Some(i) = {{{{{{Some(1)}}}}}} {{{{{{{}}}}}}} | ^^^^^^^^^^^^^ @@ -158,7 +158,7 @@ LL | while let Some(i) = {{{{{{Some(1)}}}}}} {{{{{{{}}}}}}} = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:158:48 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:162:48 | LL | while let Some(i) = {{{{{{Some(1)}}}}}} {{{{{{{}}}}}}} | ^^^^^^^^ @@ -166,7 +166,7 @@ LL | while let Some(i) = {{{{{{Some(1)}}}}}} {{{{{{{}}}}}}} = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:160:14 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:164:14 | LL | while {{{{{{{{true}}}}}}}} {{{{{{{{{}}}}}}}}} | ^^^^^^^^^^^^^^ @@ -174,7 +174,7 @@ LL | while {{{{{{{{true}}}}}}}} {{{{{{{{{}}}}}}}}} = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:160:35 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:164:35 | LL | while {{{{{{{{true}}}}}}}} {{{{{{{{{}}}}}}}}} | ^^^^^^^^^^^^ @@ -182,7 +182,7 @@ LL | while {{{{{{{{true}}}}}}}} {{{{{{{{{}}}}}}}}} = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:162:23 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:166:23 | LL | let d = D { d: {{{{{{{{{{{{{{{{{{{{{{{3}}}}}}}}}}}}}}}}}}}}}}} }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -190,7 +190,7 @@ LL | let d = D { d: {{{{{{{{{{{{{{{{{{{{{{{3}}}}}}}}}}}}}}}}}}}}}}} }; = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:164:8 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:168:8 | LL | {{{{1;}}}}..{{{{{{3}}}}}}; | ^^^^ @@ -198,7 +198,7 @@ LL | {{{{1;}}}}..{{{{{{3}}}}}}; = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:164:20 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:168:20 | LL | {{{{1;}}}}..{{{{{{3}}}}}}; | ^^^^^^^ @@ -206,7 +206,7 @@ LL | {{{{1;}}}}..{{{{{{3}}}}}}; = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:165:8 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:169:8 | LL | {{{{1;}}}}..={{{{{{{{{{{{{{{{{{{{{{{{{{6}}}}}}}}}}}}}}}}}}}}}}}}}}; | ^^^^ @@ -214,7 +214,7 @@ LL | {{{{1;}}}}..={{{{{{{{{{{{{{{{{{{{{{{{{{6}}}}}}}}}}}}}}}}}}}}}}}}}}; = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:165:21 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:169:21 | LL | {{{{1;}}}}..={{{{{{{{{{{{{{{{{{{{{{{{{{6}}}}}}}}}}}}}}}}}}}}}}}}}}; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -222,7 +222,7 @@ LL | {{{{1;}}}}..={{{{{{{{{{{{{{{{{{{{{{{{{{6}}}}}}}}}}}}}}}}}}}}}}}}}}; = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:166:10 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:170:10 | LL | ..{{{{{{{5}}}}}}}; | ^^^^^^^^^ @@ -230,7 +230,7 @@ LL | ..{{{{{{{5}}}}}}}; = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:167:11 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:171:11 | LL | ..={{{{{3}}}}}; | ^^^^^ @@ -238,7 +238,7 @@ LL | ..={{{{{3}}}}}; = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:168:8 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:172:8 | LL | {{{{{1;}}}}}..; | ^^^^^^ @@ -246,7 +246,7 @@ LL | {{{{{1;}}}}}..; = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:170:20 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:174:20 | LL | loop { break {{{{1}}}} }; | ^^^^^ @@ -254,7 +254,7 @@ LL | loop { break {{{{1}}}} }; = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:171:13 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:175:13 | LL | loop {{{{{{}}}}}} | ^^^^^^ @@ -262,7 +262,7 @@ LL | loop {{{{{{}}}}}} = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:173:14 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:177:14 | LL | match {{{{{{true}}}}}} { | ^^^^^^^^^^ @@ -270,7 +270,7 @@ LL | match {{{{{{true}}}}}} { = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:174:20 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:178:20 | LL | true => {{{{}}}}, | ^^ @@ -278,7 +278,7 @@ LL | true => {{{{}}}}, = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:175:21 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:179:21 | LL | false => {{{{}}}}, | ^^ @@ -286,7 +286,7 @@ LL | false => {{{{}}}}, = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:181:17 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:185:17 | LL | / { LL | | println!("warning! :)"); @@ -296,7 +296,7 @@ LL | | } = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:190:28 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:194:28 | LL | async fn c() -> u32 {{{{{{{0}}}}}}} | ^^^^^^^^^ @@ -304,7 +304,7 @@ LL | async fn c() -> u32 {{{{{{{0}}}}}}} = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:196:8 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:200:8 | LL | {{{{b().await}}}}; | ^^^^^^^^^^^ diff --git a/tests/ui/crashes/ice-6254.rs b/tests/ui/crashes/ice-6254.rs index 8af60890390..aaca32ab2d9 100644 --- a/tests/ui/crashes/ice-6254.rs +++ b/tests/ui/crashes/ice-6254.rs @@ -2,7 +2,8 @@ // panicked at 'assertion failed: rows.iter().all(|r| r.len() == v.len())', // compiler/rustc_mir_build/src/thir/pattern/_match.rs:2030:5 -#[allow(clippy::derive_partial_eq_without_eq)] +#![allow(clippy::derive_partial_eq_without_eq, clippy::single_match)] + #[derive(PartialEq)] struct Foo(i32); const FOO_REF_REF: &&Foo = &&Foo(42); diff --git a/tests/ui/patterns.fixed b/tests/ui/patterns.fixed index 332cba97155..feaec33ac15 100644 --- a/tests/ui/patterns.fixed +++ b/tests/ui/patterns.fixed @@ -1,7 +1,7 @@ //@aux-build:proc_macros.rs #![warn(clippy::all)] #![allow(unused)] -#![allow(clippy::uninlined_format_args)] +#![allow(clippy::uninlined_format_args, clippy::single_match)] #[macro_use] extern crate proc_macros; diff --git a/tests/ui/patterns.rs b/tests/ui/patterns.rs index 45d907688e3..53812c7deec 100644 --- a/tests/ui/patterns.rs +++ b/tests/ui/patterns.rs @@ -1,7 +1,7 @@ //@aux-build:proc_macros.rs #![warn(clippy::all)] #![allow(unused)] -#![allow(clippy::uninlined_format_args)] +#![allow(clippy::uninlined_format_args, clippy::single_match)] #[macro_use] extern crate proc_macros; diff --git a/tests/ui/single_match.fixed b/tests/ui/single_match.fixed index acd70416d8b..5249c440889 100644 --- a/tests/ui/single_match.fixed +++ b/tests/ui/single_match.fixed @@ -253,3 +253,46 @@ mod issue8634 { } } } + +fn issue11365() { + enum Foo { + A, + B, + C, + } + use Foo::{A, B, C}; + + match Some(A) { + Some(A | B | C) => println!(), + None => {}, + } + + match Some(A) { + Some(A | B) => println!(), + Some { 0: C } | None => {}, + } + + match [A, A] { + [A, _] => println!(), + [_, A | B | C] => {}, + } + + match Ok::<_, u32>(Some(A)) { + Ok(Some(A)) => println!(), + Err(_) | Ok(None | Some(B | C)) => {}, + } + + if let Ok(Some(A)) = Ok::<_, u32>(Some(A)) { println!() } + + match &Some(A) { + Some(A | B | C) => println!(), + None => {}, + } + + match &Some(A) { + &Some(A | B | C) => println!(), + None => {}, + } + + if let Some(A | B) = &Some(A) { println!() } +} diff --git a/tests/ui/single_match.rs b/tests/ui/single_match.rs index bde78199810..882098a56e7 100644 --- a/tests/ui/single_match.rs +++ b/tests/ui/single_match.rs @@ -311,3 +311,52 @@ fn block_comment(x: Result) { } } } + +fn issue11365() { + enum Foo { + A, + B, + C, + } + use Foo::{A, B, C}; + + match Some(A) { + Some(A | B | C) => println!(), + None => {}, + } + + match Some(A) { + Some(A | B) => println!(), + Some { 0: C } | None => {}, + } + + match [A, A] { + [A, _] => println!(), + [_, A | B | C] => {}, + } + + match Ok::<_, u32>(Some(A)) { + Ok(Some(A)) => println!(), + Err(_) | Ok(None | Some(B | C)) => {}, + } + + match Ok::<_, u32>(Some(A)) { + Ok(Some(A)) => println!(), + Err(_) | Ok(None | Some(_)) => {}, + } + + match &Some(A) { + Some(A | B | C) => println!(), + None => {}, + } + + match &Some(A) { + &Some(A | B | C) => println!(), + None => {}, + } + + match &Some(A) { + Some(A | B) => println!(), + None | Some(_) => {}, + } +} diff --git a/tests/ui/single_match.stderr b/tests/ui/single_match.stderr index a249c120ee4..ceb2a193bf7 100644 --- a/tests/ui/single_match.stderr +++ b/tests/ui/single_match.stderr @@ -198,5 +198,23 @@ LL + } LL + } | -error: aborting due to 18 previous errors +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> tests/ui/single_match.rs:343:5 + | +LL | / match Ok::<_, u32>(Some(A)) { +LL | | Ok(Some(A)) => println!(), +LL | | Err(_) | Ok(None | Some(_)) => {}, +LL | | } + | |_____^ help: try: `if let Ok(Some(A)) = Ok::<_, u32>(Some(A)) { println!() }` + +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> tests/ui/single_match.rs:358:5 + | +LL | / match &Some(A) { +LL | | Some(A | B) => println!(), +LL | | None | Some(_) => {}, +LL | | } + | |_____^ help: try: `if let Some(A | B) = &Some(A) { println!() }` + +error: aborting due to 20 previous errors diff --git a/tests/ui/unneeded_field_pattern.rs b/tests/ui/unneeded_field_pattern.rs index 0dc21f4ce94..1d42f81711b 100644 --- a/tests/ui/unneeded_field_pattern.rs +++ b/tests/ui/unneeded_field_pattern.rs @@ -1,6 +1,6 @@ //@aux-build:proc_macros.rs #![warn(clippy::unneeded_field_pattern)] -#![allow(dead_code, unused)] +#![allow(dead_code, unused, clippy::single_match)] #[macro_use] extern crate proc_macros;