mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-27 18:57:42 +03:00
Multiple fixes to FNs of question_mark (#16769)
Closes rust-lang/rust-clippy#16751 changelog: [`question_mark`] fix FN for manual unwrap with `if let` or `match` changelog: [`question_mark`] fix FN when the match scrutinee is behind reference changelog: [`question_mark`] fix FN when match binding is behind `ref` or `mut`
This commit is contained in:
@@ -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 `<local>[.<field>]*` 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 `<local>[.<field>]*` 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;
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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<TryMode> {
|
||||
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<HirId> {
|
||||
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<IfLetOrMatchThen<'tcx>> {
|
||||
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<IfLetOrMatchThen<'tcx>> {
|
||||
(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);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -308,18 +308,15 @@ fn extract_local(cx: &LateContext<'_>, mut expr: &Expr<'_>) -> Option<Local> {
|
||||
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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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<i32> {
|
||||
|
||||
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<i32> {
|
||||
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<usize>) -> Option<usize> {
|
||||
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 }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<String> {
|
||||
};
|
||||
|
||||
let s: &str = match &Some(String::new()) {
|
||||
//~^ question_mark
|
||||
Some(v) => v,
|
||||
None => return None,
|
||||
};
|
||||
@@ -178,6 +184,7 @@ fn f() -> Option<String> {
|
||||
};
|
||||
|
||||
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<usize>) -> Option<usize> {
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
+158
-41
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user