introduce as_some_expr, is_none_expr

This commit is contained in:
Ada Alakbarova 2025-10-21 16:27:32 +02:00
parent 59890a9736
commit 605cc879bc
No known key found for this signature in database
13 changed files with 62 additions and 63 deletions

View file

@ -2,14 +2,13 @@ use clippy_config::Conf;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::eager_or_lazy::switch_to_eager_eval;
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::res::{MaybeDef, MaybeQPath};
use clippy_utils::source::{snippet_with_applicability, snippet_with_context, walk_span_to_context};
use clippy_utils::sugg::Sugg;
use clippy_utils::{
contains_return, expr_adjustment_requires_coercion, higher, is_else_clause, is_in_const_context, peel_blocks, sym,
as_some_expr, contains_return, expr_adjustment_requires_coercion, higher, is_else_clause, is_in_const_context,
is_none_expr, peel_blocks, sym,
};
use rustc_errors::Applicability;
use rustc_hir::LangItem::{OptionNone, OptionSome};
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::impl_lint_pass;
@ -70,11 +69,10 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
}) = higher::If::hir(expr)
&& let ExprKind::Block(then_block, _) = then.kind
&& let Some(then_expr) = then_block.expr
&& let ExprKind::Call(then_call, [then_arg]) = then_expr.kind
&& let Some(then_arg) = as_some_expr(cx, then_expr)
&& !expr.span.from_expansion()
&& !then_expr.span.from_expansion()
&& then_call.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome)
&& peel_blocks(els).res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone)
&& is_none_expr(cx, peel_blocks(els))
&& !is_else_clause(cx.tcx, expr)
&& !is_in_const_context(cx)
&& self.msrv.meets(cx, msrvs::BOOL_THEN)

View file

@ -5,7 +5,7 @@ use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath};
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::implements_trait;
use clippy_utils::usage::contains_return_break_continue_macro;
use clippy_utils::{higher, peel_blocks_with_stmt};
use clippy_utils::{as_some_expr, higher, peel_blocks_with_stmt};
use rustc_errors::Applicability;
use rustc_hir::lang_items::LangItem;
use rustc_hir::{BindingMode, Block, Expr, ExprKind, HirId, Node, Pat, PatKind, Stmt, StmtKind};
@ -33,8 +33,7 @@ pub(super) fn check<'tcx>(
&& let [stmt] = block.stmts
&& let StmtKind::Semi(semi) = stmt.kind
&& let ExprKind::Ret(Some(ret_value)) = semi.kind
&& let ExprKind::Call(ctor, [inner_ret]) = ret_value.kind
&& ctor.res(cx).ctor_parent(cx).is_lang_item(cx, LangItem::OptionSome)
&& let Some(inner_ret) = as_some_expr(cx, ret_value)
&& inner_ret.res_local_id() == Some(binding_id)
&& !contains_return_break_continue_macro(cond)
&& let Some((last_stmt, last_ret)) = last_stmt_and_ret(cx, expr)

View file

@ -1,8 +1,9 @@
use clippy_utils::as_some_expr;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath};
use clippy_utils::visitors::contains_unsafe_block;
use rustc_hir::LangItem::{OptionNone, OptionSome};
use rustc_hir::LangItem::OptionNone;
use rustc_hir::{Arm, Expr, ExprKind, HirId, Pat, PatKind};
use rustc_lint::LateContext;
use rustc_span::{SyntaxContext, sym};
@ -62,11 +63,9 @@ fn peels_blocks_incl_unsafe<'a>(expr: &'a Expr<'a>) -> &'a Expr<'a> {
fn is_some_expr(cx: &LateContext<'_>, target: HirId, ctxt: SyntaxContext, expr: &Expr<'_>) -> bool {
if let Some(inner_expr) = peels_blocks_incl_unsafe_opt(expr)
// there can be not statements in the block as they would be removed when switching to `.filter`
&& let ExprKind::Call(callee, [arg]) = inner_expr.kind
&& let Some(arg) = as_some_expr(cx, inner_expr)
{
return ctxt == expr.span.ctxt()
&& callee.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome)
&& arg.res_local_id() == Some(target);
return ctxt == expr.span.ctxt() && arg.res_local_id() == Some(target);
}
false
}

View file

@ -1,12 +1,12 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::res::{MaybeDef, MaybeQPath};
use clippy_utils::res::MaybeDef;
use clippy_utils::source::{indent_of, reindent_multiline};
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::{option_arg_ty, peel_and_count_ty_refs};
use clippy_utils::{get_parent_expr, peel_blocks, span_contains_comment};
use clippy_utils::{as_some_expr, get_parent_expr, is_none_expr, peel_blocks, span_contains_comment};
use rustc_ast::{BindingMode, Mutability};
use rustc_errors::Applicability;
use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr};
use rustc_hir::LangItem::ResultErr;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::{Arm, Expr, ExprKind, Pat, PatExpr, PatExprKind, PatKind, Path, QPath};
use rustc_lint::{LateContext, LintContext};
@ -106,8 +106,7 @@ fn is_ok_or_err<'hir>(cx: &LateContext<'_>, pat: &Pat<'hir>) -> Option<(bool, &'
/// Check if `expr` contains `Some(ident)`, possibly as a block
fn is_some_ident<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, ident: &Ident, ty: Ty<'tcx>) -> bool {
if let ExprKind::Call(body_callee, [body_arg]) = peel_blocks(expr).kind
&& body_callee.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome)
if let Some(body_arg) = as_some_expr(cx, peel_blocks(expr))
&& cx.typeck_results().expr_ty(body_arg) == ty
&& let ExprKind::Path(QPath::Resolved(
_,
@ -124,7 +123,7 @@ fn is_some_ident<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, ident: &Ident, t
/// Check if `expr` is `None`, possibly as a block
fn is_none(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
peel_blocks(expr).res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone)
is_none_expr(cx, peel_blocks(expr))
}
/// Suggest replacing `expr` by `scrutinee.METHOD()`, where `METHOD` is either `ok` or

View file

@ -1,16 +1,15 @@
use crate::map_unit_fn::OPTION_MAP_UNIT_FN;
use crate::matches::MATCH_AS_REF;
use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath};
use clippy_utils::res::{MaybeDef, MaybeResPath};
use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::{is_copy, is_unsafe_fn, peel_and_count_ty_refs};
use clippy_utils::{
CaptureKind, as_some_pattern, can_move_expr_to_closure, expr_requires_coercion, is_else_clause, is_lint_allowed,
is_none_pattern, peel_blocks, peel_hir_expr_refs, peel_hir_expr_while,
is_none_expr, is_none_pattern, peel_blocks, peel_hir_expr_refs, peel_hir_expr_while,
};
use rustc_ast::util::parser::ExprPrecedence;
use rustc_errors::Applicability;
use rustc_hir::LangItem::OptionNone;
use rustc_hir::def::Res;
use rustc_hir::{BindingMode, Expr, ExprKind, HirId, Mutability, Pat, PatKind, Path, QPath};
use rustc_lint::LateContext;
@ -44,16 +43,16 @@ where
try_parse_pattern(cx, then_pat, expr_ctxt),
else_pat.map_or(Some(OptionPat::Wild), |p| try_parse_pattern(cx, p, expr_ctxt)),
) {
(Some(OptionPat::Wild), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_body) => {
(Some(OptionPat::Wild), Some(OptionPat::Some { pattern, ref_count })) if is_none_arm_body(cx, then_body) => {
(else_body, pattern, ref_count, true)
},
(Some(OptionPat::None), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_body) => {
(Some(OptionPat::None), Some(OptionPat::Some { pattern, ref_count })) if is_none_arm_body(cx, then_body) => {
(else_body, pattern, ref_count, false)
},
(Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::Wild)) if is_none_expr(cx, else_body) => {
(Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::Wild)) if is_none_arm_body(cx, else_body) => {
(then_body, pattern, ref_count, true)
},
(Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::None)) if is_none_expr(cx, else_body) => {
(Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::None)) if is_none_arm_body(cx, else_body) => {
(then_body, pattern, ref_count, false)
},
_ => return None,
@ -268,6 +267,6 @@ pub(super) fn try_parse_pattern<'tcx>(
}
/// Checks for the `None` value, possibly in a block.
fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
peel_blocks(expr).res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone)
fn is_none_arm_body(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
is_none_expr(cx, peel_blocks(expr))
}

View file

@ -1,10 +1,9 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::res::{MaybeDef, MaybeQPath};
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::option_arg_ty;
use clippy_utils::{as_some_pattern, is_none_arm, peel_blocks};
use clippy_utils::{as_some_expr, as_some_pattern, is_none_arm, peel_blocks};
use rustc_errors::Applicability;
use rustc_hir::{Arm, BindingMode, ByRef, Expr, ExprKind, LangItem, Mutability, PatKind, QPath};
use rustc_hir::{Arm, BindingMode, ByRef, Expr, ExprKind, Mutability, PatKind, QPath};
use rustc_lint::LateContext;
use rustc_middle::ty;
@ -84,8 +83,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr:
fn as_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option<Mutability> {
if let Some([first_pat, ..]) = as_some_pattern(cx, arm.pat)
&& let PatKind::Binding(BindingMode(ByRef::Yes(mutabl), _), .., ident, _) = first_pat.kind
&& let ExprKind::Call(e, [arg]) = peel_blocks(arm.body).kind
&& e.res(cx).ctor_parent(cx).is_lang_item(cx, LangItem::OptionSome)
&& let Some(arg) = as_some_expr(cx, peel_blocks(arm.body))
&& let ExprKind::Path(QPath::Resolved(_, path2)) = arg.kind
&& path2.segments.len() == 1
&& ident.name == path2.segments[0].ident.name

View file

@ -1,13 +1,14 @@
use clippy_config::Conf;
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::res::{MaybeDef, MaybeQPath};
use clippy_utils::res::MaybeDef;
use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::is_non_aggregate_primitive_type;
use clippy_utils::{is_default_equivalent, is_expr_used_or_unified, peel_ref_operators, std_or_core};
use clippy_utils::{
as_some_expr, is_default_equivalent, is_expr_used_or_unified, is_none_expr, peel_ref_operators, std_or_core,
};
use rustc_errors::Applicability;
use rustc_hir::LangItem::{OptionNone, OptionSome};
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::impl_lint_pass;
@ -128,7 +129,7 @@ impl_lint_pass!(MemReplace =>
[MEM_REPLACE_OPTION_WITH_NONE, MEM_REPLACE_OPTION_WITH_SOME, MEM_REPLACE_WITH_UNINIT, MEM_REPLACE_WITH_DEFAULT]);
fn check_replace_option_with_none(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) -> bool {
if src.res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone) {
if is_none_expr(cx, src) {
// Since this is a late pass (already type-checked),
// and we already know that the second argument is an
// `Option`, we do not need to check the first
@ -161,8 +162,7 @@ fn check_replace_option_with_some(
expr_span: Span,
msrv: Msrv,
) -> bool {
if let ExprKind::Call(src_func, [src_arg]) = src.kind
&& src_func.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome)
if let Some(src_arg) = as_some_expr(cx, src)
&& msrv.meets(cx, msrvs::OPTION_REPLACE)
{
// We do not have to check for a `const` context here, because `core::mem::replace()` and

View file

@ -1,13 +1,11 @@
use std::iter::once;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::res::{MaybeDef, MaybeQPath};
use clippy_utils::source::snippet;
use clippy_utils::ty::{ExprFnSig, expr_sig, ty_sig};
use clippy_utils::{get_expr_use_or_unification_node, std_or_core, sym};
use clippy_utils::{as_some_expr, get_expr_use_or_unification_node, is_none_expr, std_or_core, sym};
use rustc_errors::Applicability;
use rustc_hir::LangItem::{OptionNone, OptionSome};
use rustc_hir::hir_id::HirId;
use rustc_hir::{Expr, ExprKind, Node};
use rustc_lint::LateContext;
@ -68,15 +66,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method
let item = match recv.kind {
ExprKind::Array([]) => None,
ExprKind::Array([e]) => Some(e),
ExprKind::Path(ref p)
if cx
.qpath_res(p, recv.hir_id)
.ctor_parent(cx)
.is_lang_item(cx, OptionNone) =>
{
None
},
ExprKind::Call(f, [arg]) if f.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome) => Some(arg),
_ if is_none_expr(cx, recv) => None,
_ if let Some(arg) = as_some_expr(cx, recv) => Some(arg),
_ => return,
};
let iter_type = match method_name {

View file

@ -1,9 +1,10 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::is_none_expr;
use clippy_utils::res::{MaybeDef, MaybeQPath};
use clippy_utils::source::snippet;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::LangItem::{OptionNone, OptionSome};
use rustc_hir::LangItem::OptionSome;
use rustc_lint::LateContext;
use rustc_span::symbol::sym;
@ -48,7 +49,7 @@ pub(super) fn check<'tcx>(
return;
}
if !def_arg.res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone) {
if !is_none_expr(cx, def_arg) {
// nothing to lint!
return;
}

View file

@ -1,10 +1,10 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::peel_blocks;
use clippy_utils::res::{MaybeDef, MaybeQPath};
use clippy_utils::source::snippet;
use clippy_utils::{is_none_expr, peel_blocks};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::LangItem::{OptionNone, OptionSome};
use rustc_hir::LangItem::OptionSome;
use rustc_lint::LateContext;
use rustc_span::symbol::sym;
@ -25,7 +25,7 @@ pub(super) fn check<'tcx>(
&& let hir::ExprKind::Closure(&hir::Closure { body, .. }) = def_arg.kind
&& let body = cx.tcx.hir_body(body)
// And finally we check that we return a `None` in the "else case".
&& peel_blocks(body.value).res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone)
&& is_none_expr(cx, peel_blocks(body.value))
{
let msg = "called `map_or_else(|_| None, Some)` on a `Result` value";
let self_snippet = snippet(cx, recv.span, "..");

View file

@ -1,10 +1,10 @@
use super::utils::clone_or_copy_needed;
use clippy_utils::diagnostics::span_lint;
use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath, MaybeTypeckRes};
use clippy_utils::sym;
use clippy_utils::ty::{is_copy, option_arg_ty};
use clippy_utils::usage::mutated_variables;
use clippy_utils::visitors::{Descend, for_each_expr_without_closures};
use clippy_utils::{as_some_expr, sym};
use core::ops::ControlFlow;
use rustc_hir as hir;
use rustc_hir::LangItem::{OptionNone, OptionSome};
@ -47,8 +47,7 @@ pub(super) fn check<'tcx>(
let sugg = if !found_filtering {
// Check if the closure is .filter_map(|x| Some(x))
if kind.is_filter_map()
&& let hir::ExprKind::Call(expr, [arg]) = body.value.kind
&& expr.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome)
&& let Some(arg) = as_some_expr(cx, body.value)
&& let hir::ExprKind::Path(_) = arg.kind
{
span_lint(

View file

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::res::{MaybeDef, MaybeQPath};
use clippy_utils::{last_path_segment, sym};
use clippy_utils::{is_none_expr, last_path_segment, sym};
use rustc_errors::Applicability;
use rustc_hir::{self as hir, AmbigArg};
use rustc_lint::LateContext;
@ -56,7 +56,7 @@ pub(super) fn check(
} else {
return;
}
} else if init.res(cx).ctor_parent(cx).is_lang_item(cx, hir::LangItem::OptionNone) {
} else if is_none_expr(cx, init) {
let call_args: &[hir::Expr<'_>] = &[];
(sym::None, call_args, None)
} else {

View file

@ -131,7 +131,7 @@ use crate::ast_utils::unordered_over;
use crate::consts::{ConstEvalCtxt, Constant};
use crate::higher::Range;
use crate::msrvs::Msrv;
use crate::res::{MaybeDef, MaybeResPath};
use crate::res::{MaybeDef, MaybeQPath, MaybeResPath};
use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type};
use crate::visitors::for_each_expr_without_closures;
@ -300,6 +300,22 @@ pub fn is_lang_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: LangItem) ->
cx.tcx.lang_items().get(item) == Some(did)
}
/// Checks is `expr` is `None`
pub fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
expr.res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone)
}
/// If `expr` is `Some(inner)`, returns `inner`
pub fn as_some_expr<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
if let ExprKind::Call(e, [arg]) = expr.kind
&& e.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome)
{
Some(arg)
} else {
None
}
}
/// Checks if `expr` is an empty block or an empty tuple.
pub fn is_unit_expr(expr: &Expr<'_>) -> bool {
matches!(