Auto merge of #11441 - Jarcho:issue_11365, r=xFrednet

`single_match`: fix checking of explicitly matched enums

fixes #11365

This approach has false-negatives, but fixing them will require a significant amount of additional state tracking. The comment in `add_and_pats` has the explanation.

changelog: `single_match`: correct checking if the match explicitly matches all of an enum's variants.
This commit is contained in:
bors 2024-08-08 09:20:56 +00:00
commit 5ccf5432ab
12 changed files with 440 additions and 198 deletions

View file

@ -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);
}
}

View file

@ -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)?;

View file

@ -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, Block, 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,52 +32,58 @@ fn empty_arm_has_comment(cx: &LateContext<'_>, span: Span) -> bool {
}
#[rustfmt::skip]
pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
if arms.len() == 2 && arms[0].guard.is_none() && arms[1].guard.is_none() {
if expr.span.from_expansion() {
// Don't lint match expressions present in
// macro_rules! block
return;
}
if let PatKind::Or(..) = arms[0].pat.kind {
// don't lint for or patterns for now, this makes
// the lint noisy in unnecessary situations
return;
}
let els = arms[1].body;
let els = if is_unit_expr(peel_blocks(els)) && !empty_arm_has_comment(cx, els.span) {
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()
&& !expr.span.from_expansion()
// don't lint for or patterns for now, this makes
// the lint noisy in unnecessary situations
&& !matches!(arm1.pat.kind, PatKind::Or(..))
{
let els = if is_unit_expr(peel_blocks(arm2.body)) && !empty_arm_has_comment(cx, arm2.body.span) {
None
} else if let ExprKind::Block(Block { stmts, expr: block_expr, .. }, _) = els.kind {
if stmts.len() == 1 && block_expr.is_none() || stmts.is_empty() && block_expr.is_some() {
} else if let ExprKind::Block(block, _) = arm2.body.kind {
if matches!((block.stmts, block.expr), ([], Some(_)) | ([_], None)) {
// single statement/expr "else" block, don't lint
return;
}
// block with 2+ statements or 1 expr and 1+ statement
Some(els)
Some(arm2.body)
} else {
// not a block or an empty block w/ comments, don't lint
return;
};
let ty = cx.typeck_results().expr_ty(ex);
if (*ty.kind() != ty::Bool || is_lint_allowed(cx, MATCH_BOOL, ex.hir_id)) &&
(check_single_pattern(arms) || check_opt_like(cx, arms, ty)) {
report_single_pattern(cx, ex, arms, expr, els);
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);
}
}
}
fn check_single_pattern(arms: &[Arm<'_>]) -> bool {
is_wild(arms[1].pat)
}
fn report_single_pattern(
cx: &LateContext<'_>,
ex: &Expr<'_>,
arms: &[Arm<'_>],
expr: &Expr<'_>,
els: Option<&Expr<'_>>,
) {
fn report_single_pattern(cx: &LateContext<'_>, ex: &Expr<'_>, arm: &Arm<'_>, expr: &Expr<'_>, els: Option<&Expr<'_>>) {
let lint = if els.is_some() { SINGLE_MATCH_ELSE } else { SINGLE_MATCH };
let ctxt = expr.span.ctxt();
let mut app = Applicability::MachineApplicable;
@ -82,7 +91,7 @@ fn report_single_pattern(
format!(" else {}", expr_block(cx, els, ctxt, "..", Some(expr.span), &mut app))
});
let (pat, pat_ref_count) = peel_hir_pat_refs(arms[0].pat);
let (pat, pat_ref_count) = peel_hir_pat_refs(arm.pat);
let (msg, sugg) = if let PatKind::Path(_) | PatKind::Lit(_) = pat.kind
&& let (ty, ty_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(ex))
&& let Some(spe_trait_id) = cx.tcx.lang_items().structural_peq_trait()
@ -116,17 +125,17 @@ fn report_single_pattern(
snippet(cx, ex.span, ".."),
// PartialEq for different reference counts may not exist.
"&".repeat(ref_count_diff),
snippet(cx, arms[0].pat.span, ".."),
expr_block(cx, arms[0].body, ctxt, "..", Some(expr.span), &mut app),
snippet(cx, arm.pat.span, ".."),
expr_block(cx, arm.body, ctxt, "..", Some(expr.span), &mut app),
);
(msg, sugg)
} else {
let msg = "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`";
let sugg = format!(
"if let {} = {} {}{els_str}",
snippet(cx, arms[0].pat.span, ".."),
snippet(cx, arm.pat.span, ".."),
snippet(cx, ex.span, ".."),
expr_block(cx, arms[0].body, ctxt, "..", Some(expr.span), &mut app),
expr_block(cx, arm.body, ctxt, "..", Some(expr.span), &mut app),
);
(msg, sugg)
};
@ -134,106 +143,227 @@ fn report_single_pattern(
span_lint_and_sugg(cx, lint, expr.span, msg, "try", sugg, app);
}
fn check_opt_like<'a>(cx: &LateContext<'a>, arms: &[Arm<'_>], ty: Ty<'a>) -> bool {
// We don't want to lint if the second arm contains an enum which could
// have more variants in the future.
form_exhaustive_matches(cx, ty, arms[0].pat, arms[1].pat)
struct PatVisitor<'tcx> {
typeck: &'tcx TypeckResults<'tcx>,
has_enum: bool,
}
/// 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))
}
/// 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<Ty<'a>>, 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<Item = &'tcx Pat<'tcx>>,
) -> 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<Item = &'tcx Pat<'tcx>>,
) -> 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
},
}
}
}

View file

@ -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;

View file

@ -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}}}};
| ^^^^^^^^^^^

View file

@ -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);

View file

@ -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;

View file

@ -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;

View file

@ -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!() }
}

View file

@ -311,3 +311,52 @@ 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)) => {},
}
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(_) => {},
}
}

View file

@ -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

View file

@ -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;