Change divergence checking to match the compiler's type system based definition of divergence.

This commit is contained in:
Jason Newcomb 2023-11-09 17:29:01 -05:00
parent 16d58a2982
commit a44bb07900
4 changed files with 481 additions and 133 deletions

View file

@ -64,7 +64,7 @@ impl<'tcx> QuestionMark {
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
&& is_never_expr(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())
{
@ -88,10 +88,9 @@ impl<'tcx> QuestionMark {
return;
}
let check_types = self.matches_behaviour == MatchLintBehaviour::WellKnownTypes;
let diverging_arm_opt = arms
.iter()
.enumerate()
.find(|(_, arm)| is_never_expr(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;
};

View file

@ -71,6 +71,7 @@ pub use self::hir_utils::{
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;
@ -88,7 +89,7 @@ use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
use rustc_hir::{
self as hir, def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Closure, Destination, Expr,
ExprField, ExprKind, FnDecl, FnRetTy, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, ImplItemRef, Item, ItemId,
ExprField, ExprKind, FnDecl, FnRetTy, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, ImplItemRef, Item,
ItemKind, LangItem, Local, MatchSource, Mutability, Node, OwnerId, Param, Pat, PatKind, Path, PathSegment, PrimTy,
QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitItemRef, TraitRef, TyKind, UnOp,
};
@ -117,7 +118,7 @@ use crate::ty::{
adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type,
ty_is_fn_once_param,
};
use crate::visitors::{for_each_expr, Descend};
use crate::visitors::for_each_expr;
use rustc_middle::hir::nested_filter;
@ -2975,100 +2976,247 @@ pub fn pat_is_wild<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx PatKind<'_>, body: i
}
}
/// Check if the expression either returns, or could be coerced into returning, `!`.
pub fn is_never_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
#[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>,
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
break_targets: Vec<BreakTarget>,
break_targets_for_result_ty: u32,
in_final_expr: bool,
requires_semi: bool,
is_never: bool,
}
if self.res.is_break() {
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;
// 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(())
if self.is_never {
self.visit_expr(then);
if let Some(else_) = else_ {
self.visit_expr(else_);
}
} 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| is_never_expr(self.cx, ex));
let diverges =
is_never_expr(self.cx, if_expr) || (else_diverges && is_never_expr(self.cx, if_then));
if diverges {
ControlFlow::Break(())
} else {
ControlFlow::Continue(Descend::No)
}
},
ExprKind::Match(match_expr, match_arms, _) => {
let diverges = is_never_expr(self.cx, match_expr)
|| match_arms.iter().all(|arm| {
let guard_diverges = arm.guard.as_ref().map_or(false, |g| is_never_expr(self.cx, g.body()));
guard_diverges || is_never_expr(self.cx, arm.body)
});
if diverges {
ControlFlow::Break(())
} else {
ControlFlow::Continue(Descend::No)
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;
// 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 {
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_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);
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);
}
}
// Avoid unnecessary `walk_*` calls.
fn visit_ty(&mut self, _: &'tcx hir::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) {}
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,
res: ControlFlow::Continue(Descend::Yes),
break_targets: Vec::new(),
break_targets_for_result_ty: 0,
in_final_expr: true,
requires_semi: false,
is_never: false,
};
expr.visit(&mut v);
v.res.is_break()
v.visit_expr(e);
v.is_never
.then_some(if v.requires_semi && matches!(e.kind, ExprKind::Block(..)) {
RequiresSemi::Yes
} else {
RequiresSemi::No
})
}
}

View file

@ -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!()
}
};
// An if's expression can cause divergence:
let v = if let Some(v_some) = g() { v_some } else { if 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() {
//~^ 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 @@ fn not_fire() {
} 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> {

View file

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