Rollup merge of #94951 - est31:irrefutable_let_chain_patterns, r=estebank
Extend the irrefutable_let_patterns lint to let chains Implements the suggestion from https://github.com/rust-lang/rust/pull/94927#issuecomment-1067078300 We only look for complete suffixes or prefixes of irrefutable let patterns, so that an irrefutable let pattern in a chain surrounded by refutable ones is not linted, as it is an useful pattern that has no low-cost replacement (unlike suffixes or prefixes which can just be copied outside of the `if`: either into the `if`'s block, or the block surrounding the `if`). If all patterns in a let chain are irrefutable, we lint as well. Depends on #94958 ~~so I included it into the PR for now~~ *which has been merged since*. r? `@estebank` cc `@joshtriplett` `@c410-f3r`
This commit is contained in:
commit
2bd5c44e93
5 changed files with 339 additions and 45 deletions
|
@ -158,7 +158,7 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
|
||||||
self.check_patterns(pat, Refutable);
|
self.check_patterns(pat, Refutable);
|
||||||
let mut cx = self.new_cx(scrutinee.hir_id);
|
let mut cx = self.new_cx(scrutinee.hir_id);
|
||||||
let tpat = self.lower_pattern(&mut cx, pat, &mut false);
|
let tpat = self.lower_pattern(&mut cx, pat, &mut false);
|
||||||
check_let_reachability(&mut cx, pat.hir_id, tpat, span);
|
self.check_let_reachability(&mut cx, pat.hir_id, tpat, span);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_match(
|
fn check_match(
|
||||||
|
@ -176,7 +176,7 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
|
||||||
if let Some(hir::Guard::IfLet(ref pat, _)) = arm.guard {
|
if let Some(hir::Guard::IfLet(ref pat, _)) = arm.guard {
|
||||||
self.check_patterns(pat, Refutable);
|
self.check_patterns(pat, Refutable);
|
||||||
let tpat = self.lower_pattern(&mut cx, pat, &mut false);
|
let tpat = self.lower_pattern(&mut cx, pat, &mut false);
|
||||||
check_let_reachability(&mut cx, pat.hir_id, tpat, tpat.span());
|
self.check_let_reachability(&mut cx, pat.hir_id, tpat, tpat.span());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,6 +224,157 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_let_reachability(
|
||||||
|
&mut self,
|
||||||
|
cx: &mut MatchCheckCtxt<'p, 'tcx>,
|
||||||
|
pat_id: HirId,
|
||||||
|
pat: &'p DeconstructedPat<'p, 'tcx>,
|
||||||
|
span: Span,
|
||||||
|
) {
|
||||||
|
if self.check_let_chain(cx, pat_id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_let_irrefutable(cx, pat_id, pat) {
|
||||||
|
irrefutable_let_pattern(cx.tcx, pat_id, span);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_let_chain(&mut self, cx: &mut MatchCheckCtxt<'p, 'tcx>, pat_id: HirId) -> bool {
|
||||||
|
let hir = self.tcx.hir();
|
||||||
|
let parent = hir.get_parent_node(pat_id);
|
||||||
|
|
||||||
|
// First, figure out if the given pattern is part of a let chain,
|
||||||
|
// and if so, obtain the top node of the chain.
|
||||||
|
let mut top = parent;
|
||||||
|
let mut part_of_chain = false;
|
||||||
|
loop {
|
||||||
|
let new_top = hir.get_parent_node(top);
|
||||||
|
if let hir::Node::Expr(
|
||||||
|
hir::Expr {
|
||||||
|
kind: hir::ExprKind::Binary(Spanned { node: hir::BinOpKind::And, .. }, lhs, rhs),
|
||||||
|
..
|
||||||
|
},
|
||||||
|
..,
|
||||||
|
) = hir.get(new_top)
|
||||||
|
{
|
||||||
|
// If this isn't the first iteration, we need to check
|
||||||
|
// if there is a let expr before us in the chain, so
|
||||||
|
// that we avoid doubly checking the let chain.
|
||||||
|
|
||||||
|
// The way a chain of &&s is encoded is ((let ... && let ...) && let ...) && let ...
|
||||||
|
// as && is left-to-right associative. Thus, we need to check rhs.
|
||||||
|
if part_of_chain && matches!(rhs.kind, hir::ExprKind::Let(..)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// If there is a let at the lhs, and we provide the rhs, we don't do any checking either.
|
||||||
|
if !part_of_chain && matches!(lhs.kind, hir::ExprKind::Let(..)) && rhs.hir_id == top
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// We've reached the top.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since this function is called within a let context, it is reasonable to assume that any parent
|
||||||
|
// `&&` infers a let chain
|
||||||
|
part_of_chain = true;
|
||||||
|
top = new_top;
|
||||||
|
}
|
||||||
|
if !part_of_chain {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Second, obtain the refutabilities of all exprs in the chain,
|
||||||
|
// and record chain members that aren't let exprs.
|
||||||
|
let mut chain_refutabilities = Vec::new();
|
||||||
|
let hir::Node::Expr(top_expr) = hir.get(top) else {
|
||||||
|
// We ensure right above that it's an Expr
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
let mut cur_expr = top_expr;
|
||||||
|
loop {
|
||||||
|
let mut add = |expr: &hir::Expr<'tcx>| {
|
||||||
|
let refutability = match expr.kind {
|
||||||
|
hir::ExprKind::Let(hir::Let { pat, init, span, .. }) => {
|
||||||
|
let mut ncx = self.new_cx(init.hir_id);
|
||||||
|
let tpat = self.lower_pattern(&mut ncx, pat, &mut false);
|
||||||
|
|
||||||
|
let refutable = !is_let_irrefutable(&mut ncx, pat.hir_id, tpat);
|
||||||
|
Some((*span, refutable))
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
chain_refutabilities.push(refutability);
|
||||||
|
};
|
||||||
|
if let hir::Expr {
|
||||||
|
kind: hir::ExprKind::Binary(Spanned { node: hir::BinOpKind::And, .. }, lhs, rhs),
|
||||||
|
..
|
||||||
|
} = cur_expr
|
||||||
|
{
|
||||||
|
add(rhs);
|
||||||
|
cur_expr = lhs;
|
||||||
|
} else {
|
||||||
|
add(cur_expr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
chain_refutabilities.reverse();
|
||||||
|
|
||||||
|
// Third, emit the actual warnings.
|
||||||
|
|
||||||
|
if chain_refutabilities.iter().all(|r| matches!(*r, Some((_, false)))) {
|
||||||
|
// The entire chain is made up of irrefutable `let` statements
|
||||||
|
let let_source = let_source_parent(self.tcx, top, None);
|
||||||
|
irrefutable_let_patterns(
|
||||||
|
cx.tcx,
|
||||||
|
top,
|
||||||
|
let_source,
|
||||||
|
chain_refutabilities.len(),
|
||||||
|
top_expr.span,
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
let lint_affix = |affix: &[Option<(Span, bool)>], kind, suggestion| {
|
||||||
|
let span_start = affix[0].unwrap().0;
|
||||||
|
let span_end = affix.last().unwrap().unwrap().0;
|
||||||
|
let span = span_start.to(span_end);
|
||||||
|
let cnt = affix.len();
|
||||||
|
cx.tcx.struct_span_lint_hir(IRREFUTABLE_LET_PATTERNS, top, span, |lint| {
|
||||||
|
let s = pluralize!(cnt);
|
||||||
|
let mut diag = lint.build(&format!("{kind} irrefutable pattern{s} in let chain"));
|
||||||
|
diag.note(&format!(
|
||||||
|
"{these} pattern{s} will always match",
|
||||||
|
these = pluralize!("this", cnt),
|
||||||
|
));
|
||||||
|
diag.help(&format!(
|
||||||
|
"consider moving {} {suggestion}",
|
||||||
|
if cnt > 1 { "them" } else { "it" }
|
||||||
|
));
|
||||||
|
diag.emit()
|
||||||
|
});
|
||||||
|
};
|
||||||
|
if let Some(until) = chain_refutabilities.iter().position(|r| !matches!(*r, Some((_, false)))) && until > 0 {
|
||||||
|
// The chain has a non-zero prefix of irrefutable `let` statements.
|
||||||
|
|
||||||
|
// Check if the let source is while, for there is no alternative place to put a prefix,
|
||||||
|
// and we shouldn't lint.
|
||||||
|
let let_source = let_source_parent(self.tcx, top, None);
|
||||||
|
if !matches!(let_source, LetSource::WhileLet) {
|
||||||
|
// Emit the lint
|
||||||
|
let prefix = &chain_refutabilities[..until];
|
||||||
|
lint_affix(prefix, "leading", "outside of the construct");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(from) = chain_refutabilities.iter().rposition(|r| !matches!(*r, Some((_, false)))) && from != (chain_refutabilities.len() - 1) {
|
||||||
|
// The chain has a non-empty suffix of irrefutable `let` statements
|
||||||
|
let suffix = &chain_refutabilities[from + 1..];
|
||||||
|
lint_affix(suffix, "trailing", "into the body");
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn check_irrefutable(&self, pat: &'tcx Pat<'tcx>, origin: &str, sp: Option<Span>) {
|
fn check_irrefutable(&self, pat: &'tcx Pat<'tcx>, origin: &str, sp: Option<Span>) {
|
||||||
let mut cx = self.new_cx(pat.hir_id);
|
let mut cx = self.new_cx(pat.hir_id);
|
||||||
|
|
||||||
|
@ -453,6 +604,17 @@ fn unreachable_pattern(tcx: TyCtxt<'_>, span: Span, id: HirId, catchall: Option<
|
||||||
}
|
}
|
||||||
|
|
||||||
fn irrefutable_let_pattern(tcx: TyCtxt<'_>, id: HirId, span: Span) {
|
fn irrefutable_let_pattern(tcx: TyCtxt<'_>, id: HirId, span: Span) {
|
||||||
|
let source = let_source(tcx, id);
|
||||||
|
irrefutable_let_patterns(tcx, id, source, 1, span);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn irrefutable_let_patterns(
|
||||||
|
tcx: TyCtxt<'_>,
|
||||||
|
id: HirId,
|
||||||
|
source: LetSource,
|
||||||
|
count: usize,
|
||||||
|
span: Span,
|
||||||
|
) {
|
||||||
macro_rules! emit_diag {
|
macro_rules! emit_diag {
|
||||||
(
|
(
|
||||||
$lint:expr,
|
$lint:expr,
|
||||||
|
@ -460,14 +622,15 @@ fn irrefutable_let_pattern(tcx: TyCtxt<'_>, id: HirId, span: Span) {
|
||||||
$note_sufix:expr,
|
$note_sufix:expr,
|
||||||
$help_sufix:expr
|
$help_sufix:expr
|
||||||
) => {{
|
) => {{
|
||||||
let mut diag = $lint.build(concat!("irrefutable ", $source_name, " pattern"));
|
let s = pluralize!(count);
|
||||||
diag.note(concat!("this pattern will always match, so the ", $note_sufix));
|
let these = pluralize!("this", count);
|
||||||
|
let mut diag = $lint.build(&format!("irrefutable {} pattern{s}", $source_name));
|
||||||
|
diag.note(&format!("{these} pattern{s} will always match, so the {}", $note_sufix));
|
||||||
diag.help(concat!("consider ", $help_sufix));
|
diag.help(concat!("consider ", $help_sufix));
|
||||||
diag.emit()
|
diag.emit()
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
let source = let_source(tcx, id);
|
|
||||||
let span = match source {
|
let span = match source {
|
||||||
LetSource::LetElse(span) => span,
|
LetSource::LetElse(span) => span,
|
||||||
_ => span,
|
_ => span,
|
||||||
|
@ -511,16 +674,11 @@ fn irrefutable_let_pattern(tcx: TyCtxt<'_>, id: HirId, span: Span) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_let_reachability<'p, 'tcx>(
|
fn is_let_irrefutable<'p, 'tcx>(
|
||||||
cx: &mut MatchCheckCtxt<'p, 'tcx>,
|
cx: &mut MatchCheckCtxt<'p, 'tcx>,
|
||||||
pat_id: HirId,
|
pat_id: HirId,
|
||||||
pat: &'p DeconstructedPat<'p, 'tcx>,
|
pat: &'p DeconstructedPat<'p, 'tcx>,
|
||||||
span: Span,
|
) -> bool {
|
||||||
) {
|
|
||||||
if is_let_chain(cx.tcx, pat_id) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let arms = [MatchArm { pat, hir_id: pat_id, has_guard: false }];
|
let arms = [MatchArm { pat, hir_id: pat_id, has_guard: false }];
|
||||||
let report = compute_match_usefulness(&cx, &arms, pat_id, pat.ty());
|
let report = compute_match_usefulness(&cx, &arms, pat_id, pat.ty());
|
||||||
|
|
||||||
|
@ -529,10 +687,9 @@ fn check_let_reachability<'p, 'tcx>(
|
||||||
// `is_uninhabited` check.
|
// `is_uninhabited` check.
|
||||||
report_arm_reachability(&cx, &report);
|
report_arm_reachability(&cx, &report);
|
||||||
|
|
||||||
if report.non_exhaustiveness_witnesses.is_empty() {
|
// If the list of witnesses is empty, the match is exhaustive,
|
||||||
// The match is exhaustive, i.e. the `if let` pattern is irrefutable.
|
// i.e. the `if let` pattern is irrefutable.
|
||||||
irrefutable_let_pattern(cx.tcx, pat_id, span);
|
report.non_exhaustiveness_witnesses.is_empty()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Report unreachable arms, if any.
|
/// Report unreachable arms, if any.
|
||||||
|
@ -941,13 +1098,19 @@ fn let_source(tcx: TyCtxt<'_>, pat_id: HirId) -> LetSource {
|
||||||
let hir = tcx.hir();
|
let hir = tcx.hir();
|
||||||
|
|
||||||
let parent = hir.get_parent_node(pat_id);
|
let parent = hir.get_parent_node(pat_id);
|
||||||
|
let_source_parent(tcx, parent, Some(pat_id))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn let_source_parent(tcx: TyCtxt<'_>, parent: HirId, pat_id: Option<HirId>) -> LetSource {
|
||||||
|
let hir = tcx.hir();
|
||||||
|
|
||||||
let parent_node = hir.get(parent);
|
let parent_node = hir.get(parent);
|
||||||
|
|
||||||
match parent_node {
|
match parent_node {
|
||||||
hir::Node::Arm(hir::Arm {
|
hir::Node::Arm(hir::Arm {
|
||||||
guard: Some(hir::Guard::IfLet(&hir::Pat { hir_id, .. }, _)),
|
guard: Some(hir::Guard::IfLet(&hir::Pat { hir_id, .. }, _)),
|
||||||
..
|
..
|
||||||
}) if hir_id == pat_id => {
|
}) if Some(hir_id) == pat_id => {
|
||||||
return LetSource::IfLetGuard;
|
return LetSource::IfLetGuard;
|
||||||
}
|
}
|
||||||
hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Let(..), span, .. }) => {
|
hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Let(..), span, .. }) => {
|
||||||
|
@ -980,21 +1143,3 @@ fn let_source(tcx: TyCtxt<'_>, pat_id: HirId) -> LetSource {
|
||||||
|
|
||||||
LetSource::GenericLet
|
LetSource::GenericLet
|
||||||
}
|
}
|
||||||
|
|
||||||
// Since this function is called within a let context, it is reasonable to assume that any parent
|
|
||||||
// `&&` infers a let chain
|
|
||||||
fn is_let_chain(tcx: TyCtxt<'_>, pat_id: HirId) -> bool {
|
|
||||||
let hir = tcx.hir();
|
|
||||||
let parent = hir.get_parent_node(pat_id);
|
|
||||||
let parent_parent = hir.get_parent_node(parent);
|
|
||||||
matches!(
|
|
||||||
hir.get(parent_parent),
|
|
||||||
hir::Node::Expr(
|
|
||||||
hir::Expr {
|
|
||||||
kind: hir::ExprKind::Binary(Spanned { node: hir::BinOpKind::And, .. }, ..),
|
|
||||||
..
|
|
||||||
},
|
|
||||||
..
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
// See `mir_drop_order.rs` for more information
|
// See `mir_drop_order.rs` for more information
|
||||||
|
|
||||||
#![feature(let_chains)]
|
#![feature(let_chains)]
|
||||||
|
#![allow(irrefutable_let_patterns)]
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::panic;
|
use std::panic;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
// run-pass
|
// run-pass
|
||||||
|
|
||||||
#![feature(let_chains)]
|
#![feature(let_chains)]
|
||||||
|
#![allow(irrefutable_let_patterns)]
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let first = Some(1);
|
let first = Some(1);
|
||||||
|
|
|
@ -0,0 +1,115 @@
|
||||||
|
error: leading irrefutable pattern in let chain
|
||||||
|
--> $DIR/irrefutable-lets.rs:13:8
|
||||||
|
|
|
||||||
|
LL | if let first = &opt && let Some(ref second) = first && let None = second.start {}
|
||||||
|
| ^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
note: the lint level is defined here
|
||||||
|
--> $DIR/irrefutable-lets.rs:6:30
|
||||||
|
|
|
||||||
|
LL | #![cfg_attr(disallowed, deny(irrefutable_let_patterns))]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
= note: this pattern will always match
|
||||||
|
= help: consider moving it outside of the construct
|
||||||
|
|
||||||
|
error: irrefutable `if let` patterns
|
||||||
|
--> $DIR/irrefutable-lets.rs:19:8
|
||||||
|
|
|
||||||
|
LL | if let first = &opt && let (a, b) = (1, 2) {}
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: these patterns will always match, so the `if let` is useless
|
||||||
|
= help: consider replacing the `if let` with a `let`
|
||||||
|
|
||||||
|
error: leading irrefutable pattern in let chain
|
||||||
|
--> $DIR/irrefutable-lets.rs:22:8
|
||||||
|
|
|
||||||
|
LL | if let first = &opt && let Some(ref second) = first && let None = second.start && let v = 0 {}
|
||||||
|
| ^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: this pattern will always match
|
||||||
|
= help: consider moving it outside of the construct
|
||||||
|
|
||||||
|
error: trailing irrefutable pattern in let chain
|
||||||
|
--> $DIR/irrefutable-lets.rs:22:87
|
||||||
|
|
|
||||||
|
LL | if let first = &opt && let Some(ref second) = first && let None = second.start && let v = 0 {}
|
||||||
|
| ^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: this pattern will always match
|
||||||
|
= help: consider moving it into the body
|
||||||
|
|
||||||
|
error: trailing irrefutable patterns in let chain
|
||||||
|
--> $DIR/irrefutable-lets.rs:26:37
|
||||||
|
|
|
||||||
|
LL | if let Some(ref first) = opt && let second = first && let _third = second {}
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: these patterns will always match
|
||||||
|
= help: consider moving them into the body
|
||||||
|
|
||||||
|
error: leading irrefutable pattern in let chain
|
||||||
|
--> $DIR/irrefutable-lets.rs:29:8
|
||||||
|
|
|
||||||
|
LL | if let Range { start: local_start, end: _ } = (None..Some(1)) && let None = local_start {}
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: this pattern will always match
|
||||||
|
= help: consider moving it outside of the construct
|
||||||
|
|
||||||
|
error: leading irrefutable pattern in let chain
|
||||||
|
--> $DIR/irrefutable-lets.rs:32:8
|
||||||
|
|
|
||||||
|
LL | if let (a, b, c) = (Some(1), Some(1), Some(1)) && let None = Some(1) {}
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: this pattern will always match
|
||||||
|
= help: consider moving it outside of the construct
|
||||||
|
|
||||||
|
error: leading irrefutable pattern in let chain
|
||||||
|
--> $DIR/irrefutable-lets.rs:35:8
|
||||||
|
|
|
||||||
|
LL | if let first = &opt && let None = Some(1) {}
|
||||||
|
| ^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: this pattern will always match
|
||||||
|
= help: consider moving it outside of the construct
|
||||||
|
|
||||||
|
error: irrefutable `let` patterns
|
||||||
|
--> $DIR/irrefutable-lets.rs:44:28
|
||||||
|
|
|
||||||
|
LL | Some(ref first) if let second = first && let _third = second && let v = 4 + 4 => {},
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: these patterns will always match, so the `let` is useless
|
||||||
|
= help: consider removing `let`
|
||||||
|
|
||||||
|
error: leading irrefutable pattern in let chain
|
||||||
|
--> $DIR/irrefutable-lets.rs:50:28
|
||||||
|
|
|
||||||
|
LL | Some(ref first) if let Range { start: local_start, end: _ } = first
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: this pattern will always match
|
||||||
|
= help: consider moving it outside of the construct
|
||||||
|
|
||||||
|
error: irrefutable `while let` patterns
|
||||||
|
--> $DIR/irrefutable-lets.rs:59:11
|
||||||
|
|
|
||||||
|
LL | while let first = &opt && let (a, b) = (1, 2) {}
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: these patterns will always match, so the loop will never exit
|
||||||
|
= help: consider instead using a `loop { ... }` with a `let` inside it
|
||||||
|
|
||||||
|
error: trailing irrefutable patterns in let chain
|
||||||
|
--> $DIR/irrefutable-lets.rs:62:40
|
||||||
|
|
|
||||||
|
LL | while let Some(ref first) = opt && let second = first && let _third = second {}
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: these patterns will always match
|
||||||
|
= help: consider moving them into the body
|
||||||
|
|
||||||
|
error: aborting due to 12 previous errors
|
||||||
|
|
|
@ -1,35 +1,67 @@
|
||||||
// check-pass
|
// revisions: allowed disallowed
|
||||||
|
//[allowed] check-pass
|
||||||
|
|
||||||
#![feature(if_let_guard, let_chains)]
|
#![feature(if_let_guard, let_chains)]
|
||||||
|
#![cfg_attr(allowed, allow(irrefutable_let_patterns))]
|
||||||
|
#![cfg_attr(disallowed, deny(irrefutable_let_patterns))]
|
||||||
|
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let opt = Some(None..Some(1));
|
let opt = Some(None..Some(1));
|
||||||
|
|
||||||
if let first = &opt && let Some(ref second) = first && let None = second.start {
|
if let first = &opt && let Some(ref second) = first && let None = second.start {}
|
||||||
}
|
//[disallowed]~^ ERROR leading irrefutable pattern in let chain
|
||||||
if let Some(ref first) = opt && let second = first && let _third = second {
|
|
||||||
}
|
// No lint as the irrefutable pattern is surrounded by other stuff
|
||||||
|
if 4 * 2 == 0 && let first = &opt && let Some(ref second) = first && let None = second.start {}
|
||||||
|
|
||||||
|
if let first = &opt && let (a, b) = (1, 2) {}
|
||||||
|
//[disallowed]~^ ERROR irrefutable `if let` patterns
|
||||||
|
|
||||||
|
if let first = &opt && let Some(ref second) = first && let None = second.start && let v = 0 {}
|
||||||
|
//[disallowed]~^ ERROR leading irrefutable pattern in let chain
|
||||||
|
//[disallowed]~^^ ERROR trailing irrefutable pattern in let chain
|
||||||
|
|
||||||
|
if let Some(ref first) = opt && let second = first && let _third = second {}
|
||||||
|
//[disallowed]~^ ERROR trailing irrefutable patterns in let chain
|
||||||
|
|
||||||
|
if let Range { start: local_start, end: _ } = (None..Some(1)) && let None = local_start {}
|
||||||
|
//[disallowed]~^ ERROR leading irrefutable pattern in let chain
|
||||||
|
|
||||||
|
if let (a, b, c) = (Some(1), Some(1), Some(1)) && let None = Some(1) {}
|
||||||
|
//[disallowed]~^ ERROR leading irrefutable pattern in let chain
|
||||||
|
|
||||||
|
if let first = &opt && let None = Some(1) {}
|
||||||
|
//[disallowed]~^ ERROR leading irrefutable pattern in let chain
|
||||||
|
|
||||||
if let Some(ref first) = opt
|
if let Some(ref first) = opt
|
||||||
&& let Range { start: local_start, end: _ } = first
|
&& let Range { start: local_start, end: _ } = first
|
||||||
&& let None = local_start {
|
&& let None = local_start {
|
||||||
}
|
}
|
||||||
|
|
||||||
match opt {
|
match opt {
|
||||||
Some(ref first) if let second = first && let _third = second => {},
|
Some(ref first) if let second = first && let _third = second && let v = 4 + 4 => {},
|
||||||
|
//[disallowed]~^ ERROR irrefutable `let` patterns
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
match opt {
|
match opt {
|
||||||
Some(ref first) if let Range { start: local_start, end: _ } = first
|
Some(ref first) if let Range { start: local_start, end: _ } = first
|
||||||
|
//[disallowed]~^ ERROR leading irrefutable pattern in let chain
|
||||||
&& let None = local_start => {},
|
&& let None = local_start => {},
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
while let first = &opt && let Some(ref second) = first && let None = second.start {
|
// No error, despite the prefix being irrefutable
|
||||||
}
|
while let first = &opt && let Some(ref second) = first && let None = second.start {}
|
||||||
while let Some(ref first) = opt && let second = first && let _third = second {
|
|
||||||
}
|
while let first = &opt && let (a, b) = (1, 2) {}
|
||||||
|
//[disallowed]~^ ERROR irrefutable `while let` patterns
|
||||||
|
|
||||||
|
while let Some(ref first) = opt && let second = first && let _third = second {}
|
||||||
|
//[disallowed]~^ ERROR trailing irrefutable patterns in let chain
|
||||||
|
|
||||||
while let Some(ref first) = opt
|
while let Some(ref first) = opt
|
||||||
&& let Range { start: local_start, end: _ } = first
|
&& let Range { start: local_start, end: _ } = first
|
||||||
&& let None = local_start {
|
&& let None = local_start {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue