From ca5b59ab66e3ba99a1b9d30c69003c2713682f25 Mon Sep 17 00:00:00 2001 From: Jacherr Date: Thu, 9 Nov 2023 23:00:20 +0000 Subject: [PATCH] new lint `unnecessary_map_or` --- CHANGELOG.md | 1 + clippy_dev/src/setup/vscode.rs | 2 +- clippy_lints/src/attrs/useless_attribute.rs | 2 +- clippy_lints/src/attrs/utils.rs | 4 +- clippy_lints/src/bool_assert_comparison.rs | 4 +- clippy_lints/src/booleans.rs | 2 +- clippy_lints/src/box_default.rs | 8 +- clippy_lints/src/casts/unnecessary_cast.rs | 3 +- clippy_lints/src/comparison_chain.rs | 2 +- clippy_lints/src/copies.rs | 16 +-- clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/default_numeric_fallback.rs | 2 +- clippy_lints/src/derivable_impls.rs | 2 +- clippy_lints/src/derive.rs | 2 +- clippy_lints/src/doc/missing_headers.rs | 2 +- clippy_lints/src/drop_forget_ref.rs | 2 +- clippy_lints/src/eta_reduction.rs | 2 +- .../src/functions/not_unsafe_ptr_arg_deref.rs | 2 +- clippy_lints/src/infinite_iter.rs | 8 +- clippy_lints/src/item_name_repetitions.rs | 4 +- .../src/iter_not_returning_iterator.rs | 2 +- clippy_lints/src/len_zero.rs | 2 +- clippy_lints/src/lifetimes.rs | 2 +- clippy_lints/src/loops/manual_find.rs | 2 +- clippy_lints/src/loops/manual_memcpy.rs | 2 +- clippy_lints/src/loops/mut_range_bound.rs | 2 +- clippy_lints/src/loops/same_item_push.rs | 2 +- clippy_lints/src/loops/utils.rs | 7 +- clippy_lints/src/manual_clamp.rs | 2 +- clippy_lints/src/manual_strip.rs | 4 +- .../src/matches/match_like_matches.rs | 2 +- .../src/matches/redundant_pattern_match.rs | 2 +- clippy_lints/src/matches/single_match.rs | 2 +- clippy_lints/src/matches/try_err.rs | 2 +- clippy_lints/src/methods/expect_fun_call.rs | 2 +- clippy_lints/src/methods/filter_next.rs | 7 +- clippy_lints/src/methods/manual_next_back.rs | 4 +- clippy_lints/src/methods/manual_str_repeat.rs | 4 +- clippy_lints/src/methods/map_clone.rs | 2 +- clippy_lints/src/methods/mod.rs | 32 ++++- clippy_lints/src/methods/needless_collect.rs | 2 +- .../src/methods/option_as_ref_deref.rs | 2 +- clippy_lints/src/methods/or_fun_call.rs | 4 +- clippy_lints/src/methods/str_splitn.rs | 2 +- .../src/methods/unnecessary_map_or.rs | 131 ++++++++++++++++++ .../src/methods/unnecessary_sort_by.rs | 7 +- clippy_lints/src/methods/useless_asref.rs | 2 +- clippy_lints/src/misc_early/mod.rs | 2 +- .../src/needless_borrows_for_generic_args.rs | 10 +- clippy_lints/src/needless_continue.rs | 4 +- clippy_lints/src/nonstandard_macro_braces.rs | 2 +- clippy_lints/src/only_used_in_recursion.rs | 4 +- .../src/operators/arithmetic_side_effects.rs | 2 +- clippy_lints/src/operators/cmp_owned.rs | 6 +- .../src/operators/numeric_arithmetic.rs | 2 +- clippy_lints/src/pass_by_ref_or_value.rs | 2 +- clippy_lints/src/ptr.rs | 5 +- clippy_lints/src/question_mark.rs | 2 +- clippy_lints/src/redundant_slicing.rs | 4 +- clippy_lints/src/swap.rs | 4 +- clippy_lints/src/types/borrowed_box.rs | 3 +- clippy_lints/src/unit_types/let_unit_value.rs | 2 +- clippy_lints/src/vec_init_then_push.rs | 2 +- clippy_lints/src/write.rs | 2 +- clippy_utils/src/check_proc_macro.rs | 6 +- clippy_utils/src/hir_utils.rs | 8 +- clippy_utils/src/lib.rs | 31 ++--- clippy_utils/src/numeric_literal.rs | 2 +- clippy_utils/src/sugg.rs | 2 +- clippy_utils/src/ty.rs | 56 +++++++- clippy_utils/src/visitors.rs | 10 +- tests/ui-internal/unnecessary_def_path.fixed | 1 + tests/ui-internal/unnecessary_def_path.rs | 1 + tests/ui-internal/unnecessary_def_path.stderr | 30 ++-- ...sensitive_file_extension_comparisons.fixed | 1 + ...se_sensitive_file_extension_comparisons.rs | 1 + ...ensitive_file_extension_comparisons.stderr | 12 +- tests/ui/unnecessary_map_or.fixed | 64 +++++++++ tests/ui/unnecessary_map_or.rs | 67 +++++++++ tests/ui/unnecessary_map_or.stderr | 99 +++++++++++++ 80 files changed, 590 insertions(+), 166 deletions(-) create mode 100644 clippy_lints/src/methods/unnecessary_map_or.rs create mode 100644 tests/ui/unnecessary_map_or.fixed create mode 100644 tests/ui/unnecessary_map_or.rs create mode 100644 tests/ui/unnecessary_map_or.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 161fa630ed47..dd3124ee9a3b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6071,6 +6071,7 @@ Released 2018-09-13 [`unnecessary_literal_bound`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_literal_bound [`unnecessary_literal_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_literal_unwrap [`unnecessary_map_on_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_map_on_constructor +[`unnecessary_map_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_map_or [`unnecessary_min_or_max`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_min_or_max [`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed [`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation diff --git a/clippy_dev/src/setup/vscode.rs b/clippy_dev/src/setup/vscode.rs index 204f4af2cf1b..a37c873eed4f 100644 --- a/clippy_dev/src/setup/vscode.rs +++ b/clippy_dev/src/setup/vscode.rs @@ -84,7 +84,7 @@ fn delete_vs_task_file(path: &Path) -> bool { /// It may fail silently. fn try_delete_vs_directory_if_empty() { let path = Path::new(VSCODE_DIR); - if path.read_dir().map_or(false, |mut iter| iter.next().is_none()) { + if path.read_dir().is_ok_and(|mut iter| iter.next().is_none()) { // The directory is empty. We just try to delete it but allow a silence // fail as an empty `.vscode` directory is still valid let _silence_result = fs::remove_dir(path); diff --git a/clippy_lints/src/attrs/useless_attribute.rs b/clippy_lints/src/attrs/useless_attribute.rs index dc5a68570896..e21853598c3b 100644 --- a/clippy_lints/src/attrs/useless_attribute.rs +++ b/clippy_lints/src/attrs/useless_attribute.rs @@ -16,7 +16,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, item: &Item, attrs: &[Attribute]) { return; } if let Some(lint_list) = &attr.meta_item_list() { - if attr.ident().map_or(false, |ident| is_lint_level(ident.name, attr.id)) { + if attr.ident().is_some_and(|ident| is_lint_level(ident.name, attr.id)) { for lint in lint_list { match item.kind { ItemKind::Use(..) => { diff --git a/clippy_lints/src/attrs/utils.rs b/clippy_lints/src/attrs/utils.rs index 9b10ae836514..3bb02688bf22 100644 --- a/clippy_lints/src/attrs/utils.rs +++ b/clippy_lints/src/attrs/utils.rs @@ -50,7 +50,7 @@ fn is_relevant_block(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_ block .expr .as_ref() - .map_or(false, |e| is_relevant_expr(cx, typeck_results, e)), + .is_some_and(|e| is_relevant_expr(cx, typeck_results, e)), |stmt| match &stmt.kind { StmtKind::Let(_) => true, StmtKind::Expr(expr) | StmtKind::Semi(expr) => is_relevant_expr(cx, typeck_results, expr), @@ -60,7 +60,7 @@ fn is_relevant_block(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_ } fn is_relevant_expr(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_>, expr: &Expr<'_>) -> bool { - if macro_backtrace(expr.span).last().map_or(false, |macro_call| { + if macro_backtrace(expr.span).last().is_some_and(|macro_call| { is_panic(cx, macro_call.def_id) || cx.tcx.item_name(macro_call.def_id) == sym::unreachable }) { return false; diff --git a/clippy_lints/src/bool_assert_comparison.rs b/clippy_lints/src/bool_assert_comparison.rs index db5792188dd4..7d89195eeca7 100644 --- a/clippy_lints/src/bool_assert_comparison.rs +++ b/clippy_lints/src/bool_assert_comparison.rs @@ -60,8 +60,8 @@ fn is_impl_not_trait_with_bool_out<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) - trait_id, ) }) - .map_or(false, |assoc_item| { - let proj = Ty::new_projection_from_args(cx.tcx, assoc_item.def_id, cx.tcx.mk_args_trait(ty, [])); + .is_some_and(|assoc_item| { + let proj = Ty::new_projection(cx.tcx, assoc_item.def_id, cx.tcx.mk_args_trait(ty, [])); let nty = cx.tcx.normalize_erasing_regions(cx.param_env, proj); nty.is_bool() diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index 26a20bc99a05..896bd5fd03d5 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -667,5 +667,5 @@ fn implements_ord(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let ty = cx.typeck_results().expr_ty(expr); cx.tcx .get_diagnostic_item(sym::Ord) - .map_or(false, |id| implements_trait(cx, ty, id, &[])) + .is_some_and(|id| implements_trait(cx, ty, id, &[])) } diff --git a/clippy_lints/src/box_default.rs b/clippy_lints/src/box_default.rs index bf1d077fec28..a1ca23e65ff1 100644 --- a/clippy_lints/src/box_default.rs +++ b/clippy_lints/src/box_default.rs @@ -45,7 +45,7 @@ impl LateLintPass<'_> for BoxDefault { // And that method is `new` && seg.ident.name == sym::new // And the call is that of a `Box` method - && path_def_id(cx, ty).map_or(false, |id| Some(id) == cx.tcx.lang_items().owned_box()) + && path_def_id(cx, ty).is_some_and(|id| Some(id) == cx.tcx.lang_items().owned_box()) // And the single argument to the call is another function call // This is the `T::default()` (or default equivalent) of `Box::new(T::default())` && let ExprKind::Call(arg_path, _) = arg.kind @@ -83,9 +83,9 @@ fn is_plain_default(cx: &LateContext<'_>, arg_path: &Expr<'_>) -> bool { } fn is_local_vec_expn(cx: &LateContext<'_>, expr: &Expr<'_>, ref_expr: &Expr<'_>) -> bool { - macro_backtrace(expr.span).next().map_or(false, |call| { - cx.tcx.is_diagnostic_item(sym::vec_macro, call.def_id) && call.span.eq_ctxt(ref_expr.span) - }) + macro_backtrace(expr.span) + .next() + .is_some_and(|call| cx.tcx.is_diagnostic_item(sym::vec_macro, call.def_id) && call.span.eq_ctxt(ref_expr.span)) } #[derive(Default)] diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs index abd80abffe69..332e897def7e 100644 --- a/clippy_lints/src/casts/unnecessary_cast.rs +++ b/clippy_lints/src/casts/unnecessary_cast.rs @@ -159,8 +159,7 @@ pub(super) fn check<'tcx>( // The same is true if the expression encompassing the cast expression is a unary // expression or an addressof expression. let needs_block = matches!(cast_expr.kind, ExprKind::Unary(..) | ExprKind::AddrOf(..)) - || get_parent_expr(cx, expr) - .map_or(false, |e| matches!(e.kind, ExprKind::Unary(..) | ExprKind::AddrOf(..))); + || get_parent_expr(cx, expr).is_some_and(|e| matches!(e.kind, ExprKind::Unary(..) | ExprKind::AddrOf(..))); span_lint_and_sugg( cx, diff --git a/clippy_lints/src/comparison_chain.rs b/clippy_lints/src/comparison_chain.rs index b9baf9af248e..c85e3500ebdc 100644 --- a/clippy_lints/src/comparison_chain.rs +++ b/clippy_lints/src/comparison_chain.rs @@ -110,7 +110,7 @@ impl<'tcx> LateLintPass<'tcx> for ComparisonChain { let is_ord = cx .tcx .get_diagnostic_item(sym::Ord) - .map_or(false, |id| implements_trait(cx, ty, id, &[])); + .is_some_and(|id| implements_trait(cx, ty, id, &[])); if !is_ord { return; diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index c4afdc757d81..3ecd36d3711f 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -345,7 +345,7 @@ fn eq_binding_names(s: &Stmt<'_>, names: &[(HirId, Symbol)]) -> bool { let mut i = 0usize; let mut res = true; l.pat.each_binding_or_first(&mut |_, _, _, name| { - if names.get(i).map_or(false, |&(_, n)| n == name.name) { + if names.get(i).is_some_and(|&(_, n)| n == name.name) { i += 1; } else { res = false; @@ -389,12 +389,10 @@ fn eq_stmts( let new_bindings = &moved_bindings[old_count..]; blocks .iter() - .all(|b| get_stmt(b).map_or(false, |s| eq_binding_names(s, new_bindings))) + .all(|b| get_stmt(b).is_some_and(|s| eq_binding_names(s, new_bindings))) } else { true - }) && blocks - .iter() - .all(|b| get_stmt(b).map_or(false, |s| eq.eq_stmt(s, stmt))) + }) && blocks.iter().all(|b| get_stmt(b).is_some_and(|s| eq.eq_stmt(s, stmt))) } #[expect(clippy::too_many_lines)] @@ -451,9 +449,7 @@ fn scan_block_for_eq<'tcx>( // x + 50 let expr_hash_eq = if let Some(e) = block.expr { let hash = hash_expr(cx, e); - blocks - .iter() - .all(|b| b.expr.map_or(false, |e| hash_expr(cx, e) == hash)) + blocks.iter().all(|b| b.expr.is_some_and(|e| hash_expr(cx, e) == hash)) } else { blocks.iter().all(|b| b.expr.is_none()) }; @@ -514,7 +510,7 @@ fn scan_block_for_eq<'tcx>( }); if let Some(e) = block.expr { for block in blocks { - if block.expr.map_or(false, |expr| !eq.eq_expr(expr, e)) { + if block.expr.is_some_and(|expr| !eq.eq_expr(expr, e)) { moved_locals.truncate(moved_locals_at_start); return BlockEq { start_end_eq, @@ -533,7 +529,7 @@ fn scan_block_for_eq<'tcx>( } fn check_for_warn_of_moved_symbol(cx: &LateContext<'_>, symbols: &[(HirId, Symbol)], if_expr: &Expr<'_>) -> bool { - get_enclosing_block(cx, if_expr.hir_id).map_or(false, |block| { + get_enclosing_block(cx, if_expr.hir_id).is_some_and(|block| { let ignore_span = block.span.shrink_to_lo().to(if_expr.span); symbols diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index edb52851e0cb..dff60f76b746 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -481,6 +481,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::methods::UNNECESSARY_JOIN_INFO, crate::methods::UNNECESSARY_LAZY_EVALUATIONS_INFO, crate::methods::UNNECESSARY_LITERAL_UNWRAP_INFO, + crate::methods::UNNECESSARY_MAP_OR_INFO, crate::methods::UNNECESSARY_MIN_OR_MAX_INFO, crate::methods::UNNECESSARY_RESULT_MAP_OR_ELSE_INFO, crate::methods::UNNECESSARY_SORT_BY_INFO, diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs index 4808c372754c..ef6b141920d0 100644 --- a/clippy_lints/src/default_numeric_fallback.rs +++ b/clippy_lints/src/default_numeric_fallback.rs @@ -253,6 +253,6 @@ impl<'tcx> From> for ExplicitTyBound { impl<'tcx> From>> for ExplicitTyBound { fn from(v: Option>) -> Self { - Self(v.map_or(false, Ty::is_numeric)) + Self(v.is_some_and(Ty::is_numeric)) } } diff --git a/clippy_lints/src/derivable_impls.rs b/clippy_lints/src/derivable_impls.rs index 2b6bfafe695b..767dda552bcd 100644 --- a/clippy_lints/src/derivable_impls.rs +++ b/clippy_lints/src/derivable_impls.rs @@ -81,7 +81,7 @@ fn is_path_self(e: &Expr<'_>) -> bool { fn contains_trait_object(ty: Ty<'_>) -> bool { match ty.kind() { ty::Ref(_, ty, _) => contains_trait_object(*ty), - ty::Adt(def, args) => def.is_box() && args[0].as_type().map_or(false, contains_trait_object), + ty::Adt(def, args) => def.is_box() && args[0].as_type().is_some_and(contains_trait_object), ty::Dynamic(..) => true, _ => false, } diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index e569c4dc7863..0db6a822ec05 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -327,7 +327,7 @@ fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &h // there's a Copy impl for any instance of the adt. if !is_copy(cx, ty) { if ty_subs.non_erasable_generics().next().is_some() { - let has_copy_impl = cx.tcx.all_local_trait_impls(()).get(©_id).map_or(false, |impls| { + let has_copy_impl = cx.tcx.all_local_trait_impls(()).get(©_id).is_some_and(|impls| { impls.iter().any(|&id| { matches!(cx.tcx.type_of(id).instantiate_identity().kind(), ty::Adt(adt, _) if ty_adt.did() == adt.did()) diff --git a/clippy_lints/src/doc/missing_headers.rs b/clippy_lints/src/doc/missing_headers.rs index c9173030a55e..40377dd841e0 100644 --- a/clippy_lints/src/doc/missing_headers.rs +++ b/clippy_lints/src/doc/missing_headers.rs @@ -47,7 +47,7 @@ pub fn check( ), _ => (), } - if !headers.panics && panic_info.map_or(false, |el| !el.1) { + if !headers.panics && panic_info.is_some_and(|el| !el.1) { span_lint_and_note( cx, MISSING_PANICS_DOC, diff --git a/clippy_lints/src/drop_forget_ref.rs b/clippy_lints/src/drop_forget_ref.rs index c7dd7292a14b..55afdbf22e1f 100644 --- a/clippy_lints/src/drop_forget_ref.rs +++ b/clippy_lints/src/drop_forget_ref.rs @@ -112,7 +112,7 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef { MEM_FORGET, Cow::Owned(format!( "usage of `mem::forget` on {}", - if arg_ty.ty_adt_def().map_or(false, |def| def.has_dtor(cx.tcx)) { + if arg_ty.ty_adt_def().is_some_and(|def| def.has_dtor(cx.tcx)) { "`Drop` type" } else { "type with `Drop` fields" diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index de10b7bf5339..6c87a05ace39 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -196,7 +196,7 @@ fn check_clousure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tc { span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| { if let Some(mut snippet) = snippet_opt(cx, callee.span) { - if path_to_local(callee).map_or(false, |l| { + if path_to_local(callee).is_some_and(|l| { // FIXME: Do we really need this `local_used_in` check? // Isn't it checking something like... `callee(callee)`? // If somehow this check is needed, add some test for it, diff --git a/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs b/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs index c2b40ae01d4c..4986a311eba8 100644 --- a/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs +++ b/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs @@ -87,7 +87,7 @@ fn raw_ptr_arg(cx: &LateContext<'_>, arg: &hir::Param<'_>) -> Option { } fn check_arg(cx: &LateContext<'_>, raw_ptrs: &HirIdSet, arg: &hir::Expr<'_>) { - if path_to_local(arg).map_or(false, |id| raw_ptrs.contains(&id)) { + if path_to_local(arg).is_some_and(|id| raw_ptrs.contains(&id)) { span_lint( cx, NOT_UNSAFE_PTR_ARG_DEREF, diff --git a/clippy_lints/src/infinite_iter.rs b/clippy_lints/src/infinite_iter.rs index 48874d6064b6..d3aade31f14f 100644 --- a/clippy_lints/src/infinite_iter.rs +++ b/clippy_lints/src/infinite_iter.rs @@ -171,13 +171,13 @@ fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { if let ExprKind::Path(ref qpath) = path.kind { cx.qpath_res(qpath, path.hir_id) .opt_def_id() - .map_or(false, |id| cx.tcx.is_diagnostic_item(sym::iter_repeat, id)) + .is_some_and(|id| cx.tcx.is_diagnostic_item(sym::iter_repeat, id)) .into() } else { Finite } }, - ExprKind::Struct(..) => higher::Range::hir(expr).map_or(false, |r| r.end.is_none()).into(), + ExprKind::Struct(..) => higher::Range::hir(expr).is_some_and(|r| r.end.is_none()).into(), _ => Finite, } } @@ -228,9 +228,7 @@ fn complete_infinite_iter(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { let not_double_ended = cx .tcx .get_diagnostic_item(sym::DoubleEndedIterator) - .map_or(false, |id| { - !implements_trait(cx, cx.typeck_results().expr_ty(receiver), id, &[]) - }); + .is_some_and(|id| !implements_trait(cx, cx.typeck_results().expr_ty(receiver), id, &[])); if not_double_ended { return is_infinite(cx, receiver); } diff --git a/clippy_lints/src/item_name_repetitions.rs b/clippy_lints/src/item_name_repetitions.rs index dd90e2a6e94f..6363f717a5c2 100644 --- a/clippy_lints/src/item_name_repetitions.rs +++ b/clippy_lints/src/item_name_repetitions.rs @@ -309,8 +309,8 @@ fn check_enum_start(cx: &LateContext<'_>, item_name: &str, variant: &Variant<'_> let item_name_chars = item_name.chars().count(); if count_match_start(item_name, name).char_count == item_name_chars - && name.chars().nth(item_name_chars).map_or(false, |c| !c.is_lowercase()) - && name.chars().nth(item_name_chars + 1).map_or(false, |c| !c.is_numeric()) + && name.chars().nth(item_name_chars).is_some_and(|c| !c.is_lowercase()) + && name.chars().nth(item_name_chars + 1).is_some_and(|c| !c.is_numeric()) { span_lint_hir( cx, diff --git a/clippy_lints/src/iter_not_returning_iterator.rs b/clippy_lints/src/iter_not_returning_iterator.rs index b19b348c7430..25105817ad97 100644 --- a/clippy_lints/src/iter_not_returning_iterator.rs +++ b/clippy_lints/src/iter_not_returning_iterator.rs @@ -75,7 +75,7 @@ fn check_sig(cx: &LateContext<'_>, name: Symbol, sig: &FnSig<'_>, fn_id: LocalDe if cx .tcx .get_diagnostic_item(sym::Iterator) - .map_or(false, |iter_id| !implements_trait(cx, ret_ty, iter_id, &[])) + .is_some_and(|iter_id| !implements_trait(cx, ret_ty, iter_id, &[])) { span_lint( cx, diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index b7887ef76a53..3ea758e176f0 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -622,7 +622,7 @@ fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let ty = &cx.typeck_results().expr_ty(expr).peel_refs(); match ty.kind() { - ty::Dynamic(tt, ..) => tt.principal().map_or(false, |principal| { + ty::Dynamic(tt, ..) => tt.principal().is_some_and(|principal| { let is_empty = sym!(is_empty); cx.tcx .associated_items(principal.def_id()) diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index d55be2b036ac..ce0e1a24a7b5 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -737,7 +737,7 @@ fn elision_suggestions( suggestions.extend( usages .iter() - .filter(|usage| named_lifetime(usage).map_or(false, |id| elidable_lts.contains(&id))) + .filter(|usage| named_lifetime(usage).is_some_and(|id| elidable_lts.contains(&id))) .map(|usage| { match cx.tcx.parent_hir_node(usage.hir_id) { Node::Ty(Ty { diff --git a/clippy_lints/src/loops/manual_find.rs b/clippy_lints/src/loops/manual_find.rs index bfe2e68b5d1f..1721f569541b 100644 --- a/clippy_lints/src/loops/manual_find.rs +++ b/clippy_lints/src/loops/manual_find.rs @@ -58,7 +58,7 @@ pub(super) fn check<'tcx>( .tcx .lang_items() .copy_trait() - .map_or(false, |id| implements_trait(cx, ty, id, &[])) + .is_some_and(|id| implements_trait(cx, ty, id, &[])) { snippet.push_str( &format!( diff --git a/clippy_lints/src/loops/manual_memcpy.rs b/clippy_lints/src/loops/manual_memcpy.rs index af0894517595..701567a7d84e 100644 --- a/clippy_lints/src/loops/manual_memcpy.rs +++ b/clippy_lints/src/loops/manual_memcpy.rs @@ -416,7 +416,7 @@ fn get_assignments<'a, 'tcx>( .chain(*expr) .filter(move |e| { if let ExprKind::AssignOp(_, place, _) = e.kind { - path_to_local(place).map_or(false, |id| { + path_to_local(place).is_some_and(|id| { !loop_counters .iter() // skip the first item which should be `StartKind::Range` diff --git a/clippy_lints/src/loops/mut_range_bound.rs b/clippy_lints/src/loops/mut_range_bound.rs index 094b19473244..39e5e140b7a4 100644 --- a/clippy_lints/src/loops/mut_range_bound.rs +++ b/clippy_lints/src/loops/mut_range_bound.rs @@ -126,7 +126,7 @@ impl BreakAfterExprVisitor { break_after_expr: false, }; - get_enclosing_block(cx, hir_id).map_or(false, |block| { + get_enclosing_block(cx, hir_id).is_some_and(|block| { visitor.visit_block(block); visitor.break_after_expr }) diff --git a/clippy_lints/src/loops/same_item_push.rs b/clippy_lints/src/loops/same_item_push.rs index d255fea3af20..951ebc9caef0 100644 --- a/clippy_lints/src/loops/same_item_push.rs +++ b/clippy_lints/src/loops/same_item_push.rs @@ -50,7 +50,7 @@ pub(super) fn check<'tcx>( .tcx .lang_items() .clone_trait() - .map_or(false, |id| implements_trait(cx, ty, id, &[])) + .is_some_and(|id| implements_trait(cx, ty, id, &[])) { // Make sure that the push does not involve possibly mutating values match pushed_item.kind { diff --git a/clippy_lints/src/loops/utils.rs b/clippy_lints/src/loops/utils.rs index c4c504e1ae4f..51fde5288ab9 100644 --- a/clippy_lints/src/loops/utils.rs +++ b/clippy_lints/src/loops/utils.rs @@ -256,9 +256,10 @@ fn is_conditional(expr: &Expr<'_>) -> bool { /// If `arg` was the argument to a `for` loop, return the "cleanest" way of writing the /// actual `Iterator` that the loop uses. pub(super) fn make_iterator_snippet(cx: &LateContext<'_>, arg: &Expr<'_>, applic_ref: &mut Applicability) -> String { - let impls_iterator = cx.tcx.get_diagnostic_item(sym::Iterator).map_or(false, |id| { - implements_trait(cx, cx.typeck_results().expr_ty(arg), id, &[]) - }); + let impls_iterator = cx + .tcx + .get_diagnostic_item(sym::Iterator) + .is_some_and(|id| implements_trait(cx, cx.typeck_results().expr_ty(arg), id, &[])); if impls_iterator { format!( "{}", diff --git a/clippy_lints/src/manual_clamp.rs b/clippy_lints/src/manual_clamp.rs index fd66cacdfe93..016ec7320a6a 100644 --- a/clippy_lints/src/manual_clamp.rs +++ b/clippy_lints/src/manual_clamp.rs @@ -226,7 +226,7 @@ impl TypeClampability { } else if cx .tcx .get_diagnostic_item(sym::Ord) - .map_or(false, |id| implements_trait(cx, ty, id, &[])) + .is_some_and(|id| implements_trait(cx, ty, id, &[])) { Some(TypeClampability::Ord) } else { diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index 828c5a3f6ffd..3f401eff6bdb 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -162,9 +162,9 @@ fn eq_pattern_length<'tcx>(cx: &LateContext<'tcx>, pattern: &Expr<'_>, expr: &'t .. }) = expr.kind { - constant_length(cx, pattern).map_or(false, |length| *n == length) + constant_length(cx, pattern).is_some_and(|length| *n == length) } else { - len_arg(cx, expr).map_or(false, |arg| eq_expr_value(cx, pattern, arg)) + len_arg(cx, expr).is_some_and(|arg| eq_expr_value(cx, pattern, arg)) } } diff --git a/clippy_lints/src/matches/match_like_matches.rs b/clippy_lints/src/matches/match_like_matches.rs index 6d62b530ae7e..47472ab831f2 100644 --- a/clippy_lints/src/matches/match_like_matches.rs +++ b/clippy_lints/src/matches/match_like_matches.rs @@ -74,7 +74,7 @@ where && b0 != b1 && (first_guard.is_none() || iter.len() == 0) && first_attrs.is_empty() - && iter.all(|arm| find_bool_lit(&arm.2.kind).map_or(false, |b| b == b0) && arm.3.is_none() && arm.0.is_empty()) + && iter.all(|arm| find_bool_lit(&arm.2.kind).is_some_and(|b| b == b0) && arm.3.is_none() && arm.0.is_empty()) { if let Some(last_pat) = last_pat_opt { if !is_wild(last_pat) { diff --git a/clippy_lints/src/matches/redundant_pattern_match.rs b/clippy_lints/src/matches/redundant_pattern_match.rs index ca45ed6ea59c..264458a86ef4 100644 --- a/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/clippy_lints/src/matches/redundant_pattern_match.rs @@ -447,7 +447,7 @@ fn is_pat_variant(cx: &LateContext<'_>, pat: &Pat<'_>, path: &QPath<'_>, expecte .tcx .lang_items() .get(expected_lang_item) - .map_or(false, |expected_id| cx.tcx.parent(id) == expected_id), + .is_some_and(|expected_id| cx.tcx.parent(id) == expected_id), Item::Diag(expected_ty, expected_variant) => { let ty = cx.typeck_results().pat_ty(pat); diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index 2aea25b5f12a..95a4bf6f60de 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -175,7 +175,7 @@ impl<'tcx> Visitor<'tcx> for PatVisitor<'tcx> { if matches!(pat.kind, PatKind::Binding(..)) { ControlFlow::Break(()) } else { - self.has_enum |= self.typeck.pat_ty(pat).ty_adt_def().map_or(false, AdtDef::is_enum); + self.has_enum |= self.typeck.pat_ty(pat).ty_adt_def().is_some_and(AdtDef::is_enum); walk_pat(self, pat) } } diff --git a/clippy_lints/src/matches/try_err.rs b/clippy_lints/src/matches/try_err.rs index c7e1b70d19e7..6c02207af49d 100644 --- a/clippy_lints/src/matches/try_err.rs +++ b/clippy_lints/src/matches/try_err.rs @@ -58,7 +58,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, scrutine let span = hygiene::walk_chain(err_arg.span, try_arg.span.ctxt()); let mut applicability = Applicability::MachineApplicable; let origin_snippet = snippet_with_applicability(cx, span, "_", &mut applicability); - let ret_prefix = if get_parent_expr(cx, expr).map_or(false, |e| matches!(e.kind, ExprKind::Ret(_))) { + let ret_prefix = if get_parent_expr(cx, expr).is_some_and(|e| matches!(e.kind, ExprKind::Ret(_))) { "" // already returns } else { "return " diff --git a/clippy_lints/src/methods/expect_fun_call.rs b/clippy_lints/src/methods/expect_fun_call.rs index c288dbdabe96..6dc48c26ba93 100644 --- a/clippy_lints/src/methods/expect_fun_call.rs +++ b/clippy_lints/src/methods/expect_fun_call.rs @@ -83,7 +83,7 @@ pub(super) fn check<'tcx>( hir::ExprKind::MethodCall(..) => { cx.typeck_results() .type_dependent_def_id(arg.hir_id) - .map_or(false, |method_id| { + .is_some_and(|method_id| { matches!( cx.tcx.fn_sig(method_id).instantiate_identity().output().skip_binder().kind(), ty::Ref(re, ..) if re.is_static() diff --git a/clippy_lints/src/methods/filter_next.rs b/clippy_lints/src/methods/filter_next.rs index e697ba656f5f..6c1a14fc8829 100644 --- a/clippy_lints/src/methods/filter_next.rs +++ b/clippy_lints/src/methods/filter_next.rs @@ -32,9 +32,10 @@ pub(super) fn check<'tcx>( filter_arg: &'tcx hir::Expr<'_>, ) { // lint if caller of `.filter().next()` is an Iterator - let recv_impls_iterator = cx.tcx.get_diagnostic_item(sym::Iterator).map_or(false, |id| { - implements_trait(cx, cx.typeck_results().expr_ty(recv), id, &[]) - }); + let recv_impls_iterator = cx + .tcx + .get_diagnostic_item(sym::Iterator) + .is_some_and(|id| implements_trait(cx, cx.typeck_results().expr_ty(recv), id, &[])); if recv_impls_iterator { let msg = "called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling \ `.find(..)` instead"; diff --git a/clippy_lints/src/methods/manual_next_back.rs b/clippy_lints/src/methods/manual_next_back.rs index 5f3fec53827a..9a03559b2237 100644 --- a/clippy_lints/src/methods/manual_next_back.rs +++ b/clippy_lints/src/methods/manual_next_back.rs @@ -19,9 +19,7 @@ pub(super) fn check<'tcx>( if cx .tcx .get_diagnostic_item(sym::DoubleEndedIterator) - .map_or(false, |double_ended_iterator| { - implements_trait(cx, rev_recv_ty, double_ended_iterator, &[]) - }) + .is_some_and(|double_ended_iterator| implements_trait(cx, rev_recv_ty, double_ended_iterator, &[])) && is_trait_method(cx, rev_call, sym::Iterator) && is_trait_method(cx, expr, sym::Iterator) { diff --git a/clippy_lints/src/methods/manual_str_repeat.rs b/clippy_lints/src/methods/manual_str_repeat.rs index 61e74369cb05..098721dc046f 100644 --- a/clippy_lints/src/methods/manual_str_repeat.rs +++ b/clippy_lints/src/methods/manual_str_repeat.rs @@ -36,8 +36,8 @@ fn parse_repeat_arg(cx: &LateContext<'_>, e: &Expr<'_>) -> Option { } else { let ty = cx.typeck_results().expr_ty(e); if is_type_lang_item(cx, ty, LangItem::String) - || (is_type_lang_item(cx, ty, LangItem::OwnedBox) && get_ty_param(ty).map_or(false, Ty::is_str)) - || (is_type_diagnostic_item(cx, ty, sym::Cow) && get_ty_param(ty).map_or(false, Ty::is_str)) + || (is_type_lang_item(cx, ty, LangItem::OwnedBox) && get_ty_param(ty).is_some_and(Ty::is_str)) + || (is_type_diagnostic_item(cx, ty, sym::Cow) && get_ty_param(ty).is_some_and(Ty::is_str)) { Some(RepeatKind::String) } else { diff --git a/clippy_lints/src/methods/map_clone.rs b/clippy_lints/src/methods/map_clone.rs index 515d4a11ed51..d5594b21db5d 100644 --- a/clippy_lints/src/methods/map_clone.rs +++ b/clippy_lints/src/methods/map_clone.rs @@ -70,7 +70,7 @@ pub(super) fn check(cx: &LateContext<'_>, e: &hir::Expr<'_>, recv: &hir::Expr<'_ if ident_eq(name, obj) && method.ident.name == sym::clone && let Some(fn_id) = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id) && let Some(trait_id) = cx.tcx.trait_of_item(fn_id) - && cx.tcx.lang_items().clone_trait().map_or(false, |id| id == trait_id) + && cx.tcx.lang_items().clone_trait() == Some(trait_id) // no autoderefs && !cx.typeck_results().expr_adjustments(obj).iter() .any(|a| matches!(a.kind, Adjust::Deref(Some(..)))) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index b1809796355d..795e041ffd9d 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -121,6 +121,7 @@ mod unnecessary_iter_cloned; mod unnecessary_join; mod unnecessary_lazy_eval; mod unnecessary_literal_unwrap; +mod unnecessary_map_or; mod unnecessary_min_or_max; mod unnecessary_result_map_or_else; mod unnecessary_sort_by; @@ -4099,6 +4100,33 @@ declare_clippy_lint! { "is_empty() called on strings known at compile time" } +declare_clippy_lint! { + /// ### What it does + /// Converts some constructs mapping an Enum value for equality comparison. + /// + /// ### Why is this bad? + /// Calls such as `opt.map_or(false, |val| val == 5)` are needlessly long and cumbersome, + /// and can be reduced to, for example, `opt == Some(5)` assuming `opt` implements `PartialEq`. + /// This lint offers readability and conciseness improvements. + /// + /// ### Example + /// ```no_run + /// pub fn a(x: Option) -> bool { + /// x.map_or(false, |n| n == 5) + /// } + /// ``` + /// Use instead: + /// ```no_run + /// pub fn a(x: Option) -> bool { + /// x == Some(5) + /// } + /// ``` + #[clippy::version = "1.75.0"] + pub UNNECESSARY_MAP_OR, + style, + "reduce unnecessary pattern matching for constructs that implement `PartialEq`" +} + declare_clippy_lint! { /// ### What it does /// Checks if an iterator is used to check if a string is ascii. @@ -4417,6 +4445,7 @@ impl_lint_pass!(Methods => [ NEEDLESS_AS_BYTES, MAP_ALL_ANY_IDENTITY, MAP_WITH_UNUSED_ARGUMENT_OVER_RANGES, + UNNECESSARY_MAP_OR, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -4950,6 +4979,7 @@ impl Methods { option_map_or_none::check(cx, expr, recv, def, map); manual_ok_or::check(cx, expr, recv, def, map); option_map_or_err_ok::check(cx, expr, recv, def, map); + unnecessary_map_or::check(cx, expr, recv, def, map, &self.msrv); }, ("map_or_else", [def, map]) => { result_map_or_else_none::check(cx, expr, recv, def, map); @@ -5343,7 +5373,7 @@ impl SelfKind { boxed_ty == parent_ty } else if is_type_diagnostic_item(cx, ty, sym::Rc) || is_type_diagnostic_item(cx, ty, sym::Arc) { if let ty::Adt(_, args) = ty.kind() { - args.types().next().map_or(false, |t| t == parent_ty) + args.types().next() == Some(parent_ty) } else { false } diff --git a/clippy_lints/src/methods/needless_collect.rs b/clippy_lints/src/methods/needless_collect.rs index 055107068ae0..9c41528e6476 100644 --- a/clippy_lints/src/methods/needless_collect.rs +++ b/clippy_lints/src/methods/needless_collect.rs @@ -193,7 +193,7 @@ fn check_collect_into_intoiterator<'tcx>( /// Checks if the given method call matches the expected signature of `([&[mut]] self) -> bool` fn is_is_empty_sig(cx: &LateContext<'_>, call_id: HirId) -> bool { - cx.typeck_results().type_dependent_def_id(call_id).map_or(false, |id| { + cx.typeck_results().type_dependent_def_id(call_id).is_some_and(|id| { let sig = cx.tcx.fn_sig(id).instantiate_identity().skip_binder(); sig.inputs().len() == 1 && sig.output().is_bool() }) diff --git a/clippy_lints/src/methods/option_as_ref_deref.rs b/clippy_lints/src/methods/option_as_ref_deref.rs index 389e02056b2b..998bdee01576 100644 --- a/clippy_lints/src/methods/option_as_ref_deref.rs +++ b/clippy_lints/src/methods/option_as_ref_deref.rs @@ -45,7 +45,7 @@ pub(super) fn check( hir::ExprKind::Path(ref expr_qpath) => { cx.qpath_res(expr_qpath, map_arg.hir_id) .opt_def_id() - .map_or(false, |fun_def_id| { + .is_some_and(|fun_def_id| { cx.tcx.is_diagnostic_item(sym::deref_method, fun_def_id) || cx.tcx.is_diagnostic_item(sym::deref_mut_method, fun_def_id) || deref_aliases diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index b685a466b728..6b39b753885e 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -59,9 +59,7 @@ pub(super) fn check<'tcx>( let output_ty = cx.tcx.fn_sig(def_id).instantiate(cx.tcx, args).skip_binder().output(); cx.tcx .get_diagnostic_item(sym::Default) - .map_or(false, |default_trait_id| { - implements_trait(cx, output_ty, default_trait_id, &[]) - }) + .is_some_and(|default_trait_id| implements_trait(cx, output_ty, default_trait_id, &[])) } else { false } diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs index a2a7de905caf..1cee28e19861 100644 --- a/clippy_lints/src/methods/str_splitn.rs +++ b/clippy_lints/src/methods/str_splitn.rs @@ -348,7 +348,7 @@ fn parse_iter_usage<'tcx>( && cx .typeck_results() .type_dependent_def_id(e.hir_id) - .map_or(false, |id| is_diag_item_method(cx, id, sym::Option)) => + .is_some_and(|id| is_diag_item_method(cx, id, sym::Option)) => { (Some(UnwrapKind::Unwrap), e.span) }, diff --git a/clippy_lints/src/methods/unnecessary_map_or.rs b/clippy_lints/src/methods/unnecessary_map_or.rs new file mode 100644 index 000000000000..adc27cd437f4 --- /dev/null +++ b/clippy_lints/src/methods/unnecessary_map_or.rs @@ -0,0 +1,131 @@ +use std::borrow::Cow; + +use clippy_config::msrvs::{self, Msrv}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::eager_or_lazy::switch_to_eager_eval; +use clippy_utils::source::snippet_opt; +use clippy_utils::sugg::{Sugg, make_binop}; +use clippy_utils::ty::{get_type_diagnostic_name, implements_trait}; +use clippy_utils::visitors::is_local_used; +use clippy_utils::{is_from_proc_macro, path_to_local_id}; +use rustc_ast::LitKind::Bool; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Expr, ExprKind, PatKind}; +use rustc_lint::LateContext; +use rustc_span::sym; + +use super::UNNECESSARY_MAP_OR; + +pub(super) enum Variant { + Ok, + Some, +} +impl Variant { + pub fn variant_name(&self) -> &'static str { + match self { + Variant::Ok => "Ok", + Variant::Some => "Some", + } + } + + pub fn method_name(&self) -> &'static str { + match self { + Variant::Ok => "is_ok_and", + Variant::Some => "is_some_and", + } + } +} + +pub(super) fn check<'a>( + cx: &LateContext<'a>, + expr: &Expr<'a>, + recv: &Expr<'_>, + def: &Expr<'_>, + map: &Expr<'_>, + msrv: &Msrv, +) { + let ExprKind::Lit(def_kind) = def.kind else { + return; + }; + + let recv_ty = cx.typeck_results().expr_ty(recv); + + let Bool(def_bool) = def_kind.node else { + return; + }; + + let variant = match get_type_diagnostic_name(cx, recv_ty) { + Some(sym::Option) => Variant::Some, + Some(sym::Result) => Variant::Ok, + Some(_) | None => return, + }; + + let (sugg, method) = if let ExprKind::Closure(map_closure) = map.kind + && let closure_body = cx.tcx.hir().body(map_closure.body) + && let closure_body_value = closure_body.value.peel_blocks() + && let ExprKind::Binary(op, l, r) = closure_body_value.kind + && let Some(param) = closure_body.params.first() + && let PatKind::Binding(_, hir_id, _, _) = param.pat.kind + // checking that map_or is one of the following: + // .map_or(false, |x| x == y) + // .map_or(false, |x| y == x) - swapped comparison + // .map_or(true, |x| x != y) + // .map_or(true, |x| y != x) - swapped comparison + && ((BinOpKind::Eq == op.node && !def_bool) || (BinOpKind::Ne == op.node && def_bool)) + && let non_binding_location = if path_to_local_id(l, hir_id) { r } else { l } + && switch_to_eager_eval(cx, non_binding_location) + // xor, because if its both then thats a strange edge case and + // we can just ignore it, since by default clippy will error on this + && (path_to_local_id(l, hir_id) ^ path_to_local_id(r, hir_id)) + && !is_local_used(cx, non_binding_location, hir_id) + && let typeck_results = cx.typeck_results() + && typeck_results.expr_ty(l) == typeck_results.expr_ty(r) + && let Some(partial_eq) = cx.tcx.get_diagnostic_item(sym::PartialEq) + && implements_trait(cx, recv_ty, partial_eq, &[recv_ty.into()]) + { + let wrap = variant.variant_name(); + + // we may need to add parens around the suggestion + // in case the parent expression has additional method calls, + // since for example `Some(5).map_or(false, |x| x == 5).then(|| 1)` + // being converted to `Some(5) == Some(5).then(|| 1)` isnt + // the same thing + + let inner_non_binding = Sugg::NonParen(Cow::Owned(format!( + "{wrap}({})", + Sugg::hir(cx, non_binding_location, "") + ))); + + let binop = make_binop(op.node, &Sugg::hir(cx, recv, ".."), &inner_non_binding) + .maybe_par() + .into_string(); + + (binop, "a standard comparison") + } else if !def_bool + && msrv.meets(msrvs::OPTION_RESULT_IS_VARIANT_AND) + && let Some(recv_callsite) = snippet_opt(cx, recv.span.source_callsite()) + && let Some(span_callsite) = snippet_opt(cx, map.span.source_callsite()) + { + let suggested_name = variant.method_name(); + ( + format!("{recv_callsite}.{suggested_name}({span_callsite})",), + suggested_name, + ) + } else { + return; + }; + + if is_from_proc_macro(cx, expr) { + return; + } + + span_lint_and_sugg( + cx, + UNNECESSARY_MAP_OR, + expr.span, + "this `map_or` is redundant", + format!("use {method} instead"), + sugg, + Applicability::MaybeIncorrect, + ); +} diff --git a/clippy_lints/src/methods/unnecessary_sort_by.rs b/clippy_lints/src/methods/unnecessary_sort_by.rs index a92fe9e55a46..8ba10938ce61 100644 --- a/clippy_lints/src/methods/unnecessary_sort_by.rs +++ b/clippy_lints/src/methods/unnecessary_sort_by.rs @@ -167,9 +167,10 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: &Exp }, )) = &left_expr.kind && left_name == left_ident - && cx.tcx.get_diagnostic_item(sym::Ord).map_or(false, |id| { - implements_trait(cx, cx.typeck_results().expr_ty(left_expr), id, &[]) - }) + && cx + .tcx + .get_diagnostic_item(sym::Ord) + .is_some_and(|id| implements_trait(cx, cx.typeck_results().expr_ty(left_expr), id, &[])) { return Some(LintTrigger::Sort(SortDetection { vec_name })); } diff --git a/clippy_lints/src/methods/useless_asref.rs b/clippy_lints/src/methods/useless_asref.rs index c309e7781166..82313257e5c4 100644 --- a/clippy_lints/src/methods/useless_asref.rs +++ b/clippy_lints/src/methods/useless_asref.rs @@ -124,7 +124,7 @@ fn is_calling_clone(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool { && let Some(fn_id) = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id) && let Some(trait_id) = cx.tcx.trait_of_item(fn_id) // We check it's the `Clone` trait. - && cx.tcx.lang_items().clone_trait().map_or(false, |id| id == trait_id) + && cx.tcx.lang_items().clone_trait().is_some_and(|id| id == trait_id) // no autoderefs && !cx.typeck_results().expr_adjustments(obj).iter() .any(|a| matches!(a.kind, Adjust::Deref(Some(..)))) diff --git a/clippy_lints/src/misc_early/mod.rs b/clippy_lints/src/misc_early/mod.rs index 4cba13a05c24..37d7427f9a5d 100644 --- a/clippy_lints/src/misc_early/mod.rs +++ b/clippy_lints/src/misc_early/mod.rs @@ -427,7 +427,7 @@ impl MiscEarlyLints { // See for a regression. // FIXME: Find a better way to detect those cases. let lit_snip = match snippet_opt(cx, span) { - Some(snip) if snip.chars().next().map_or(false, |c| c.is_ascii_digit()) => snip, + Some(snip) if snip.chars().next().is_some_and(|c| c.is_ascii_digit()) => snip, _ => return, }; diff --git a/clippy_lints/src/needless_borrows_for_generic_args.rs b/clippy_lints/src/needless_borrows_for_generic_args.rs index f7fa31d83aa4..c1424b9f1dc8 100644 --- a/clippy_lints/src/needless_borrows_for_generic_args.rs +++ b/clippy_lints/src/needless_borrows_for_generic_args.rs @@ -134,9 +134,11 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowsForGenericArgs<'tcx> { } fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &Body<'_>) { - if self.possible_borrowers.last().map_or(false, |&(local_def_id, _)| { - local_def_id == cx.tcx.hir().body_owner_def_id(body.id()) - }) { + if self + .possible_borrowers + .last() + .is_some_and(|&(local_def_id, _)| local_def_id == cx.tcx.hir().body_owner_def_id(body.id())) + { self.possible_borrowers.pop(); } } @@ -232,7 +234,7 @@ fn needless_borrow_count<'tcx>( let mut check_reference_and_referent = |reference: &Expr<'tcx>, referent: &Expr<'tcx>| { if let ExprKind::Field(base, _) = &referent.kind { let base_ty = cx.typeck_results().expr_ty(base); - if drop_trait_def_id.map_or(false, |id| implements_trait(cx, base_ty, id, &[])) { + if drop_trait_def_id.is_some_and(|id| implements_trait(cx, base_ty, id, &[])) { return false; } } diff --git a/clippy_lints/src/needless_continue.rs b/clippy_lints/src/needless_continue.rs index c48367729336..c48232f99058 100644 --- a/clippy_lints/src/needless_continue.rs +++ b/clippy_lints/src/needless_continue.rs @@ -153,7 +153,7 @@ fn needless_continue_in_else(else_expr: &ast::Expr, label: Option<&ast::Label>) } fn is_first_block_stmt_continue(block: &ast::Block, label: Option<&ast::Label>) -> bool { - block.stmts.first().map_or(false, |stmt| match stmt.kind { + block.stmts.first().is_some_and(|stmt| match stmt.kind { ast::StmtKind::Semi(ref e) | ast::StmtKind::Expr(ref e) => { if let ast::ExprKind::Continue(ref l) = e.kind { compare_labels(label, l.as_ref()) @@ -390,7 +390,7 @@ fn check_and_warn(cx: &EarlyContext<'_>, expr: &ast::Expr) { #[must_use] fn erode_from_back(s: &str) -> String { let mut ret = s.to_string(); - while ret.pop().map_or(false, |c| c != '}') {} + while ret.pop().is_some_and(|c| c != '}') {} while let Some(c) = ret.pop() { if !c.is_whitespace() { ret.push(c); diff --git a/clippy_lints/src/nonstandard_macro_braces.rs b/clippy_lints/src/nonstandard_macro_braces.rs index a13391a59457..83f7d9319697 100644 --- a/clippy_lints/src/nonstandard_macro_braces.rs +++ b/clippy_lints/src/nonstandard_macro_braces.rs @@ -88,7 +88,7 @@ fn is_offending_macro(cx: &EarlyContext<'_>, span: Span, mac_braces: &MacroBrace || span .macro_backtrace() .last() - .map_or(false, |e| e.macro_def_id.map_or(false, DefId::is_local)) + .is_some_and(|e| e.macro_def_id.is_some_and(DefId::is_local)) }; let span_call_site = span.ctxt().outer_expn_data().call_site; if let ExpnKind::Macro(MacroKind::Bang, mac_name) = span.ctxt().outer_expn_data().kind diff --git a/clippy_lints/src/only_used_in_recursion.rs b/clippy_lints/src/only_used_in_recursion.rs index 372128ac16f0..13b3d2407007 100644 --- a/clippy_lints/src/only_used_in_recursion.rs +++ b/clippy_lints/src/only_used_in_recursion.rs @@ -290,7 +290,7 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion { Some((Node::Expr(parent), child_id)) => match parent.kind { // Recursive call. Track which index the parameter is used in. ExprKind::Call(callee, args) - if path_def_id(cx, callee).map_or(false, |id| { + if path_def_id(cx, callee).is_some_and(|id| { id == param.fn_id && has_matching_args(param.fn_kind, typeck.node_args(callee.hir_id)) }) => { @@ -300,7 +300,7 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion { return; }, ExprKind::MethodCall(_, receiver, args, _) - if typeck.type_dependent_def_id(parent.hir_id).map_or(false, |id| { + if typeck.type_dependent_def_id(parent.hir_id).is_some_and(|id| { id == param.fn_id && has_matching_args(param.fn_kind, typeck.node_args(parent.hir_id)) }) => { diff --git a/clippy_lints/src/operators/arithmetic_side_effects.rs b/clippy_lints/src/operators/arithmetic_side_effects.rs index 8b9f899d82d3..65ef56fd2112 100644 --- a/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -325,7 +325,7 @@ impl ArithmeticSideEffects { fn should_skip_expr<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &hir::Expr<'tcx>) -> bool { is_lint_allowed(cx, ARITHMETIC_SIDE_EFFECTS, expr.hir_id) || self.expr_span.is_some() - || self.const_span.map_or(false, |sp| sp.contains(expr.span)) + || self.const_span.is_some_and(|sp| sp.contains(expr.span)) } } diff --git a/clippy_lints/src/operators/cmp_owned.rs b/clippy_lints/src/operators/cmp_owned.rs index 208b20a7a069..b0d872e98fd4 100644 --- a/clippy_lints/src/operators/cmp_owned.rs +++ b/clippy_lints/src/operators/cmp_owned.rs @@ -42,14 +42,12 @@ fn check_op(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) if typeck .type_dependent_def_id(expr.hir_id) .and_then(|id| cx.tcx.trait_of_item(id)) - .map_or(false, |id| { - matches!(cx.tcx.get_diagnostic_name(id), Some(sym::ToString | sym::ToOwned)) - }) => + .is_some_and(|id| matches!(cx.tcx.get_diagnostic_name(id), Some(sym::ToString | sym::ToOwned))) => { (arg, arg.span) }, ExprKind::Call(path, [arg]) - if path_def_id(cx, path).map_or(false, |did| { + if path_def_id(cx, path).is_some_and(|did| { if cx.tcx.is_diagnostic_item(sym::from_str_method, did) { true } else if cx.tcx.is_diagnostic_item(sym::from_fn, did) { diff --git a/clippy_lints/src/operators/numeric_arithmetic.rs b/clippy_lints/src/operators/numeric_arithmetic.rs index 565294bb40a8..d369978b8be8 100644 --- a/clippy_lints/src/operators/numeric_arithmetic.rs +++ b/clippy_lints/src/operators/numeric_arithmetic.rs @@ -14,7 +14,7 @@ pub struct Context { } impl Context { fn skip_expr(&mut self, e: &hir::Expr<'_>) -> bool { - self.expr_id.is_some() || self.const_span.map_or(false, |span| span.contains(e.span)) + self.expr_id.is_some() || self.const_span.is_some_and(|span| span.contains(e.span)) } pub fn check_binary<'tcx>( diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs index 1bddfab39c69..b2089487a9f4 100644 --- a/clippy_lints/src/pass_by_ref_or_value.rs +++ b/clippy_lints/src/pass_by_ref_or_value.rs @@ -192,7 +192,7 @@ impl PassByRefOrValue { continue; } } - let value_type = if fn_body.and_then(|body| body.params.get(index)).map_or(false, is_self) { + let value_type = if fn_body.and_then(|body| body.params.get(index)).is_some_and(is_self) { "self".into() } else { snippet(cx, decl_ty.span, "_").into() diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index a548c6ef3b1f..ecc095f38599 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -727,9 +727,8 @@ fn get_ref_lm<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutabili fn is_null_path(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { if let ExprKind::Call(pathexp, []) = expr.kind { - path_def_id(cx, pathexp).map_or(false, |id| { - matches!(cx.tcx.get_diagnostic_name(id), Some(sym::ptr_null | sym::ptr_null_mut)) - }) + path_def_id(cx, pathexp) + .is_some_and(|id| matches!(cx.tcx.get_diagnostic_name(id), Some(sym::ptr_null | sym::ptr_null_mut))) } else { false } diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index df35cea96726..a00fd01a62e0 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -129,7 +129,7 @@ fn check_let_some_else_return_none(cx: &LateContext<'_>, stmt: &Stmt<'_>) { cx.tcx .lang_items() .try_trait() - .map_or(false, |did| implements_trait(cx, init_ty, did, &[])) + .is_some_and(|did| implements_trait(cx, init_ty, did, &[])) } if let StmtKind::Let(LetStmt { diff --git a/clippy_lints/src/redundant_slicing.rs b/clippy_lints/src/redundant_slicing.rs index 030df535c35d..dc66fb28fa8c 100644 --- a/clippy_lints/src/redundant_slicing.rs +++ b/clippy_lints/src/redundant_slicing.rs @@ -85,7 +85,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing { let (expr_ty, expr_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(expr)); let (indexed_ty, indexed_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(indexed)); let parent_expr = get_parent_expr(cx, expr); - let needs_parens_for_prefix = parent_expr.map_or(false, |parent| parent.precedence().order() > PREC_PREFIX); + let needs_parens_for_prefix = parent_expr.is_some_and(|parent| parent.precedence().order() > PREC_PREFIX); if expr_ty == indexed_ty { if expr_ref_count > indexed_ref_count { @@ -107,7 +107,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing { kind: ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, _), .. }) - ) || cx.typeck_results().expr_adjustments(expr).first().map_or(false, |a| { + ) || cx.typeck_results().expr_adjustments(expr).first().is_some_and(|a| { matches!( a.kind, Adjust::Borrow(AutoBorrow::Ref(AutoBorrowMutability::Mut { .. })) diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index 23362506ccad..78f0b7d121c2 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -412,9 +412,7 @@ impl<'tcx> IndexBinding<'_, 'tcx> { } Self::is_used_slice_indexed(lhs, idx_ident) || Self::is_used_slice_indexed(rhs, idx_ident) }, - ExprKind::Path(QPath::Resolved(_, path)) => { - path.segments.first().map_or(false, |idx| idx.ident == idx_ident) - }, + ExprKind::Path(QPath::Resolved(_, path)) => path.segments.first().is_some_and(|idx| idx.ident == idx_ident), _ => false, } } diff --git a/clippy_lints/src/types/borrowed_box.rs b/clippy_lints/src/types/borrowed_box.rs index eb7ffbbe360d..bde88ab61adf 100644 --- a/clippy_lints/src/types/borrowed_box.rs +++ b/clippy_lints/src/types/borrowed_box.rs @@ -51,8 +51,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, m format!("&{ltopt}({inner_snippet})") }, TyKind::Path(qpath) - if get_bounds_if_impl_trait(cx, qpath, inner.hir_id) - .map_or(false, |bounds| bounds.len() > 1) => + if get_bounds_if_impl_trait(cx, qpath, inner.hir_id).is_some_and(|bounds| bounds.len() > 1) => { format!("&{ltopt}({inner_snippet})") }, diff --git a/clippy_lints/src/unit_types/let_unit_value.rs b/clippy_lints/src/unit_types/let_unit_value.rs index 1a1284ce9c43..6eef582b4b28 100644 --- a/clippy_lints/src/unit_types/let_unit_value.rs +++ b/clippy_lints/src/unit_types/let_unit_value.rs @@ -38,7 +38,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, local: &'tcx LetStmt<'_>) { return; } - if (local.ty.map_or(false, |ty| !matches!(ty.kind, TyKind::Infer)) + if (local.ty.is_some_and(|ty| !matches!(ty.kind, TyKind::Infer)) || matches!(local.pat.kind, PatKind::Tuple([], ddpos) if ddpos.as_opt_usize().is_none())) && expr_needs_inferred_result(cx, init) { diff --git a/clippy_lints/src/vec_init_then_push.rs b/clippy_lints/src/vec_init_then_push.rs index 91dff5a95236..cbc6885ae5de 100644 --- a/clippy_lints/src/vec_init_then_push.rs +++ b/clippy_lints/src/vec_init_then_push.rs @@ -98,7 +98,7 @@ impl VecPushSearcher { needs_mut |= cx.typeck_results().expr_ty_adjusted(last_place).ref_mutability() == Some(Mutability::Mut) || get_parent_expr(cx, last_place) - .map_or(false, |e| matches!(e.kind, ExprKind::AddrOf(_, Mutability::Mut, _))); + .is_some_and(|e| matches!(e.kind, ExprKind::AddrOf(_, Mutability::Mut, _))); }, ExprKind::MethodCall(_, recv, ..) if recv.hir_id == e.hir_id diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 1dd46ed5a891..a42ddcdae353 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -295,7 +295,7 @@ impl<'tcx> LateLintPass<'tcx> for Write { .opts .crate_name .as_ref() - .map_or(false, |crate_name| crate_name == "build_script_build"); + .is_some_and(|crate_name| crate_name == "build_script_build"); let allowed_in_tests = self.allow_print_in_tests && is_in_test(cx.tcx, expr.hir_id); match diag_name { diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs index 75169e05734a..3269bf758ac0 100644 --- a/clippy_utils/src/check_proc_macro.rs +++ b/clippy_utils/src/check_proc_macro.rs @@ -51,7 +51,7 @@ fn span_matches_pat(sess: &Session, span: Span, start_pat: Pat, end_pat: Pat) -> return false; }; let end = span.hi() - pos.sf.start_pos; - src.get(pos.pos.0 as usize..end.0 as usize).map_or(false, |s| { + src.get(pos.pos.0 as usize..end.0 as usize).is_some_and(|s| { // Spans can be wrapped in a mixture or parenthesis, whitespace, and trailing commas. let start_str = s.trim_start_matches(|c: char| c.is_whitespace() || c == '('); let end_str = s.trim_end_matches(|c: char| c.is_whitespace() || c == ')' || c == ','); @@ -60,13 +60,13 @@ fn span_matches_pat(sess: &Session, span: Span, start_pat: Pat, end_pat: Pat) -> Pat::MultiStr(texts) => texts.iter().any(|s| start_str.starts_with(s)), Pat::OwnedMultiStr(texts) => texts.iter().any(|s| start_str.starts_with(s)), Pat::Sym(sym) => start_str.starts_with(sym.as_str()), - Pat::Num => start_str.as_bytes().first().map_or(false, u8::is_ascii_digit), + Pat::Num => start_str.as_bytes().first().is_some_and(u8::is_ascii_digit), } && match end_pat { Pat::Str(text) => end_str.ends_with(text), Pat::MultiStr(texts) => texts.iter().any(|s| end_str.ends_with(s)), Pat::OwnedMultiStr(texts) => texts.iter().any(|s| end_str.ends_with(s)), Pat::Sym(sym) => end_str.ends_with(sym.as_str()), - Pat::Num => end_str.as_bytes().last().map_or(false, u8::is_ascii_hexdigit), + Pat::Num => end_str.as_bytes().last().is_some_and(u8::is_ascii_hexdigit), }) }) } diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index cb69f8e5a0ed..c73ab4bfa688 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -258,7 +258,7 @@ impl HirEqInterExpr<'_, '_, '_> { } fn should_ignore(&mut self, expr: &Expr<'_>) -> bool { - macro_backtrace(expr.span).last().map_or(false, |macro_call| { + macro_backtrace(expr.span).last().is_some_and(|macro_call| { matches!( &self.inner.cx.tcx.get_diagnostic_name(macro_call.def_id), Some(sym::todo_macro | sym::unimplemented_macro) @@ -322,7 +322,7 @@ impl HirEqInterExpr<'_, '_, '_> { (&ExprKind::Block(l, _), &ExprKind::Block(r, _)) => self.eq_block(l, r), (&ExprKind::Binary(l_op, ll, lr), &ExprKind::Binary(r_op, rl, rr)) => { l_op.node == r_op.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr) - || swap_binop(l_op.node, ll, lr).map_or(false, |(l_op, ll, lr)| { + || swap_binop(l_op.node, ll, lr).is_some_and(|(l_op, ll, lr)| { l_op == r_op.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr) }) }, @@ -444,7 +444,7 @@ impl HirEqInterExpr<'_, '_, '_> { ) => false, }; (is_eq && (!self.should_ignore(left) || !self.should_ignore(right))) - || self.inner.expr_fallback.as_mut().map_or(false, |f| f(left, right)) + || self.inner.expr_fallback.as_mut().is_some_and(|f| f(left, right)) } fn eq_exprs(&mut self, left: &[Expr<'_>], right: &[Expr<'_>]) -> bool { @@ -724,7 +724,7 @@ fn swap_binop<'a>( /// `eq_fn`. pub fn both(l: Option<&X>, r: Option<&X>, mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool { l.as_ref() - .map_or_else(|| r.is_none(), |x| r.as_ref().map_or(false, |y| eq_fn(x, y))) + .map_or_else(|| r.is_none(), |x| r.as_ref().is_some_and(|y| eq_fn(x, y))) } /// Checks if two slices are equal as per `eq_fn`. diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 50d334acf18e..19316a906835 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -342,10 +342,9 @@ pub fn is_ty_alias(qpath: &QPath<'_>) -> bool { /// Checks if the method call given in `expr` belongs to the given trait. /// This is a deprecated function, consider using [`is_trait_method`]. pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) -> bool { - cx.typeck_results() - .type_dependent_def_id(expr.hir_id) - .and_then(|defid| cx.tcx.trait_of_item(defid)) - .map_or(false, |trt_id| match_def_path(cx, trt_id, path)) + let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap(); + let trt_id = cx.tcx.trait_of_item(def_id); + trt_id.is_some_and(|trt_id| match_def_path(cx, trt_id, path)) } /// Checks if the given method call expression calls an inherent method. @@ -379,7 +378,7 @@ pub fn is_diag_trait_item(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol pub fn is_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool { cx.typeck_results() .type_dependent_def_id(expr.hir_id) - .map_or(false, |did| is_diag_trait_item(cx, did, diag_item)) + .is_some_and(|did| is_diag_trait_item(cx, did, diag_item)) } /// Checks if the `def_id` belongs to a function that is part of a trait impl. @@ -406,7 +405,7 @@ pub fn is_trait_item(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) - if let ExprKind::Path(ref qpath) = expr.kind { cx.qpath_res(qpath, expr.hir_id) .opt_def_id() - .map_or(false, |def_id| is_diag_trait_item(cx, def_id, diag_item)) + .is_some_and(|def_id| is_diag_trait_item(cx, def_id, diag_item)) } else { false } @@ -466,13 +465,13 @@ pub fn match_qpath(path: &QPath<'_>, segments: &[&str]) -> bool { /// /// Please use `is_path_diagnostic_item` if the target is a diagnostic item. pub fn is_expr_path_def_path(cx: &LateContext<'_>, expr: &Expr<'_>, segments: &[&str]) -> bool { - path_def_id(cx, expr).map_or(false, |id| match_def_path(cx, id, segments)) + path_def_id(cx, expr).is_some_and(|id| match_def_path(cx, id, segments)) } /// If `maybe_path` is a path node which resolves to an item, resolves it to a `DefId` and checks if /// it matches the given lang item. pub fn is_path_lang_item<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>, lang_item: LangItem) -> bool { - path_def_id(cx, maybe_path).map_or(false, |id| cx.tcx.lang_items().get(lang_item) == Some(id)) + path_def_id(cx, maybe_path).is_some_and(|id| cx.tcx.lang_items().get(lang_item) == Some(id)) } /// If `maybe_path` is a path node which resolves to an item, resolves it to a `DefId` and checks if @@ -482,7 +481,7 @@ pub fn is_path_diagnostic_item<'tcx>( maybe_path: &impl MaybePath<'tcx>, diag_item: Symbol, ) -> bool { - path_def_id(cx, maybe_path).map_or(false, |id| cx.tcx.is_diagnostic_item(diag_item, id)) + path_def_id(cx, maybe_path).is_some_and(|id| cx.tcx.is_diagnostic_item(diag_item, id)) } /// THIS METHOD IS DEPRECATED. Matches a `Path` against a slice of segment string literals. @@ -1315,7 +1314,7 @@ pub fn method_chain_args<'a>(expr: &'a Expr<'_>, methods: &[&str]) -> Option, def_id: DefId) -> bool { cx.tcx .entry_fn(()) - .map_or(false, |(entry_fn_def_id, _)| def_id == entry_fn_def_id) + .is_some_and(|(entry_fn_def_id, _)| def_id == entry_fn_def_id) } /// Returns `true` if the expression is in the program's `#[panic_handler]`. @@ -1753,8 +1752,8 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { match pat.kind { PatKind::Wild | PatKind::Never => false, // If `!` typechecked then the type is empty, so not refutable. - PatKind::Binding(_, _, _, pat) => pat.map_or(false, |pat| is_refutable(cx, pat)), - PatKind::Box(pat) | PatKind::Deref(pat) | PatKind::Ref(pat, _) => is_refutable(cx, pat), + PatKind::Binding(_, _, _, pat) => pat.is_some_and(|pat| is_refutable(cx, pat)), + PatKind::Box(pat) | PatKind::Ref(pat, _) => is_refutable(cx, pat), PatKind::Path(ref qpath) => is_enum_variant(cx, qpath, pat.hir_id), PatKind::Or(pats) => { // TODO: should be the honest check, that pats is exhaustive set @@ -1778,7 +1777,7 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { }, } }, - PatKind::Lit(..) | PatKind::Range(..) | PatKind::Err(_) => true, + PatKind::Lit(..) | PatKind::Range(..) | PatKind::Err(_) | PatKind::Deref(_) => true, } } @@ -2021,7 +2020,7 @@ pub fn match_libc_symbol(cx: &LateContext<'_>, did: DefId, name: &str) -> bool { let path = cx.get_def_path(did); // libc is meant to be used as a flat list of names, but they're all actually defined in different // modules based on the target platform. Ignore everything but crate name and the item name. - path.first().map_or(false, |s| s.as_str() == "libc") && path.last().map_or(false, |s| s.as_str() == name) + path.first().is_some_and(|s| s.as_str() == "libc") && path.last().is_some_and(|s| s.as_str() == name) } /// Returns the list of condition expressions and the list of blocks in a @@ -2104,7 +2103,7 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { _ => None, }; - did.map_or(false, |did| cx.tcx.has_attr(did, sym::must_use)) + did.is_some_and(|did| cx.tcx.has_attr(did, sym::must_use)) } /// Checks if a function's body represents the identity function. Looks for bodies of the form: @@ -2211,7 +2210,7 @@ pub fn is_expr_untyped_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { match expr.kind { ExprKind::Closure(&Closure { body, .. }) => is_body_identity_function(cx, cx.tcx.hir().body(body)), - _ => path_def_id(cx, expr).map_or(false, |id| cx.tcx.is_diagnostic_item(sym::convert_identity, id)), + _ => path_def_id(cx, expr).is_some_and(|id| cx.tcx.is_diagnostic_item(sym::convert_identity, id)), } } diff --git a/clippy_utils/src/numeric_literal.rs b/clippy_utils/src/numeric_literal.rs index c5a34160e3da..2c49df9d807f 100644 --- a/clippy_utils/src/numeric_literal.rs +++ b/clippy_utils/src/numeric_literal.rs @@ -53,7 +53,7 @@ impl<'a> NumericLiteral<'a> { .trim_start() .chars() .next() - .map_or(false, |c| c.is_ascii_digit()) + .is_some_and(|c| c.is_ascii_digit()) { let (unsuffixed, suffix) = split_suffix(src, lit_kind); let float = matches!(lit_kind, LitKind::Float(..)); diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index 3255c51d009c..25ebe879192f 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -905,7 +905,7 @@ impl<'tcx> DerefDelegate<'_, 'tcx> { _ => return false, }; - ty.map_or(false, |ty| matches!(ty.kind(), ty::Ref(_, inner, _) if inner.is_ref())) + ty.is_some_and(|ty| matches!(ty.kind(), ty::Ref(_, inner, _) if inner.is_ref())) } } diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index ce1a20e0066d..565eabe658a9 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -45,7 +45,7 @@ pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { pub fn has_debug_impl<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { cx.tcx .get_diagnostic_item(sym::Debug) - .map_or(false, |debug| implements_trait(cx, ty, debug, &[])) + .is_some_and(|debug| implements_trait(cx, ty, debug, &[])) } /// Checks whether a type can be partially moved. @@ -487,7 +487,7 @@ pub fn needs_ordered_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { .tcx .lang_items() .drop_trait() - .map_or(false, |id| implements_trait(cx, ty, id, &[])) + .is_some_and(|id| implements_trait(cx, ty, id, &[])) { // This type doesn't implement drop, so no side effects here. // Check if any component type has any. @@ -718,8 +718,8 @@ pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option None, @@ -753,7 +753,7 @@ fn sig_from_bounds<'tcx>( && p.self_ty() == ty => { let i = pred.kind().rebind(p.trait_ref.args.type_at(1)); - if inputs.map_or(false, |inputs| i != inputs) { + if inputs.is_some_and(|inputs| i != inputs) { // Multiple different fn trait impls. Is this even allowed? return None; } @@ -794,7 +794,7 @@ fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: AliasTy<'tcx>) -> Option { let i = pred.kind().rebind(p.trait_ref.args.type_at(1)); - if inputs.map_or(false, |inputs| inputs != i) { + if inputs.is_some_and(|inputs| inputs != i) { // Multiple different fn trait impls. Is this even allowed? return None; } @@ -1236,6 +1236,48 @@ impl<'tcx> InteriorMut<'tcx> { } } +/// Check if given type has inner mutability such as [`std::cell::Cell`] or [`std::cell::RefCell`] +/// etc. +pub fn is_interior_mut_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + match *ty.kind() { + ty::Ref(_, inner_ty, mutbl) => mutbl == Mutability::Mut || is_interior_mut_ty(cx, inner_ty), + ty::Slice(inner_ty) => is_interior_mut_ty(cx, inner_ty), + ty::Array(inner_ty, size) => { + size.try_eval_target_usize(cx.tcx, cx.param_env) != Some(0) && is_interior_mut_ty(cx, inner_ty) + }, + ty::Tuple(fields) => fields.iter().any(|ty| is_interior_mut_ty(cx, ty)), + ty::Adt(def, args) => { + // Special case for collections in `std` who's impl of `Hash` or `Ord` delegates to + // that of their type parameters. Note: we don't include `HashSet` and `HashMap` + // because they have no impl for `Hash` or `Ord`. + let def_id = def.did(); + let is_std_collection = [ + sym::Option, + sym::Result, + sym::LinkedList, + sym::Vec, + sym::VecDeque, + sym::BTreeMap, + sym::BTreeSet, + sym::Rc, + sym::Arc, + ] + .iter() + .any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, def_id)); + let is_box = Some(def_id) == cx.tcx.lang_items().owned_box(); + if is_std_collection || is_box { + // The type is mutable if any of its type parameters are + args.types().any(|ty| is_interior_mut_ty(cx, ty)) + } else { + !ty.has_escaping_bound_vars() + && cx.tcx.layout_of(cx.param_env.and(ty)).is_ok() + && !ty.is_freeze(cx.tcx, cx.param_env) + } + }, + _ => false, + } +} + pub fn make_normalized_projection_with_regions<'tcx>( tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, @@ -1291,7 +1333,7 @@ pub fn normalize_with_regions<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx> /// Checks if the type is `core::mem::ManuallyDrop<_>` pub fn is_manually_drop(ty: Ty<'_>) -> bool { - ty.ty_adt_def().map_or(false, AdtDef::is_manually_drop) + ty.ty_adt_def().is_some_and(AdtDef::is_manually_drop) } /// Returns the deref chain of a type, starting with the type itself. diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs index 8f5ec185bf15..a79be5ca7d4c 100644 --- a/clippy_utils/src/visitors.rs +++ b/clippy_utils/src/visitors.rs @@ -344,13 +344,13 @@ pub fn is_const_evaluatable<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> .cx .qpath_res(p, hir_id) .opt_def_id() - .map_or(false, |id| self.cx.tcx.is_const_fn(id)) => {}, + .is_some_and(|id| self.cx.tcx.is_const_fn(id)) => {}, ExprKind::MethodCall(..) if self .cx .typeck_results() .type_dependent_def_id(e.hir_id) - .map_or(false, |id| self.cx.tcx.is_const_fn(id)) => {}, + .is_some_and(|id| self.cx.tcx.is_const_fn(id)) => {}, ExprKind::Binary(_, lhs, rhs) if self.cx.typeck_results().expr_ty(lhs).peel_refs().is_primitive_ty() && self.cx.typeck_results().expr_ty(rhs).peel_refs().is_primitive_ty() => {}, @@ -426,9 +426,7 @@ pub fn is_expr_unsafe<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool { .cx .typeck_results() .type_dependent_def_id(e.hir_id) - .map_or(false, |id| { - self.cx.tcx.fn_sig(id).skip_binder().safety() == Safety::Unsafe - }) => + .is_some_and(|id| self.cx.tcx.fn_sig(id).skip_binder().safety() == Safety::Unsafe) => { ControlFlow::Break(()) }, @@ -444,7 +442,7 @@ pub fn is_expr_unsafe<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool { .cx .qpath_res(p, e.hir_id) .opt_def_id() - .map_or(false, |id| self.cx.tcx.is_mutable_static(id)) => + .is_some_and(|id| self.cx.tcx.is_mutable_static(id)) => { ControlFlow::Break(()) }, diff --git a/tests/ui-internal/unnecessary_def_path.fixed b/tests/ui-internal/unnecessary_def_path.fixed index 0a9948428341..d3fab60f9e3e 100644 --- a/tests/ui-internal/unnecessary_def_path.fixed +++ b/tests/ui-internal/unnecessary_def_path.fixed @@ -1,6 +1,7 @@ //@aux-build:paths.rs #![deny(clippy::internal)] #![feature(rustc_private)] +#![allow(clippy::unnecessary_map_or)] extern crate clippy_utils; extern crate paths; diff --git a/tests/ui-internal/unnecessary_def_path.rs b/tests/ui-internal/unnecessary_def_path.rs index ba68de6c6d01..1b36f6b09e9c 100644 --- a/tests/ui-internal/unnecessary_def_path.rs +++ b/tests/ui-internal/unnecessary_def_path.rs @@ -1,6 +1,7 @@ //@aux-build:paths.rs #![deny(clippy::internal)] #![feature(rustc_private)] +#![allow(clippy::unnecessary_map_or)] extern crate clippy_utils; extern crate paths; diff --git a/tests/ui-internal/unnecessary_def_path.stderr b/tests/ui-internal/unnecessary_def_path.stderr index 79da17316134..79521c5037a8 100644 --- a/tests/ui-internal/unnecessary_def_path.stderr +++ b/tests/ui-internal/unnecessary_def_path.stderr @@ -1,5 +1,5 @@ error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:36:13 + --> tests/ui-internal/unnecessary_def_path.rs:37:13 | LL | let _ = match_type(cx, ty, &OPTION); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Option)` @@ -12,61 +12,61 @@ LL | #![deny(clippy::internal)] = note: `#[deny(clippy::unnecessary_def_path)]` implied by `#[deny(clippy::internal)]` error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:37:13 + --> tests/ui-internal/unnecessary_def_path.rs:38:13 | LL | let _ = match_type(cx, ty, RESULT); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Result)` error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:38:13 + --> tests/ui-internal/unnecessary_def_path.rs:39:13 | LL | let _ = match_type(cx, ty, &["core", "result", "Result"]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Result)` error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:42:13 + --> tests/ui-internal/unnecessary_def_path.rs:43:13 | LL | let _ = clippy_utils::ty::match_type(cx, ty, rc_path); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Rc)` error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:44:13 + --> tests/ui-internal/unnecessary_def_path.rs:45:13 | LL | let _ = match_type(cx, ty, &paths::OPTION); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Option)` error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:45:13 + --> tests/ui-internal/unnecessary_def_path.rs:46:13 | LL | let _ = match_type(cx, ty, paths::RESULT); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Result)` error: use of a def path to a `LangItem` - --> tests/ui-internal/unnecessary_def_path.rs:47:13 + --> tests/ui-internal/unnecessary_def_path.rs:48:13 | LL | let _ = match_type(cx, ty, &["alloc", "boxed", "Box"]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_lang_item(cx, ty, LangItem::OwnedBox)` error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:48:13 + --> tests/ui-internal/unnecessary_def_path.rs:49:13 | LL | let _ = match_type(cx, ty, &["core", "mem", "maybe_uninit", "MaybeUninit", "uninit"]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::maybe_uninit_uninit)` error: use of a def path to a `LangItem` - --> tests/ui-internal/unnecessary_def_path.rs:50:13 + --> tests/ui-internal/unnecessary_def_path.rs:51:13 | LL | let _ = match_def_path(cx, did, &["alloc", "boxed", "Box"]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.lang_items().get(LangItem::OwnedBox) == Some(did)` error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:51:13 + --> tests/ui-internal/unnecessary_def_path.rs:52:13 | LL | let _ = match_def_path(cx, did, &["core", "option", "Option"]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.is_diagnostic_item(sym::Option, did)` error: use of a def path to a `LangItem` - --> tests/ui-internal/unnecessary_def_path.rs:52:13 + --> tests/ui-internal/unnecessary_def_path.rs:53:13 | LL | let _ = match_def_path(cx, did, &["core", "option", "Option", "Some"]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.lang_items().get(LangItem::OptionSome) == Some(did)` @@ -74,25 +74,25 @@ LL | let _ = match_def_path(cx, did, &["core", "option", "Option", "Some"]); = help: if this `DefId` came from a constructor expression or pattern then the parent `DefId` should be used instead error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:54:13 + --> tests/ui-internal/unnecessary_def_path.rs:55:13 | LL | let _ = match_trait_method(cx, expr, &["core", "convert", "AsRef"]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_trait_method(cx, expr, sym::AsRef)` error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:56:13 + --> tests/ui-internal/unnecessary_def_path.rs:57:13 | LL | let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option"]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_path_diagnostic_item(cx, expr, sym::Option)` error: use of a def path to a `LangItem` - --> tests/ui-internal/unnecessary_def_path.rs:57:13 + --> tests/ui-internal/unnecessary_def_path.rs:58:13 | LL | let _ = is_expr_path_def_path(cx, expr, &["core", "iter", "traits", "Iterator", "next"]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `path_res(cx, expr).opt_def_id().map_or(false, |id| cx.tcx.lang_items().get(LangItem::IteratorNext) == Some(id))` error: use of a def path to a `LangItem` - --> tests/ui-internal/unnecessary_def_path.rs:58:13 + --> tests/ui-internal/unnecessary_def_path.rs:59:13 | LL | let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option", "Some"]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_res_lang_ctor(cx, path_res(cx, expr), LangItem::OptionSome)` diff --git a/tests/ui/case_sensitive_file_extension_comparisons.fixed b/tests/ui/case_sensitive_file_extension_comparisons.fixed index 8b4a165a97c6..c4d77f24f126 100644 --- a/tests/ui/case_sensitive_file_extension_comparisons.fixed +++ b/tests/ui/case_sensitive_file_extension_comparisons.fixed @@ -1,4 +1,5 @@ #![warn(clippy::case_sensitive_file_extension_comparisons)] +#![allow(clippy::unnecessary_map_or)] use std::string::String; diff --git a/tests/ui/case_sensitive_file_extension_comparisons.rs b/tests/ui/case_sensitive_file_extension_comparisons.rs index e4b8178110b6..690e93c2639a 100644 --- a/tests/ui/case_sensitive_file_extension_comparisons.rs +++ b/tests/ui/case_sensitive_file_extension_comparisons.rs @@ -1,4 +1,5 @@ #![warn(clippy::case_sensitive_file_extension_comparisons)] +#![allow(clippy::unnecessary_map_or)] use std::string::String; diff --git a/tests/ui/case_sensitive_file_extension_comparisons.stderr b/tests/ui/case_sensitive_file_extension_comparisons.stderr index d203f91b8323..e21815f251b7 100644 --- a/tests/ui/case_sensitive_file_extension_comparisons.stderr +++ b/tests/ui/case_sensitive_file_extension_comparisons.stderr @@ -1,5 +1,5 @@ error: case-sensitive file extension comparison - --> tests/ui/case_sensitive_file_extension_comparisons.rs:13:5 + --> tests/ui/case_sensitive_file_extension_comparisons.rs:14:5 | LL | filename.ends_with(".rs") | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -15,7 +15,7 @@ LL + .map_or(false, |ext| ext.eq_ignore_ascii_case("rs")) | error: case-sensitive file extension comparison - --> tests/ui/case_sensitive_file_extension_comparisons.rs:18:13 + --> tests/ui/case_sensitive_file_extension_comparisons.rs:19:13 | LL | let _ = String::new().ends_with(".ext12"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -29,7 +29,7 @@ LL ~ .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12")); | error: case-sensitive file extension comparison - --> tests/ui/case_sensitive_file_extension_comparisons.rs:19:13 + --> tests/ui/case_sensitive_file_extension_comparisons.rs:20:13 | LL | let _ = "str".ends_with(".ext12"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -43,7 +43,7 @@ LL ~ .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12")); | error: case-sensitive file extension comparison - --> tests/ui/case_sensitive_file_extension_comparisons.rs:23:17 + --> tests/ui/case_sensitive_file_extension_comparisons.rs:24:17 | LL | let _ = "str".ends_with(".ext12"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -57,7 +57,7 @@ LL ~ .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12")); | error: case-sensitive file extension comparison - --> tests/ui/case_sensitive_file_extension_comparisons.rs:30:13 + --> tests/ui/case_sensitive_file_extension_comparisons.rs:31:13 | LL | let _ = String::new().ends_with(".EXT12"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -71,7 +71,7 @@ LL ~ .map_or(false, |ext| ext.eq_ignore_ascii_case("EXT12")); | error: case-sensitive file extension comparison - --> tests/ui/case_sensitive_file_extension_comparisons.rs:31:13 + --> tests/ui/case_sensitive_file_extension_comparisons.rs:32:13 | LL | let _ = "str".ends_with(".EXT12"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/unnecessary_map_or.fixed b/tests/ui/unnecessary_map_or.fixed new file mode 100644 index 000000000000..2d932a70e9d9 --- /dev/null +++ b/tests/ui/unnecessary_map_or.fixed @@ -0,0 +1,64 @@ +//@aux-build:proc_macros.rs +#![warn(clippy::unnecessary_map_or)] +#![allow(clippy::no_effect)] +#![allow(clippy::eq_op)] +#![allow(clippy::unnecessary_lazy_evaluations)] +#[clippy::msrv = "1.70.0"] +#[macro_use] +extern crate proc_macros; + +fn main() { + // should trigger + let _ = (Some(5) == Some(5)); + let _ = (Some(5) != Some(5)); + let _ = (Some(5) == Some(5)); + let _ = Some(5).is_some_and(|n| { + let _ = n; + 6 >= 5 + }); + let _ = Some(vec![5]).is_some_and(|n| n == [5]); + let _ = Some(vec![1]).is_some_and(|n| vec![2] == n); + let _ = Some(5).is_some_and(|n| n == n); + let _ = Some(5).is_some_and(|n| n == if 2 > 1 { n } else { 0 }); + let _ = Ok::, i32>(vec![5]).is_ok_and(|n| n == [5]); + let _ = (Ok::(5) == Ok(5)); + let _ = (Some(5) == Some(5)).then(|| 1); + + // shouldnt trigger + let _ = Some(5).map_or(true, |n| n == 5); + let _ = Some(5).map_or(true, |n| 5 == n); + macro_rules! x { + () => { + Some(1) + }; + } + // methods lints dont fire on macros + let _ = x!().map_or(false, |n| n == 1); + let _ = x!().map_or(false, |n| n == vec![1][0]); + + msrv_1_69(); + + external! { + let _ = Some(5).map_or(false, |n| n == 5); + } + + with_span! { + let _ = Some(5).map_or(false, |n| n == 5); + } + + // check for presence of PartialEq, and alter suggestion to use `is_ok_and` if absent + struct S; + let r: Result = Ok(3); + let _ = r.is_ok_and(|x| x == 7); + + #[derive(PartialEq)] + struct S2; + let r: Result = Ok(4); + let _ = (r == Ok(8)); +} + +#[clippy::msrv = "1.69.0"] +fn msrv_1_69() { + // is_some_and added in 1.70.0 + let _ = Some(5).map_or(false, |n| n == if 2 > 1 { n } else { 0 }); +} diff --git a/tests/ui/unnecessary_map_or.rs b/tests/ui/unnecessary_map_or.rs new file mode 100644 index 000000000000..4a9d69be1e9d --- /dev/null +++ b/tests/ui/unnecessary_map_or.rs @@ -0,0 +1,67 @@ +//@aux-build:proc_macros.rs +#![warn(clippy::unnecessary_map_or)] +#![allow(clippy::no_effect)] +#![allow(clippy::eq_op)] +#![allow(clippy::unnecessary_lazy_evaluations)] +#[clippy::msrv = "1.70.0"] +#[macro_use] +extern crate proc_macros; + +fn main() { + // should trigger + let _ = Some(5).map_or(false, |n| n == 5); + let _ = Some(5).map_or(true, |n| n != 5); + let _ = Some(5).map_or(false, |n| { + let _ = 1; + n == 5 + }); + let _ = Some(5).map_or(false, |n| { + let _ = n; + 6 >= 5 + }); + let _ = Some(vec![5]).map_or(false, |n| n == [5]); + let _ = Some(vec![1]).map_or(false, |n| vec![2] == n); + let _ = Some(5).map_or(false, |n| n == n); + let _ = Some(5).map_or(false, |n| n == if 2 > 1 { n } else { 0 }); + let _ = Ok::, i32>(vec![5]).map_or(false, |n| n == [5]); + let _ = Ok::(5).map_or(false, |n| n == 5); + let _ = Some(5).map_or(false, |n| n == 5).then(|| 1); + + // shouldnt trigger + let _ = Some(5).map_or(true, |n| n == 5); + let _ = Some(5).map_or(true, |n| 5 == n); + macro_rules! x { + () => { + Some(1) + }; + } + // methods lints dont fire on macros + let _ = x!().map_or(false, |n| n == 1); + let _ = x!().map_or(false, |n| n == vec![1][0]); + + msrv_1_69(); + + external! { + let _ = Some(5).map_or(false, |n| n == 5); + } + + with_span! { + let _ = Some(5).map_or(false, |n| n == 5); + } + + // check for presence of PartialEq, and alter suggestion to use `is_ok_and` if absent + struct S; + let r: Result = Ok(3); + let _ = r.map_or(false, |x| x == 7); + + #[derive(PartialEq)] + struct S2; + let r: Result = Ok(4); + let _ = r.map_or(false, |x| x == 8); +} + +#[clippy::msrv = "1.69.0"] +fn msrv_1_69() { + // is_some_and added in 1.70.0 + let _ = Some(5).map_or(false, |n| n == if 2 > 1 { n } else { 0 }); +} diff --git a/tests/ui/unnecessary_map_or.stderr b/tests/ui/unnecessary_map_or.stderr new file mode 100644 index 000000000000..299a4e5da7aa --- /dev/null +++ b/tests/ui/unnecessary_map_or.stderr @@ -0,0 +1,99 @@ +error: this `map_or` is redundant + --> tests/ui/unnecessary_map_or.rs:12:13 + | +LL | let _ = Some(5).map_or(false, |n| n == 5); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(Some(5) == Some(5))` + | + = note: `-D clippy::unnecessary-map-or` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_map_or)]` + +error: this `map_or` is redundant + --> tests/ui/unnecessary_map_or.rs:13:13 + | +LL | let _ = Some(5).map_or(true, |n| n != 5); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(Some(5) != Some(5))` + +error: this `map_or` is redundant + --> tests/ui/unnecessary_map_or.rs:14:13 + | +LL | let _ = Some(5).map_or(false, |n| { + | _____________^ +LL | | let _ = 1; +LL | | n == 5 +LL | | }); + | |______^ help: use a standard comparison instead: `(Some(5) == Some(5))` + +error: this `map_or` is redundant + --> tests/ui/unnecessary_map_or.rs:18:13 + | +LL | let _ = Some(5).map_or(false, |n| { + | _____________^ +LL | | let _ = n; +LL | | 6 >= 5 +LL | | }); + | |______^ + | +help: use is_some_and instead + | +LL ~ let _ = Some(5).is_some_and(|n| { +LL + let _ = n; +LL + 6 >= 5 +LL ~ }); + | + +error: this `map_or` is redundant + --> tests/ui/unnecessary_map_or.rs:22:13 + | +LL | let _ = Some(vec![5]).map_or(false, |n| n == [5]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_some_and instead: `Some(vec![5]).is_some_and(|n| n == [5])` + +error: this `map_or` is redundant + --> tests/ui/unnecessary_map_or.rs:23:13 + | +LL | let _ = Some(vec![1]).map_or(false, |n| vec![2] == n); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_some_and instead: `Some(vec![1]).is_some_and(|n| vec![2] == n)` + +error: this `map_or` is redundant + --> tests/ui/unnecessary_map_or.rs:24:13 + | +LL | let _ = Some(5).map_or(false, |n| n == n); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_some_and instead: `Some(5).is_some_and(|n| n == n)` + +error: this `map_or` is redundant + --> tests/ui/unnecessary_map_or.rs:25:13 + | +LL | let _ = Some(5).map_or(false, |n| n == if 2 > 1 { n } else { 0 }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_some_and instead: `Some(5).is_some_and(|n| n == if 2 > 1 { n } else { 0 })` + +error: this `map_or` is redundant + --> tests/ui/unnecessary_map_or.rs:26:13 + | +LL | let _ = Ok::, i32>(vec![5]).map_or(false, |n| n == [5]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_ok_and instead: `Ok::, i32>(vec![5]).is_ok_and(|n| n == [5])` + +error: this `map_or` is redundant + --> tests/ui/unnecessary_map_or.rs:27:13 + | +LL | let _ = Ok::(5).map_or(false, |n| n == 5); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(Ok::(5) == Ok(5))` + +error: this `map_or` is redundant + --> tests/ui/unnecessary_map_or.rs:28:13 + | +LL | let _ = Some(5).map_or(false, |n| n == 5).then(|| 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(Some(5) == Some(5))` + +error: this `map_or` is redundant + --> tests/ui/unnecessary_map_or.rs:55:13 + | +LL | let _ = r.map_or(false, |x| x == 7); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_ok_and instead: `r.is_ok_and(|x| x == 7)` + +error: this `map_or` is redundant + --> tests/ui/unnecessary_map_or.rs:60:13 + | +LL | let _ = r.map_or(false, |x| x == 8); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(r == Ok(8))` + +error: aborting due to 13 previous errors +