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) => { 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) if let Some(ident_map) = expr_simple_identity_map(local.pat, let_pat, if_then)
&& let Some(if_else) = if_else && 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) && 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()) && (qm_allowed || pat_and_expr_can_be_question_mark(cx, let_pat, if_else).is_none())
{ {
@ -88,10 +88,9 @@ impl<'tcx> QuestionMark {
return; return;
} }
let check_types = self.matches_behaviour == MatchLintBehaviour::WellKnownTypes; let check_types = self.matches_behaviour == MatchLintBehaviour::WellKnownTypes;
let diverging_arm_opt = arms let diverging_arm_opt = arms.iter().enumerate().find(|(_, arm)| {
.iter() is_never_expr(cx, arm.body).is_some() && pat_allowed_for_else(cx, arm.pat, check_types)
.enumerate() });
.find(|(_, arm)| is_never_expr(cx, arm.body) && pat_allowed_for_else(cx, arm.pat, check_types));
let Some((idx, diverging_arm)) = diverging_arm_opt else { let Some((idx, diverging_arm)) = diverging_arm_opt else {
return; 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, both, count_eq, eq_expr_value, hash_expr, hash_stmt, is_bool, over, HirEqInterExpr, SpanlessEq, SpanlessHash,
}; };
use core::mem;
use core::ops::ControlFlow; use core::ops::ControlFlow;
use std::collections::hash_map::Entry; use std::collections::hash_map::Entry;
use std::hash::BuildHasherDefault; 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::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
use rustc_hir::{ use rustc_hir::{
self as hir, def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Closure, Destination, Expr, 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, ItemKind, LangItem, Local, MatchSource, Mutability, Node, OwnerId, Param, Pat, PatKind, Path, PathSegment, PrimTy,
QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitItemRef, TraitRef, TyKind, UnOp, 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, adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type,
ty_is_fn_once_param, ty_is_fn_once_param,
}; };
use crate::visitors::{for_each_expr, Descend}; use crate::visitors::for_each_expr;
use rustc_middle::hir::nested_filter; 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, `!`. #[derive(Clone, Copy)]
pub fn is_never_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { 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> { struct V<'cx, 'tcx> {
cx: &'cx LateContext<'tcx>, cx: &'cx LateContext<'tcx>,
res: ControlFlow<(), Descend>, break_targets: Vec<BreakTarget>,
break_targets_for_result_ty: u32,
in_final_expr: bool,
requires_semi: bool,
is_never: bool,
} }
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() { 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; 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 if self.is_never {
// sometimes coerces the ! type to something different before we can get self.visit_expr(then);
// our hands on it. So instead, we do a manual search. We do fall back to if let Some(else_) = else_ {
// is_never in some places when there is no better alternative. self.visit_expr(else_);
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 { } else {
ControlFlow::Continue(Descend::Yes) self.visit_expr(then);
} let is_never = mem::replace(&mut self.is_never, false);
}, if let Some(else_) = else_ {
ExprKind::MethodCall(..) => { self.visit_expr(else_);
if is_never(self.cx, e) { self.is_never &= is_never;
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)
} }
}, },
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, if self.is_never {
// and we'd have to start checking labels. for arm in arms {
ExprKind::Block(_, Some(_)) | ExprKind::Loop(..) => ControlFlow::Continue(Descend::No), self.visit_arm(arm);
}
// Default: descend } else {
_ => ControlFlow::Continue(Descend::Yes), let mut is_never = true;
}; for arm in arms {
if let ControlFlow::Continue(Descend::Yes) = self.res { self.is_never = false;
walk_expr(self, e); 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<'_>) { fn visit_block(&mut self, b: &'tcx Block<'_>) {
// Don't visit the else block of a let/else statement as it will not make let in_final_expr = mem::replace(&mut self.in_final_expr, false);
// the statement divergent even though the else block is divergent. for s in b.stmts {
if let Some(init) = local.init { self.visit_stmt(s);
self.visit_expr(init); }
self.in_final_expr = in_final_expr;
if let Some(e) = b.expr {
self.visit_expr(e);
} }
} }
// Avoid unnecessary `walk_*` calls. fn visit_local(&mut self, l: &'tcx Local<'_>) {
fn visit_ty(&mut self, _: &'tcx hir::Ty<'tcx>) {} if let Some(e) = l.init {
fn visit_pat(&mut self, _: &'tcx Pat<'tcx>) {} self.visit_expr(e);
fn visit_qpath(&mut self, _: &'tcx QPath<'tcx>, _: HirId, _: Span) {} }
// Avoid monomorphising all `visit_*` functions. if let Some(else_) = l.els {
fn visit_nested_item(&mut self, _: ItemId) {} 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);
}
} }
let mut v = V { if cx.typeck_results().expr_ty(e).is_never() {
cx, Some(RequiresSemi::No)
res: ControlFlow::Continue(Descend::Yes), } else if let ExprKind::Block(b, _) = e.kind
}; && !b.targeted_by_break
expr.visit(&mut v); && b.expr.is_none()
v.res.is_break() {
// 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
})
}
} }

