diff --git a/clippy_lints/src/matches/manual_utils.rs b/clippy_lints/src/matches/manual_utils.rs index 6a755fac45fe..26fd936767aa 100644 --- a/clippy_lints/src/matches/manual_utils.rs +++ b/clippy_lints/src/matches/manual_utils.rs @@ -87,28 +87,24 @@ pub(super) fn check_with<'tcx, F>( None => "", }; - match can_move_expr_to_closure(cx, some_expr.expr) { - Some(captures) => { - // Check if captures the closure will need conflict with borrows made in the scrutinee. - // TODO: check all the references made in the scrutinee expression. This will require interacting - // with the borrow checker. Currently only `[.]*` is checked for. - if let Some(binding_ref_mutability) = binding_ref { - let e = peel_hir_expr_while(scrutinee, |e| match e.kind { - ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) => Some(e), - _ => None, - }); - if let ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(l), .. })) = e.kind { - match captures.get(l) { - Some(CaptureKind::Value | CaptureKind::Use | CaptureKind::Ref(Mutability::Mut)) => return None, - Some(CaptureKind::Ref(Mutability::Not)) if binding_ref_mutability == Mutability::Mut => { - return None; - }, - Some(CaptureKind::Ref(Mutability::Not)) | None => (), - } - } + let captures = can_move_expr_to_closure(cx, some_expr.expr)?; + // Check if captures the closure will need conflict with borrows made in the scrutinee. + // TODO: check all the references made in the scrutinee expression. This will require interacting + // with the borrow checker. Currently only `[.]*` is checked for. + if let Some(binding_ref_mutability) = binding_ref { + let e = peel_hir_expr_while(scrutinee, |e| match e.kind { + ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) => Some(e), + _ => None, + }); + if let ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(l), .. })) = e.kind { + match captures.get(l) { + Some(CaptureKind::Value | CaptureKind::Use | CaptureKind::Ref(Mutability::Mut)) => return None, + Some(CaptureKind::Ref(Mutability::Not)) if binding_ref_mutability == Mutability::Mut => { + return None; + }, + Some(CaptureKind::Ref(Mutability::Not)) | None => (), } - }, - None => return None, + } } let mut app = Applicability::MachineApplicable; diff --git a/clippy_lints/src/methods/iter_out_of_bounds.rs b/clippy_lints/src/methods/iter_out_of_bounds.rs index 6e998ccf896d..b0e805815bc9 100644 --- a/clippy_lints/src/methods/iter_out_of_bounds.rs +++ b/clippy_lints/src/methods/iter_out_of_bounds.rs @@ -35,13 +35,12 @@ fn get_iterator_length<'tcx>(cx: &LateContext<'tcx>, iter: &'tcx Expr<'tcx>) -> if let ty::Array(_, len) = cx.typeck_results().expr_ty(recv).peel_refs().kind() { // For slice::Iter<'_, T>, the receiver might be an array literal: [1,2,3].iter().skip(..) len.try_to_target_usize(cx.tcx).map(u128::from) - } else if let Some(args) = VecArgs::hir(cx, expr_or_init(cx, recv)) { + } else { + let args = VecArgs::hir(cx, expr_or_init(cx, recv))?; match args { VecArgs::Vec(vec) => vec.len().try_into().ok(), VecArgs::Repeat(_, len) => expr_as_u128(cx, len), } - } else { - None } }, Some(sym::IterEmpty) => Some(0), diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index dfd7834a149b..4bd6b1696b35 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -2,10 +2,10 @@ use crate::question_mark_used::QUESTION_MARK_USED; use clippy_config::Conf; use clippy_config::types::MatchLintBehaviour; -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; -use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; +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; @@ -24,6 +24,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty}; use rustc_session::impl_lint_pass; +use rustc_span::Span; use rustc_span::symbol::Symbol; declare_clippy_lint! { @@ -222,13 +223,11 @@ fn is_early_return(smbl: Symbol, cx: &LateContext<'_>, if_block: &IfBlockType<'_ // We only need to check `if let Some(x) = option` not `if let None = option`, // because the later one will be suggested as `if option.is_none()` thus causing conflict. res.ctor_parent(cx).is_lang_item(cx, OptionSome) - && if_else.is_some() - && expr_return_none_or_err(smbl, cx, if_else.unwrap(), let_expr, None) + && matches!(if_else, Some(inner) if expr_return_none_or_err(smbl, cx, inner, let_expr, None)) }, sym::Result => { (res.ctor_parent(cx).is_lang_item(cx, ResultOk) - && if_else.is_some() - && expr_return_none_or_err(smbl, cx, if_else.unwrap(), let_expr, Some(let_pat_sym))) + && matches!(if_else, Some(inner) if expr_return_none_or_err(smbl, cx, inner, let_expr, Some(let_pat_sym)))) || res.ctor_parent(cx).is_lang_item(cx, ResultErr) && expr_return_none_or_err(smbl, cx, if_then, let_expr, Some(let_pat_sym)) && if_else.is_none() @@ -328,7 +327,7 @@ enum TryMode { } fn find_try_mode<'tcx>(cx: &LateContext<'tcx>, scrutinee: &Expr<'tcx>) -> Option { - let scrutinee_ty = cx.typeck_results().expr_ty_adjusted(scrutinee); + let scrutinee_ty = cx.typeck_results().expr_ty_adjusted(scrutinee).peel_refs(); let ty::Adt(scrutinee_adt_def, _) = scrutinee_ty.kind() else { return None; }; @@ -360,14 +359,18 @@ fn extract_ctor_call<'a, 'tcx>( // Extracts the local ID of a plain `val` pattern. fn extract_binding_pat(pat: &Pat<'_>) -> Option { - if let PatKind::Binding(BindingMode::NONE, binding, _, None) = pat.kind { + if let PatKind::Binding(_, binding, _, None) = pat.kind { Some(binding) } else { None } } -fn check_arm_is_some_or_ok<'tcx>(cx: &LateContext<'tcx>, mode: TryMode, arm: &Arm<'tcx>) -> bool { +fn check_arm_is_some_or_ok<'tcx>( + cx: &LateContext<'tcx>, + mode: TryMode, + arm: &Arm<'tcx>, +) -> Option> { let happy_ctor = match mode { TryMode::Result => ResultOk, TryMode::Option => OptionSome, @@ -378,13 +381,16 @@ fn check_arm_is_some_or_ok<'tcx>(cx: &LateContext<'tcx>, mode: TryMode, arm: &Ar && let Some(val_binding) = extract_ctor_call(cx, happy_ctor, arm.pat) // Extract out `val` && let Some(binding) = extract_binding_pat(val_binding) - // Check body is just `=> val` - && peel_blocks(arm.body).res_local_id() == Some(binding) { - true - } else { - false + // Check body is just `=> val` + return Some(if peel_blocks(arm.body).res_local_id() == Some(binding) { + IfLetOrMatchThen::DirectReturn + } else { + IfLetOrMatchThen::ManualUnwrap(val_binding.span, arm.body) + }); } + + None } fn check_arm_is_none_or_err<'tcx>(cx: &LateContext<'tcx>, mode: TryMode, arm: &Arm<'tcx>) -> bool { @@ -439,9 +445,23 @@ fn is_local_or_local_into(cx: &LateContext<'_>, expr: &Expr<'_>, val: HirId) -> } } -fn check_arms_are_try<'tcx>(cx: &LateContext<'tcx>, mode: TryMode, arm1: &Arm<'tcx>, arm2: &Arm<'tcx>) -> bool { - (check_arm_is_some_or_ok(cx, mode, arm1) && check_arm_is_none_or_err(cx, mode, arm2)) - || (check_arm_is_some_or_ok(cx, mode, arm2) && check_arm_is_none_or_err(cx, mode, arm1)) +fn check_arms_are_try<'tcx>( + cx: &LateContext<'tcx>, + mode: TryMode, + arm1: &Arm<'tcx>, + arm2: &Arm<'tcx>, +) -> Option> { + (check_arm_is_none_or_err(cx, mode, arm2).then(|| check_arm_is_some_or_ok(cx, mode, arm1))) + .or_else(|| check_arm_is_none_or_err(cx, mode, arm1).then(|| check_arm_is_some_or_ok(cx, mode, arm2))) + .flatten() +} + +#[derive(Debug)] +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>), } fn check_if_try_match<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { @@ -449,19 +469,47 @@ fn check_if_try_match<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { && !expr.span.from_expansion() && let Some(mode) = find_try_mode(cx, scrutinee) && !span_contains_cfg(cx, expr.span) - && check_arms_are_try(cx, mode, arm1, arm2) + && let Some(if_let_or_match_then) = check_arms_are_try(cx, mode, arm1, arm2) { - let mut applicability = Applicability::MachineApplicable; - let snippet = snippet_with_applicability(cx, scrutinee.span.source_callsite(), "..", &mut applicability); - - span_lint_and_sugg( + span_lint_and_then( cx, QUESTION_MARK, expr.span, "this `match` expression can be replaced with `?`", - "try instead", - snippet.into_owned() + "?", - applicability, + |diag| { + let mut applicability = Applicability::MachineApplicable; + let scrutinee_snippet = + snippet_with_applicability(cx, scrutinee.span.source_callsite(), "..", &mut applicability); + match if_let_or_match_then { + IfLetOrMatchThen::DirectReturn => { + diag.span_suggestion( + expr.span, + "try instead", + scrutinee_snippet.into_owned() + "?", + 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.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}}}" + ); + } + diag.span_suggestion(expr.span, "try instead", sugg, applicability); + }, + } + }, ); } } @@ -486,8 +534,8 @@ fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: if_then, if_else, ) - && ((is_early_return(sym::Option, cx, &if_block) && peel_blocks(if_then).res_local_id() == Some(bind_id)) - || is_early_return(sym::Result, cx, &if_block)) + && let is_option_early_return = is_early_return(sym::Option, cx, &if_block) + && (is_option_early_return || is_early_return(sym::Result, cx, &if_block)) && if_else .map(|e| eq_expr_value(cx, let_expr, peel_blocks(e))) .is_none_or(|e| !e) @@ -499,32 +547,53 @@ fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: return; } - 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 = 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()", - ByRef::No => "", - }; - - let mut sugg = format!( - "{receiver_str}{method_call_str}?{}", - if requires_semi { ";" } else { "" } - ); - if is_else_clause(cx.tcx, expr) || (requires_semi && !matches!(parent, Node::Stmt(_) | Node::Block(_))) { - sugg = format!("{{ {sugg} }}"); + // Leave `if let Some(x) = opt { .. } else { None }` to `needless_match` or `manual_map_option`. + if is_option_early_return + && if_else.is_some_and(|else_| !matches!(peel_blocks_with_stmt(else_).kind, ExprKind::Ret(_))) + { + return; } - span_lint_and_sugg( + span_lint_and_then( cx, QUESTION_MARK, expr.span, "this block may be rewritten with the `?` operator", - "replace it with", - sugg, - applicability, + |diag| { + let mut applicability = Applicability::MachineApplicable; + let receiver_str = snippet_with_applicability(cx, let_expr.span, "..", &mut applicability); + 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()", + ByRef::No => "", + }; + + let mut sugg = format!( + "{receiver_str}{method_call_str}?{}", + if requires_semi { ";" } else { "" } + ); + if is_else_clause(cx.tcx, expr) + || (requires_semi && !matches!(parent, Node::Stmt(_) | Node::Block(_))) + { + sugg = format!("{{ {sugg} }}"); + } + + diag.span_suggestion(expr.span, "replace it with", sugg, applicability); + 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)), + ); + diag.span_suggestion(expr.span, "replace it with", sugg, applicability); + }, ); } } diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index cfd3f420db5a..d87289e1362d 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -308,18 +308,15 @@ fn extract_local(cx: &LateContext<'_>, mut expr: &Expr<'_>) -> Option { field_indices.push(field_idx); expr = recv; } - if let Some(local_id) = expr.res_local_id() { - if field_indices.is_empty() { - Some(Local::Pure { local_id }) - } else { - Some(Local::WithFieldAccess { - local_id, - field_indices, - span, - }) - } + let local_id = expr.res_local_id()?; + if field_indices.is_empty() { + Some(Local::Pure { local_id }) } else { - None + Some(Local::WithFieldAccess { + local_id, + field_indices, + span, + }) } } diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index 1ac417a8d692..6f38d7d0e609 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -1240,17 +1240,14 @@ pub fn deref_chain<'cx, 'tcx>(cx: &'cx LateContext<'tcx>, ty: Ty<'tcx>) -> impl /// This does not look for impls in the type's `Deref::Target` type. /// If you need this, you should wrap this call in `clippy_utils::ty::deref_chain().any(...)`. pub fn get_adt_inherent_method<'a>(cx: &'a LateContext<'_>, ty: Ty<'_>, method_name: Symbol) -> Option<&'a AssocItem> { - if let Some(ty_did) = ty.ty_adt_def().map(AdtDef::did) { - cx.tcx.inherent_impls(ty_did).iter().find_map(|&did| { - cx.tcx - .associated_items(did) - .filter_by_name_unhygienic(method_name) - .next() - .filter(|item| item.tag() == AssocTag::Fn) - }) - } else { - None - } + let ty_did = ty.ty_adt_def().map(AdtDef::did)?; + cx.tcx.inherent_impls(ty_did).iter().find_map(|&did| { + cx.tcx + .associated_items(did) + .filter_by_name_unhygienic(method_name) + .next() + .filter(|item| item.tag() == AssocTag::Fn) + }) } /// Gets the type of a field by name. diff --git a/tests/ui/manual_filter.fixed b/tests/ui/manual_filter.fixed index a0fb0e32d601..22af8c2f1aa1 100644 --- a/tests/ui/manual_filter.fixed +++ b/tests/ui/manual_filter.fixed @@ -1,5 +1,5 @@ #![warn(clippy::manual_filter)] -#![allow(unused_variables, dead_code, clippy::useless_vec)] +#![allow(unused_variables, clippy::question_mark, clippy::useless_vec)] fn main() { Some(0).filter(|&x| x <= 0); diff --git a/tests/ui/manual_filter.rs b/tests/ui/manual_filter.rs index a9d0c35f8bb7..2568851b0110 100644 --- a/tests/ui/manual_filter.rs +++ b/tests/ui/manual_filter.rs @@ -1,5 +1,5 @@ #![warn(clippy::manual_filter)] -#![allow(unused_variables, dead_code, clippy::useless_vec)] +#![allow(unused_variables, clippy::question_mark, clippy::useless_vec)] fn main() { match Some(0) { diff --git a/tests/ui/needless_match.fixed b/tests/ui/needless_match.fixed index 57273012a192..2d7b0fa2bb2c 100644 --- a/tests/ui/needless_match.fixed +++ b/tests/ui/needless_match.fixed @@ -1,5 +1,5 @@ #![warn(clippy::needless_match)] -#![allow(clippy::manual_map)] +#![allow(clippy::manual_map, clippy::question_mark)] #![allow(dead_code)] #![allow(unused)] #[derive(Clone, Copy)] diff --git a/tests/ui/needless_match.rs b/tests/ui/needless_match.rs index 3eb577868f2b..83e3e73feb6c 100644 --- a/tests/ui/needless_match.rs +++ b/tests/ui/needless_match.rs @@ -1,5 +1,5 @@ #![warn(clippy::needless_match)] -#![allow(clippy::manual_map)] +#![allow(clippy::manual_map, clippy::question_mark)] #![allow(dead_code)] #![allow(unused)] #[derive(Clone, Copy)] diff --git a/tests/ui/question_mark.fixed b/tests/ui/question_mark.fixed index e209da5c8258..bf4b4ff0a21e 100644 --- a/tests/ui/question_mark.fixed +++ b/tests/ui/question_mark.fixed @@ -1,5 +1,10 @@ #![feature(try_blocks)] -#![allow(clippy::unnecessary_wraps, clippy::no_effect)] +#![allow( + clippy::unnecessary_wraps, + clippy::no_effect, + clippy::needless_return, + clippy::toplevel_ref_arg +)] use std::sync::MutexGuard; @@ -110,10 +115,7 @@ fn func() -> Option { let _val = f()?; - let s: &str = match &Some(String::new()) { - Some(v) => v, - None => return None, - }; + let s: &str = &Some(String::new())?; f()?; @@ -124,12 +126,10 @@ fn func() -> Option { None => return opt_none!(), }; - match f() { - Some(val) => { - println!("{val}"); - val - }, - None => return None, + { + let val = f()?; + println!("{val}"); + val }; Some(0) @@ -537,3 +537,30 @@ fn issue16654() -> Result<(), i32> { Ok(()) } + +#[rustfmt::skip] +fn issue16751(mut v: Option) -> Option { + let _ = { + let n = &v?; + println!("{n}"); + Some(42) + }; + + let _ = { + let ref mut n = v?; + println!("{n}"); + Some(42) + }; + + let _ = { + let ref mut n = v?; + //~^ question_mark + println!("{n}"); + 42 + }; + + { + let n = v?; + if n > 10 { Some(42) } else { None } + } +} diff --git a/tests/ui/question_mark.rs b/tests/ui/question_mark.rs index 579b51461d13..93f76f16576c 100644 --- a/tests/ui/question_mark.rs +++ b/tests/ui/question_mark.rs @@ -1,5 +1,10 @@ #![feature(try_blocks)] -#![allow(clippy::unnecessary_wraps, clippy::no_effect)] +#![allow( + clippy::unnecessary_wraps, + clippy::no_effect, + clippy::needless_return, + clippy::toplevel_ref_arg +)] use std::sync::MutexGuard; @@ -156,6 +161,7 @@ fn f() -> Option { }; let s: &str = match &Some(String::new()) { + //~^ question_mark Some(v) => v, None => return None, }; @@ -178,6 +184,7 @@ fn f() -> Option { }; match f() { + //~^ question_mark Some(val) => { println!("{val}"); val @@ -668,3 +675,38 @@ fn issue16654() -> Result<(), i32> { Ok(()) } + +#[rustfmt::skip] +fn issue16751(mut v: Option) -> Option { + let _ = match &v { + //~^ question_mark + Some(n) => { + println!("{n}"); + Some(42) + } + None => return None, + }; + + let _ = match v { + //~^ question_mark + Some(ref mut n) => { + println!("{n}"); + Some(42) + } + None => return None, + }; + + let _ = if let Some(ref mut n) = v { + //~^ question_mark + println!("{n}"); + 42 + } else { + return None; + }; + + match v { + //~^ question_mark + Some(n) => if n > 10 { Some(42) } else { None }, + None => return None, + } +} diff --git a/tests/ui/question_mark.stderr b/tests/ui/question_mark.stderr index 1d7f665a2662..9d7cfc205764 100644 --- a/tests/ui/question_mark.stderr +++ b/tests/ui/question_mark.stderr @@ -1,5 +1,5 @@ error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:7:5 + --> tests/ui/question_mark.rs:12:5 | LL | / if a.is_none() { LL | | @@ -11,7 +11,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::question_mark)]` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:53:9 + --> tests/ui/question_mark.rs:58:9 | LL | / if (self.opt).is_none() { LL | | @@ -20,7 +20,7 @@ LL | | } | |_________^ help: replace it with: `(self.opt)?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:58:9 + --> tests/ui/question_mark.rs:63:9 | LL | / if self.opt.is_none() { LL | | @@ -29,7 +29,7 @@ LL | | } | |_________^ help: replace it with: `self.opt?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:63:17 + --> tests/ui/question_mark.rs:68:17 | LL | let _ = if self.opt.is_none() { | _________________^ @@ -41,7 +41,7 @@ LL | | }; | |_________^ help: replace it with: `Some(self.opt?)` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:70:17 + --> tests/ui/question_mark.rs:75:17 | LL | let _ = if let Some(x) = self.opt { | _________________^ @@ -53,7 +53,7 @@ LL | | }; | |_________^ help: replace it with: `self.opt?` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:88:9 + --> tests/ui/question_mark.rs:93:9 | LL | / if self.opt.is_none() { LL | | @@ -62,7 +62,7 @@ LL | | } | |_________^ help: replace it with: `self.opt.as_ref()?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:97:9 + --> tests/ui/question_mark.rs:102:9 | LL | / if self.opt.is_none() { LL | | @@ -71,7 +71,7 @@ LL | | } | |_________^ help: replace it with: `self.opt.as_ref()?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:106:9 + --> tests/ui/question_mark.rs:111:9 | LL | / if self.opt.is_none() { LL | | @@ -80,7 +80,7 @@ LL | | } | |_________^ help: replace it with: `self.opt.as_ref()?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:114:26 + --> tests/ui/question_mark.rs:119:26 | LL | let v: &Vec<_> = if let Some(ref v) = self.opt { | __________________________^ @@ -92,7 +92,7 @@ LL | | }; | |_________^ help: replace it with: `self.opt.as_ref()?` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:125:17 + --> tests/ui/question_mark.rs:130:17 | LL | let v = if let Some(v) = self.opt { | _________________^ @@ -104,7 +104,7 @@ LL | | }; | |_________^ help: replace it with: `self.opt?` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:147:5 + --> tests/ui/question_mark.rs:152:5 | LL | / if f().is_none() { LL | | @@ -113,7 +113,7 @@ LL | | } | |_____^ help: replace it with: `f()?;` error: this `match` expression can be replaced with `?` - --> tests/ui/question_mark.rs:152:16 + --> tests/ui/question_mark.rs:157:16 | LL | let _val = match f() { | ________________^ @@ -124,7 +124,18 @@ LL | | }; | |_____^ help: try instead: `f()?` error: this `match` expression can be replaced with `?` - --> tests/ui/question_mark.rs:163:5 + --> tests/ui/question_mark.rs:163:19 + | +LL | let s: &str = match &Some(String::new()) { + | ___________________^ +LL | | +LL | | Some(v) => v, +LL | | None => return None, +LL | | }; + | |_____^ help: try instead: `&Some(String::new())?` + +error: this `match` expression can be replaced with `?` + --> tests/ui/question_mark.rs:169:5 | LL | / match f() { LL | | @@ -134,7 +145,7 @@ LL | | }; | |_____^ help: try instead: `f()?` error: this `match` expression can be replaced with `?` - --> tests/ui/question_mark.rs:169:5 + --> tests/ui/question_mark.rs:175:5 | LL | / match opt_none!() { LL | | @@ -143,14 +154,35 @@ LL | | None => return None, LL | | }; | |_____^ help: try instead: `opt_none!()?` +error: this `match` expression can be replaced with `?` + --> tests/ui/question_mark.rs:186:5 + | +LL | / match f() { +LL | | +LL | | Some(val) => { +LL | | println!("{val}"); +... | +LL | | None => return None, +LL | | }; + | |_____^ + | +help: try instead + | +LL ~ { +LL + let val = f()?; +LL + println!("{val}"); +LL + val +LL ~ }; + | + error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:196:13 + --> tests/ui/question_mark.rs:203:13 | LL | let _ = if let Ok(x) = x { x } else { return x }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x?` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:199:5 + --> tests/ui/question_mark.rs:206:5 | LL | / if x.is_err() { LL | | @@ -159,7 +191,7 @@ LL | | } | |_____^ help: replace it with: `x?;` error: this `match` expression can be replaced with `?` - --> tests/ui/question_mark.rs:204:16 + --> tests/ui/question_mark.rs:211:16 | LL | let _val = match func_returning_result() { | ________________^ @@ -170,7 +202,7 @@ LL | | }; | |_____^ help: try instead: `func_returning_result()?` error: this `match` expression can be replaced with `?` - --> tests/ui/question_mark.rs:210:5 + --> tests/ui/question_mark.rs:217:5 | LL | / match func_returning_result() { LL | | @@ -180,7 +212,7 @@ LL | | }; | |_____^ help: try instead: `func_returning_result()?` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:302:5 + --> tests/ui/question_mark.rs:309:5 | LL | / if let Err(err) = func_returning_result() { LL | | @@ -189,7 +221,7 @@ LL | | } | |_____^ help: replace it with: `func_returning_result()?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:310:5 + --> tests/ui/question_mark.rs:317:5 | LL | / if let Err(err) = func_returning_result() { LL | | @@ -198,7 +230,7 @@ LL | | } | |_____^ help: replace it with: `func_returning_result()?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:393:13 + --> tests/ui/question_mark.rs:400:13 | LL | / if a.is_none() { LL | | @@ -208,7 +240,7 @@ LL | | } | |_____________^ help: replace it with: `a?;` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:454:5 + --> tests/ui/question_mark.rs:461:5 | LL | / let Some(v) = bar.foo.owned.clone() else { LL | | return None; @@ -216,7 +248,7 @@ LL | | }; | |______^ help: replace it with: `let v = bar.foo.owned.clone()?;` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:469:5 + --> tests/ui/question_mark.rs:476:5 | LL | / let Some(ref x) = foo.opt_x else { LL | | return None; @@ -224,7 +256,7 @@ LL | | }; | |______^ help: replace it with: `let x = foo.opt_x.as_ref()?;` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:479:5 + --> tests/ui/question_mark.rs:486:5 | LL | / let Some(ref mut x) = foo.opt_x else { LL | | return None; @@ -232,7 +264,7 @@ LL | | }; | |______^ help: replace it with: `let x = foo.opt_x.as_mut()?;` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:490:5 + --> tests/ui/question_mark.rs:497:5 | LL | / let Some(ref x @ ref y) = foo.opt_x else { LL | | return None; @@ -240,7 +272,7 @@ LL | | }; | |______^ help: replace it with: `let x @ y = foo.opt_x.as_ref()?;` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:494:5 + --> tests/ui/question_mark.rs:501:5 | LL | / let Some(ref x @ WrapperStructWithString(_)) = bar else { LL | | return None; @@ -248,7 +280,7 @@ LL | | }; | |______^ help: replace it with: `let x @ &WrapperStructWithString(_) = bar.as_ref()?;` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:498:5 + --> tests/ui/question_mark.rs:505:5 | LL | / let Some(ref mut x @ WrapperStructWithString(_)) = bar else { LL | | return None; @@ -256,7 +288,7 @@ LL | | }; | |______^ help: replace it with: `let x @ &mut WrapperStructWithString(_) = bar.as_mut()?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:520:5 + --> tests/ui/question_mark.rs:527:5 | LL | / if arg.is_none() { LL | | @@ -265,7 +297,7 @@ LL | | } | |_____^ help: replace it with: `arg?;` error: this `match` expression can be replaced with `?` - --> tests/ui/question_mark.rs:524:15 + --> tests/ui/question_mark.rs:531:15 | LL | let val = match arg { | _______________^ @@ -276,7 +308,7 @@ LL | | }; | |_____^ help: try instead: `arg?` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:534:5 + --> tests/ui/question_mark.rs:541:5 | LL | / let Some(a) = *a else { LL | | return None; @@ -284,7 +316,7 @@ LL | | }; | |______^ help: replace it with: `let a = (*a)?;` error: this `match` expression can be replaced with `?` - --> tests/ui/question_mark.rs:566:5 + --> tests/ui/question_mark.rs:573:5 | LL | / match some_result { LL | | @@ -294,7 +326,7 @@ LL | | }; | |_____^ help: try instead: `some_result?` error: this `match` expression can be replaced with `?` - --> tests/ui/question_mark.rs:572:5 + --> tests/ui/question_mark.rs:579:5 | LL | / match some_result { LL | | @@ -304,7 +336,7 @@ LL | | }; | |_____^ help: try instead: `some_result?` error: this `match` expression can be replaced with `?` - --> tests/ui/question_mark.rs:578:5 + --> tests/ui/question_mark.rs:585:5 | LL | / match some_result { LL | | @@ -314,7 +346,7 @@ LL | | }; | |_____^ help: try instead: `some_result?` error: this `match` expression can be replaced with `?` - --> tests/ui/question_mark.rs:596:17 + --> tests/ui/question_mark.rs:603:17 | LL | let x = match result { | _________________^ @@ -325,7 +357,7 @@ LL | | }; | |_________^ help: try instead: `result?` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:609:9 + --> tests/ui/question_mark.rs:616:9 | LL | / if let Err(reason) = result { LL | | @@ -334,7 +366,7 @@ LL | | } | |_________^ help: replace it with: `result?;` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:626:5 + --> tests/ui/question_mark.rs:633:5 | LL | / let Some(x) = test_expr!(42) else { LL | | return None; @@ -342,7 +374,7 @@ LL | | }; | |______^ help: replace it with: `let x = test_expr!(42)?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:632:5 + --> tests/ui/question_mark.rs:639:5 | LL | / if test_expr!(42).is_none() { LL | | @@ -351,7 +383,7 @@ LL | | } | |_____^ help: replace it with: `test_expr!(42)?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:643:12 + --> tests/ui/question_mark.rs:650:12 | LL | } else if let Some(x) = a { | ____________^ @@ -363,7 +395,7 @@ LL | | }; | |_____^ help: replace it with: `{ a? }` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:658:9 + --> tests/ui/question_mark.rs:665:9 | LL | / if let Err(err) = result { LL | | @@ -372,7 +404,7 @@ LL | | } | |_________^ help: replace it with: `result?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:664:10 + --> tests/ui/question_mark.rs:671:10 | LL | _ = [if let Err(err) = result { | __________^ @@ -381,5 +413,90 @@ LL | | return Err(err); LL | | }]; | |_____^ help: replace it with: `{ result?; }` -error: aborting due to 40 previous errors +error: this `match` expression can be replaced with `?` + --> tests/ui/question_mark.rs:681:13 + | +LL | let _ = match &v { + | _____________^ +LL | | +LL | | Some(n) => { +LL | | println!("{n}"); +... | +LL | | None => return None, +LL | | }; + | |_____^ + | +help: try instead + | +LL ~ let _ = { +LL + let n = &v?; +LL + println!("{n}"); +LL + Some(42) +LL ~ }; + | + +error: this `match` expression can be replaced with `?` + --> tests/ui/question_mark.rs:690:13 + | +LL | let _ = match v { + | _____________^ +LL | | +LL | | Some(ref mut n) => { +LL | | println!("{n}"); +... | +LL | | None => return None, +LL | | }; + | |_____^ + | +help: try instead + | +LL ~ let _ = { +LL + let ref mut n = v?; +LL + println!("{n}"); +LL + Some(42) +LL ~ }; + | + +error: this block may be rewritten with the `?` operator + --> tests/ui/question_mark.rs:699:13 + | +LL | let _ = if let Some(ref mut n) = v { + | _____________^ +LL | | +LL | | println!("{n}"); +LL | | 42 +LL | | } else { +LL | | return None; +LL | | }; + | |_____^ + | +help: replace it with + | +LL ~ let _ = { +LL + let ref mut n = v?; +LL + +LL + println!("{n}"); +LL + 42 +LL ~ }; + | + +error: this `match` expression can be replaced with `?` + --> tests/ui/question_mark.rs:707:5 + | +LL | / match v { +LL | | +LL | | Some(n) => if n > 10 { Some(42) } else { None }, +LL | | None => return None, +LL | | } + | |_____^ + | +help: try instead + | +LL ~ { +LL + let n = v?; +LL + if n > 10 { Some(42) } else { None } +LL + } + | + +error: aborting due to 46 previous errors