From 892cb18e97772f98690722aa441d9ece4382f254 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 21 Oct 2025 17:02:16 +0200 Subject: [PATCH 1/3] clean-up --- clippy_lints/src/matches/manual_filter.rs | 4 +-- clippy_lints/src/matches/manual_utils.rs | 2 +- .../src/methods/unnecessary_literal_unwrap.rs | 28 ++++++++++--------- clippy_lints/src/non_canonical_impls.rs | 5 +--- clippy_lints/src/option_if_let_else.rs | 13 +++++---- 5 files changed, 27 insertions(+), 25 deletions(-) diff --git a/clippy_lints/src/matches/manual_filter.rs b/clippy_lints/src/matches/manual_filter.rs index d7224052ebc5..afa6f22ea75b 100644 --- a/clippy_lints/src/matches/manual_filter.rs +++ b/clippy_lints/src/matches/manual_filter.rs @@ -52,13 +52,13 @@ fn peels_blocks_incl_unsafe<'a>(expr: &'a Expr<'a>) -> &'a Expr<'a> { peels_blocks_incl_unsafe_opt(expr).unwrap_or(expr) } -// function called for each expression: +/// Checks whether resolves to `Some(target)` +// NOTE: called for each expression: // Some(x) => if { // // } else { // // } -// Returns true if resolves to `Some(x)`, `false` otherwise 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` diff --git a/clippy_lints/src/matches/manual_utils.rs b/clippy_lints/src/matches/manual_utils.rs index 235cb9e4ecce..1026bc49f1f6 100644 --- a/clippy_lints/src/matches/manual_utils.rs +++ b/clippy_lints/src/matches/manual_utils.rs @@ -281,7 +281,7 @@ pub(super) fn try_parse_pattern<'tcx>( f(cx, pat, 0, ctxt) } -// Checks for the `None` value. +/// 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) } diff --git a/clippy_lints/src/methods/unnecessary_literal_unwrap.rs b/clippy_lints/src/methods/unnecessary_literal_unwrap.rs index 410e973f855b..54d2062bfc12 100644 --- a/clippy_lints/src/methods/unnecessary_literal_unwrap.rs +++ b/clippy_lints/src/methods/unnecessary_literal_unwrap.rs @@ -23,6 +23,7 @@ fn get_ty_from_args<'a>(args: Option<&'a [hir::GenericArg<'a>]>, index: usize) - } } +#[expect(clippy::too_many_lines)] pub(super) fn check( cx: &LateContext<'_>, expr: &hir::Expr<'_>, @@ -38,19 +39,20 @@ pub(super) fn check( } let (constructor, call_args, ty) = if let hir::ExprKind::Call(call, call_args) = init.kind { - let Some((qpath, hir_id)) = call.opt_qpath() else { - return; - }; - - let args = last_path_segment(qpath).args.map(|args| args.args); - let res = cx.qpath_res(qpath, hir_id); - - if res.ctor_parent(cx).is_lang_item(cx, hir::LangItem::OptionSome) { - (sym::Some, call_args, get_ty_from_args(args, 0)) - } else if res.ctor_parent(cx).is_lang_item(cx, hir::LangItem::ResultOk) { - (sym::Ok, call_args, get_ty_from_args(args, 0)) - } else if res.ctor_parent(cx).is_lang_item(cx, hir::LangItem::ResultErr) { - (sym::Err, call_args, get_ty_from_args(args, 1)) + if let Some((qpath, hir_id)) = call.opt_qpath() + && let args = last_path_segment(qpath).args.map(|args| args.args) + && let Some(did) = cx.qpath_res(qpath, hir_id).ctor_parent(cx).opt_def_id() + { + let lang_items = cx.tcx.lang_items(); + if Some(did) == lang_items.option_some_variant() { + (sym::Some, call_args, get_ty_from_args(args, 0)) + } else if Some(did) == lang_items.result_ok_variant() { + (sym::Ok, call_args, get_ty_from_args(args, 0)) + } else if Some(did) == lang_items.result_err_variant() { + (sym::Err, call_args, get_ty_from_args(args, 1)) + } else { + return; + } } else { return; } diff --git a/clippy_lints/src/non_canonical_impls.rs b/clippy_lints/src/non_canonical_impls.rs index 3285023b34aa..0f821027fe75 100644 --- a/clippy_lints/src/non_canonical_impls.rs +++ b/clippy_lints/src/non_canonical_impls.rs @@ -354,10 +354,7 @@ fn self_cmp_call<'tcx>( needs_fully_qualified: &mut bool, ) -> bool { match cmp_expr.kind { - ExprKind::Call(path, [_, _]) => path - .res(typeck) - .opt_def_id() - .is_some_and(|def_id| cx.tcx.is_diagnostic_item(sym::ord_cmp_method, def_id)), + ExprKind::Call(path, [_, _]) => path.res(typeck).is_diag_item(cx, sym::ord_cmp_method), ExprKind::MethodCall(_, recv, [_], ..) => { let ExprKind::Path(path) = recv.kind else { return false; diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index c32c74a8fe60..da6da32889cb 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -10,7 +10,7 @@ use clippy_utils::{ }; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; -use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; +use rustc_hir::LangItem::{OptionNone, ResultErr}; use rustc_hir::def::Res; use rustc_hir::intravisit::{Visitor, walk_expr, walk_path}; use rustc_hir::{ @@ -313,11 +313,14 @@ impl<'tcx> Visitor<'tcx> for ReferenceVisitor<'_, 'tcx> { } fn try_get_inner_pat_and_is_result<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>) -> Option<(&'tcx Pat<'tcx>, bool)> { - if let PatKind::TupleStruct(ref qpath, [inner_pat], ..) = pat.kind { - let res = cx.qpath_res(qpath, pat.hir_id); - if res.ctor_parent(cx).is_lang_item(cx, OptionSome) { + if let PatKind::TupleStruct(ref qpath, [inner_pat], ..) = pat.kind + && let res = cx.qpath_res(qpath, pat.hir_id) + && let Some(did) = res.ctor_parent(cx).opt_def_id() + { + let lang_items = cx.tcx.lang_items(); + if Some(did) == lang_items.option_some_variant() { return Some((inner_pat, false)); - } else if res.ctor_parent(cx).is_lang_item(cx, ResultOk) { + } else if Some(did) == lang_items.result_ok_variant() { return Some((inner_pat, true)); } } From 59890a973646c609ee20e29e00d66601c6434ed0 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 21 Oct 2025 17:25:47 +0200 Subject: [PATCH 2/3] introduce `as_some_pattern` --- .../src/loops/while_let_on_iterator.rs | 7 ++--- clippy_lints/src/manual_option_as_slice.rs | 9 ++---- clippy_lints/src/match_result_ok.rs | 7 ++--- clippy_lints/src/matches/manual_utils.rs | 28 +++++-------------- clippy_lints/src/matches/match_as_ref.rs | 8 ++---- clippy_lints/src/option_if_let_else.rs | 16 +++-------- clippy_utils/src/lib.rs | 25 +++++++++++++---- 7 files changed, 42 insertions(+), 58 deletions(-) diff --git a/clippy_lints/src/loops/while_let_on_iterator.rs b/clippy_lints/src/loops/while_let_on_iterator.rs index 3ea6ba341bed..2545f81f1afa 100644 --- a/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/clippy_lints/src/loops/while_let_on_iterator.rs @@ -5,11 +5,11 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::visitors::is_res_used; -use clippy_utils::{get_enclosing_loop_or_multi_call_closure, higher, is_refutable}; +use clippy_utils::{as_some_pattern, get_enclosing_loop_or_multi_call_closure, higher, is_refutable}; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::intravisit::{Visitor, walk_expr}; -use rustc_hir::{Closure, Expr, ExprKind, HirId, LangItem, LetStmt, Mutability, PatKind, UnOp}; +use rustc_hir::{Closure, Expr, ExprKind, HirId, LetStmt, Mutability, UnOp}; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter::OnlyBodies; use rustc_middle::ty::adjustment::Adjust; @@ -19,8 +19,7 @@ use rustc_span::symbol::sym; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let Some(higher::WhileLet { if_then, let_pat, let_expr, label, .. }) = higher::WhileLet::hir(expr) // check for `Some(..)` pattern - && let PatKind::TupleStruct(ref pat_path, some_pat, _) = let_pat.kind - && cx.qpath_res(pat_path, let_pat.hir_id).ctor_parent(cx).is_lang_item(cx, LangItem::OptionSome) + && let Some(some_pat) = as_some_pattern(cx, let_pat) // check for call to `Iterator::next` && let ExprKind::MethodCall(method_name, iter_expr, [], _) = let_expr.kind && method_name.ident.name == sym::next diff --git a/clippy_lints/src/manual_option_as_slice.rs b/clippy_lints/src/manual_option_as_slice.rs index 3342bf834785..9cf2d5d85433 100644 --- a/clippy_lints/src/manual_option_as_slice.rs +++ b/clippy_lints/src/manual_option_as_slice.rs @@ -3,10 +3,9 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::Msrv; use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; use clippy_utils::source::snippet_with_context; -use clippy_utils::{is_none_pattern, msrvs, peel_hir_expr_refs, sym}; +use clippy_utils::{as_some_pattern, is_none_pattern, msrvs, peel_hir_expr_refs, sym}; use rustc_errors::Applicability; -use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{Arm, Expr, ExprKind, LangItem, Pat, PatKind, QPath, is_range_literal}; +use rustc_hir::{Arm, Expr, ExprKind, Pat, PatKind, QPath, is_range_literal}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; use rustc_span::{Span, Symbol}; @@ -154,10 +153,8 @@ fn check_as_ref(cx: &LateContext<'_>, expr: &Expr<'_>, span: Span, msrv: Msrv) { } fn extract_ident_from_some_pat(cx: &LateContext<'_>, pat: &Pat<'_>) -> Option { - if let PatKind::TupleStruct(QPath::Resolved(None, path), [binding], _) = pat.kind - && let Res::Def(DefKind::Ctor(..), def_id) = path.res + if let Some([binding]) = as_some_pattern(cx, pat) && let PatKind::Binding(_mode, _hir_id, ident, _inner_pat) = binding.kind - && clippy_utils::is_lang_item_or_ctor(cx, def_id, LangItem::OptionSome) { Some(ident.name) } else { diff --git a/clippy_lints/src/match_result_ok.rs b/clippy_lints/src/match_result_ok.rs index fb83f7cf65dd..1ebbd209ae52 100644 --- a/clippy_lints/src/match_result_ok.rs +++ b/clippy_lints/src/match_result_ok.rs @@ -1,9 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_context; -use clippy_utils::{higher, sym}; +use clippy_utils::{as_some_pattern, higher, sym}; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, LangItem, PatKind}; +use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -55,10 +55,9 @@ impl<'tcx> LateLintPass<'tcx> for MatchResultOk { }; if let ExprKind::MethodCall(ok_path, recv, [], ..) = let_expr.kind //check is expr.ok() has type Result.ok(, _) - && let PatKind::TupleStruct(ref pat_path, [ok_pat], _) = let_pat.kind //get operation && ok_path.ident.name == sym::ok && cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result) - && cx.qpath_res(pat_path, let_pat.hir_id).ctor_parent(cx).is_lang_item(cx, LangItem::OptionSome) + && let Some([ok_pat]) = as_some_pattern(cx, let_pat) //get operation && let ctxt = expr.span.ctxt() && let_expr.span.ctxt() == ctxt && let_pat.span.ctxt() == ctxt diff --git a/clippy_lints/src/matches/manual_utils.rs b/clippy_lints/src/matches/manual_utils.rs index 1026bc49f1f6..485d6bdc6ee8 100644 --- a/clippy_lints/src/matches/manual_utils.rs +++ b/clippy_lints/src/matches/manual_utils.rs @@ -5,14 +5,14 @@ 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, can_move_expr_to_closure, expr_requires_coercion, is_else_clause, is_lint_allowed, peel_blocks, - peel_hir_expr_refs, peel_hir_expr_while, + 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, }; use rustc_ast::util::parser::ExprPrecedence; use rustc_errors::Applicability; -use rustc_hir::LangItem::{OptionNone, OptionSome}; +use rustc_hir::LangItem::OptionNone; use rustc_hir::def::Res; -use rustc_hir::{BindingMode, Expr, ExprKind, HirId, Mutability, Pat, PatExpr, PatExprKind, PatKind, Path, QPath}; +use rustc_hir::{BindingMode, Expr, ExprKind, HirId, Mutability, Pat, PatKind, Path, QPath}; use rustc_lint::LateContext; use rustc_span::{SyntaxContext, sym}; @@ -255,23 +255,9 @@ pub(super) fn try_parse_pattern<'tcx>( match pat.kind { PatKind::Wild => Some(OptionPat::Wild), PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt), - PatKind::Expr(PatExpr { - kind: PatExprKind::Path(qpath), - hir_id, - .. - }) if cx - .qpath_res(qpath, *hir_id) - .ctor_parent(cx) - .is_lang_item(cx, OptionNone) => - { - Some(OptionPat::None) - }, - PatKind::TupleStruct(ref qpath, [pattern], _) - if cx - .qpath_res(qpath, pat.hir_id) - .ctor_parent(cx) - .is_lang_item(cx, OptionSome) - && pat.span.ctxt() == ctxt => + _ if is_none_pattern(cx, pat) => Some(OptionPat::None), + _ if let Some([pattern]) = as_some_pattern(cx, pat) + && pat.span.ctxt() == ctxt => { Some(OptionPat::Some { pattern, ref_count }) }, diff --git a/clippy_lints/src/matches/match_as_ref.rs b/clippy_lints/src/matches/match_as_ref.rs index 7ac57fae690c..f633c5839739 100644 --- a/clippy_lints/src/matches/match_as_ref.rs +++ b/clippy_lints/src/matches/match_as_ref.rs @@ -2,7 +2,7 @@ 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::{is_none_arm, peel_blocks}; +use clippy_utils::{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_lint::LateContext; @@ -82,11 +82,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: // Checks if arm has the form `Some(ref v) => Some(v)` (checks for `ref` and `ref mut`) fn as_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option { - if let PatKind::TupleStruct(ref qpath, [first_pat, ..], _) = arm.pat.kind - && cx - .qpath_res(qpath, arm.pat.hir_id) - .ctor_parent(cx) - .is_lang_item(cx, LangItem::OptionSome) + 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) diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index da6da32889cb..85cf483fce90 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -6,16 +6,15 @@ use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_copy; use clippy_utils::{ CaptureKind, can_move_expr_to_closure, eager_or_lazy, expr_requires_coercion, higher, is_else_clause, - is_in_const_context, peel_blocks, peel_hir_expr_while, + is_in_const_context, is_none_pattern, peel_blocks, peel_hir_expr_while, }; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; -use rustc_hir::LangItem::{OptionNone, ResultErr}; +use rustc_hir::LangItem::ResultErr; use rustc_hir::def::Res; use rustc_hir::intravisit::{Visitor, walk_expr, walk_path}; use rustc_hir::{ - Arm, BindingMode, Expr, ExprKind, HirId, MatchSource, Mutability, Node, Pat, PatExpr, PatExprKind, PatKind, Path, - QPath, UnOp, + Arm, BindingMode, Expr, ExprKind, HirId, MatchSource, Mutability, Node, Pat, PatKind, Path, QPath, UnOp, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; @@ -379,14 +378,7 @@ fn try_convert_match<'tcx>( fn is_none_or_err_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { match arm.pat.kind { - PatKind::Expr(PatExpr { - kind: PatExprKind::Path(qpath), - hir_id, - .. - }) => cx - .qpath_res(qpath, *hir_id) - .ctor_parent(cx) - .is_lang_item(cx, OptionNone), + _ if is_none_pattern(cx, arm.pat) => true, PatKind::TupleStruct(ref qpath, [first_pat], _) => { cx.qpath_res(qpath, arm.pat.hir_id) .ctor_parent(cx) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index c8943523e179..e1ba40934620 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -320,6 +320,25 @@ pub fn is_wild(pat: &Pat<'_>) -> bool { matches!(pat.kind, PatKind::Wild) } +/// If `pat` is: +/// - `Some(inner)`, returns `inner` +/// - it will _usually_ contain just one element, but could have two, given patterns like +/// `Some(inner, ..)` or `Some(.., inner)` +/// - `Some`, returns `[]` +/// - otherwise, returns `None` +pub fn as_some_pattern<'a, 'hir>(cx: &LateContext<'_>, pat: &'a Pat<'hir>) -> Option<&'a [Pat<'hir>]> { + if let PatKind::TupleStruct(ref qpath, inner, _) = pat.kind + && cx + .qpath_res(qpath, pat.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, OptionSome) + { + Some(inner) + } else { + None + } +} + /// Checks if the `pat` is `None`. pub fn is_none_pattern(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { matches!(pat.kind, @@ -2783,11 +2802,7 @@ pub fn pat_and_expr_can_be_question_mark<'a, 'hir>( pat: &'a Pat<'hir>, else_body: &Expr<'_>, ) -> Option<&'a Pat<'hir>> { - if let PatKind::TupleStruct(pat_path, [inner_pat], _) = pat.kind - && cx - .qpath_res(&pat_path, pat.hir_id) - .ctor_parent(cx) - .is_lang_item(cx, OptionSome) + if let Some([inner_pat]) = as_some_pattern(cx, pat) && !is_refutable(cx, inner_pat) && let else_body = peel_blocks(else_body) && let ExprKind::Ret(Some(ret_val)) = else_body.kind From 605cc879bceed280865054abb4ca268fe0f606a7 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 21 Oct 2025 16:27:32 +0200 Subject: [PATCH 3/3] introduce `as_some_expr`, `is_none_expr` --- clippy_lints/src/if_then_some_else_none.rs | 10 ++++------ clippy_lints/src/loops/manual_find.rs | 5 ++--- clippy_lints/src/matches/manual_filter.rs | 9 ++++----- clippy_lints/src/matches/manual_ok_err.rs | 11 +++++------ clippy_lints/src/matches/manual_utils.rs | 17 ++++++++--------- clippy_lints/src/matches/match_as_ref.rs | 8 +++----- clippy_lints/src/mem_replace.rs | 12 ++++++------ .../iter_on_single_or_empty_collections.rs | 15 +++------------ clippy_lints/src/methods/option_map_or_none.rs | 5 +++-- .../src/methods/result_map_or_else_none.rs | 6 +++--- .../src/methods/unnecessary_filter_map.rs | 5 ++--- .../src/methods/unnecessary_literal_unwrap.rs | 4 ++-- clippy_utils/src/lib.rs | 18 +++++++++++++++++- 13 files changed, 62 insertions(+), 63 deletions(-) diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs index dfc8411baa00..7f3ef58c93d1 100644 --- a/clippy_lints/src/if_then_some_else_none.rs +++ b/clippy_lints/src/if_then_some_else_none.rs @@ -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) diff --git a/clippy_lints/src/loops/manual_find.rs b/clippy_lints/src/loops/manual_find.rs index c38cf83f4410..34917252d049 100644 --- a/clippy_lints/src/loops/manual_find.rs +++ b/clippy_lints/src/loops/manual_find.rs @@ -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) diff --git a/clippy_lints/src/matches/manual_filter.rs b/clippy_lints/src/matches/manual_filter.rs index afa6f22ea75b..da68f8421c16 100644 --- a/clippy_lints/src/matches/manual_filter.rs +++ b/clippy_lints/src/matches/manual_filter.rs @@ -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 } diff --git a/clippy_lints/src/matches/manual_ok_err.rs b/clippy_lints/src/matches/manual_ok_err.rs index c9293412fba8..3259655c9a1f 100644 --- a/clippy_lints/src/matches/manual_ok_err.rs +++ b/clippy_lints/src/matches/manual_ok_err.rs @@ -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 diff --git a/clippy_lints/src/matches/manual_utils.rs b/clippy_lints/src/matches/manual_utils.rs index 485d6bdc6ee8..09e49c16624c 100644 --- a/clippy_lints/src/matches/manual_utils.rs +++ b/clippy_lints/src/matches/manual_utils.rs @@ -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)) } diff --git a/clippy_lints/src/matches/match_as_ref.rs b/clippy_lints/src/matches/match_as_ref.rs index f633c5839739..30d703df4df4 100644 --- a/clippy_lints/src/matches/match_as_ref.rs +++ b/clippy_lints/src/matches/match_as_ref.rs @@ -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 { 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 diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs index ac3cbaec55f3..0f32f89666a0 100644 --- a/clippy_lints/src/mem_replace.rs +++ b/clippy_lints/src/mem_replace.rs @@ -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 diff --git a/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs b/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs index 8183c30f8c56..cdef98be14af 100644 --- a/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs +++ b/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs @@ -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 { diff --git a/clippy_lints/src/methods/option_map_or_none.rs b/clippy_lints/src/methods/option_map_or_none.rs index 342ffea51d65..817388915f18 100644 --- a/clippy_lints/src/methods/option_map_or_none.rs +++ b/clippy_lints/src/methods/option_map_or_none.rs @@ -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; } diff --git a/clippy_lints/src/methods/result_map_or_else_none.rs b/clippy_lints/src/methods/result_map_or_else_none.rs index e2946c22a46b..d5477b9be4c1 100644 --- a/clippy_lints/src/methods/result_map_or_else_none.rs +++ b/clippy_lints/src/methods/result_map_or_else_none.rs @@ -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, ".."); diff --git a/clippy_lints/src/methods/unnecessary_filter_map.rs b/clippy_lints/src/methods/unnecessary_filter_map.rs index 7f729ac7ca94..72f1c42da2ee 100644 --- a/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -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( diff --git a/clippy_lints/src/methods/unnecessary_literal_unwrap.rs b/clippy_lints/src/methods/unnecessary_literal_unwrap.rs index 54d2062bfc12..da6f03931e24 100644 --- a/clippy_lints/src/methods/unnecessary_literal_unwrap.rs +++ b/clippy_lints/src/methods/unnecessary_literal_unwrap.rs @@ -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 { diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index e1ba40934620..cfe2442461ca 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -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!(