From 01bef4f6c979e67d1edfec7d03d70b5540bb2287 Mon Sep 17 00:00:00 2001 From: linshuy2 Date: Thu, 30 Apr 2026 03:08:04 +0000 Subject: [PATCH] `question_mark`: remove redundant block when outer scope will not be polluted --- .../src/matches/match_single_binding.rs | 134 +------------- clippy_lints/src/question_mark.rs | 109 +++++++++--- clippy_utils/src/usage.rs | 168 +++++++++++++++++- tests/ui/question_mark.fixed | 52 ++++-- tests/ui/question_mark.rs | 42 +++++ tests/ui/question_mark.stderr | 116 ++++++++++-- 6 files changed, 435 insertions(+), 186 deletions(-) diff --git a/clippy_lints/src/matches/match_single_binding.rs b/clippy_lints/src/matches/match_single_binding.rs index 2a15d732939c..f512ea633623 100644 --- a/clippy_lints/src/matches/match_single_binding.rs +++ b/clippy_lints/src/matches/match_single_binding.rs @@ -1,16 +1,12 @@ -use std::ops::ControlFlow; - use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::macros::HirNode as _; use clippy_utils::source::{indent_of, reindent_multiline, snippet, snippet_block_with_context, snippet_with_context}; +use clippy_utils::usage::{variable_names_of_pat, variable_names_used_after_expr}; use clippy_utils::{is_expr_identity_of_pat, is_refutable, peel_blocks}; -use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; -use rustc_hir::def::Res; -use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_path, walk_stmt}; -use rustc_hir::{Arm, Block, Expr, ExprKind, HirId, Item, ItemKind, Node, PatKind, Path, Stmt, StmtKind}; +use rustc_hir::{Arm, Expr, ExprKind, Item, ItemKind, Node, PatKind, StmtKind}; use rustc_lint::LateContext; -use rustc_span::{Span, Symbol}; +use rustc_span::Span; use super::MATCH_SINGLE_BINDING; @@ -57,7 +53,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e &mut app, Some(span), true, - is_var_binding_used_later(cx, expr, &arms[0]), + variable_names_from_match_used_after_expr(cx, expr, &arms[0]), ); span_lint_and_sugg( @@ -103,7 +99,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e &mut app, None, true, - is_var_binding_used_later(cx, expr, &arms[0]), + variable_names_from_match_used_after_expr(cx, expr, &arms[0]), ); (expr.span, sugg) }, @@ -157,123 +153,9 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e } } -struct VarBindingVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - identifiers: FxHashSet, -} - -impl<'tcx> Visitor<'tcx> for VarBindingVisitor<'_, 'tcx> { - type Result = ControlFlow<()>; - - fn visit_path(&mut self, path: &Path<'tcx>, _: HirId) -> Self::Result { - if let Res::Local(_) = path.res - && let [segment] = path.segments - && self.identifiers.contains(&segment.ident.name) - { - return ControlFlow::Break(()); - } - - walk_path(self, path) - } - - fn visit_block(&mut self, block: &'tcx Block<'tcx>) -> Self::Result { - let before = self.identifiers.clone(); - walk_block(self, block)?; - self.identifiers = before; - ControlFlow::Continue(()) - } - - fn visit_stmt(&mut self, stmt: &'tcx Stmt<'tcx>) -> Self::Result { - if let StmtKind::Let(let_stmt) = stmt.kind { - if let Some(init) = let_stmt.init { - self.visit_expr(init)?; - } - - let_stmt.pat.each_binding(|_, _, _, ident| { - self.identifiers.remove(&ident.name); - }); - } - walk_stmt(self, stmt) - } - - fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) -> Self::Result { - match expr.kind { - ExprKind::If( - Expr { - kind: ExprKind::Let(let_expr), - .. - }, - then, - else_, - ) => { - self.visit_expr(let_expr.init)?; - let before = self.identifiers.clone(); - let_expr.pat.each_binding(|_, _, _, ident| { - self.identifiers.remove(&ident.name); - }); - - self.visit_expr(then)?; - self.identifiers = before; - if let Some(else_) = else_ { - self.visit_expr(else_)?; - } - ControlFlow::Continue(()) - }, - ExprKind::Closure(closure) => { - let body = self.cx.tcx.hir_body(closure.body); - let before = self.identifiers.clone(); - for param in body.params { - param.pat.each_binding(|_, _, _, ident| { - self.identifiers.remove(&ident.name); - }); - } - self.visit_expr(body.value)?; - self.identifiers = before; - ControlFlow::Continue(()) - }, - ExprKind::Match(expr, arms, _) => { - self.visit_expr(expr)?; - for arm in arms { - let before = self.identifiers.clone(); - arm.pat.each_binding(|_, _, _, ident| { - self.identifiers.remove(&ident.name); - }); - if let Some(guard) = arm.guard { - self.visit_expr(guard)?; - } - self.visit_expr(arm.body)?; - self.identifiers = before; - } - ControlFlow::Continue(()) - }, - _ => walk_expr(self, expr), - } - } -} - -fn is_var_binding_used_later(cx: &LateContext<'_>, expr: &Expr<'_>, arm: &Arm<'_>) -> bool { - let Node::Stmt(stmt) = cx.tcx.parent_hir_node(expr.hir_id) else { - return false; - }; - let Node::Block(block) = cx.tcx.parent_hir_node(stmt.hir_id) else { - return false; - }; - - let mut identifiers = FxHashSet::default(); - arm.pat.each_binding(|_, _, _, ident| { - identifiers.insert(ident.name); - }); - - let mut visitor = VarBindingVisitor { cx, identifiers }; - block - .stmts - .iter() - .skip_while(|s| s.hir_id != stmt.hir_id) - .skip(1) - .any(|stmt| matches!(visitor.visit_stmt(stmt), ControlFlow::Break(()))) - || block - .expr - .is_some_and(|expr| matches!(visitor.visit_expr(expr), ControlFlow::Break(()))) +fn variable_names_from_match_used_after_expr(cx: &LateContext<'_>, expr: &Expr<'_>, arm: &Arm<'_>) -> bool { + let names = variable_names_of_pat(arm.pat); + variable_names_used_after_expr(cx, names, expr) } /// Returns true if the `ex` match expression is in a local (`let`) or assign expression diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index 0141835b2214..6a5fd67a3d9f 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -8,7 +8,9 @@ use clippy_utils::res::{MaybeDef as _, MaybeQPath as _, MaybeResPath as _}; use clippy_utils::source::{indent_of, reindent_multiline, snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{implements_trait, is_copy}; -use clippy_utils::usage::local_used_after_expr; +use clippy_utils::usage::{ + local_used_after_expr, variable_names_of_block, variable_names_of_pat, variable_names_used_after_expr, +}; use clippy_utils::{ eq_expr_value, fn_def_id_with_node_args, higher, is_else_clause, is_in_const_context, is_lint_allowed, is_none_expr, is_none_pattern, pat_and_expr_can_be_question_mark, peel_blocks, peel_blocks_with_stmt, @@ -23,7 +25,6 @@ use rustc_hir::{ }; use rustc_lint::{LateContext, LateLintPass, impl_lint_pass}; use rustc_middle::ty::{self, Ty}; -use rustc_span::Span; use rustc_span::symbol::Symbol; declare_clippy_lint! { @@ -387,7 +388,7 @@ fn check_arm_is_some_or_ok<'tcx>( return Some(if peel_blocks(arm.body).res_local_id() == Some(binding) { IfLetOrMatchThen::DirectReturn } else { - IfLetOrMatchThen::ManualUnwrap(val_binding.span, arm.body) + IfLetOrMatchThen::ManualUnwrap(val_binding, arm.body) }); } @@ -462,7 +463,7 @@ enum IfLetOrMatchThen<'tcx> { /// Return the binding from an if let or match arm as is. DirectReturn, /// Working on the binding from an if let or match arm as if it comes from a `?`. - ManualUnwrap(Span, &'tcx Expr<'tcx>), + ManualUnwrap(&'tcx Pat<'tcx>, &'tcx Expr<'tcx>), } fn check_if_try_match<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { @@ -490,23 +491,16 @@ fn check_if_try_match<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { applicability, ); }, - IfLetOrMatchThen::ManualUnwrap(binding_span, arm_body) => { - let indent = indent_of(cx, expr.span).unwrap_or_default(); - let arm_body_snippet = snippet_with_applicability(cx, arm_body.span, "..", &mut applicability); - let mut sugg = reindent_multiline(&arm_body_snippet, true, Some(indent)); - let binding_snippet = snippet_with_applicability(cx, binding_span, "..", &mut applicability); - let inner_indent = " ".repeat(indent + 4); - if matches!(arm_body.kind, ExprKind::Block(..)) && sugg.starts_with('{') { - sugg.insert_str( - 1, - &format!("\n{inner_indent}let {binding_snippet} = {scrutinee_snippet}?;"), - ); - } else { - let outer_indent = " ".repeat(indent); - sugg = format!( - "{{\n{inner_indent}let {binding_snippet} = {scrutinee_snippet}?;\n{inner_indent}{sugg}\n{outer_indent}}}" - ); - } + IfLetOrMatchThen::ManualUnwrap(binding, arm_body) => { + let sugg = build_suggestion_for_if_let_or_match( + cx, + expr, + binding, + arm_body, + &scrutinee_snippet, + &mut applicability, + false, + ); diag.span_suggestion(expr.span, "try instead", sugg, applicability); }, } @@ -563,9 +557,12 @@ fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: |diag| { let mut applicability = Applicability::MachineApplicable; let receiver_str = snippet_with_applicability(cx, let_expr.span, "..", &mut applicability); + let parent = cx.tcx.parent_hir_node(expr.hir_id); + let requires_semi = match parent { + Node::Stmt(stmt) => matches!(stmt.kind, StmtKind::Expr(_)), + _ => cx.typeck_results().expr_ty(expr).is_unit(), + }; if !is_option_early_return || peel_blocks(if_then).res_local_id() == Some(bind_id) { - let parent = cx.tcx.parent_hir_node(expr.hir_id); - let requires_semi = matches!(parent, Node::Stmt(_)) || cx.typeck_results().expr_ty(expr).is_unit(); let method_call_str = match by_ref { ByRef::Yes(_, Mutability::Mut) => ".as_mut()", ByRef::Yes(_, Mutability::Not) => ".as_ref()", @@ -586,12 +583,14 @@ fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: return; } - let mut sugg = snippet_with_applicability(cx, if_then.span, "..", &mut applicability).into_owned(); - let binding_snippet = snippet_with_applicability(cx, field.span, "..", &mut applicability); - let indent = indent_of(cx, expr.span).unwrap_or_default(); - sugg.insert_str( - 1, - &format!("\n{}let {binding_snippet} = {receiver_str}?;", " ".repeat(indent + 4)), + let sugg = build_suggestion_for_if_let_or_match( + cx, + expr, + field, + if_then, + &receiver_str, + &mut applicability, + requires_semi, ); diag.span_suggestion(expr.span, "replace it with", sugg, applicability); }, @@ -599,6 +598,58 @@ fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: } } +fn build_suggestion_for_if_let_or_match<'tcx>( + cx: &LateContext<'tcx>, + expr: &Expr<'tcx>, + pat: &Pat<'_>, + then: &Expr<'_>, + scrutinee_snippet: &str, + applicability: &mut Applicability, + mut requires_semi: bool, +) -> String { + let then_snippet = snippet_with_applicability(cx, then.span, "..", applicability); + let pat_snippet = snippet_with_applicability(cx, pat.span, "..", applicability); + + let then_is_block = matches!(then.kind, ExprKind::Block(..)); + let sugg = if then_is_block && then_snippet.starts_with('{') { + then_snippet.trim_start_matches('{').trim_end_matches('}').trim() + } else { + // Add a semicolon if `then` is a block without braces, which indicates it is an assignment + // desugaring, e.g. `(a, b) = (c, d)`. + if then_is_block { + requires_semi = true; + } + &then_snippet + }; + + let indent = indent_of(cx, expr.span).unwrap_or_default(); + let parent = cx.tcx.parent_hir_node(expr.hir_id); + let outer_indent = " ".repeat(indent); + if !matches!(parent, Node::Stmt(_) | Node::Block(_)) || { + let mut names = variable_names_of_pat(pat); + if let ExprKind::Block(block, _) = then.kind { + #[expect( + rustc::potential_query_instability, + reason = "checking if variable names are used is not sensitive to order" + )] + names.extend(variable_names_of_block(block)); + } + variable_names_used_after_expr(cx, names, expr) + } { + let inner_indent = " ".repeat(indent + 4); + format!( + "{{\n{inner_indent}let {pat_snippet} = {scrutinee_snippet}?;\n{inner_indent}{}\n{outer_indent}}}", + reindent_multiline(sugg, true, Some(indent + 4)) + ) + } else { + format!( + "let {pat_snippet} = {scrutinee_snippet}?;\n{outer_indent}{}{}", + reindent_multiline(sugg, true, Some(indent)), + if requires_semi { ";" } else { "" } + ) + } +} + impl QuestionMark { fn inside_try_block(&self) -> bool { self.try_block_depth_stack.last() > Some(&0) diff --git a/clippy_utils/src/usage.rs b/clippy_utils/src/usage.rs index bc575701f6ee..2491e5e0e0c5 100644 --- a/clippy_utils/src/usage.rs +++ b/clippy_utils/src/usage.rs @@ -4,13 +4,15 @@ use crate::visitors::{Descend, Visitable, for_each_expr, for_each_expr_without_c use crate::{self as utils, get_enclosing_loop_or_multi_call_closure, sym}; use core::ops::ControlFlow; use hir::def::Res; -use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{self as hir, Expr, ExprKind, HirId, HirIdSet}; +use rustc_data_structures::fx::FxHashSet; +use rustc_hir::intravisit::{self, Visitor, walk_block, walk_expr, walk_path, walk_stmt}; +use rustc_hir::{self as hir, Block, Expr, ExprKind, HirId, HirIdSet, Pat, Path, Stmt, StmtKind}; use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, Place, PlaceBase, PlaceWithHirId}; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; use rustc_middle::mir::FakeReadCause; use rustc_middle::ty; +use rustc_span::Symbol; /// Returns a set of mutated local variable IDs, or `None` if mutations could not be determined. pub fn mutated_variables<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> Option { @@ -102,7 +104,7 @@ impl<'tcx> ParamBindingIdCollector { } } impl<'tcx> Visitor<'tcx> for ParamBindingIdCollector { - fn visit_pat(&mut self, pat: &'tcx hir::Pat<'tcx>) { + fn visit_pat(&mut self, pat: &'tcx Pat<'tcx>) { if let hir::PatKind::Binding(_, hir_id, ..) = pat.kind { self.binding_hir_ids.push(hir_id); } @@ -127,7 +129,7 @@ impl<'tcx> Visitor<'tcx> for BindingUsageFinder<'_, 'tcx> { type Result = ControlFlow<()>; type NestedFilter = nested_filter::OnlyBodies; - fn visit_path(&mut self, path: &hir::Path<'tcx>, _: HirId) -> Self::Result { + fn visit_path(&mut self, path: &Path<'tcx>, _: HirId) -> Self::Result { if let Res::Local(id) = path.res && self.binding_ids.contains(&id) { @@ -158,7 +160,7 @@ pub fn is_todo_unimplemented_stub(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool } return block.stmts.last().is_some_and(|stmt| { - if let hir::StmtKind::Expr(expr) | hir::StmtKind::Semi(expr) = stmt.kind { + if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind { return is_todo_unimplemented_macro(cx, expr); } false @@ -237,3 +239,159 @@ pub fn local_used_after_expr(cx: &LateContext<'_>, local_id: HirId, after: &Expr }) .is_some() } + +struct VariableNameUsageVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + expr_id: HirId, + names: FxHashSet, + past_expr: bool, +} + +impl<'tcx> Visitor<'tcx> for VariableNameUsageVisitor<'_, 'tcx> { + type Result = ControlFlow<()>; + + fn visit_path(&mut self, path: &Path<'tcx>, _: HirId) -> Self::Result { + if self.past_expr + && let Res::Local(_) = path.res + && let [segment] = path.segments + && self.names.contains(&segment.ident.name) + { + return ControlFlow::Break(()); + } + + walk_path(self, path) + } + + fn visit_block(&mut self, block: &'tcx Block<'tcx>) -> Self::Result { + if self.past_expr { + let before = self.names.clone(); + walk_block(self, block)?; + self.names = before; + return ControlFlow::Continue(()); + } + walk_block(self, block) + } + + fn visit_stmt(&mut self, stmt: &'tcx Stmt<'tcx>) -> Self::Result { + if self.past_expr + && let StmtKind::Let(let_stmt) = stmt.kind + { + if let Some(init) = let_stmt.init { + self.visit_expr(init)?; + } + + let_stmt.pat.each_binding(|_, _, _, ident| { + self.names.remove(&ident.name); + }); + } + walk_stmt(self, stmt) + } + + fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) -> Self::Result { + if self.past_expr { + return match expr.kind { + ExprKind::If( + Expr { + kind: ExprKind::Let(let_expr), + .. + }, + then, + else_, + ) => { + self.visit_expr(let_expr.init)?; + let before = self.names.clone(); + let_expr.pat.each_binding(|_, _, _, ident| { + self.names.remove(&ident.name); + }); + + self.visit_expr(then)?; + self.names = before; + if let Some(else_) = else_ { + self.visit_expr(else_)?; + } + ControlFlow::Continue(()) + }, + ExprKind::Closure(closure) => { + let body = self.cx.tcx.hir_body(closure.body); + let before = self.names.clone(); + for param in body.params { + param.pat.each_binding(|_, _, _, ident| { + self.names.remove(&ident.name); + }); + } + self.visit_expr(body.value)?; + self.names = before; + ControlFlow::Continue(()) + }, + ExprKind::Match(expr, arms, _) => { + self.visit_expr(expr)?; + for arm in arms { + let before = self.names.clone(); + arm.pat.each_binding(|_, _, _, ident| { + self.names.remove(&ident.name); + }); + if let Some(guard) = arm.guard { + self.visit_expr(guard)?; + } + self.visit_expr(arm.body)?; + self.names = before; + } + ControlFlow::Continue(()) + }, + _ => walk_expr(self, expr), + }; + } + + self.past_expr = expr.hir_id == self.expr_id; + if !self.past_expr { + return walk_expr(self, expr); + } + + ControlFlow::Continue(()) + } +} + +/// Checks if any of the given variable names are used after the given expression. This can be +/// helpful to check if removing a block would cause shadowing of variables declared outside the +/// block. +#[expect( + clippy::implicit_hasher, + reason = "`FxHashSet` is preferred for rustc data structures" +)] +pub fn variable_names_used_after_expr(cx: &LateContext<'_>, names: FxHashSet, after: &Expr<'_>) -> bool { + let Some(block) = utils::get_enclosing_block(cx, after.hir_id) else { + return false; + }; + + let loop_start = get_enclosing_loop_or_multi_call_closure(cx, after).map(|e| e.hir_id); + + let mut visitor = VariableNameUsageVisitor { + cx, + expr_id: loop_start.unwrap_or(after.hir_id), + names, + past_expr: false, + }; + visitor.visit_block(block).is_break() || !visitor.past_expr +} + +/// Returns the set of variable names declared in the given pattern. +pub fn variable_names_of_pat(pat: &Pat<'_>) -> FxHashSet { + let mut names = FxHashSet::default(); + pat.each_binding(|_, _, _, ident| { + names.insert(ident.name); + }); + names +} + +/// Returns the set of variable names declared in the given block. +pub fn variable_names_of_block(block: &Block<'_>) -> FxHashSet { + let mut names = FxHashSet::default(); + for stmt in block.stmts { + if let StmtKind::Let(let_stmt) = stmt.kind { + let_stmt.pat.each_binding(|_, _, _, ident| { + names.insert(ident.name); + }); + } + } + names +} diff --git a/tests/ui/question_mark.fixed b/tests/ui/question_mark.fixed index 32effaf99ddd..6ce31afff8d3 100644 --- a/tests/ui/question_mark.fixed +++ b/tests/ui/question_mark.fixed @@ -127,11 +127,9 @@ fn func() -> Option { None => return opt_none!(), }; - { - let val = f()?; - println!("{val}"); - val - }; + let val = f()?; + println!("{val}"); + val; Some(0) } @@ -559,19 +557,47 @@ fn issue16751(mut v: Option) -> Option { 42 }; - { - let n = v?; - if n > 10 { Some(42) } else { None } - } + let n = v?; + if n > 10 { Some(42) } else { None } } fn issue_destructuring_assignment() -> Option<(i32, i32)> { let mut a = 0i32; let mut b = 0i32; let opt: Option<(i32, i32)> = Some((1, 2)); - { - let x = opt?; - (a, b) = x - } + let x = opt?; + (a, b) = x; Some((a, b)) } + +#[rustfmt::skip] +fn issue16892() -> Option<()> { + let w = Some(1)?; + todo!(); + + let x = Some(1)?; + //~^ question_mark + todo!(); + + _ = { + let y = Some(1)?; + //~^ question_mark + println!("{y}"); + todo!() + }; + + let x = Some(1)?; + //~^ question_mark + let x = x + 1; + todo!(); + + let mut x = 1; + { + let x = Some(1)?; + //~^ question_mark + todo!() + }; + x += 1; + + Some(()) +} diff --git a/tests/ui/question_mark.rs b/tests/ui/question_mark.rs index 18e8404dea22..af37add9b983 100644 --- a/tests/ui/question_mark.rs +++ b/tests/ui/question_mark.rs @@ -722,3 +722,45 @@ fn issue_destructuring_assignment() -> Option<(i32, i32)> { } Some((a, b)) } + +#[rustfmt::skip] +fn issue16892() -> Option<()> { + if let Some(w) = Some(1) { todo!() } else { + //~^ question_mark + return None; + } + + if let Some(x) = Some(1) { + //~^ question_mark + todo!() + } else { + return None; + } + + _ = if let Some(y) = Some(1) { + //~^ question_mark + println!("{y}"); + todo!() + } else { + return None; + }; + + if let Some(x) = Some(1) { + //~^ question_mark + let x = x + 1; + todo!() + } else { + return None; + }; + + let mut x = 1; + if let Some(x) = Some(1) { + //~^ question_mark + todo!() + } else { + return None; + }; + x += 1; + + Some(()) +} diff --git a/tests/ui/question_mark.stderr b/tests/ui/question_mark.stderr index abb4c9531754..922cdd1703f6 100644 --- a/tests/ui/question_mark.stderr +++ b/tests/ui/question_mark.stderr @@ -168,11 +168,9 @@ LL | | }; | help: try instead | -LL ~ { -LL + let val = f()?; -LL + println!("{val}"); -LL + val -LL ~ }; +LL ~ let val = f()?; +LL + println!("{val}"); +LL ~ val; | error: this block may be rewritten with the `?` operator @@ -492,10 +490,8 @@ LL | | } | help: try instead | -LL ~ { -LL + let n = v?; -LL + if n > 10 { Some(42) } else { None } -LL + } +LL ~ let n = v?; +LL + if n > 10 { Some(42) } else { None } | error: this `match` expression can be replaced with `?` @@ -510,11 +506,105 @@ LL | | } | help: try instead | +LL ~ let x = opt?; +LL + (a, b) = x; + | + +error: this block may be rewritten with the `?` operator + --> tests/ui/question_mark.rs:728:5 + | +LL | / if let Some(w) = Some(1) { todo!() } else { +LL | | +LL | | return None; +LL | | } + | |_____^ + | +help: replace it with + | +LL ~ let w = Some(1)?; +LL + todo!(); + | + +error: this block may be rewritten with the `?` operator + --> tests/ui/question_mark.rs:733:5 + | +LL | / if let Some(x) = Some(1) { +LL | | +LL | | todo!() +LL | | } else { +LL | | return None; +LL | | } + | |_____^ + | +help: replace it with + | +LL ~ let x = Some(1)?; +LL + +LL + todo!(); + | + +error: this block may be rewritten with the `?` operator + --> tests/ui/question_mark.rs:740:9 + | +LL | _ = if let Some(y) = Some(1) { + | _________^ +LL | | +LL | | println!("{y}"); +LL | | todo!() +LL | | } else { +LL | | return None; +LL | | }; + | |_____^ + | +help: replace it with + | +LL ~ _ = { +LL + let y = Some(1)?; +LL + +LL + println!("{y}"); +LL + todo!() +LL ~ }; + | + +error: this block may be rewritten with the `?` operator + --> tests/ui/question_mark.rs:748:5 + | +LL | / if let Some(x) = Some(1) { +LL | | +LL | | let x = x + 1; +LL | | todo!() +LL | | } else { +LL | | return None; +LL | | }; + | |_____^ + | +help: replace it with + | +LL ~ let x = Some(1)?; +LL + +LL + let x = x + 1; +LL ~ todo!(); + | + +error: this block may be rewritten with the `?` operator + --> tests/ui/question_mark.rs:757:5 + | +LL | / if let Some(x) = Some(1) { +LL | | +LL | | todo!() +LL | | } else { +LL | | return None; +LL | | }; + | |_____^ + | +help: replace it with + | LL ~ { -LL + let x = opt?; -LL + (a, b) = x -LL + } +LL + let x = Some(1)?; +LL + +LL + todo!() +LL ~ }; | -error: aborting due to 47 previous errors +error: aborting due to 52 previous errors