View file

@ -5,7 +5,9 @@
clippy::let_unit_value, clippy::let_unit_value,
clippy::match_single_binding, clippy::match_single_binding,
clippy::never_loop, clippy::never_loop,
clippy::needless_if clippy::needless_if,
clippy::diverging_sub_expression,
clippy::single_match
)] )]
#![warn(clippy::manual_let_else)] #![warn(clippy::manual_let_else)]
//@no-rustfix //@no-rustfix
@ -24,7 +26,7 @@ fn main() {}
fn fire() { fn fire() {
let v = if let Some(v_some) = g() { v_some } else { return }; let v = if let Some(v_some) = g() { v_some } else { return };
//~^ ERROR: this could be rewritten as `let...else` //~^ 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() { let v = if let Some(v_some) = g() {
//~^ ERROR: this could be rewritten as `let...else` //~^ ERROR: this could be rewritten as `let...else`
v_some v_some
@ -79,22 +81,76 @@ fn fire() {
panic!(); panic!();
}; };
// A match diverges if all branches diverge: // The final expression will need to be turned into a statement.
// 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.
let v = if let Some(v_some) = g() { let v = if let Some(v_some) = g() {
//~^ ERROR: this could be rewritten as `let...else` //~^ ERROR: this could be rewritten as `let...else`
v_some v_some
} else { } else {
match () { panic!();
_ if 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: // An if's expression can cause divergence:
let v = if let Some(v_some) = g() { v_some } else { if panic!() {} }; let v = if let Some(v_some) = g() {
//~^ ERROR: this could be rewritten as `let...else` //~^ ERROR: this could be rewritten as `let...else`
v_some
} else {
if panic!() {};
};
// An expression of a match can cause divergence: // An expression of a match can cause divergence:
let v = if let Some(v_some) = g() { let v = if let Some(v_some) = g() {
@ -103,7 +159,7 @@ fn fire() {
} else { } else {
match panic!() { match panic!() {
_ => {}, _ => {},
} };
}; };
// Top level else if // Top level else if
@ -342,6 +398,43 @@ fn not_fire() {
} else { } else {
return; 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> { struct S<T> {

View file

@ -1,5 +1,5 @@
error: this could be rewritten as `let...else` 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 }; LL | let v = if let Some(v_some) = g() { v_some } else { return };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() 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)]` = help: to override `-D warnings` add `#[allow(clippy::manual_let_else)]`
error: this could be rewritten as `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 | / let v = if let Some(v_some) = g() {
LL | | LL | |
@ -26,7 +26,7 @@ LL + };
| |
error: this could be rewritten as `let...else` 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 | / let v = if let Some(v) = g() {
LL | | LL | |
@ -47,25 +47,25 @@ LL + };
| |
error: this could be rewritten as `let...else` 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 }; LL | let v = if let Some(v_some) = g() { v_some } else { continue };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { continue };` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { continue };`
error: this could be rewritten as `let...else` 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 }; LL | let v = if let Some(v_some) = g() { v_some } else { break };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { break };` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { break };`
error: this could be rewritten as `let...else` 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!() }; LL | let v = if let Some(v_some) = g() { v_some } else { panic!() };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { panic!() };` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { panic!() };`
error: this could be rewritten as `let...else` 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 | / let v = if let Some(v_some) = g() {
LL | | LL | |
@ -83,7 +83,7 @@ LL + };
| |
error: this could be rewritten as `let...else` 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 | / let v = if let Some(v_some) = g() {
LL | | LL | |
@ -101,7 +101,7 @@ LL + };
| |
error: this could be rewritten as `let...else` 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 | / let v = if let Some(v_some) = g() {
LL | | LL | |
@ -127,29 +127,21 @@ LL | / let v = if let Some(v_some) = g() {
LL | | LL | |
LL | | v_some LL | | v_some
LL | | } else { LL | | } else {
... | LL | | panic!();
LL | | } LL | | ()
LL | | }; LL | | };
| |______^ | |______^
| |
help: consider writing help: consider writing
| |
LL ~ let Some(v) = g() else { LL ~ let Some(v) = g() else {
LL + match () { LL + panic!();
LL + _ if panic!() => {}, LL + ()
LL + _ => panic!(),
LL + }
LL + }; LL + };
| |
error: this could be rewritten as `let...else` error: this could be rewritten as `let...else`
--> $DIR/manual_let_else.rs:96:5 --> $DIR/manual_let_else.rs:94: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
| |
LL | / let v = if let Some(v_some) = g() { LL | / let v = if let Some(v_some) = g() {
LL | | LL | |
@ -163,8 +155,14 @@ LL | | };
help: consider writing help: consider writing
| |
LL ~ let Some(v) = g() else { LL ~ let Some(v) = g() else {
LL + match panic!() { LL + panic!();
LL + _ => {}, LL + if true {
LL + match 0 {
LL + 0 => (),
LL + _ => (),
LL + }
LL + } else {
LL + panic!()
LL + } LL + }
LL + }; LL + };
| |
@ -175,6 +173,116 @@ error: this could be rewritten as `let...else`
LL | / let v = if let Some(v_some) = g() { LL | / let v = if let Some(v_some) = g() {
LL | | LL | |
LL | | v_some 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 | | } else if true {
... | ... |
LL | | panic!("diverge"); LL | | panic!("diverge");
@ -191,7 +299,7 @@ LL + } };
| |
error: this could be rewritten as `let...else` 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 | / let v = if let Some(v_some) = g() {
LL | | LL | |
@ -220,7 +328,7 @@ LL + };
| |
error: this could be rewritten as `let...else` 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 | / let (v, w) = if let Some(v_some) = g().map(|v| (v, 42)) {
LL | | LL | |
@ -238,7 +346,7 @@ LL + };
| |
error: this could be rewritten as `let...else` 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 | / let (w, S { v }) = if let (Some(v_some), w_some) = (g().map(|_| S { v: 0 }), 0) {
LL | | LL | |
@ -256,7 +364,7 @@ LL + };
| |
error: this could be rewritten as `let...else` 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 }; LL | let $n = if let Some(v) = $e { v } else { return };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some($n) = g() 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) = 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` 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 }; LL | let v = if let Variant::A(a, 0) = e() { a } else { return };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Variant::A(v, 0) = e() else { return };` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Variant::A(v, 0) = e() else { return };`
error: this could be rewritten as `let...else` 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 }; LL | let mut v = if let Variant::B(b) = e() { b } else { return };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Variant::B(mut v) = e() else { return };` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Variant::B(mut v) = e() else { return };`
error: this could be rewritten as `let...else` 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 | / let v = if let Ok(Some(Variant::B(b))) | Err(Some(Variant::A(b, _))) = nested {
LL | | LL | |
@ -297,19 +405,19 @@ LL + };
| |
error: this could be rewritten as `let...else` 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 }; LL | let v = if let Variant::A(.., a) = e() { a } else { return };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Variant::A(.., v) = e() else { return };` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Variant::A(.., v) = e() else { return };`
error: this could be rewritten as `let...else` 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 }; LL | let w = if let (Some(v), ()) = (g(), ()) { v } else { return };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let (Some(w), ()) = (g(), ()) else { return };` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let (Some(w), ()) = (g(), ()) else { return };`
error: this could be rewritten as `let...else` 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 | / let w = if let Some(S { v: x }) = Some(S { v: 0 }) {
LL | | LL | |
@ -327,7 +435,7 @@ LL + };
| |
error: this could be rewritten as `let...else` 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 | / let v = if let Some(S { v: x }) = Some(S { v: 0 }) {
LL | | LL | |
@ -345,7 +453,7 @@ LL + };
| |
error: this could be rewritten as `let...else` 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 | / let (x, S { v }, w) = if let Some(U { v, w, x }) = None::<U<S<()>>> {
LL | | LL | |
@ -363,7 +471,7 @@ LL + };
| |
error: this could be rewritten as `let...else` 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 | / let _ = match ff {
LL | | LL | |
@ -372,5 +480,5 @@ LL | | _ => macro_call!(),
LL | | }; LL | | };
| |______^ help: consider writing: `let Some(_) = ff else { macro_call!() };` | |______^ help: consider writing: `let Some(_) = ff else { macro_call!() };`
error: aborting due to 26 previous errors error: aborting due to 30 previous errors