Auto merge of #11787 - Jarcho:divergence_check, r=dswij
Fixes to `manual_let_else`'s divergence check A few changes to the divergence check in `manual_let_else` and moves it the implementation to `clippy_utils` since it's generally useful: * Handle internal `break` and `continue` expressions. e.g. The first loop is divergent, but the second is not. ```rust { loop { break 'outer; }; } { loop { break; }; } ``` * Match rust's definition of divergence which is defined via the type system. e.g. The following is not considered divergent by rustc as the inner block has a result type of `()`: ```rust { 'a: { panic!(); break 'a; }; } ``` * Handle when adding a single semicolon would make the expression divergent. e.g. The following would be a divergent if a semicolon were added after the `if` expression: ```rust { if panic!() { 0 } else { 1 } } ``` changelog: None
This commit is contained in:
commit
6a15f3bd49
@ -5,18 +5,15 @@
|
||||
use clippy_utils::higher::IfLetOrMatch;
|
||||
use clippy_utils::source::snippet_with_context;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::visitors::{Descend, Visitable};
|
||||
use clippy_utils::{is_lint_allowed, pat_and_expr_can_be_question_mark, peel_blocks};
|
||||
use clippy_utils::{is_lint_allowed, is_never_expr, pat_and_expr_can_be_question_mark, peel_blocks};
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::{walk_expr, Visitor};
|
||||
use rustc_hir::{Expr, ExprKind, HirId, ItemId, Local, MatchSource, Pat, PatKind, QPath, Stmt, StmtKind, Ty};
|
||||
use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatKind, QPath, Stmt, StmtKind};
|
||||
use rustc_lint::{LateContext, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::declare_tool_lint;
|
||||
use rustc_span::symbol::{sym, Symbol};
|
||||
use rustc_span::Span;
|
||||
use std::ops::ControlFlow;
|
||||
use std::slice;
|
||||
|
||||
declare_clippy_lint! {
|
||||
@ -51,7 +48,7 @@
|
||||
}
|
||||
|
||||
impl<'tcx> QuestionMark {
|
||||
pub(crate) fn check_manual_let_else(&mut self, cx: &LateContext<'_>, stmt: &'tcx Stmt<'tcx>) {
|
||||
pub(crate) fn check_manual_let_else(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) {
|
||||
if !self.msrv.meets(msrvs::LET_ELSE) || in_external_macro(cx.sess(), stmt.span) {
|
||||
return;
|
||||
}
|
||||
@ -67,7 +64,7 @@ pub(crate) fn check_manual_let_else(&mut self, cx: &LateContext<'_>, stmt: &'tcx
|
||||
IfLetOrMatch::IfLet(if_let_expr, let_pat, if_then, if_else) => {
|
||||
if let Some(ident_map) = expr_simple_identity_map(local.pat, let_pat, if_then)
|
||||
&& let Some(if_else) = if_else
|
||||
&& expr_diverges(cx, if_else)
|
||||
&& is_never_expr(cx, if_else).is_some()
|
||||
&& let qm_allowed = is_lint_allowed(cx, QUESTION_MARK, stmt.hir_id)
|
||||
&& (qm_allowed || pat_and_expr_can_be_question_mark(cx, let_pat, if_else).is_none())
|
||||
{
|
||||
@ -91,10 +88,9 @@ pub(crate) fn check_manual_let_else(&mut self, cx: &LateContext<'_>, stmt: &'tcx
|
||||
return;
|
||||
}
|
||||
let check_types = self.matches_behaviour == MatchLintBehaviour::WellKnownTypes;
|
||||
let diverging_arm_opt = arms
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|(_, arm)| expr_diverges(cx, arm.body) && pat_allowed_for_else(cx, arm.pat, check_types));
|
||||
let diverging_arm_opt = arms.iter().enumerate().find(|(_, arm)| {
|
||||
is_never_expr(cx, arm.body).is_some() && pat_allowed_for_else(cx, arm.pat, check_types)
|
||||
});
|
||||
let Some((idx, diverging_arm)) = diverging_arm_opt else {
|
||||
return;
|
||||
};
|
||||
@ -272,104 +268,6 @@ fn replace_in_pattern(
|
||||
sn_pat.into_owned()
|
||||
}
|
||||
|
||||
/// Check whether an expression is divergent. May give false negatives.
|
||||
fn expr_diverges(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
struct V<'cx, 'tcx> {
|
||||
cx: &'cx LateContext<'tcx>,
|
||||
res: ControlFlow<(), Descend>,
|
||||
}
|
||||
impl<'tcx> Visitor<'tcx> for V<'_, '_> {
|
||||
fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) {
|
||||
fn is_never(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool {
|
||||
if let Some(ty) = cx.typeck_results().expr_ty_opt(expr) {
|
||||
return ty.is_never();
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
if self.res.is_break() {
|
||||
return;
|
||||
}
|
||||
|
||||
// We can't just call is_never on expr and be done, because the type system
|
||||
// sometimes coerces the ! type to something different before we can get
|
||||
// our hands on it. So instead, we do a manual search. We do fall back to
|
||||
// is_never in some places when there is no better alternative.
|
||||
self.res = match e.kind {
|
||||
ExprKind::Continue(_) | ExprKind::Break(_, _) | ExprKind::Ret(_) => ControlFlow::Break(()),
|
||||
ExprKind::Call(call, _) => {
|
||||
if is_never(self.cx, e) || is_never(self.cx, call) {
|
||||
ControlFlow::Break(())
|
||||
} else {
|
||||
ControlFlow::Continue(Descend::Yes)
|
||||
}
|
||||
},
|
||||
ExprKind::MethodCall(..) => {
|
||||
if is_never(self.cx, e) {
|
||||
ControlFlow::Break(())
|
||||
} else {
|
||||
ControlFlow::Continue(Descend::Yes)
|
||||
}
|
||||
},
|
||||
ExprKind::If(if_expr, if_then, if_else) => {
|
||||
let else_diverges = if_else.map_or(false, |ex| expr_diverges(self.cx, ex));
|
||||
let diverges =
|
||||
expr_diverges(self.cx, if_expr) || (else_diverges && expr_diverges(self.cx, if_then));
|
||||
if diverges {
|
||||
ControlFlow::Break(())
|
||||
} else {
|
||||
ControlFlow::Continue(Descend::No)
|
||||
}
|
||||
},
|
||||
ExprKind::Match(match_expr, match_arms, _) => {
|
||||
let diverges = expr_diverges(self.cx, match_expr)
|
||||
|| match_arms.iter().all(|arm| {
|
||||
let guard_diverges = arm.guard.as_ref().map_or(false, |g| expr_diverges(self.cx, g.body()));
|
||||
guard_diverges || expr_diverges(self.cx, arm.body)
|
||||
});
|
||||
if diverges {
|
||||
ControlFlow::Break(())
|
||||
} else {
|
||||
ControlFlow::Continue(Descend::No)
|
||||
}
|
||||
},
|
||||
|
||||
// Don't continue into loops or labeled blocks, as they are breakable,
|
||||
// and we'd have to start checking labels.
|
||||
ExprKind::Block(_, Some(_)) | ExprKind::Loop(..) => ControlFlow::Continue(Descend::No),
|
||||
|
||||
// Default: descend
|
||||
_ => ControlFlow::Continue(Descend::Yes),
|
||||
};
|
||||
if let ControlFlow::Continue(Descend::Yes) = self.res {
|
||||
walk_expr(self, e);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_local(&mut self, local: &'tcx Local<'_>) {
|
||||
// Don't visit the else block of a let/else statement as it will not make
|
||||
// the statement divergent even though the else block is divergent.
|
||||
if let Some(init) = local.init {
|
||||
self.visit_expr(init);
|
||||
}
|
||||
}
|
||||
|
||||
// Avoid unnecessary `walk_*` calls.
|
||||
fn visit_ty(&mut self, _: &'tcx Ty<'tcx>) {}
|
||||
fn visit_pat(&mut self, _: &'tcx Pat<'tcx>) {}
|
||||
fn visit_qpath(&mut self, _: &'tcx QPath<'tcx>, _: HirId, _: Span) {}
|
||||
// Avoid monomorphising all `visit_*` functions.
|
||||
fn visit_nested_item(&mut self, _: ItemId) {}
|
||||
}
|
||||
|
||||
let mut v = V {
|
||||
cx,
|
||||
res: ControlFlow::Continue(Descend::Yes),
|
||||
};
|
||||
expr.visit(&mut v);
|
||||
v.res.is_break()
|
||||
}
|
||||
|
||||
fn pat_allowed_for_else(cx: &LateContext<'_>, pat: &'_ Pat<'_>, check_types: bool) -> bool {
|
||||
// Check whether the pattern contains any bindings, as the
|
||||
// binding might potentially be used in the body.
|
||||
|
@ -71,6 +71,7 @@
|
||||
both, count_eq, eq_expr_value, hash_expr, hash_stmt, is_bool, over, HirEqInterExpr, SpanlessEq, SpanlessHash,
|
||||
};
|
||||
|
||||
use core::mem;
|
||||
use core::ops::ControlFlow;
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::hash::BuildHasherDefault;
|
||||
@ -2973,3 +2974,248 @@ pub fn pat_is_wild<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx PatKind<'_>, body: i
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum RequiresSemi {
|
||||
Yes,
|
||||
No,
|
||||
}
|
||||
impl RequiresSemi {
|
||||
pub fn requires_semi(self) -> bool {
|
||||
matches!(self, Self::Yes)
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if the expression return `!`, a type coerced from `!`, or could return `!` if the final
|
||||
/// expression were turned into a statement.
|
||||
#[expect(clippy::too_many_lines)]
|
||||
pub fn is_never_expr<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option<RequiresSemi> {
|
||||
struct BreakTarget {
|
||||
id: HirId,
|
||||
unused: bool,
|
||||
}
|
||||
|
||||
struct V<'cx, 'tcx> {
|
||||
cx: &'cx LateContext<'tcx>,
|
||||
break_targets: Vec<BreakTarget>,
|
||||
break_targets_for_result_ty: u32,
|
||||
in_final_expr: bool,
|
||||
requires_semi: bool,
|
||||
is_never: bool,
|
||||
}
|
||||
|
||||
impl<'tcx> V<'_, 'tcx> {
|
||||
fn push_break_target(&mut self, id: HirId) {
|
||||
self.break_targets.push(BreakTarget { id, unused: true });
|
||||
self.break_targets_for_result_ty += u32::from(self.in_final_expr);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
|
||||
fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
|
||||
// Note: Part of the complexity here comes from the fact that
|
||||
// coercions are applied to the innermost expression.
|
||||
// e.g. In `let x: u32 = { break () };` the never-to-any coercion
|
||||
// is applied to the break expression. This means we can't just
|
||||
// check the block's type as it will be `u32` despite the fact
|
||||
// that the block always diverges.
|
||||
|
||||
// The rest of the complexity comes from checking blocks which
|
||||
// syntactically return a value, but will always diverge before
|
||||
// reaching that point.
|
||||
// e.g. In `let x = { foo(panic!()) };` the block's type will be the
|
||||
// return type of `foo` even though it will never actually run. This
|
||||
// can be trivially fixed by adding a semicolon after the call, but
|
||||
// we must first detect that a semicolon is needed to make that
|
||||
// suggestion.
|
||||
|
||||
if self.is_never && self.break_targets.is_empty() {
|
||||
if self.in_final_expr && !self.requires_semi {
|
||||
// This expression won't ever run, but we still need to check
|
||||
// if it can affect the type of the final expression.
|
||||
match e.kind {
|
||||
ExprKind::DropTemps(e) => self.visit_expr(e),
|
||||
ExprKind::If(_, then, Some(else_)) => {
|
||||
self.visit_expr(then);
|
||||
self.visit_expr(else_);
|
||||
},
|
||||
ExprKind::Match(_, arms, _) => {
|
||||
for arm in arms {
|
||||
self.visit_expr(arm.body);
|
||||
}
|
||||
},
|
||||
ExprKind::Loop(b, ..) => {
|
||||
self.push_break_target(e.hir_id);
|
||||
self.in_final_expr = false;
|
||||
self.visit_block(b);
|
||||
self.break_targets.pop();
|
||||
},
|
||||
ExprKind::Block(b, _) => {
|
||||
if b.targeted_by_break {
|
||||
self.push_break_target(b.hir_id);
|
||||
self.visit_block(b);
|
||||
self.break_targets.pop();
|
||||
} else {
|
||||
self.visit_block(b);
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
self.requires_semi = !self.cx.typeck_results().expr_ty(e).is_never();
|
||||
},
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
match e.kind {
|
||||
ExprKind::DropTemps(e) => self.visit_expr(e),
|
||||
ExprKind::Ret(None) | ExprKind::Continue(_) => self.is_never = true,
|
||||
ExprKind::Ret(Some(e)) | ExprKind::Become(e) => {
|
||||
self.in_final_expr = false;
|
||||
self.visit_expr(e);
|
||||
self.is_never = true;
|
||||
},
|
||||
ExprKind::Break(dest, e) => {
|
||||
if let Some(e) = e {
|
||||
self.in_final_expr = false;
|
||||
self.visit_expr(e);
|
||||
}
|
||||
if let Ok(id) = dest.target_id
|
||||
&& let Some((i, target)) = self
|
||||
.break_targets
|
||||
.iter_mut()
|
||||
.enumerate()
|
||||
.find(|(_, target)| target.id == id)
|
||||
{
|
||||
target.unused &= self.is_never;
|
||||
if i < self.break_targets_for_result_ty as usize {
|
||||
self.requires_semi = true;
|
||||
}
|
||||
}
|
||||
self.is_never = true;
|
||||
},
|
||||
ExprKind::If(cond, then, else_) => {
|
||||
let in_final_expr = mem::replace(&mut self.in_final_expr, false);
|
||||
self.visit_expr(cond);
|
||||
self.in_final_expr = in_final_expr;
|
||||
|
||||
if self.is_never {
|
||||
self.visit_expr(then);
|
||||
if let Some(else_) = else_ {
|
||||
self.visit_expr(else_);
|
||||
}
|
||||
} else {
|
||||
self.visit_expr(then);
|
||||
let is_never = mem::replace(&mut self.is_never, false);
|
||||
if let Some(else_) = else_ {
|
||||
self.visit_expr(else_);
|
||||
self.is_never &= is_never;
|
||||
}
|
||||
}
|
||||
},
|
||||
ExprKind::Match(scrutinee, arms, _) => {
|
||||
let in_final_expr = mem::replace(&mut self.in_final_expr, false);
|
||||
self.visit_expr(scrutinee);
|
||||
self.in_final_expr = in_final_expr;
|
||||
|
||||
if self.is_never {
|
||||
for arm in arms {
|
||||
self.visit_arm(arm);
|
||||
}
|
||||
} else {
|
||||
let mut is_never = true;
|
||||
for arm in arms {
|
||||
self.is_never = false;
|
||||
if let Some(guard) = arm.guard {
|
||||
let in_final_expr = mem::replace(&mut self.in_final_expr, false);
|
||||
self.visit_expr(guard.body());
|
||||
self.in_final_expr = in_final_expr;
|
||||
// The compiler doesn't consider diverging guards as causing the arm to diverge.
|
||||
self.is_never = false;
|
||||
}
|
||||
self.visit_expr(arm.body);
|
||||
is_never &= self.is_never;
|
||||
}
|
||||
self.is_never = is_never;
|
||||
}
|
||||
},
|
||||
ExprKind::Loop(b, _, _, _) => {
|
||||
self.push_break_target(e.hir_id);
|
||||
self.in_final_expr = false;
|
||||
self.visit_block(b);
|
||||
self.is_never = self.break_targets.pop().unwrap().unused;
|
||||
},
|
||||
ExprKind::Block(b, _) => {
|
||||
if b.targeted_by_break {
|
||||
self.push_break_target(b.hir_id);
|
||||
self.visit_block(b);
|
||||
self.is_never &= self.break_targets.pop().unwrap().unused;
|
||||
} else {
|
||||
self.visit_block(b);
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
self.in_final_expr = false;
|
||||
walk_expr(self, e);
|
||||
self.is_never |= self.cx.typeck_results().expr_ty(e).is_never();
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_block(&mut self, b: &'tcx Block<'_>) {
|
||||
let in_final_expr = mem::replace(&mut self.in_final_expr, false);
|
||||
for s in b.stmts {
|
||||
self.visit_stmt(s);
|
||||
}
|
||||
self.in_final_expr = in_final_expr;
|
||||
if let Some(e) = b.expr {
|
||||
self.visit_expr(e);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_local(&mut self, l: &'tcx Local<'_>) {
|
||||
if let Some(e) = l.init {
|
||||
self.visit_expr(e);
|
||||
}
|
||||
if let Some(else_) = l.els {
|
||||
let is_never = self.is_never;
|
||||
self.visit_block(else_);
|
||||
self.is_never = is_never;
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_arm(&mut self, arm: &Arm<'tcx>) {
|
||||
if let Some(guard) = arm.guard {
|
||||
let in_final_expr = mem::replace(&mut self.in_final_expr, false);
|
||||
self.visit_expr(guard.body());
|
||||
self.in_final_expr = in_final_expr;
|
||||
}
|
||||
self.visit_expr(arm.body);
|
||||
}
|
||||
}
|
||||
|
||||
if cx.typeck_results().expr_ty(e).is_never() {
|
||||
Some(RequiresSemi::No)
|
||||
} else if let ExprKind::Block(b, _) = e.kind
|
||||
&& !b.targeted_by_break
|
||||
&& b.expr.is_none()
|
||||
{
|
||||
// If a block diverges without a final expression then it's type is `!`.
|
||||
None
|
||||
} else {
|
||||
let mut v = V {
|
||||
cx,
|
||||
break_targets: Vec::new(),
|
||||
break_targets_for_result_ty: 0,
|
||||
in_final_expr: true,
|
||||
requires_semi: false,
|
||||
is_never: false,
|
||||
};
|
||||
v.visit_expr(e);
|
||||
v.is_never
|
||||
.then_some(if v.requires_semi && matches!(e.kind, ExprKind::Block(..)) {
|
||||
RequiresSemi::Yes
|
||||
} else {
|
||||
RequiresSemi::No
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,9 @@
|
||||
clippy::let_unit_value,
|
||||
clippy::match_single_binding,
|
||||
clippy::never_loop,
|
||||
clippy::needless_if
|
||||
clippy::needless_if,
|
||||
clippy::diverging_sub_expression,
|
||||
clippy::single_match
|
||||
)]
|
||||
#![warn(clippy::manual_let_else)]
|
||||
//@no-rustfix
|
||||
@ -24,7 +26,7 @@ fn main() {}
|
||||
fn fire() {
|
||||
let v = if let Some(v_some) = g() { v_some } else { return };
|
||||
//~^ ERROR: this could be rewritten as `let...else`
|
||||
//~| NOTE: `-D clippy::manual-let-else` implied by `-D warnings`
|
||||
|
||||
let v = if let Some(v_some) = g() {
|
||||
//~^ ERROR: this could be rewritten as `let...else`
|
||||
v_some
|
||||
@ -79,22 +81,76 @@ fn fire() {
|
||||
panic!();
|
||||
};
|
||||
|
||||
// A match diverges if all branches diverge:
|
||||
// Note: the corresponding let-else requires a ; at the end of the match
|
||||
// as otherwise the type checker does not turn it into a ! type.
|
||||
// The final expression will need to be turned into a statement.
|
||||
let v = if let Some(v_some) = g() {
|
||||
//~^ ERROR: this could be rewritten as `let...else`
|
||||
v_some
|
||||
} else {
|
||||
match () {
|
||||
_ if panic!() => {},
|
||||
_ => panic!(),
|
||||
panic!();
|
||||
()
|
||||
};
|
||||
|
||||
// Even if the result is buried multiple expressions deep.
|
||||
let v = if let Some(v_some) = g() {
|
||||
//~^ ERROR: this could be rewritten as `let...else`
|
||||
v_some
|
||||
} else {
|
||||
panic!();
|
||||
if true {
|
||||
match 0 {
|
||||
0 => (),
|
||||
_ => (),
|
||||
}
|
||||
} else {
|
||||
panic!()
|
||||
}
|
||||
};
|
||||
|
||||
// Or if a break gives the value.
|
||||
let v = if let Some(v_some) = g() {
|
||||
//~^ ERROR: this could be rewritten as `let...else`
|
||||
v_some
|
||||
} else {
|
||||
loop {
|
||||
panic!();
|
||||
break ();
|
||||
}
|
||||
};
|
||||
|
||||
// Even if the break is in a weird position.
|
||||
let v = if let Some(v_some) = g() {
|
||||
//~^ ERROR: this could be rewritten as `let...else`
|
||||
v_some
|
||||
} else {
|
||||
'a: loop {
|
||||
panic!();
|
||||
loop {
|
||||
match 0 {
|
||||
0 if (return break 'a ()) => {},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// A match diverges if all branches diverge:
|
||||
let v = if let Some(v_some) = g() {
|
||||
//~^ ERROR: this could be rewritten as `let...else`
|
||||
v_some
|
||||
} else {
|
||||
match 0 {
|
||||
0 if true => panic!(),
|
||||
_ => panic!(),
|
||||
};
|
||||
};
|
||||
|
||||
// An if's expression can cause divergence:
|
||||
let v = if let Some(v_some) = g() { v_some } else { if panic!() {} };
|
||||
//~^ ERROR: this could be rewritten as `let...else`
|
||||
let v = if let Some(v_some) = g() {
|
||||
//~^ ERROR: this could be rewritten as `let...else`
|
||||
v_some
|
||||
} else {
|
||||
if panic!() {};
|
||||
};
|
||||
|
||||
// An expression of a match can cause divergence:
|
||||
let v = if let Some(v_some) = g() {
|
||||
@ -103,7 +159,7 @@ fn fire() {
|
||||
} else {
|
||||
match panic!() {
|
||||
_ => {},
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// Top level else if
|
||||
@ -342,6 +398,43 @@ macro_rules! macro_call {
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
// A break that skips the divergent statement will cause the expression to be non-divergent.
|
||||
let _x = if let Some(x) = Some(0) {
|
||||
x
|
||||
} else {
|
||||
'foo: loop {
|
||||
break 'foo 0;
|
||||
panic!();
|
||||
}
|
||||
};
|
||||
|
||||
// Even in inner loops.
|
||||
let _x = if let Some(x) = Some(0) {
|
||||
x
|
||||
} else {
|
||||
'foo: {
|
||||
loop {
|
||||
break 'foo 0;
|
||||
}
|
||||
panic!();
|
||||
}
|
||||
};
|
||||
|
||||
// But a break that can't ever be reached still affects divergence checking.
|
||||
let _x = if let Some(x) = g() {
|
||||
x
|
||||
} else {
|
||||
'foo: {
|
||||
'bar: loop {
|
||||
loop {
|
||||
break 'bar ();
|
||||
}
|
||||
break 'foo ();
|
||||
}
|
||||
panic!();
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
struct S<T> {
|
||||
|
@ -1,5 +1,5 @@
|
||||
error: this could be rewritten as `let...else`
|
||||
--> $DIR/manual_let_else.rs:25:5
|
||||
--> $DIR/manual_let_else.rs:27:5
|
||||
|
|
||||
LL | let v = if let Some(v_some) = g() { v_some } else { return };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { return };`
|
||||
@ -8,7 +8,7 @@ LL | let v = if let Some(v_some) = g() { v_some } else { return };
|
||||
= help: to override `-D warnings` add `#[allow(clippy::manual_let_else)]`
|
||||
|
||||
error: this could be rewritten as `let...else`
|
||||
--> $DIR/manual_let_else.rs:28:5
|
||||
--> $DIR/manual_let_else.rs:30:5
|
||||
|
|
||||
LL | / let v = if let Some(v_some) = g() {
|
||||
LL | |
|
||||
@ -26,7 +26,7 @@ LL + };
|
||||
|
|
||||
|
||||
error: this could be rewritten as `let...else`
|
||||
--> $DIR/manual_let_else.rs:35:5
|
||||
--> $DIR/manual_let_else.rs:37:5
|
||||
|
|
||||
LL | / let v = if let Some(v) = g() {
|
||||
LL | |
|
||||
@ -47,25 +47,25 @@ LL + };
|
||||
|
|
||||
|
||||
error: this could be rewritten as `let...else`
|
||||
--> $DIR/manual_let_else.rs:47:9
|
||||
--> $DIR/manual_let_else.rs:49:9
|
||||
|
|
||||
LL | let v = if let Some(v_some) = g() { v_some } else { continue };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { continue };`
|
||||
|
||||
error: this could be rewritten as `let...else`
|
||||
--> $DIR/manual_let_else.rs:49:9
|
||||
--> $DIR/manual_let_else.rs:51:9
|
||||
|
|
||||
LL | let v = if let Some(v_some) = g() { v_some } else { break };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { break };`
|
||||
|
||||
error: this could be rewritten as `let...else`
|
||||
--> $DIR/manual_let_else.rs:54:5
|
||||
--> $DIR/manual_let_else.rs:56:5
|
||||
|
|
||||
LL | let v = if let Some(v_some) = g() { v_some } else { panic!() };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { panic!() };`
|
||||
|
||||
error: this could be rewritten as `let...else`
|
||||
--> $DIR/manual_let_else.rs:58:5
|
||||
--> $DIR/manual_let_else.rs:60:5
|
||||
|
|
||||
LL | / let v = if let Some(v_some) = g() {
|
||||
LL | |
|
||||
@ -83,7 +83,7 @@ LL + };
|
||||
|
|
||||
|
||||
error: this could be rewritten as `let...else`
|
||||
--> $DIR/manual_let_else.rs:66:5
|
||||
--> $DIR/manual_let_else.rs:68:5
|
||||
|
|
||||
LL | / let v = if let Some(v_some) = g() {
|
||||
LL | |
|
||||
@ -101,7 +101,7 @@ LL + };
|
||||
|
|
||||
|
||||
error: this could be rewritten as `let...else`
|
||||
--> $DIR/manual_let_else.rs:74:5
|
||||
--> $DIR/manual_let_else.rs:76:5
|
||||
|
|
||||
LL | / let v = if let Some(v_some) = g() {
|
||||
LL | |
|
||||
@ -127,29 +127,21 @@ LL | / let v = if let Some(v_some) = g() {
|
||||
LL | |
|
||||
LL | | v_some
|
||||
LL | | } else {
|
||||
... |
|
||||
LL | | }
|
||||
LL | | panic!();
|
||||
LL | | ()
|
||||
LL | | };
|
||||
| |______^
|
||||
|
|
||||
help: consider writing
|
||||
|
|
||||
LL ~ let Some(v) = g() else {
|
||||
LL + match () {
|
||||
LL + _ if panic!() => {},
|
||||
LL + _ => panic!(),
|
||||
LL + }
|
||||
LL + panic!();
|
||||
LL + ()
|
||||
LL + };
|
||||
|
|
||||
|
||||
error: this could be rewritten as `let...else`
|
||||
--> $DIR/manual_let_else.rs:96:5
|
||||
|
|
||||
LL | let v = if let Some(v_some) = g() { v_some } else { if panic!() {} };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { if panic!() {} };`
|
||||
|
||||
error: this could be rewritten as `let...else`
|
||||
--> $DIR/manual_let_else.rs:100:5
|
||||
--> $DIR/manual_let_else.rs:94:5
|
||||
|
|
||||
LL | / let v = if let Some(v_some) = g() {
|
||||
LL | |
|
||||
@ -163,8 +155,14 @@ LL | | };
|
||||
help: consider writing
|
||||
|
|
||||
LL ~ let Some(v) = g() else {
|
||||
LL + match panic!() {
|
||||
LL + _ => {},
|
||||
LL + panic!();
|
||||
LL + if true {
|
||||
LL + match 0 {
|
||||
LL + 0 => (),
|
||||
LL + _ => (),
|
||||
LL + }
|
||||
LL + } else {
|
||||
LL + panic!()
|
||||
LL + }
|
||||
LL + };
|
||||
|
|
||||
@ -175,6 +173,116 @@ error: this could be rewritten as `let...else`
|
||||
LL | / let v = if let Some(v_some) = g() {
|
||||
LL | |
|
||||
LL | | v_some
|
||||
LL | | } else {
|
||||
... |
|
||||
LL | | }
|
||||
LL | | };
|
||||
| |______^
|
||||
|
|
||||
help: consider writing
|
||||
|
|
||||
LL ~ let Some(v) = g() else {
|
||||
LL + loop {
|
||||
LL + panic!();
|
||||
LL + break ();
|
||||
LL + }
|
||||
LL + };
|
||||
|
|
||||
|
||||
error: this could be rewritten as `let...else`
|
||||
--> $DIR/manual_let_else.rs:121:5
|
||||
|
|
||||
LL | / let v = if let Some(v_some) = g() {
|
||||
LL | |
|
||||
LL | | v_some
|
||||
LL | | } else {
|
||||
... |
|
||||
LL | | }
|
||||
LL | | };
|
||||
| |______^
|
||||
|
|
||||
help: consider writing
|
||||
|
|
||||
LL ~ let Some(v) = g() else {
|
||||
LL + 'a: loop {
|
||||
LL + panic!();
|
||||
LL + loop {
|
||||
LL + match 0 {
|
||||
LL + 0 if (return break 'a ()) => {},
|
||||
LL + _ => {},
|
||||
LL + }
|
||||
LL + }
|
||||
LL + }
|
||||
LL + };
|
||||
|
|
||||
|
||||
error: this could be rewritten as `let...else`
|
||||
--> $DIR/manual_let_else.rs:137:5
|
||||
|
|
||||
LL | / let v = if let Some(v_some) = g() {
|
||||
LL | |
|
||||
LL | | v_some
|
||||
LL | | } else {
|
||||
... |
|
||||
LL | | };
|
||||
LL | | };
|
||||
| |______^
|
||||
|
|
||||
help: consider writing
|
||||
|
|
||||
LL ~ let Some(v) = g() else {
|
||||
LL + match 0 {
|
||||
LL + 0 if true => panic!(),
|
||||
LL + _ => panic!(),
|
||||
LL + };
|
||||
LL + };
|
||||
|
|
||||
|
||||
error: this could be rewritten as `let...else`
|
||||
--> $DIR/manual_let_else.rs:148:5
|
||||
|
|
||||
LL | / let v = if let Some(v_some) = g() {
|
||||
LL | |
|
||||
LL | | v_some
|
||||
LL | | } else {
|
||||
LL | | if panic!() {};
|
||||
LL | | };
|
||||
| |______^
|
||||
|
|
||||
help: consider writing
|
||||
|
|
||||
LL ~ let Some(v) = g() else {
|
||||
LL + if panic!() {};
|
||||
LL + };
|
||||
|
|
||||
|
||||
error: this could be rewritten as `let...else`
|
||||
--> $DIR/manual_let_else.rs:156:5
|
||||
|
|
||||
LL | / let v = if let Some(v_some) = g() {
|
||||
LL | |
|
||||
LL | | v_some
|
||||
LL | | } else {
|
||||
... |
|
||||
LL | | };
|
||||
LL | | };
|
||||
| |______^
|
||||
|
|
||||
help: consider writing
|
||||
|
|
||||
LL ~ let Some(v) = g() else {
|
||||
LL + match panic!() {
|
||||
LL + _ => {},
|
||||
LL + };
|
||||
LL + };
|
||||
|
|
||||
|
||||
error: this could be rewritten as `let...else`
|
||||
--> $DIR/manual_let_else.rs:166:5
|
||||
|
|
||||
LL | / let v = if let Some(v_some) = g() {
|
||||
LL | |
|
||||
LL | | v_some
|
||||
LL | | } else if true {
|
||||
... |
|
||||
LL | | panic!("diverge");
|
||||
@ -191,7 +299,7 @@ LL + } };
|
||||
|
|
||||
|
||||
error: this could be rewritten as `let...else`
|
||||
--> $DIR/manual_let_else.rs:120:5
|
||||
--> $DIR/manual_let_else.rs:176:5
|
||||
|
|
||||
LL | / let v = if let Some(v_some) = g() {
|
||||
LL | |
|
||||
@ -220,7 +328,7 @@ LL + };
|
||||
|
|
||||
|
||||
error: this could be rewritten as `let...else`
|
||||
--> $DIR/manual_let_else.rs:138:5
|
||||
--> $DIR/manual_let_else.rs:194:5
|
||||
|
|
||||
LL | / let (v, w) = if let Some(v_some) = g().map(|v| (v, 42)) {
|
||||
LL | |
|
||||
@ -238,7 +346,7 @@ LL + };
|
||||
|
|
||||
|
||||
error: this could be rewritten as `let...else`
|
||||
--> $DIR/manual_let_else.rs:146:5
|
||||
--> $DIR/manual_let_else.rs:202:5
|
||||
|
|
||||
LL | / let (w, S { v }) = if let (Some(v_some), w_some) = (g().map(|_| S { v: 0 }), 0) {
|
||||
LL | |
|
||||
@ -256,7 +364,7 @@ LL + };
|
||||
|
|
||||
|
||||
error: this could be rewritten as `let...else`
|
||||
--> $DIR/manual_let_else.rs:156:13
|
||||
--> $DIR/manual_let_else.rs:212:13
|
||||
|
|
||||
LL | let $n = if let Some(v) = $e { v } else { return };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some($n) = g() else { return };`
|
||||
@ -267,19 +375,19 @@ LL | create_binding_if_some!(w, g());
|
||||
= note: this error originates in the macro `create_binding_if_some` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: this could be rewritten as `let...else`
|
||||
--> $DIR/manual_let_else.rs:165:5
|
||||
--> $DIR/manual_let_else.rs:221:5
|
||||
|
|
||||
LL | let v = if let Variant::A(a, 0) = e() { a } else { return };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Variant::A(v, 0) = e() else { return };`
|
||||
|
||||
error: this could be rewritten as `let...else`
|
||||
--> $DIR/manual_let_else.rs:169:5
|
||||
--> $DIR/manual_let_else.rs:225:5
|
||||
|
|
||||
LL | let mut v = if let Variant::B(b) = e() { b } else { return };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Variant::B(mut v) = e() else { return };`
|
||||
|
||||
error: this could be rewritten as `let...else`
|
||||
--> $DIR/manual_let_else.rs:174:5
|
||||
--> $DIR/manual_let_else.rs:230:5
|
||||
|
|
||||
LL | / let v = if let Ok(Some(Variant::B(b))) | Err(Some(Variant::A(b, _))) = nested {
|
||||
LL | |
|
||||
@ -297,19 +405,19 @@ LL + };
|
||||
|
|
||||
|
||||
error: this could be rewritten as `let...else`
|
||||
--> $DIR/manual_let_else.rs:181:5
|
||||
--> $DIR/manual_let_else.rs:237:5
|
||||
|
|
||||
LL | let v = if let Variant::A(.., a) = e() { a } else { return };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Variant::A(.., v) = e() else { return };`
|
||||
|
||||
error: this could be rewritten as `let...else`
|
||||
--> $DIR/manual_let_else.rs:185:5
|
||||
--> $DIR/manual_let_else.rs:241:5
|
||||
|
|
||||
LL | let w = if let (Some(v), ()) = (g(), ()) { v } else { return };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let (Some(w), ()) = (g(), ()) else { return };`
|
||||
|
||||
error: this could be rewritten as `let...else`
|
||||
--> $DIR/manual_let_else.rs:189:5
|
||||
--> $DIR/manual_let_else.rs:245:5
|
||||
|
|
||||
LL | / let w = if let Some(S { v: x }) = Some(S { v: 0 }) {
|
||||
LL | |
|
||||
@ -327,7 +435,7 @@ LL + };
|
||||
|
|
||||
|
||||
error: this could be rewritten as `let...else`
|
||||
--> $DIR/manual_let_else.rs:197:5
|
||||
--> $DIR/manual_let_else.rs:253:5
|
||||
|
|
||||
LL | / let v = if let Some(S { v: x }) = Some(S { v: 0 }) {
|
||||
LL | |
|
||||
@ -345,7 +453,7 @@ LL + };
|
||||
|
|
||||
|
||||
error: this could be rewritten as `let...else`
|
||||
--> $DIR/manual_let_else.rs:205:5
|
||||
--> $DIR/manual_let_else.rs:261:5
|
||||
|
|
||||
LL | / let (x, S { v }, w) = if let Some(U { v, w, x }) = None::<U<S<()>>> {
|
||||
LL | |
|
||||
@ -363,7 +471,7 @@ LL + };
|
||||
|
|
||||
|
||||
error: this could be rewritten as `let...else`
|
||||
--> $DIR/manual_let_else.rs:322:5
|
||||
--> $DIR/manual_let_else.rs:378:5
|
||||
|
|
||||
LL | / let _ = match ff {
|
||||
LL | |
|
||||
@ -372,5 +480,5 @@ LL | | _ => macro_call!(),
|
||||
LL | | };
|
||||
| |______^ help: consider writing: `let Some(_) = ff else { macro_call!() };`
|
||||
|
||||
error: aborting due to 26 previous errors
|
||||
error: aborting due to 30 previous errors
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user