From a44bb079005bac4dca0bdf7d94d5d87c52166674 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 9 Nov 2023 17:29:01 -0500 Subject: [PATCH] Change divergence checking to match the compiler's type system based definition of divergence. --- clippy_lints/src/manual_let_else.rs | 9 +- clippy_utils/src/lib.rs | 306 +++++++++++++++++++++------- tests/ui/manual_let_else.rs | 115 ++++++++++- tests/ui/manual_let_else.stderr | 184 +++++++++++++---- 4 files changed, 481 insertions(+), 133 deletions(-) diff --git a/clippy_lints/src/manual_let_else.rs b/clippy_lints/src/manual_let_else.rs index 67a2a0ef8ba..01eccb56a0a 100644 --- a/clippy_lints/src/manual_let_else.rs +++ b/clippy_lints/src/manual_let_else.rs @@ -64,7 +64,7 @@ pub(crate) fn check_manual_let_else(&mut self, cx: &LateContext<'tcx>, stmt: &'t 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 @@ pub(crate) fn check_manual_let_else(&mut self, cx: &LateContext<'tcx>, stmt: &'t 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; }; diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index e11f7867ff0..19e4d4ad759 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -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; @@ -88,7 +89,7 @@ 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 @@ 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 { + struct BreakTarget { + id: HirId, + unused: bool, + } + struct V<'cx, 'tcx> { cx: &'cx LateContext<'tcx>, - res: ControlFlow<(), Descend>, + break_targets: Vec, + 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; } + 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 { - walk_expr(self, e); + 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); + } } - let mut v = V { - cx, - res: ControlFlow::Continue(Descend::Yes), - }; - expr.visit(&mut v); - v.res.is_break() + 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 + }) + } } diff --git a/tests/ui/manual_let_else.rs b/tests/ui/manual_let_else.rs index 27717ab3a73..5d94660ec89 100644 --- a/tests/ui/manual_let_else.rs +++ b/tests/ui/manual_let_else.rs @@ -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 { diff --git a/tests/ui/manual_let_else.stderr b/tests/ui/manual_let_else.stderr index 2b6504a1827..3beaf766efb 100644 --- a/tests/ui/manual_let_else.stderr +++ b/tests/ui/manual_let_else.stderr @@ -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::>> { 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