From 1ceaa9041331c277c143078942ba5683d888a98c Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 14 Nov 2024 19:35:26 +0100 Subject: [PATCH 01/12] Merge commit '786fbd6d683933cd0e567fdcd25d449a69b4320c' into clippy-subtree-update --- 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/large_const_arrays.rs | 3 +- clippy_lints/src/large_include_file.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 +- clippy_lints/src/operators/identity_op.rs | 190 ++++++++---------- .../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 | 172 +++++++++++++++- 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 | 12 +- clippy_utils/src/visitors.rs | 10 +- rust-toolchain | 2 +- tests/ui-internal/unnecessary_def_path.fixed | 1 + tests/ui-internal/unnecessary_def_path.rs | 1 + tests/ui-internal/unnecessary_def_path.stderr | 30 +-- tests/ui-toml/large_include_file/empty.txt | 0 .../large_include_file/large_include_file.rs | 6 +- ...sensitive_file_extension_comparisons.fixed | 1 + ...se_sensitive_file_extension_comparisons.rs | 1 + ...ensitive_file_extension_comparisons.stderr | 12 +- tests/ui/identity_op.fixed | 46 ++++- tests/ui/identity_op.rs | 44 ++++ tests/ui/identity_op.stderr | 70 ++++++- tests/ui/question_mark.fixed | 56 ++++++ tests/ui/question_mark.rs | 71 +++++++ tests/ui/question_mark.stderr | 63 +++++- tests/ui/unnecessary_map_or.fixed | 64 ++++++ tests/ui/unnecessary_map_or.rs | 67 ++++++ tests/ui/unnecessary_map_or.stderr | 99 +++++++++ 92 files changed, 1149 insertions(+), 286 deletions(-) create mode 100644 clippy_lints/src/methods/unnecessary_map_or.rs create mode 100644 tests/ui-toml/large_include_file/empty.txt 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 @@ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { // 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 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { 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 @@ 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 @@ fn from(v: Ty<'tcx>) -> Self { 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 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { 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/large_const_arrays.rs b/clippy_lints/src/large_const_arrays.rs index 44b9d6adaaab..c5a2760234fc 100644 --- a/clippy_lints/src/large_const_arrays.rs +++ b/clippy_lints/src/large_const_arrays.rs @@ -56,7 +56,8 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { && !item.span.from_expansion() && let ty = cx.tcx.type_of(item.owner_id).instantiate_identity() && let ty::Array(element_type, cst) = ty.kind() - && let Some((ty::ValTree::Leaf(element_count), _)) = cx.tcx.try_normalize_erasing_regions(ParamEnv::empty(), *cst).unwrap_or(*cst).try_to_valtree() + && let Some((ty::ValTree::Leaf(element_count), _)) = cx.tcx + .try_normalize_erasing_regions(ParamEnv::empty(), *cst).unwrap_or(*cst).try_to_valtree() && let element_count = element_count.to_target_usize(cx.tcx) && let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes()) && u128::from(self.maximum_allowed_size) < u128::from(element_count) * u128::from(element_size) diff --git a/clippy_lints/src/large_include_file.rs b/clippy_lints/src/large_include_file.rs index ab3d19f89c3f..4f22931a4ded 100644 --- a/clippy_lints/src/large_include_file.rs +++ b/clippy_lints/src/large_include_file.rs @@ -94,6 +94,8 @@ fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &Attribute) { // Currently, rustc limits the usage of macro at the top-level of attributes, // so we don't need to recurse into each level. && let AttrKind::Normal(ref normal) = attr.kind + && let Some(doc) = attr.doc_str() + && doc.as_str().len() as u64 > self.max_file_size && let AttrArgs::Eq(_, AttrArgsEq::Hir(ref meta)) = normal.item.args && !attr.span.contains(meta.span) // Since the `include_str` is already expanded at this point, we can only take the 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_impl(cx: &LateContext<'_>, id: DefId) -> 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 @@ pub fn is_found(cx: &LateContext<'_>, hir_id: HirId) -> bool { 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 @@ fn emit_lint(cx: &LateContext<'_>, vec: &Expr<'_>, pushed_item: &Expr<'_>, ctxt: .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 @@ fn is_clampable<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option(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 @@ fn find_matches_sugg<'a, 'b, I>( && 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 @@ fn visit_pat(&mut self, pat: &'tcx Pat<'_>) -> Self::Result { 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 @@ fn can_be_static_str(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool { 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_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 @@ "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 @@ pub fn new(conf: &'static Conf, format_args: FormatArgsStorage) -> Self { 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 @@ fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { 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 @@ fn matches_value<'a>(cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> boo 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 @@ fn check_unwrap_or_default( 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 6911da69b945..603916e06c95 100644 --- a/clippy_lints/src/methods/unnecessary_sort_by.rs +++ b/clippy_lints/src/methods/unnecessary_sort_by.rs @@ -166,9 +166,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 @@ fn check_lit(cx: &EarlyContext<'_>, lit: token::Lit, span: Span) { // 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 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { } 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 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) { 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 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) { 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 @@ fn manage_unary_ops<'tcx>( 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/identity_op.rs b/clippy_lints/src/operators/identity_op.rs index 830be50c8ba4..1c2d6e90fc95 100644 --- a/clippy_lints/src/operators/identity_op.rs +++ b/clippy_lints/src/operators/identity_op.rs @@ -42,83 +42,43 @@ pub(crate) fn check<'tcx>( match op { BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => { - let _ = check_op( - cx, - left, - 0, - expr.span, - peeled_right_span, - needs_parenthesis(cx, expr, right), - right_is_coerced_to_value, - ) || check_op( - cx, - right, - 0, - expr.span, - peeled_left_span, - Parens::Unneeded, - left_is_coerced_to_value, - ); + if is_redundant_op(cx, left, 0) { + let paren = needs_parenthesis(cx, expr, right); + span_ineffective_operation(cx, expr.span, peeled_right_span, paren, right_is_coerced_to_value); + } else if is_redundant_op(cx, right, 0) { + let paren = needs_parenthesis(cx, expr, left); + span_ineffective_operation(cx, expr.span, peeled_left_span, paren, left_is_coerced_to_value); + } }, BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => { - let _ = check_op( - cx, - right, - 0, - expr.span, - peeled_left_span, - Parens::Unneeded, - left_is_coerced_to_value, - ); + if is_redundant_op(cx, right, 0) { + let paren = needs_parenthesis(cx, expr, left); + span_ineffective_operation(cx, expr.span, peeled_left_span, paren, left_is_coerced_to_value); + } }, BinOpKind::Mul => { - let _ = check_op( - cx, - left, - 1, - expr.span, - peeled_right_span, - needs_parenthesis(cx, expr, right), - right_is_coerced_to_value, - ) || check_op( - cx, - right, - 1, - expr.span, - peeled_left_span, - Parens::Unneeded, - left_is_coerced_to_value, - ); + if is_redundant_op(cx, left, 1) { + let paren = needs_parenthesis(cx, expr, right); + span_ineffective_operation(cx, expr.span, peeled_right_span, paren, right_is_coerced_to_value); + } else if is_redundant_op(cx, right, 1) { + let paren = needs_parenthesis(cx, expr, left); + span_ineffective_operation(cx, expr.span, peeled_left_span, paren, left_is_coerced_to_value); + } }, BinOpKind::Div => { - let _ = check_op( - cx, - right, - 1, - expr.span, - peeled_left_span, - Parens::Unneeded, - left_is_coerced_to_value, - ); + if is_redundant_op(cx, right, 1) { + let paren = needs_parenthesis(cx, expr, left); + span_ineffective_operation(cx, expr.span, peeled_left_span, paren, left_is_coerced_to_value); + } }, BinOpKind::BitAnd => { - let _ = check_op( - cx, - left, - -1, - expr.span, - peeled_right_span, - needs_parenthesis(cx, expr, right), - right_is_coerced_to_value, - ) || check_op( - cx, - right, - -1, - expr.span, - peeled_left_span, - Parens::Unneeded, - left_is_coerced_to_value, - ); + if is_redundant_op(cx, left, -1) { + let paren = needs_parenthesis(cx, expr, right); + span_ineffective_operation(cx, expr.span, peeled_right_span, paren, right_is_coerced_to_value); + } else if is_redundant_op(cx, right, -1) { + let paren = needs_parenthesis(cx, expr, left); + span_ineffective_operation(cx, expr.span, peeled_left_span, paren, left_is_coerced_to_value); + } }, BinOpKind::Rem => check_remainder(cx, left, right, expr.span, left.span), _ => (), @@ -138,43 +98,70 @@ enum Parens { Unneeded, } -/// Checks if `left op right` needs parenthesis when reduced to `right` +/// Checks if a binary expression needs parenthesis when reduced to just its +/// right or left child. +/// +/// e.g. `-(x + y + 0)` cannot be reduced to `-x + y`, as the behavior changes silently. +/// e.g. `1u64 + ((x + y + 0i32) as u64)` cannot be reduced to `1u64 + x + y as u64`, since +/// the the cast expression will not apply to the same expression. /// e.g. `0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }` cannot be reduced /// to `if b { 1 } else { 2 } + if b { 3 } else { 4 }` where the `if` could be -/// interpreted as a statement +/// interpreted as a statement. The same behavior happens for `match`, `loop`, +/// and blocks. +/// e.g. `2 * (0 + { a })` can be reduced to `2 * { a }` without the need for parenthesis, +/// but `1 * ({ a } + 4)` cannot be reduced to `{ a } + 4`, as a block at the start of a line +/// will be interpreted as a statement instead of an expression. /// -/// See #8724 -fn needs_parenthesis(cx: &LateContext<'_>, binary: &Expr<'_>, right: &Expr<'_>) -> Parens { - match right.kind { +/// See #8724, #13470 +fn needs_parenthesis(cx: &LateContext<'_>, binary: &Expr<'_>, child: &Expr<'_>) -> Parens { + match child.kind { ExprKind::Binary(_, lhs, _) | ExprKind::Cast(lhs, _) => { - // ensure we're checking against the leftmost expression of `right` - // - // ~~~ `lhs` - // 0 + {4} * 2 - // ~~~~~~~ `right` - return needs_parenthesis(cx, binary, lhs); + // For casts and binary expressions, we want to add parenthesis if + // the parent HIR node is an expression, or if the parent HIR node + // is a Block or Stmt, and the new left hand side would need + // parenthesis be treated as a statement rather than an expression. + if let Some((_, parent)) = cx.tcx.hir().parent_iter(binary.hir_id).next() { + match parent { + Node::Expr(_) => return Parens::Needed, + Node::Block(_) | Node::Stmt(_) => { + // ensure we're checking against the leftmost expression of `child` + // + // ~~~~~~~~~~~ `binary` + // ~~~ `lhs` + // 0 + {4} * 2 + // ~~~~~~~ `child` + return needs_parenthesis(cx, binary, lhs); + }, + _ => return Parens::Unneeded, + } + } + }, + ExprKind::If(..) | ExprKind::Match(..) | ExprKind::Block(..) | ExprKind::Loop(..) => { + // For if, match, block, and loop expressions, we want to add parenthesis if + // the closest ancestor node that is not an expression is a block or statement. + // This would mean that the rustfix suggestion will appear at the start of a line, which causes + // these expressions to be interpreted as statements if they do not have parenthesis. + let mut prev_id = binary.hir_id; + for (_, parent) in cx.tcx.hir().parent_iter(binary.hir_id) { + if let Node::Expr(expr) = parent + && let ExprKind::Binary(_, lhs, _) | ExprKind::Cast(lhs, _) | ExprKind::Unary(_, lhs) = expr.kind + && lhs.hir_id == prev_id + { + // keep going until we find a node that encompasses left of `binary` + prev_id = expr.hir_id; + continue; + } + + match parent { + Node::Block(_) | Node::Stmt(_) => return Parens::Needed, + _ => return Parens::Unneeded, + }; + } + }, + _ => { + return Parens::Unneeded; }, - ExprKind::If(..) | ExprKind::Match(..) | ExprKind::Block(..) | ExprKind::Loop(..) => {}, - _ => return Parens::Unneeded, } - - let mut prev_id = binary.hir_id; - for (_, node) in cx.tcx.hir().parent_iter(binary.hir_id) { - if let Node::Expr(expr) = node - && let ExprKind::Binary(_, lhs, _) | ExprKind::Cast(lhs, _) = expr.kind - && lhs.hir_id == prev_id - { - // keep going until we find a node that encompasses left of `binary` - prev_id = expr.hir_id; - continue; - } - - match node { - Node::Block(_) | Node::Stmt(_) => break, - _ => return Parens::Unneeded, - }; - } - Parens::Needed } @@ -199,7 +186,7 @@ fn check_remainder(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>, span } } -fn check_op(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span, parens: Parens, is_erased: bool) -> bool { +fn is_redundant_op(cx: &LateContext<'_>, e: &Expr<'_>, m: i8) -> bool { if let Some(Constant::Int(v)) = ConstEvalCtxt::new(cx).eval_simple(e).map(Constant::peel_refs) { let check = match *cx.typeck_results().expr_ty(e).peel_refs().kind() { ty::Int(ity) => unsext(cx.tcx, -1_i128, ity), @@ -212,7 +199,6 @@ fn check_op(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span, pa 1 => v == 1, _ => unreachable!(), } { - span_ineffective_operation(cx, span, arg, parens, is_erased); return true; } } 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 @@ fn check_poly_fn(&mut self, cx: &LateContext<'_>, def_id: LocalDefId, decl: &FnD 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 cb374fcf20e3..a00fd01a62e0 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -8,18 +8,18 @@ use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use clippy_utils::{ eq_expr_value, higher, is_else_clause, is_in_const_context, is_lint_allowed, is_path_lang_item, is_res_lang_ctor, - pat_and_expr_can_be_question_mark, path_to_local, path_to_local_id, peel_blocks, peel_blocks_with_stmt, - span_contains_comment, + pat_and_expr_can_be_question_mark, path_res, path_to_local, path_to_local_id, peel_blocks, peel_blocks_with_stmt, + span_contains_cfg, span_contains_comment, }; use rustc_errors::Applicability; use rustc_hir::LangItem::{self, OptionNone, OptionSome, ResultErr, ResultOk}; use rustc_hir::def::Res; use rustc_hir::{ - BindingMode, Block, Body, ByRef, Expr, ExprKind, LetStmt, Mutability, Node, PatKind, PathSegment, QPath, Stmt, - StmtKind, + Arm, BindingMode, Block, Body, ByRef, Expr, ExprKind, FnRetTy, HirId, LetStmt, MatchSource, Mutability, Node, Pat, + PatKind, PathSegment, QPath, Stmt, StmtKind, }; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::Ty; +use rustc_middle::ty::{self, Ty}; use rustc_session::impl_lint_pass; use rustc_span::sym; use rustc_span::symbol::Symbol; @@ -58,6 +58,9 @@ pub struct QuestionMark { /// if it is greater than zero. /// As for why we need this in the first place: try_block_depth_stack: Vec, + /// Keeps track of the number of inferred return type closures we are inside, to avoid problems + /// with the `Err(x.into())` expansion being ambiguious. + inferred_ret_closure_stack: u16, } impl_lint_pass!(QuestionMark => [QUESTION_MARK, MANUAL_LET_ELSE]); @@ -68,6 +71,7 @@ pub fn new(conf: &'static Conf) -> Self { msrv: conf.msrv.clone(), matches_behaviour: conf.matches_for_let_else, try_block_depth_stack: Vec::new(), + inferred_ret_closure_stack: 0, } } } @@ -125,7 +129,7 @@ fn init_expr_can_use_question_mark(cx: &LateContext<'_>, init_expr: &Expr<'_>) - 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 { @@ -271,6 +275,135 @@ fn check_is_none_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: &Ex } } +#[derive(Clone, Copy, Debug)] +enum TryMode { + Result, + Option, +} + +fn find_try_mode<'tcx>(cx: &LateContext<'tcx>, scrutinee: &Expr<'tcx>) -> Option { + let scrutinee_ty = cx.typeck_results().expr_ty_adjusted(scrutinee); + let ty::Adt(scrutinee_adt_def, _) = scrutinee_ty.kind() else { + return None; + }; + + match cx.tcx.get_diagnostic_name(scrutinee_adt_def.did())? { + sym::Result => Some(TryMode::Result), + sym::Option => Some(TryMode::Option), + _ => None, + } +} + +// Check that `pat` is `{ctor_lang_item}(val)`, returning `val`. +fn extract_ctor_call<'a, 'tcx>( + cx: &LateContext<'tcx>, + expected_ctor: LangItem, + pat: &'a Pat<'tcx>, +) -> Option<&'a Pat<'tcx>> { + if let PatKind::TupleStruct(variant_path, [val_binding], _) = &pat.kind + && is_res_lang_ctor(cx, cx.qpath_res(variant_path, pat.hir_id), expected_ctor) + { + Some(val_binding) + } else { + None + } +} + +// Extracts the local ID of a plain `val` pattern. +fn extract_binding_pat(pat: &Pat<'_>) -> Option { + if let PatKind::Binding(BindingMode::NONE, binding, _, None) = pat.kind { + Some(binding) + } else { + None + } +} + +fn check_arm_is_some_or_ok<'tcx>(cx: &LateContext<'tcx>, mode: TryMode, arm: &Arm<'tcx>) -> bool { + let happy_ctor = match mode { + TryMode::Result => ResultOk, + TryMode::Option => OptionSome, + }; + + // Check for `Ok(val)` or `Some(val)` + if arm.guard.is_none() + && let Some(val_binding) = extract_ctor_call(cx, happy_ctor, arm.pat) + // Extract out `val` + && let Some(binding) = extract_binding_pat(val_binding) + // Check body is just `=> val` + && path_to_local_id(peel_blocks(arm.body), binding) + { + true + } else { + false + } +} + +fn check_arm_is_none_or_err<'tcx>(cx: &LateContext<'tcx>, mode: TryMode, arm: &Arm<'tcx>) -> bool { + if arm.guard.is_some() { + return false; + } + + let arm_body = peel_blocks(arm.body); + match mode { + TryMode::Result => { + // Check that pat is Err(val) + if let Some(ok_pat) = extract_ctor_call(cx, ResultErr, arm.pat) + && let Some(ok_val) = extract_binding_pat(ok_pat) + // check `=> return Err(...)` + && let ExprKind::Ret(Some(wrapped_ret_expr)) = arm_body.kind + && let ExprKind::Call(ok_ctor, [ret_expr]) = wrapped_ret_expr.kind + && is_res_lang_ctor(cx, path_res(cx, ok_ctor), ResultErr) + // check `...` is `val` from binding + && path_to_local_id(ret_expr, ok_val) + { + true + } else { + false + } + }, + TryMode::Option => { + // Check the pat is `None` + if is_res_lang_ctor(cx, path_res(cx, arm.pat), OptionNone) + // Check `=> return None` + && let ExprKind::Ret(Some(ret_expr)) = arm_body.kind + && is_res_lang_ctor(cx, path_res(cx, ret_expr), OptionNone) + && !ret_expr.span.from_expansion() + { + true + } else { + false + } + }, + } +} + +fn check_arms_are_try<'tcx>(cx: &LateContext<'tcx>, mode: TryMode, arm1: &Arm<'tcx>, arm2: &Arm<'tcx>) -> bool { + (check_arm_is_some_or_ok(cx, mode, arm1) && check_arm_is_none_or_err(cx, mode, arm2)) + || (check_arm_is_some_or_ok(cx, mode, arm2) && check_arm_is_none_or_err(cx, mode, arm1)) +} + +fn check_if_try_match<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { + if let ExprKind::Match(scrutinee, [arm1, arm2], MatchSource::Normal | MatchSource::Postfix) = expr.kind + && !expr.span.from_expansion() + && let Some(mode) = find_try_mode(cx, scrutinee) + && !span_contains_cfg(cx, expr.span) + && check_arms_are_try(cx, mode, arm1, arm2) + { + let mut applicability = Applicability::MachineApplicable; + let snippet = snippet_with_applicability(cx, scrutinee.span.source_callsite(), "..", &mut applicability); + + span_lint_and_sugg( + cx, + QUESTION_MARK, + expr.span, + "this `match` expression can be replaced with `?`", + "try instead", + snippet.into_owned() + "?", + applicability, + ); + } +} + fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { if let Some(higher::IfLet { let_pat, @@ -339,6 +472,17 @@ fn is_try_block(cx: &LateContext<'_>, bl: &Block<'_>) -> bool { } } +fn is_inferred_ret_closure(expr: &Expr<'_>) -> bool { + let ExprKind::Closure(closure) = expr.kind else { + return false; + }; + + match closure.fn_decl.output { + FnRetTy::Return(ret_ty) => ret_ty.is_suggestable_infer_ty(), + FnRetTy::DefaultReturn(_) => true, + } +} + impl<'tcx> LateLintPass<'tcx> for QuestionMark { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { if !is_lint_allowed(cx, QUESTION_MARK_USED, stmt.hir_id) { @@ -350,11 +494,27 @@ fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { } self.check_manual_let_else(cx, stmt); } + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if is_inferred_ret_closure(expr) { + self.inferred_ret_closure_stack += 1; + return; + } + if !self.inside_try_block() && !is_in_const_context(cx) && is_lint_allowed(cx, QUESTION_MARK_USED, expr.hir_id) { check_is_none_or_err_and_early_return(cx, expr); check_if_let_some_or_err_and_early_return(cx, expr); + + if self.inferred_ret_closure_stack == 0 { + check_if_try_match(cx, expr); + } + } + } + + fn check_expr_post(&mut self, _: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if is_inferred_ret_closure(expr) { + self.inferred_ret_closure_stack -= 1; } } 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 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { 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 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { 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 @@ fn is_used_slice_indexed(swap_index: &Expr<'_>, idx_ident: Ident) -> bool { } 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 @@ fn display_err(&self, cx: &LateContext<'_>) { 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 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { .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 @@ fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool { } 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 @@ pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool { (&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 @@ pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool { ) => 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 @@ fn are_refutable<'a, I: IntoIterator>>(cx: &LateContext<'_>, 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 @@ fn are_refutable<'a, I: IntoIterator>>(cx: &LateContext<'_>, }, } }, - 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 @@ pub fn from_lit_kind(src: &'a str, lit_kind: &LitKind) -> Option, cmt_hir _ => 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..770cd9c37865 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 @@ fn needs_ordered_drop_inner<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, seen: &m .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,7 +718,7 @@ pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option( && 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; } @@ -1291,7 +1291,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 @@ fn visit_expr(&mut self, e: &'tcx Expr<'_>) -> Self::Result { .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 @@ fn visit_expr(&mut self, e: &'tcx Expr<'_>) -> Self::Result { .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 @@ fn visit_expr(&mut self, e: &'tcx Expr<'_>) -> Self::Result { .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/rust-toolchain b/rust-toolchain index 37d9cce24650..e32e0cb36047 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,4 +1,4 @@ [toolchain] -channel = "nightly-2024-11-07" +channel = "nightly-2024-11-14" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" 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-toml/large_include_file/empty.txt b/tests/ui-toml/large_include_file/empty.txt new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/ui-toml/large_include_file/large_include_file.rs b/tests/ui-toml/large_include_file/large_include_file.rs index dc9349f75a08..8a6dd36501cf 100644 --- a/tests/ui-toml/large_include_file/large_include_file.rs +++ b/tests/ui-toml/large_include_file/large_include_file.rs @@ -15,5 +15,9 @@ const TOO_BIG_INCLUDE_STR: &str = include_str!("too_big.txt"); //~^ large_include_file -#[doc = include_str!("too_big.txt")] //~ large_include_file +#[doc = include_str!("too_big.txt")] +//~^ large_include_file +// Should not lint! +// Regression test for . +#[doc = include_str!("empty.txt")] fn main() {} 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/identity_op.fixed b/tests/ui/identity_op.fixed index b18d8560f6f8..2e8e2366de6f 100644 --- a/tests/ui/identity_op.fixed +++ b/tests/ui/identity_op.fixed @@ -116,7 +116,7 @@ fn main() { //~^ ERROR: this operation has no effect (match a { 0 => 10, _ => 20 }) + if b { 3 } else { 4 }; //~^ ERROR: this operation has no effect - (if b { 1 } else { 2 }); + ((if b { 1 } else { 2 })); //~^ ERROR: this operation has no effect ({ a }) + 3; @@ -212,3 +212,47 @@ fn issue_12050() { //~^ ERROR: this operation has no effect } } + +fn issue_13470() { + let x = 1i32; + let y = 1i32; + // Removes the + 0i32 while keeping the parentheses around x + y so the cast operation works + let _: u64 = (x + y) as u64; + //~^ ERROR: this operation has no effect + // both of the next two lines should look the same after rustfix + let _: u64 = 1u64 & (x + y) as u64; + //~^ ERROR: this operation has no effect + // Same as above, but with extra redundant parenthesis + let _: u64 = 1u64 & ((x + y)) as u64; + //~^ ERROR: this operation has no effect + // Should maintain parenthesis even if the surrounding expr has the same precedence + let _: u64 = 5u64 + ((x + y)) as u64; + //~^ ERROR: this operation has no effect + + // If we don't maintain the parens here, the behavior changes + let _ = -(x + y); + //~^ ERROR: this operation has no effect + // Similarly, we need to maintain parens here + let _ = -(x / y); + //~^ ERROR: this operation has no effect + // Maintain parenthesis if the parent expr is of higher precedence + let _ = 2i32 * (x + y); + //~^ ERROR: this operation has no effect + // Maintain parenthesis if the parent expr is the same precedence + // as not all operations are associative + let _ = 2i32 - (x - y); + //~^ ERROR: this operation has no effect + // But make sure that inner parens still exist + let z = 1i32; + let _ = 2 + (x + (y * z)); + //~^ ERROR: this operation has no effect + // Maintain parenthesis if the parent expr is of lower precedence + // This is for clarity, and clippy will not warn on these being unnecessary + let _ = 2i32 + (x * y); + //~^ ERROR: this operation has no effect + + let x = 1i16; + let y = 1i16; + let _: u64 = 1u64 + ((x as i32 + y as i32) as u64); + //~^ ERROR: this operation has no effect +} diff --git a/tests/ui/identity_op.rs b/tests/ui/identity_op.rs index f1f01b424478..3e20fa6f2b89 100644 --- a/tests/ui/identity_op.rs +++ b/tests/ui/identity_op.rs @@ -212,3 +212,47 @@ fn issue_12050() { //~^ ERROR: this operation has no effect } } + +fn issue_13470() { + let x = 1i32; + let y = 1i32; + // Removes the + 0i32 while keeping the parentheses around x + y so the cast operation works + let _: u64 = (x + y + 0i32) as u64; + //~^ ERROR: this operation has no effect + // both of the next two lines should look the same after rustfix + let _: u64 = 1u64 & (x + y + 0i32) as u64; + //~^ ERROR: this operation has no effect + // Same as above, but with extra redundant parenthesis + let _: u64 = 1u64 & ((x + y) + 0i32) as u64; + //~^ ERROR: this operation has no effect + // Should maintain parenthesis even if the surrounding expr has the same precedence + let _: u64 = 5u64 + ((x + y) + 0i32) as u64; + //~^ ERROR: this operation has no effect + + // If we don't maintain the parens here, the behavior changes + let _ = -(x + y + 0i32); + //~^ ERROR: this operation has no effect + // Similarly, we need to maintain parens here + let _ = -(x / y / 1i32); + //~^ ERROR: this operation has no effect + // Maintain parenthesis if the parent expr is of higher precedence + let _ = 2i32 * (x + y + 0i32); + //~^ ERROR: this operation has no effect + // Maintain parenthesis if the parent expr is the same precedence + // as not all operations are associative + let _ = 2i32 - (x - y - 0i32); + //~^ ERROR: this operation has no effect + // But make sure that inner parens still exist + let z = 1i32; + let _ = 2 + (x + (y * z) + 0); + //~^ ERROR: this operation has no effect + // Maintain parenthesis if the parent expr is of lower precedence + // This is for clarity, and clippy will not warn on these being unnecessary + let _ = 2i32 + (x * y * 1i32); + //~^ ERROR: this operation has no effect + + let x = 1i16; + let y = 1i16; + let _: u64 = 1u64 + ((x as i32 + y as i32) as u64 + 0u64); + //~^ ERROR: this operation has no effect +} diff --git a/tests/ui/identity_op.stderr b/tests/ui/identity_op.stderr index 9fff86b86f9f..99a58ef2c1b5 100644 --- a/tests/ui/identity_op.stderr +++ b/tests/ui/identity_op.stderr @@ -149,7 +149,7 @@ error: this operation has no effect --> tests/ui/identity_op.rs:119:5 | LL | (if b { 1 } else { 2 }) + 0; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(if b { 1 } else { 2 })` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `((if b { 1 } else { 2 }))` error: this operation has no effect --> tests/ui/identity_op.rs:122:5 @@ -313,5 +313,71 @@ error: this operation has no effect LL | let _: i32 = **&&*&x + 0; | ^^^^^^^^^^^ help: consider reducing it to: `***&&*&x` -error: aborting due to 52 previous errors +error: this operation has no effect + --> tests/ui/identity_op.rs:220:18 + | +LL | let _: u64 = (x + y + 0i32) as u64; + | ^^^^^^^^^^^^^^ help: consider reducing it to: `(x + y)` + +error: this operation has no effect + --> tests/ui/identity_op.rs:223:25 + | +LL | let _: u64 = 1u64 & (x + y + 0i32) as u64; + | ^^^^^^^^^^^^^^ help: consider reducing it to: `(x + y)` + +error: this operation has no effect + --> tests/ui/identity_op.rs:226:25 + | +LL | let _: u64 = 1u64 & ((x + y) + 0i32) as u64; + | ^^^^^^^^^^^^^^^^ help: consider reducing it to: `((x + y))` + +error: this operation has no effect + --> tests/ui/identity_op.rs:229:25 + | +LL | let _: u64 = 5u64 + ((x + y) + 0i32) as u64; + | ^^^^^^^^^^^^^^^^ help: consider reducing it to: `((x + y))` + +error: this operation has no effect + --> tests/ui/identity_op.rs:233:14 + | +LL | let _ = -(x + y + 0i32); + | ^^^^^^^^^^^^^^ help: consider reducing it to: `(x + y)` + +error: this operation has no effect + --> tests/ui/identity_op.rs:236:14 + | +LL | let _ = -(x / y / 1i32); + | ^^^^^^^^^^^^^^ help: consider reducing it to: `(x / y)` + +error: this operation has no effect + --> tests/ui/identity_op.rs:239:20 + | +LL | let _ = 2i32 * (x + y + 0i32); + | ^^^^^^^^^^^^^^ help: consider reducing it to: `(x + y)` + +error: this operation has no effect + --> tests/ui/identity_op.rs:243:20 + | +LL | let _ = 2i32 - (x - y - 0i32); + | ^^^^^^^^^^^^^^ help: consider reducing it to: `(x - y)` + +error: this operation has no effect + --> tests/ui/identity_op.rs:247:17 + | +LL | let _ = 2 + (x + (y * z) + 0); + | ^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(x + (y * z))` + +error: this operation has no effect + --> tests/ui/identity_op.rs:251:20 + | +LL | let _ = 2i32 + (x * y * 1i32); + | ^^^^^^^^^^^^^^ help: consider reducing it to: `(x * y)` + +error: this operation has no effect + --> tests/ui/identity_op.rs:256:25 + | +LL | let _: u64 = 1u64 + ((x as i32 + y as i32) as u64 + 0u64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `((x as i32 + y as i32) as u64)` + +error: aborting due to 63 previous errors diff --git a/tests/ui/question_mark.fixed b/tests/ui/question_mark.fixed index 679388372e61..b6e148e9f772 100644 --- a/tests/ui/question_mark.fixed +++ b/tests/ui/question_mark.fixed @@ -96,12 +96,42 @@ impl MoveStruct { } fn func() -> Option { + macro_rules! opt_none { + () => { + None + }; + } + fn f() -> Option { Some(String::new()) } f()?; + let _val = f()?; + + let s: &str = match &Some(String::new()) { + Some(v) => v, + None => return None, + }; + + f()?; + + opt_none!()?; + + match f() { + Some(x) => x, + None => return opt_none!(), + }; + + match f() { + Some(val) => { + println!("{val}"); + val + }, + None => return None, + }; + Some(0) } @@ -114,6 +144,10 @@ fn result_func(x: Result) -> Result { x?; + let _val = func_returning_result()?; + + func_returning_result()?; + // No warning let y = if let Ok(x) = x { x @@ -157,6 +191,28 @@ fn result_func(x: Result) -> Result { Ok(y) } +fn infer_check() { + let closure = |x: Result| { + // `?` would fail here, as it expands to `Err(val.into())` which is not constrained. + let _val = match x { + Ok(val) => val, + Err(val) => return Err(val), + }; + + Ok(()) + }; + + let closure = |x: Result| -> Result<(), _> { + // `?` would fail here, as it expands to `Err(val.into())` which is not constrained. + let _val = match x { + Ok(val) => val, + Err(val) => return Err(val), + }; + + Ok(()) + }; +} + // see issue #8019 pub enum NotOption { None, diff --git a/tests/ui/question_mark.rs b/tests/ui/question_mark.rs index 601ab78bf5aa..48dc9eb0a626 100644 --- a/tests/ui/question_mark.rs +++ b/tests/ui/question_mark.rs @@ -124,6 +124,12 @@ pub fn if_let_mov_func(self) -> Option> { } fn func() -> Option { + macro_rules! opt_none { + () => { + None + }; + } + fn f() -> Option { Some(String::new()) } @@ -132,6 +138,39 @@ fn f() -> Option { return None; } + let _val = match f() { + Some(val) => val, + None => return None, + }; + + let s: &str = match &Some(String::new()) { + Some(v) => v, + None => return None, + }; + + match f() { + Some(val) => val, + None => return None, + }; + + match opt_none!() { + Some(x) => x, + None => return None, + }; + + match f() { + Some(x) => x, + None => return opt_none!(), + }; + + match f() { + Some(val) => { + println!("{val}"); + val + }, + None => return None, + }; + Some(0) } @@ -146,6 +185,16 @@ fn result_func(x: Result) -> Result { return x; } + let _val = match func_returning_result() { + Ok(val) => val, + Err(err) => return Err(err), + }; + + match func_returning_result() { + Ok(val) => val, + Err(err) => return Err(err), + }; + // No warning let y = if let Ok(x) = x { x @@ -189,6 +238,28 @@ fn result_func(x: Result) -> Result { Ok(y) } +fn infer_check() { + let closure = |x: Result| { + // `?` would fail here, as it expands to `Err(val.into())` which is not constrained. + let _val = match x { + Ok(val) => val, + Err(val) => return Err(val), + }; + + Ok(()) + }; + + let closure = |x: Result| -> Result<(), _> { + // `?` would fail here, as it expands to `Err(val.into())` which is not constrained. + let _val = match x { + Ok(val) => val, + Err(val) => return Err(val), + }; + + Ok(()) + }; +} + // see issue #8019 pub enum NotOption { None, diff --git a/tests/ui/question_mark.stderr b/tests/ui/question_mark.stderr index 5f26a7ea2c3e..0a48c4e80cb6 100644 --- a/tests/ui/question_mark.stderr +++ b/tests/ui/question_mark.stderr @@ -94,29 +94,76 @@ LL | | }; | |_________^ help: replace it with: `self.opt?` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:131:5 + --> tests/ui/question_mark.rs:137:5 | LL | / if f().is_none() { LL | | return None; LL | | } | |_____^ help: replace it with: `f()?;` +error: this `match` expression can be replaced with `?` + --> tests/ui/question_mark.rs:141:16 + | +LL | let _val = match f() { + | ________________^ +LL | | Some(val) => val, +LL | | None => return None, +LL | | }; + | |_____^ help: try instead: `f()?` + +error: this `match` expression can be replaced with `?` + --> tests/ui/question_mark.rs:151:5 + | +LL | / match f() { +LL | | Some(val) => val, +LL | | None => return None, +LL | | }; + | |_____^ help: try instead: `f()?` + +error: this `match` expression can be replaced with `?` + --> tests/ui/question_mark.rs:156:5 + | +LL | / match opt_none!() { +LL | | Some(x) => x, +LL | | None => return None, +LL | | }; + | |_____^ help: try instead: `opt_none!()?` + error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:143:13 + --> tests/ui/question_mark.rs:182:13 | LL | let _ = if let Ok(x) = x { x } else { return x }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x?` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:145:5 + --> tests/ui/question_mark.rs:184:5 | LL | / if x.is_err() { LL | | return x; LL | | } | |_____^ help: replace it with: `x?;` +error: this `match` expression can be replaced with `?` + --> tests/ui/question_mark.rs:188:16 + | +LL | let _val = match func_returning_result() { + | ________________^ +LL | | Ok(val) => val, +LL | | Err(err) => return Err(err), +LL | | }; + | |_____^ help: try instead: `func_returning_result()?` + +error: this `match` expression can be replaced with `?` + --> tests/ui/question_mark.rs:193:5 + | +LL | / match func_returning_result() { +LL | | Ok(val) => val, +LL | | Err(err) => return Err(err), +LL | | }; + | |_____^ help: try instead: `func_returning_result()?` + error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:213:5 + --> tests/ui/question_mark.rs:284:5 | LL | / if let Err(err) = func_returning_result() { LL | | return Err(err); @@ -124,7 +171,7 @@ LL | | } | |_____^ help: replace it with: `func_returning_result()?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:220:5 + --> tests/ui/question_mark.rs:291:5 | LL | / if let Err(err) = func_returning_result() { LL | | return Err(err); @@ -132,7 +179,7 @@ LL | | } | |_____^ help: replace it with: `func_returning_result()?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:297:13 + --> tests/ui/question_mark.rs:368:13 | LL | / if a.is_none() { LL | | return None; @@ -142,12 +189,12 @@ LL | | } | |_____________^ help: replace it with: `a?;` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:357:5 + --> tests/ui/question_mark.rs:428:5 | LL | / let Some(v) = bar.foo.owned.clone() else { LL | | return None; LL | | }; | |______^ help: replace it with: `let v = bar.foo.owned.clone()?;` -error: aborting due to 17 previous errors +error: aborting due to 22 previous errors 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 + From a2b6b6b085db2459e4331dd6becaac8191c1d84b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 17 Nov 2024 12:25:19 -0800 Subject: [PATCH 02/12] Inline ExprPrecedence::order into Expr::precedence --- clippy_lints/src/dereference.rs | 10 +++++----- clippy_lints/src/loops/single_element_loop.rs | 2 +- clippy_lints/src/matches/manual_utils.rs | 2 +- clippy_lints/src/neg_multiply.rs | 2 +- clippy_lints/src/redundant_slicing.rs | 2 +- .../transmute/transmutes_expressible_as_ptr_casts.rs | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index b167d7f22087..834606094ae0 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -959,7 +959,7 @@ fn report<'tcx>( // expr_str (the suggestion) is never shown if is_final_ufcs is true, since it's // `expr.kind == ExprKind::Call`. Therefore, this is, afaik, always unnecessary. /* - expr_str = if !expr_is_macro_call && is_final_ufcs && expr.precedence().order() < PREC_PREFIX { + expr_str = if !expr_is_macro_call && is_final_ufcs && expr.precedence() < PREC_PREFIX { Cow::Owned(format!("({expr_str})")) } else { expr_str @@ -999,7 +999,7 @@ fn report<'tcx>( Node::Expr(e) => match e.kind { ExprKind::Call(callee, _) if callee.hir_id != data.first_expr.hir_id => (0, false), ExprKind::Call(..) => (PREC_UNAMBIGUOUS, matches!(expr.kind, ExprKind::Field(..))), - _ => (e.precedence().order(), false), + _ => (e.precedence(), false), }, _ => (0, false), }; @@ -1012,7 +1012,7 @@ fn report<'tcx>( ); let sugg = if !snip_is_macro - && (calls_field || expr.precedence().order() < precedence) + && (calls_field || expr.precedence() < precedence) && !has_enclosing_paren(&snip) && !is_in_tuple { @@ -1067,7 +1067,7 @@ fn report<'tcx>( let (snip, snip_is_macro) = snippet_with_context(cx, expr.span, data.first_expr.span.ctxt(), "..", &mut app); let sugg = - if !snip_is_macro && expr.precedence().order() < precedence && !has_enclosing_paren(&snip) { + if !snip_is_macro && expr.precedence() < precedence && !has_enclosing_paren(&snip) { format!("{prefix}({snip})") } else { format!("{prefix}{snip}") @@ -1154,7 +1154,7 @@ fn check_local_usage(&mut self, cx: &LateContext<'tcx>, e: &Expr<'tcx>, local: H }, Some(parent) if !parent.span.from_expansion() => { // Double reference might be needed at this point. - if parent.precedence().order() == PREC_UNAMBIGUOUS { + if parent.precedence() == PREC_UNAMBIGUOUS { // Parentheses would be needed here, don't lint. *outer_pat = None; } else { diff --git a/clippy_lints/src/loops/single_element_loop.rs b/clippy_lints/src/loops/single_element_loop.rs index 70f76ced09a4..35dc8e9aa4e2 100644 --- a/clippy_lints/src/loops/single_element_loop.rs +++ b/clippy_lints/src/loops/single_element_loop.rs @@ -84,7 +84,7 @@ pub(super) fn check<'tcx>( if !prefix.is_empty() && ( // Precedence of internal expression is less than or equal to precedence of `&expr`. - arg_expression.precedence().order() <= PREC_PREFIX || is_range_literal(arg_expression) + arg_expression.precedence() <= PREC_PREFIX || is_range_literal(arg_expression) ) { arg_snip = format!("({arg_snip})").into(); diff --git a/clippy_lints/src/matches/manual_utils.rs b/clippy_lints/src/matches/manual_utils.rs index d38560998a5a..9c6df4d8ac0d 100644 --- a/clippy_lints/src/matches/manual_utils.rs +++ b/clippy_lints/src/matches/manual_utils.rs @@ -117,7 +117,7 @@ pub(super) fn check_with<'tcx, F>( // it's being passed by value. let scrutinee = peel_hir_expr_refs(scrutinee).0; let (scrutinee_str, _) = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app); - let scrutinee_str = if scrutinee.span.eq_ctxt(expr.span) && scrutinee.precedence().order() < PREC_UNAMBIGUOUS { + let scrutinee_str = if scrutinee.span.eq_ctxt(expr.span) && scrutinee.precedence() < PREC_UNAMBIGUOUS { format!("({scrutinee_str})") } else { scrutinee_str.into() diff --git a/clippy_lints/src/neg_multiply.rs b/clippy_lints/src/neg_multiply.rs index f84d9fadb85c..a0ba2aaf5523 100644 --- a/clippy_lints/src/neg_multiply.rs +++ b/clippy_lints/src/neg_multiply.rs @@ -58,7 +58,7 @@ fn check_mul(cx: &LateContext<'_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) { { let mut applicability = Applicability::MachineApplicable; let (snip, from_macro) = snippet_with_context(cx, exp.span, span.ctxt(), "..", &mut applicability); - let suggestion = if !from_macro && exp.precedence().order() < PREC_PREFIX && !has_enclosing_paren(&snip) { + let suggestion = if !from_macro && exp.precedence() < PREC_PREFIX && !has_enclosing_paren(&snip) { format!("-({snip})") } else { format!("-{snip}") diff --git a/clippy_lints/src/redundant_slicing.rs b/clippy_lints/src/redundant_slicing.rs index dc66fb28fa8c..159404e130d6 100644 --- a/clippy_lints/src/redundant_slicing.rs +++ b/clippy_lints/src/redundant_slicing.rs @@ -85,7 +85,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { 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.is_some_and(|parent| parent.precedence().order() > PREC_PREFIX); + let needs_parens_for_prefix = parent_expr.is_some_and(|parent| parent.precedence() > PREC_PREFIX); if expr_ty == indexed_ty { if expr_ref_count > indexed_ref_count { diff --git a/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs b/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs index fca332dba401..cad15b1e9826 100644 --- a/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs +++ b/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs @@ -1,7 +1,7 @@ use super::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::sugg::Sugg; -use rustc_ast::ExprPrecedence; +use rustc_ast::util::parser::AssocOp; use rustc_errors::Applicability; use rustc_hir::{Expr, Node}; use rustc_hir_typeck::cast::check_cast; @@ -44,7 +44,7 @@ pub(super) fn check<'tcx>( }; if let Node::Expr(parent) = cx.tcx.parent_hir_node(e.hir_id) - && parent.precedence().order() > ExprPrecedence::Cast.order() + && parent.precedence() > AssocOp::As.precedence() as i8 { sugg = format!("({sugg})"); } From bb93c23c0854bbb8aa6a523f0b4462da0f75f6da Mon Sep 17 00:00:00 2001 From: lcnr Date: Fri, 15 Nov 2024 13:53:31 +0100 Subject: [PATCH 03/12] use `TypingEnv` when no `infcx` is available the behavior of the type system not only depends on the current assumptions, but also the currentnphase of the compiler. This is mostly necessary as we need to decide whether and how to reveal opaque types. We track this via the `TypingMode`. --- clippy_lints/src/assigning_clones.rs | 2 +- clippy_lints/src/bool_assert_comparison.rs | 2 +- clippy_lints/src/dereference.rs | 9 +++++---- clippy_lints/src/drop_forget_ref.rs | 4 ++-- .../src/iter_not_returning_iterator.rs | 2 +- clippy_lints/src/iter_without_into_iter.rs | 2 +- clippy_lints/src/large_const_arrays.rs | 4 ++-- clippy_lints/src/large_futures.rs | 2 +- clippy_lints/src/large_stack_frames.rs | 4 ++-- clippy_lints/src/loops/explicit_iter_loop.rs | 12 ++++++------ clippy_lints/src/methods/needless_collect.rs | 6 +++--- .../src/methods/unnecessary_min_or_max.rs | 2 +- .../src/methods/unnecessary_to_owned.rs | 2 +- clippy_lints/src/methods/zst_offset.rs | 2 +- .../src/needless_borrows_for_generic_args.rs | 2 +- clippy_lints/src/non_copy_const.rs | 18 +++++++++--------- .../src/operators/const_comparisons.rs | 2 +- clippy_lints/src/operators/erasing_op.rs | 2 +- clippy_lints/src/operators/float_cmp.rs | 3 +-- clippy_lints/src/redundant_slicing.rs | 2 +- clippy_lints/src/returns.rs | 2 +- .../src/significant_drop_tightening.rs | 2 +- clippy_lints/src/trailing_empty_array.rs | 2 +- clippy_lints/src/transmute/eager_transmute.rs | 4 ++-- .../src/transmute/transmute_undefined_repr.rs | 6 +++--- clippy_lints/src/transmute/utils.rs | 9 +++++---- clippy_lints/src/uninhabited_references.rs | 4 ++-- clippy_utils/src/consts.rs | 12 ++++++------ clippy_utils/src/eager_or_lazy.rs | 2 +- clippy_utils/src/hir_utils.rs | 6 +++--- clippy_utils/src/lib.rs | 2 +- clippy_utils/src/qualify_min_const_fn.rs | 10 ++++++---- clippy_utils/src/ty.rs | 17 +++++++++-------- 33 files changed, 83 insertions(+), 79 deletions(-) diff --git a/clippy_lints/src/assigning_clones.rs b/clippy_lints/src/assigning_clones.rs index 0b82c0cd04c1..00626a37ef84 100644 --- a/clippy_lints/src/assigning_clones.rs +++ b/clippy_lints/src/assigning_clones.rs @@ -96,7 +96,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { }, _ => return, } - && let Ok(Some(resolved_fn)) = Instance::try_resolve(cx.tcx, cx.param_env, fn_id, fn_gen_args) + && let Ok(Some(resolved_fn)) = Instance::try_resolve(cx.tcx, cx.typing_env(), fn_id, fn_gen_args) // TODO: This check currently bails if the local variable has no initializer. // That is overly conservative - the lint should fire even if there was no initializer, // but the variable has been initialized before `lhs` was evaluated. diff --git a/clippy_lints/src/bool_assert_comparison.rs b/clippy_lints/src/bool_assert_comparison.rs index 7d89195eeca7..adac2f27ea8c 100644 --- a/clippy_lints/src/bool_assert_comparison.rs +++ b/clippy_lints/src/bool_assert_comparison.rs @@ -62,7 +62,7 @@ fn is_impl_not_trait_with_bool_out<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) - }) .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); + let nty = cx.tcx.normalize_erasing_regions(cx.typing_env(), proj); nty.is_bool() }) diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index b167d7f22087..f864b7a5a8af 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -17,7 +17,7 @@ }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; -use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt, TypeVisitableExt, TypeckResults}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, TypeckResults}; use rustc_session::impl_lint_pass; use rustc_span::symbol::sym; use rustc_span::{Span, Symbol}; @@ -755,7 +755,8 @@ fn for_defined_ty<'tcx>(cx: &LateContext<'tcx>, ty: DefinedTy<'tcx>, for_return: DefinedTy::Hir(ty) => Self::for_hir_ty(ty), DefinedTy::Mir(ty) => Self::for_mir_ty( cx.tcx, - ty.param_env, + // FIXME(#132279): convert `DefinedTy` to use `TypingEnv` instead. + ty::TypingEnv::from_param_env(ty.param_env), cx.tcx.instantiate_bound_regions_with_erased(ty.value), for_return, ), @@ -823,12 +824,12 @@ fn for_hir_ty<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> Self { } } - fn for_mir_ty<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: Ty<'tcx>, for_return: bool) -> Self { + fn for_mir_ty<'tcx>(tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>, ty: Ty<'tcx>, for_return: bool) -> Self { let ty::Ref(_, mut ty, _) = *ty.kind() else { return Self::None; }; - ty = tcx.try_normalize_erasing_regions(param_env, ty).unwrap_or(ty); + ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty); loop { break match *ty.kind() { ty::Ref(_, ref_ty, _) => { diff --git a/clippy_lints/src/drop_forget_ref.rs b/clippy_lints/src/drop_forget_ref.rs index 55afdbf22e1f..617982f4da30 100644 --- a/clippy_lints/src/drop_forget_ref.rs +++ b/clippy_lints/src/drop_forget_ref.rs @@ -99,7 +99,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { sym::mem_forget if is_copy => return, sym::mem_drop if is_type_lang_item(cx, arg_ty, LangItem::ManuallyDrop) => return, sym::mem_drop - if !(arg_ty.needs_drop(cx.tcx, cx.param_env) + if !(arg_ty.needs_drop(cx.tcx, cx.typing_env()) || is_must_use_func_call(cx, arg) || is_must_use_ty(cx, arg_ty) || drop_is_single_call_in_arm) => @@ -107,7 +107,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { (DROP_NON_DROP, DROP_NON_DROP_SUMMARY.into(), Some(arg.span)) }, sym::mem_forget => { - if arg_ty.needs_drop(cx.tcx, cx.param_env) { + if arg_ty.needs_drop(cx.tcx, cx.typing_env()) { ( MEM_FORGET, Cow::Owned(format!( diff --git a/clippy_lints/src/iter_not_returning_iterator.rs b/clippy_lints/src/iter_not_returning_iterator.rs index 25105817ad97..4bc6ad0798c9 100644 --- a/clippy_lints/src/iter_not_returning_iterator.rs +++ b/clippy_lints/src/iter_not_returning_iterator.rs @@ -70,7 +70,7 @@ fn check_sig(cx: &LateContext<'_>, name: Symbol, sig: &FnSig<'_>, fn_id: LocalDe .instantiate_bound_regions_with_erased(cx.tcx.fn_sig(fn_id).instantiate_identity().output()); let ret_ty = cx .tcx - .try_normalize_erasing_regions(cx.param_env, ret_ty) + .try_normalize_erasing_regions(cx.typing_env(), ret_ty) .unwrap_or(ret_ty); if cx .tcx diff --git a/clippy_lints/src/iter_without_into_iter.rs b/clippy_lints/src/iter_without_into_iter.rs index 314d0dfa26ce..906da81b1837 100644 --- a/clippy_lints/src/iter_without_into_iter.rs +++ b/clippy_lints/src/iter_without_into_iter.rs @@ -215,7 +215,7 @@ fn check_impl_item(&mut self, cx: &LateContext<'_>, item: &rustc_hir::ImplItem<' && implements_trait(cx, ret_ty, iterator_did, &[]) && let Some(iter_ty) = make_normalized_projection( cx.tcx, - cx.param_env, + cx.typing_env(), iterator_did, sym::Item, [ret_ty], diff --git a/clippy_lints/src/large_const_arrays.rs b/clippy_lints/src/large_const_arrays.rs index c5a2760234fc..644365c9fe56 100644 --- a/clippy_lints/src/large_const_arrays.rs +++ b/clippy_lints/src/large_const_arrays.rs @@ -4,7 +4,7 @@ use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::layout::LayoutOf; -use rustc_middle::ty::{self, ParamEnv}; +use rustc_middle::ty; use rustc_session::impl_lint_pass; use rustc_span::{BytePos, Pos, Span}; @@ -57,7 +57,7 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { && let ty = cx.tcx.type_of(item.owner_id).instantiate_identity() && let ty::Array(element_type, cst) = ty.kind() && let Some((ty::ValTree::Leaf(element_count), _)) = cx.tcx - .try_normalize_erasing_regions(ParamEnv::empty(), *cst).unwrap_or(*cst).try_to_valtree() + .try_normalize_erasing_regions(cx.typing_env(), *cst).unwrap_or(*cst).try_to_valtree() && let element_count = element_count.to_target_usize(cx.tcx) && let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes()) && u128::from(self.maximum_allowed_size) < u128::from(element_count) * u128::from(element_size) diff --git a/clippy_lints/src/large_futures.rs b/clippy_lints/src/large_futures.rs index 25f9be8b2d7a..593704f206a1 100644 --- a/clippy_lints/src/large_futures.rs +++ b/clippy_lints/src/large_futures.rs @@ -63,7 +63,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { && let ty = cx.typeck_results().expr_ty(arg) && let Some(future_trait_def_id) = cx.tcx.lang_items().future_trait() && implements_trait(cx, ty, future_trait_def_id, &[]) - && let Ok(layout) = cx.tcx.layout_of(cx.param_env.and(ty)) + && let Ok(layout) = cx.tcx.layout_of(cx.typing_env().as_query_input(ty)) && let size = layout.layout.size() && size >= Size::from_bytes(self.future_size_threshold) { diff --git a/clippy_lints/src/large_stack_frames.rs b/clippy_lints/src/large_stack_frames.rs index d2bdf194adad..5ed948c02bbc 100644 --- a/clippy_lints/src/large_stack_frames.rs +++ b/clippy_lints/src/large_stack_frames.rs @@ -150,11 +150,11 @@ fn check_fn( } let mir = cx.tcx.optimized_mir(def_id); - let param_env = cx.tcx.param_env(def_id); + let typing_env = mir.typing_env(cx.tcx); let sizes_of_locals = || { mir.local_decls.iter().filter_map(|local| { - let layout = cx.tcx.layout_of(param_env.and(local.ty)).ok()?; + let layout = cx.tcx.layout_of(typing_env.as_query_input(local.ty)).ok()?; Some((local, layout.size.bytes())) }) }; diff --git a/clippy_lints/src/loops/explicit_iter_loop.rs b/clippy_lints/src/loops/explicit_iter_loop.rs index ee561ea85ed1..48318682f33c 100644 --- a/clippy_lints/src/loops/explicit_iter_loop.rs +++ b/clippy_lints/src/loops/explicit_iter_loop.rs @@ -151,7 +151,7 @@ fn is_ref_iterable<'tcx>( // Using by value won't consume anything if implements_trait(cx, self_ty, trait_id, &[]) && let Some(ty) = - make_normalized_projection(cx.tcx, cx.param_env, trait_id, sym!(IntoIter), [self_ty]) + make_normalized_projection(cx.tcx, cx.typing_env(), trait_id, sym!(IntoIter), [self_ty]) && ty == res_ty { return Some((AdjustKind::None, self_ty)); @@ -168,7 +168,7 @@ fn is_ref_iterable<'tcx>( }; if implements_trait(cx, self_ty, trait_id, &[]) && let Some(ty) = - make_normalized_projection(cx.tcx, cx.param_env, trait_id, sym!(IntoIter), [self_ty]) + make_normalized_projection(cx.tcx, cx.typing_env(), trait_id, sym!(IntoIter), [self_ty]) && ty == res_ty { return Some((AdjustKind::reborrow(mutbl), self_ty)); @@ -181,7 +181,7 @@ fn is_ref_iterable<'tcx>( // Attempt to borrow let self_ty = Ty::new_ref(cx.tcx, cx.tcx.lifetimes.re_erased, self_ty, mutbl); if implements_trait(cx, self_ty, trait_id, &[]) - && let Some(ty) = make_normalized_projection(cx.tcx, cx.param_env, trait_id, sym!(IntoIter), [self_ty]) + && let Some(ty) = make_normalized_projection(cx.tcx, cx.typing_env(), trait_id, sym!(IntoIter), [self_ty]) && ty == res_ty { return Some((AdjustKind::borrow(mutbl), self_ty)); @@ -204,7 +204,7 @@ fn is_ref_iterable<'tcx>( && target != self_ty && implements_trait(cx, target, trait_id, &[]) && let Some(ty) = - make_normalized_projection(cx.tcx, cx.param_env, trait_id, sym!(IntoIter), [target]) + make_normalized_projection(cx.tcx, cx.typing_env(), trait_id, sym!(IntoIter), [target]) && ty == res_ty { Some((AdjustKind::auto_reborrow(mutbl), target)) @@ -222,7 +222,7 @@ fn is_ref_iterable<'tcx>( if is_copy(cx, target) && implements_trait(cx, target, trait_id, &[]) && let Some(ty) = - make_normalized_projection(cx.tcx, cx.param_env, trait_id, sym!(IntoIter), [target]) + make_normalized_projection(cx.tcx, cx.typing_env(), trait_id, sym!(IntoIter), [target]) && ty == res_ty { Some((AdjustKind::Deref, target)) @@ -240,7 +240,7 @@ fn is_ref_iterable<'tcx>( if self_ty.is_ref() && implements_trait(cx, target, trait_id, &[]) && let Some(ty) = - make_normalized_projection(cx.tcx, cx.param_env, trait_id, sym!(IntoIter), [target]) + make_normalized_projection(cx.tcx, cx.typing_env(), trait_id, sym!(IntoIter), [target]) && ty == res_ty { Some((AdjustKind::auto_borrow(mutbl), target)) diff --git a/clippy_lints/src/methods/needless_collect.rs b/clippy_lints/src/methods/needless_collect.rs index 9c41528e6476..c00b9b368c43 100644 --- a/clippy_lints/src/methods/needless_collect.rs +++ b/clippy_lints/src/methods/needless_collect.rs @@ -203,10 +203,10 @@ fn is_is_empty_sig(cx: &LateContext<'_>, call_id: HirId) -> bool { fn iterates_same_ty<'tcx>(cx: &LateContext<'tcx>, iter_ty: Ty<'tcx>, collect_ty: Ty<'tcx>) -> bool { if let Some(iter_trait) = cx.tcx.get_diagnostic_item(sym::Iterator) && let Some(into_iter_trait) = cx.tcx.get_diagnostic_item(sym::IntoIterator) - && let Some(iter_item_ty) = make_normalized_projection(cx.tcx, cx.param_env, iter_trait, sym::Item, [iter_ty]) + && let Some(iter_item_ty) = make_normalized_projection(cx.tcx, cx.typing_env(), iter_trait, sym::Item, [iter_ty]) && let Some(into_iter_item_proj) = make_projection(cx.tcx, into_iter_trait, sym::Item, [collect_ty]) && let Ok(into_iter_item_ty) = cx.tcx.try_normalize_erasing_regions( - cx.param_env, + cx.typing_env(), Ty::new_projection_from_args(cx.tcx, into_iter_item_proj.def_id, into_iter_item_proj.args), ) { @@ -237,7 +237,7 @@ fn is_contains_sig(cx: &LateContext<'_>, call_id: HirId, iter_expr: &Expr<'_>) - ) && let args = cx.tcx.mk_args(&[GenericArg::from(typeck.expr_ty_adjusted(iter_expr))]) && let proj_ty = Ty::new_projection_from_args(cx.tcx, iter_item.def_id, args) - && let Ok(item_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, proj_ty) + && let Ok(item_ty) = cx.tcx.try_normalize_erasing_regions(cx.typing_env(), proj_ty) { item_ty == EarlyBinder::bind(search_ty).instantiate(cx.tcx, cx.typeck_results().node_args(call_id)) } else { diff --git a/clippy_lints/src/methods/unnecessary_min_or_max.rs b/clippy_lints/src/methods/unnecessary_min_or_max.rs index 062d1348555c..7d01bdc2269b 100644 --- a/clippy_lints/src/methods/unnecessary_min_or_max.rs +++ b/clippy_lints/src/methods/unnecessary_min_or_max.rs @@ -19,7 +19,7 @@ pub(super) fn check<'tcx>( arg: &'tcx Expr<'_>, ) { let typeck_results = cx.typeck_results(); - let ecx = ConstEvalCtxt::with_env(cx.tcx, cx.param_env, typeck_results); + let ecx = ConstEvalCtxt::with_env(cx.tcx, cx.typing_env(), typeck_results); if let Some(id) = typeck_results.type_dependent_def_id(expr.hir_id) && (cx.tcx.is_diagnostic_item(sym::cmp_ord_min, id) || cx.tcx.is_diagnostic_item(sym::cmp_ord_max, id)) { diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index 82549413fa91..84ea3554a358 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -578,7 +578,7 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty< if output_ty.contains(param_ty) { if let Ok(new_ty) = cx.tcx.try_instantiate_and_normalize_erasing_regions( new_subst, - cx.param_env, + cx.typing_env(), bound_fn_sig.rebind(output_ty), ) { expr = parent_expr; diff --git a/clippy_lints/src/methods/zst_offset.rs b/clippy_lints/src/methods/zst_offset.rs index d33021c2a7bf..102fa7bc8953 100644 --- a/clippy_lints/src/methods/zst_offset.rs +++ b/clippy_lints/src/methods/zst_offset.rs @@ -7,7 +7,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { if let ty::RawPtr(ty, _) = cx.typeck_results().expr_ty(recv).kind() - && let Ok(layout) = cx.tcx.layout_of(cx.param_env.and(*ty)) + && let Ok(layout) = cx.tcx.layout_of(cx.typing_env().as_query_input(*ty)) && layout.is_zst() { span_lint(cx, ZST_OFFSET, expr.span, "offset calculation on zero-sized value"); diff --git a/clippy_lints/src/needless_borrows_for_generic_args.rs b/clippy_lints/src/needless_borrows_for_generic_args.rs index c1424b9f1dc8..43b885fbd2c9 100644 --- a/clippy_lints/src/needless_borrows_for_generic_args.rs +++ b/clippy_lints/src/needless_borrows_for_generic_args.rs @@ -421,7 +421,7 @@ fn replace_types<'tcx>( .expect_ty(cx.tcx) .to_ty(cx.tcx); - if let Ok(projected_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, projection) + if let Ok(projected_ty) = cx.tcx.try_normalize_erasing_regions(cx.typing_env(), projection) && args[term_param_ty.index as usize] != GenericArg::from(projected_ty) { deque.push_back((*term_param_ty, projected_ty)); diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index 5e20b4064260..57fa4797c5e5 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -278,23 +278,23 @@ fn is_value_unfrozen_poly(cx: &LateContext<'tcx>, body_id: BodyId, ty: Ty<'tcx>) fn is_value_unfrozen_expr(cx: &LateContext<'tcx>, hir_id: HirId, def_id: DefId, ty: Ty<'tcx>) -> bool { let args = cx.typeck_results().node_args(hir_id); - let result = Self::const_eval_resolve(cx.tcx, cx.param_env, ty::UnevaluatedConst::new(def_id, args), DUMMY_SP); + let result = Self::const_eval_resolve(cx.tcx, cx.typing_env(), ty::UnevaluatedConst::new(def_id, args), DUMMY_SP); Self::is_value_unfrozen_raw(cx, result, ty) } pub fn const_eval_resolve( tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, + typing_env: ty::TypingEnv<'tcx>, ct: ty::UnevaluatedConst<'tcx>, span: Span, ) -> EvalToValTreeResult<'tcx> { - match ty::Instance::try_resolve(tcx, param_env, ct.def, ct.args) { + match ty::Instance::try_resolve(tcx, typing_env, ct.def, ct.args) { Ok(Some(instance)) => { let cid = GlobalId { instance, promoted: None, }; - tcx.const_eval_global_id_for_typeck(param_env, cid, span) + tcx.const_eval_global_id_for_typeck(typing_env.param_env, cid, span) }, Ok(None) => Err(ErrorHandled::TooGeneric(span)), Err(err) => Err(ErrorHandled::Reported(err.into(), span)), @@ -321,7 +321,7 @@ fn check_trait_item(&mut self, cx: &LateContext<'tcx>, trait_item: &'tcx TraitIt // Normalize assoc types because ones originated from generic params // bounded other traits could have their bound. - let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty); + let normalized = cx.tcx.normalize_erasing_regions(cx.typing_env(), ty); if self.interior_mut.is_interior_mut_ty(cx, normalized) // When there's no default value, lint it only according to its type; // in other words, lint consts whose value *could* be unfrozen, not definitely is. @@ -361,12 +361,12 @@ fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem< .trait_item_def_id && cx .tcx - .layout_of(cx.tcx.param_env(of_trait_def_id).and( + .layout_of(ty::TypingEnv::post_analysis(cx.tcx, of_trait_def_id).as_query_input( // Normalize assoc types because ones originated from generic params // bounded other traits could have their bound at the trait defs; // and, in that case, the definition is *not* generic. cx.tcx.normalize_erasing_regions( - cx.tcx.param_env(of_trait_def_id), + ty::TypingEnv::post_analysis(cx.tcx, of_trait_def_id), cx.tcx.type_of(of_assoc_item).instantiate_identity(), ), )) @@ -376,7 +376,7 @@ fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem< // similar to unknown layouts. // e.g. `layout_of(...).is_err() || has_frozen_variant(...);` && let ty = cx.tcx.type_of(impl_item.owner_id).instantiate_identity() - && let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty) + && let normalized = cx.tcx.normalize_erasing_regions(cx.typing_env(), ty) && self.interior_mut.is_interior_mut_ty(cx, normalized) && Self::is_value_unfrozen_poly(cx, *body_id, normalized) { @@ -386,7 +386,7 @@ fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem< ItemKind::Impl(Impl { of_trait: None, .. }) => { let ty = cx.tcx.type_of(impl_item.owner_id).instantiate_identity(); // Normalize assoc types originated from generic params. - let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty); + let normalized = cx.tcx.normalize_erasing_regions(cx.typing_env(), ty); if self.interior_mut.is_interior_mut_ty(cx, normalized) && Self::is_value_unfrozen_poly(cx, *body_id, normalized) diff --git a/clippy_lints/src/operators/const_comparisons.rs b/clippy_lints/src/operators/const_comparisons.rs index 5d94cfab3b02..1a0bfd8b9970 100644 --- a/clippy_lints/src/operators/const_comparisons.rs +++ b/clippy_lints/src/operators/const_comparisons.rs @@ -26,7 +26,7 @@ fn comparison_to_const<'tcx>( if let ExprKind::Binary(operator, left, right) = expr.kind && let Ok(cmp_op) = CmpOp::try_from(operator.node) { - let ecx = ConstEvalCtxt::with_env(cx.tcx, cx.param_env, typeck); + let ecx = ConstEvalCtxt::with_env(cx.tcx, cx.typing_env(), typeck); match (ecx.eval(left), ecx.eval(right)) { (Some(_), Some(_)) => None, (_, Some(con)) => Some((cmp_op, left, right, con, typeck.expr_ty(right))), diff --git a/clippy_lints/src/operators/erasing_op.rs b/clippy_lints/src/operators/erasing_op.rs index 24bfe2b050bb..e3fc8d8fea7d 100644 --- a/clippy_lints/src/operators/erasing_op.rs +++ b/clippy_lints/src/operators/erasing_op.rs @@ -39,7 +39,7 @@ fn check_op<'tcx>( other: &Expr<'tcx>, parent: &Expr<'tcx>, ) { - if ConstEvalCtxt::with_env(cx.tcx, cx.param_env, tck).eval_simple(op) == Some(Constant::Int(0)) { + if ConstEvalCtxt::with_env(cx.tcx, cx.typing_env(), tck).eval_simple(op) == Some(Constant::Int(0)) { if different_types(tck, other, parent) { return; } diff --git a/clippy_lints/src/operators/float_cmp.rs b/clippy_lints/src/operators/float_cmp.rs index ab5f91c1d672..8272d3643d42 100644 --- a/clippy_lints/src/operators/float_cmp.rs +++ b/clippy_lints/src/operators/float_cmp.rs @@ -17,8 +17,7 @@ pub(crate) fn check<'tcx>( right: &'tcx Expr<'_>, ) { if (op == BinOpKind::Eq || op == BinOpKind::Ne) && is_float(cx, left) { - let typeck = cx.typeck_results(); - let ecx = ConstEvalCtxt::with_env(cx.tcx, cx.param_env, typeck); + let ecx = ConstEvalCtxt::new(cx); let left_is_local = match ecx.eval_with_source(left) { Some((c, s)) if !is_allowed(&c) => s.is_local(), Some(_) => return, diff --git a/clippy_lints/src/redundant_slicing.rs b/clippy_lints/src/redundant_slicing.rs index dc66fb28fa8c..0ac818c21d9b 100644 --- a/clippy_lints/src/redundant_slicing.rs +++ b/clippy_lints/src/redundant_slicing.rs @@ -136,7 +136,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { }); } else if let Some(target_id) = cx.tcx.lang_items().deref_target() { if let Ok(deref_ty) = cx.tcx.try_normalize_erasing_regions( - cx.param_env, + cx.typing_env(), Ty::new_projection_from_args(cx.tcx, target_id, cx.tcx.mk_args(&[GenericArg::from(indexed_ty)])), ) { if deref_ty == expr_ty { diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 1e0f6dff1abe..aeff31d02d26 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -391,7 +391,7 @@ fn check_final_expr<'tcx>( if let Some(inner) = inner { if for_each_unconsumed_temporary(cx, inner, |temporary_ty| { - if temporary_ty.has_significant_drop(cx.tcx, cx.param_env) + if temporary_ty.has_significant_drop(cx.tcx, cx.typing_env()) && temporary_ty .walk() .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(re) if !re.is_static())) diff --git a/clippy_lints/src/significant_drop_tightening.rs b/clippy_lints/src/significant_drop_tightening.rs index abd8363456df..1a5b958e6a67 100644 --- a/clippy_lints/src/significant_drop_tightening.rs +++ b/clippy_lints/src/significant_drop_tightening.rs @@ -154,7 +154,7 @@ fn has_sig_drop_attr(&mut self, ty: Ty<'tcx>) -> bool { let ty = self .cx .tcx - .try_normalize_erasing_regions(self.cx.param_env, ty) + .try_normalize_erasing_regions(self.cx.typing_env(), ty) .unwrap_or(ty); match self.type_cache.entry(ty) { Entry::Occupied(e) => return *e.get(), diff --git a/clippy_lints/src/trailing_empty_array.rs b/clippy_lints/src/trailing_empty_array.rs index 52bb7c4bd68a..50a1577b2884 100644 --- a/clippy_lints/src/trailing_empty_array.rs +++ b/clippy_lints/src/trailing_empty_array.rs @@ -58,7 +58,7 @@ fn is_struct_with_trailing_zero_sized_array<'tcx>(cx: &LateContext<'tcx>, item: && let Some(last_field) = data.fields().last() && let field_ty = cx .tcx - .normalize_erasing_regions(cx.param_env, cx.tcx.type_of(last_field.def_id).instantiate_identity()) + .normalize_erasing_regions(cx.typing_env(), cx.tcx.type_of(last_field.def_id).instantiate_identity()) && let ty::Array(_, array_len) = *field_ty.kind() && let Some(0) = array_len.try_to_target_usize(cx.tcx) { diff --git a/clippy_lints/src/transmute/eager_transmute.rs b/clippy_lints/src/transmute/eager_transmute.rs index ca9daf2d2a03..1209bd5b34f2 100644 --- a/clippy_lints/src/transmute/eager_transmute.rs +++ b/clippy_lints/src/transmute/eager_transmute.rs @@ -88,8 +88,8 @@ pub(super) fn check<'tcx>( && is_normalizable(cx, cx.param_env, to_ty) // we only want to lint if the target type has a niche that is larger than the one of the source type // e.g. `u8` to `NonZero` should lint, but `NonZero` to `u8` should not - && let Ok(from_layout) = cx.tcx.layout_of(cx.param_env.and(from_ty)) - && let Ok(to_layout) = cx.tcx.layout_of(cx.param_env.and(to_ty)) + && let Ok(from_layout) = cx.tcx.layout_of(cx.typing_env().as_query_input(from_ty)) + && let Ok(to_layout) = cx.tcx.layout_of(cx.typing_env().as_query_input(to_ty)) && match (from_layout.largest_niche, to_layout.largest_niche) { (Some(from_niche), Some(to_niche)) => !range_fully_contained(from_niche.valid_range, to_niche.valid_range), (None, Some(_)) => true, diff --git a/clippy_lints/src/transmute/transmute_undefined_repr.rs b/clippy_lints/src/transmute/transmute_undefined_repr.rs index 3b32e4396b9f..4dc1290e8b15 100644 --- a/clippy_lints/src/transmute/transmute_undefined_repr.rs +++ b/clippy_lints/src/transmute/transmute_undefined_repr.rs @@ -244,7 +244,7 @@ enum ReducedTy<'tcx> { /// Reduce structs containing a single non-zero sized field to it's contained type. fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx> { loop { - ty = cx.tcx.try_normalize_erasing_regions(cx.param_env, ty).unwrap_or(ty); + ty = cx.tcx.try_normalize_erasing_regions(cx.typing_env(), ty).unwrap_or(ty); return match *ty.kind() { ty::Array(sub_ty, _) if matches!(sub_ty.kind(), ty::Int(_) | ty::Uint(_)) => { ReducedTy::TypeErasure { raw_ptr_only: false } @@ -297,8 +297,8 @@ fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx> } fn is_zero_sized_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { - if let Ok(ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, ty) - && let Ok(layout) = cx.tcx.layout_of(cx.param_env.and(ty)) + if let Ok(ty) = cx.tcx.try_normalize_erasing_regions(cx.typing_env(), ty) + && let Ok(layout) = cx.tcx.layout_of(cx.typing_env().as_query_input(ty)) { layout.layout.size().bytes() == 0 } else { diff --git a/clippy_lints/src/transmute/utils.rs b/clippy_lints/src/transmute/utils.rs index e8ccd35b4daf..5baa67b1f3e8 100644 --- a/clippy_lints/src/transmute/utils.rs +++ b/clippy_lints/src/transmute/utils.rs @@ -4,10 +4,11 @@ // check if the component types of the transmuted collection and the result have different ABI, // size or alignment pub(super) fn is_layout_incompatible<'tcx>(cx: &LateContext<'tcx>, from: Ty<'tcx>, to: Ty<'tcx>) -> bool { - if let Ok(from) = cx.tcx.try_normalize_erasing_regions(cx.param_env, from) - && let Ok(to) = cx.tcx.try_normalize_erasing_regions(cx.param_env, to) - && let Ok(from_layout) = cx.tcx.layout_of(cx.param_env.and(from)) - && let Ok(to_layout) = cx.tcx.layout_of(cx.param_env.and(to)) + let typing_env = cx.typing_env(); + if let Ok(from) = cx.tcx.try_normalize_erasing_regions(typing_env, from) + && let Ok(to) = cx.tcx.try_normalize_erasing_regions(typing_env, to) + && let Ok(from_layout) = cx.tcx.layout_of(typing_env.as_query_input(from)) + && let Ok(to_layout) = cx.tcx.layout_of(typing_env.as_query_input(to)) { from_layout.size != to_layout.size || from_layout.align.abi != to_layout.align.abi } else { diff --git a/clippy_lints/src/uninhabited_references.rs b/clippy_lints/src/uninhabited_references.rs index cfa565cf8037..ee9ef0172538 100644 --- a/clippy_lints/src/uninhabited_references.rs +++ b/clippy_lints/src/uninhabited_references.rs @@ -46,7 +46,7 @@ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { if let ExprKind::Unary(UnOp::Deref, _) = expr.kind { let ty = cx.typeck_results().expr_ty_adjusted(expr); - if ty.is_privately_uninhabited(cx.tcx, cx.param_env) { + if ty.is_privately_uninhabited(cx.tcx, cx.typing_env()) { span_lint( cx, UNINHABITED_REFERENCES, @@ -71,7 +71,7 @@ fn check_fn<'tcx>( } if let FnRetTy::Return(hir_ty) = fndecl.output && let TyKind::Ref(_, mut_ty) = hir_ty.kind - && lower_ty(cx.tcx, mut_ty.ty).is_privately_uninhabited(cx.tcx, cx.param_env) + && lower_ty(cx.tcx, mut_ty.ty).is_privately_uninhabited(cx.tcx, cx.typing_env()) { span_lint( cx, diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index 24a02c7ef871..52c986462899 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -18,7 +18,7 @@ use rustc_lint::LateContext; use rustc_middle::mir::ConstValue; use rustc_middle::mir::interpret::{Scalar, alloc_range}; -use rustc_middle::ty::{self, FloatTy, IntTy, ParamEnv, ScalarInt, Ty, TyCtxt, TypeckResults, UintTy}; +use rustc_middle::ty::{self, FloatTy, IntTy, ScalarInt, Ty, TyCtxt, TypeckResults, UintTy}; use rustc_middle::{bug, mir, span_bug}; use rustc_span::def_id::DefId; use rustc_span::symbol::Ident; @@ -387,7 +387,7 @@ fn cmp_s_u(s: i128, u: u128) -> Ordering { /// See the module level documentation for some context. pub struct ConstEvalCtxt<'tcx> { tcx: TyCtxt<'tcx>, - param_env: ParamEnv<'tcx>, + typing_env: ty::TypingEnv<'tcx>, typeck: &'tcx TypeckResults<'tcx>, source: Cell, } @@ -398,17 +398,17 @@ impl<'tcx> ConstEvalCtxt<'tcx> { pub fn new(cx: &LateContext<'tcx>) -> Self { Self { tcx: cx.tcx, - param_env: cx.param_env, + typing_env: cx.typing_env(), typeck: cx.typeck_results(), source: Cell::new(ConstantSource::Local), } } /// Creates an evaluation context. - pub fn with_env(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, typeck: &'tcx TypeckResults<'tcx>) -> Self { + pub fn with_env(tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>, typeck: &'tcx TypeckResults<'tcx>) -> Self { Self { tcx, - param_env, + typing_env, typeck, source: Cell::new(ConstantSource::Local), } @@ -643,7 +643,7 @@ fn fetch_path_and_apply(&self, qpath: &QPath<'_>, id: HirId, ty: Ty<'tcx>, let args = self.typeck.node_args(id); let result = self .tcx - .const_eval_resolve(self.param_env, mir::UnevaluatedConst::new(def_id, args), qpath.span()) + .const_eval_resolve(self.typing_env, mir::UnevaluatedConst::new(def_id, args), qpath.span()) .ok() .map(|val| mir::Const::from_value(val, ty))?; f(self, result) diff --git a/clippy_utils/src/eager_or_lazy.rs b/clippy_utils/src/eager_or_lazy.rs index a2e97919d042..7f0363ac9426 100644 --- a/clippy_utils/src/eager_or_lazy.rs +++ b/clippy_utils/src/eager_or_lazy.rs @@ -105,7 +105,7 @@ fn res_has_significant_drop(res: Res, cx: &LateContext<'_>, e: &Expr<'_>) -> boo { cx.typeck_results() .expr_ty(e) - .has_significant_drop(cx.tcx, cx.param_env) + .has_significant_drop(cx.tcx, cx.typing_env()) } else { false } diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index c73ab4bfa688..ea866a78d87f 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -297,8 +297,8 @@ pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool { if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results && typeck_lhs.expr_ty(left) == typeck_rhs.expr_ty(right) && let (Some(l), Some(r)) = ( - ConstEvalCtxt::with_env(self.inner.cx.tcx, self.inner.cx.param_env, typeck_lhs).eval_simple(left), - ConstEvalCtxt::with_env(self.inner.cx.tcx, self.inner.cx.param_env, typeck_rhs).eval_simple(right), + ConstEvalCtxt::with_env(self.inner.cx.tcx, self.inner.cx.typing_env(), typeck_lhs).eval_simple(left), + ConstEvalCtxt::with_env(self.inner.cx.tcx, self.inner.cx.typing_env(), typeck_rhs).eval_simple(right), ) && l == r { @@ -813,7 +813,7 @@ pub fn hash_block(&mut self, b: &Block<'_>) { #[expect(clippy::too_many_lines)] pub fn hash_expr(&mut self, e: &Expr<'_>) { let simple_const = self.maybe_typeck_results.and_then(|typeck_results| { - ConstEvalCtxt::with_env(self.cx.tcx, self.cx.param_env, typeck_results).eval_simple(e) + ConstEvalCtxt::with_env(self.cx.tcx, self.cx.typing_env(), typeck_results).eval_simple(e) }); // const hashing may result in the same hash as some unrelated node, so add a sort of diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 19316a906835..f28e5c9ed0e3 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1631,7 +1631,7 @@ pub fn is_integer_const(cx: &LateContext<'_>, e: &Expr<'_>, value: u128) -> bool } let enclosing_body = cx.tcx.hir().enclosing_body_owner(e.hir_id); if let Some(Constant::Int(v)) = - ConstEvalCtxt::with_env(cx.tcx, cx.tcx.param_env(enclosing_body), cx.tcx.typeck(enclosing_body)).eval(e) + ConstEvalCtxt::with_env(cx.tcx, cx.typing_env(), cx.tcx.typeck(enclosing_body)).eval(e) { return value == v; } diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index 971f8eeb1b33..abadca714001 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -142,7 +142,7 @@ fn check_rvalue<'tcx>( // We cannot allow this for now. return Err((span, "unsizing casts are only allowed for references right now".into())); }; - let unsized_ty = tcx.struct_tail_for_codegen(pointee_ty, tcx.param_env(def_id)); + let unsized_ty = tcx.struct_tail_for_codegen(pointee_ty, ty::TypingEnv::post_analysis(tcx, def_id)); if let ty::Slice(_) | ty::Str = unsized_ty.kind() { check_operand(tcx, op, span, body, msrv)?; // Casting/coercing things to slices is fine. @@ -408,15 +408,17 @@ fn is_ty_const_destruct_unused<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, body: &Bod return true; } + + let (infcx, param_env) = + tcx.infer_ctxt().build_with_typing_env(body.typing_env(tcx)); // FIXME(const_trait_impl) constness let obligation = Obligation::new( tcx, ObligationCause::dummy_with_span(body.span), - ConstCx::new(tcx, body).param_env, + param_env, TraitRef::new(tcx, tcx.require_lang_item(LangItem::Destruct, Some(body.span)), [ty]), ); - let infcx = tcx.infer_ctxt().build(body.typing_mode(tcx)); let mut selcx = SelectionContext::new(&infcx); let Some(impl_src) = selcx.select(&obligation).ok().flatten() else { return false; @@ -434,5 +436,5 @@ fn is_ty_const_destruct_unused<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, body: &Bod ocx.select_all_or_error().is_empty() } - !ty.needs_drop(tcx, ConstCx::new(tcx, body).param_env) + !ty.needs_drop(tcx, ConstCx::new(tcx, body).typing_env) } diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 770cd9c37865..2aad867dc0d6 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -467,7 +467,7 @@ fn needs_ordered_drop_inner<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, seen: &m if !seen.insert(ty) { return false; } - if !ty.has_significant_drop(cx.tcx, cx.param_env) { + if !ty.has_significant_drop(cx.tcx, cx.typing_env()) { false } // Check for std types which implement drop, but only for memory allocation. @@ -575,8 +575,9 @@ pub fn same_type_and_consts<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool { /// Checks if a given type looks safe to be uninitialized. pub fn is_uninit_value_valid_for_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + let typing_env = cx.typing_env().with_reveal_all_normalized(cx.tcx); cx.tcx - .check_validity_requirement((ValidityRequirement::Uninit, cx.param_env.and(ty))) + .check_validity_requirement((ValidityRequirement::Uninit, typing_env.as_query_input(ty))) .unwrap_or_else(|_| is_uninit_value_valid_for_ty_fallback(cx, ty)) } @@ -725,7 +726,7 @@ pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option None, } }, - ty::Alias(ty::Projection, proj) => match cx.tcx.try_normalize_erasing_regions(cx.param_env, ty) { + ty::Alias(ty::Projection, proj) => match cx.tcx.try_normalize_erasing_regions(cx.typing_env(), ty) { Ok(normalized_ty) if normalized_ty != ty => ty_sig(cx, normalized_ty), _ => sig_for_projection(cx, proj).or_else(|| sig_from_bounds(cx, ty, cx.param_env.caller_bounds(), None)), }, @@ -1111,12 +1112,12 @@ fn helper<'tcx>( /// succeeds as well as everything checked by `make_projection`. pub fn make_normalized_projection<'tcx>( tcx: TyCtxt<'tcx>, - param_env: ParamEnv<'tcx>, + typing_env: ty::TypingEnv<'tcx>, container_id: DefId, assoc_ty: Symbol, args: impl IntoIterator>>, ) -> Option> { - fn helper<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: AliasTy<'tcx>) -> Option> { + fn helper<'tcx>(tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>, ty: AliasTy<'tcx>) -> Option> { #[cfg(debug_assertions)] if let Some((i, arg)) = ty .args @@ -1132,7 +1133,7 @@ fn helper<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: AliasTy<'tcx>) ); return None; } - match tcx.try_normalize_erasing_regions(param_env, Ty::new_projection_from_args(tcx, ty.def_id, ty.args)) { + match tcx.try_normalize_erasing_regions(typing_env, Ty::new_projection_from_args(tcx, ty.def_id, ty.args)) { Ok(ty) => Some(ty), Err(e) => { debug_assert!(false, "failed to normalize type `{ty}`: {e:#?}"); @@ -1140,7 +1141,7 @@ fn helper<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: AliasTy<'tcx>) }, } } - helper(tcx, param_env, make_projection(tcx, container_id, assoc_ty, args)?) + helper(tcx, typing_env, make_projection(tcx, container_id, assoc_ty, args)?) } /// Helper to check if given type has inner mutability such as [`std::cell::Cell`] or @@ -1300,7 +1301,7 @@ pub fn deref_chain<'cx, 'tcx>(cx: &'cx LateContext<'tcx>, ty: Ty<'tcx>) -> impl if let Some(deref_did) = cx.tcx.lang_items().deref_trait() && implements_trait(cx, ty, deref_did, &[]) { - make_normalized_projection(cx.tcx, cx.param_env, deref_did, sym::Target, [ty]) + make_normalized_projection(cx.tcx, cx.typing_env(), deref_did, sym::Target, [ty]) } else { None } From 809b420e16d462e925399be8c7289fb18bac8689 Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 19 Nov 2024 16:13:55 +0100 Subject: [PATCH 04/12] move `fn is_item_raw` to `TypingEnv` --- clippy_lints/src/casts/ptr_as_ptr.rs | 2 +- clippy_lints/src/dereference.rs | 2 +- clippy_lints/src/functions/must_use.rs | 2 +- clippy_lints/src/let_if_seq.rs | 2 +- clippy_lints/src/methods/manual_inspect.rs | 6 +++--- clippy_lints/src/mut_mut.rs | 2 +- clippy_lints/src/needless_pass_by_value.rs | 2 +- clippy_lints/src/question_mark.rs | 2 +- clippy_lints/src/transmute/transmute_ptr_to_ptr.rs | 2 +- clippy_lints/src/transmute/transmute_undefined_repr.rs | 4 ++-- clippy_lints/src/types/redundant_allocation.rs | 2 +- clippy_lints/src/types/vec_box.rs | 2 +- clippy_lints/src/unnecessary_box_returns.rs | 2 +- clippy_utils/src/ty.rs | 2 +- 14 files changed, 17 insertions(+), 17 deletions(-) diff --git a/clippy_lints/src/casts/ptr_as_ptr.rs b/clippy_lints/src/casts/ptr_as_ptr.rs index 86c5f6b9f0ba..46d67e615c73 100644 --- a/clippy_lints/src/casts/ptr_as_ptr.rs +++ b/clippy_lints/src/casts/ptr_as_ptr.rs @@ -39,7 +39,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Msrv) { (Mutability::Not, Mutability::Not) | (Mutability::Mut, Mutability::Mut)) // The `U` in `pointer::cast` have to be `Sized` // as explained here: https://github.com/rust-lang/rust/issues/60602. - && to_pointee_ty.is_sized(cx.tcx, cx.param_env) + && to_pointee_ty.is_sized(cx.tcx, cx.typing_env()) { let mut app = Applicability::MachineApplicable; let turbofish = match &cast_to_hir_ty.kind { diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index f864b7a5a8af..9049739dddb4 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1028,7 +1028,7 @@ fn report<'tcx>( State::ExplicitDeref { mutability } => { if is_block_like(expr) && let ty::Ref(_, ty, _) = data.adjusted_ty.kind() - && ty.is_sized(cx.tcx, cx.param_env) + && ty.is_sized(cx.tcx, cx.typing_env()) { // Rustc bug: auto deref doesn't work on block expression when targeting sized types. return; diff --git a/clippy_lints/src/functions/must_use.rs b/clippy_lints/src/functions/must_use.rs index c74ba088b78e..175d92d2d790 100644 --- a/clippy_lints/src/functions/must_use.rs +++ b/clippy_lints/src/functions/must_use.rs @@ -198,7 +198,7 @@ fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, tys: &mut DefIdSet) // primitive types are never mutable ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => false, ty::Adt(adt, args) => { - tys.insert(adt.did()) && !ty.is_freeze(cx.tcx, cx.param_env) + tys.insert(adt.did()) && !ty.is_freeze(cx.tcx, cx.typing_env()) || matches!(cx.tcx.get_diagnostic_name(adt.did()), Some(sym::Rc | sym::Arc)) && args.types().any(|ty| is_mutable_ty(cx, ty, tys)) }, diff --git a/clippy_lints/src/let_if_seq.rs b/clippy_lints/src/let_if_seq.rs index 0e488cee6b74..5db28e9ae9b8 100644 --- a/clippy_lints/src/let_if_seq.rs +++ b/clippy_lints/src/let_if_seq.rs @@ -80,7 +80,7 @@ fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) { let has_interior_mutability = !cx .typeck_results() .node_type(canonical_id) - .is_freeze(cx.tcx, cx.param_env); + .is_freeze(cx.tcx, cx.typing_env()); if has_interior_mutability { return; } diff --git a/clippy_lints/src/methods/manual_inspect.rs b/clippy_lints/src/methods/manual_inspect.rs index 223b0630bfd4..7aa13d8d5b6e 100644 --- a/clippy_lints/src/methods/manual_inspect.rs +++ b/clippy_lints/src/methods/manual_inspect.rs @@ -148,7 +148,7 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name: _ => {}, } } - requires_copy |= !ty.is_copy_modulo_regions(cx.tcx, cx.param_env); + requires_copy |= !ty.is_copy_modulo_regions(cx.tcx, cx.typing_env()); break; } }, @@ -158,9 +158,9 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name: } if can_lint - && (!requires_copy || arg_ty.is_copy_modulo_regions(cx.tcx, cx.param_env)) + && (!requires_copy || arg_ty.is_copy_modulo_regions(cx.tcx, cx.typing_env())) // This case could be handled, but a fair bit of care would need to be taken. - && (!requires_deref || arg_ty.is_freeze(cx.tcx, cx.param_env)) + && (!requires_deref || arg_ty.is_freeze(cx.tcx, cx.typing_env())) { if requires_deref { edits.push((param.span.shrink_to_lo(), "&".into())); diff --git a/clippy_lints/src/mut_mut.rs b/clippy_lints/src/mut_mut.rs index 6cddd7ea813b..e2ab5e98504a 100644 --- a/clippy_lints/src/mut_mut.rs +++ b/clippy_lints/src/mut_mut.rs @@ -80,7 +80,7 @@ fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { "generally you want to avoid `&mut &mut _` if possible", ); } else if let ty::Ref(_, ty, hir::Mutability::Mut) = self.cx.typeck_results().expr_ty(e).kind() { - if ty.peel_refs().is_sized(self.cx.tcx, self.cx.param_env) { + if ty.peel_refs().is_sized(self.cx.tcx, self.cx.typing_env()) { span_lint_hir( self.cx, MUT_MUT, diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 0775d7abdbb3..ad0ab1485302 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -180,7 +180,7 @@ fn check_fn( if !is_self(arg) && !ty.is_mutable_ptr() && !is_copy(cx, ty) - && ty.is_sized(cx.tcx, cx.param_env) + && ty.is_sized(cx.tcx, cx.typing_env()) && !allowed_traits.iter().any(|&t| { implements_trait_with_env_from_iter(cx.tcx, cx.param_env, ty, t, None, [Option::< ty::GenericArg<'tcx>, diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index a00fd01a62e0..f69cb9be4cae 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -251,7 +251,7 @@ fn check_is_none_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: &Ex { let mut applicability = Applicability::MachineApplicable; let receiver_str = snippet_with_applicability(cx, caller.span, "..", &mut applicability); - let by_ref = !caller_ty.is_copy_modulo_regions(cx.tcx, cx.param_env) + let by_ref = !caller_ty.is_copy_modulo_regions(cx.tcx, cx.typing_env()) && !matches!(caller.kind, ExprKind::Call(..) | ExprKind::MethodCall(..)); let sugg = if let Some(else_inner) = r#else { if eq_expr_value(cx, caller, peel_blocks(else_inner)) { diff --git a/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs b/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs index 0772b284968a..bf6700b1b6ba 100644 --- a/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs +++ b/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs @@ -27,7 +27,7 @@ pub(super) fn check<'tcx>( |diag| { if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) { if from_mutbl == to_mutbl - && to_pointee_ty.is_sized(cx.tcx, cx.param_env) + && to_pointee_ty.is_sized(cx.tcx, cx.typing_env()) && msrv.meets(msrvs::POINTER_CAST) { diag.span_suggestion_verbose( diff --git a/clippy_lints/src/transmute/transmute_undefined_repr.rs b/clippy_lints/src/transmute/transmute_undefined_repr.rs index 4dc1290e8b15..48d65eb15d9a 100644 --- a/clippy_lints/src/transmute/transmute_undefined_repr.rs +++ b/clippy_lints/src/transmute/transmute_undefined_repr.rs @@ -206,12 +206,12 @@ fn reduce_refs<'tcx>(cx: &LateContext<'tcx>, mut from_ty: Ty<'tcx>, mut to_ty: T continue; }, (&(ty::Ref(_, unsized_ty, _) | ty::RawPtr(unsized_ty, _)), _) - if !unsized_ty.is_sized(cx.tcx, cx.param_env) => + if !unsized_ty.is_sized(cx.tcx, cx.typing_env()) => { (true, false) }, (_, &(ty::Ref(_, unsized_ty, _) | ty::RawPtr(unsized_ty, _))) - if !unsized_ty.is_sized(cx.tcx, cx.param_env) => + if !unsized_ty.is_sized(cx.tcx, cx.typing_env()) => { (false, true) }, diff --git a/clippy_lints/src/types/redundant_allocation.rs b/clippy_lints/src/types/redundant_allocation.rs index 1a656088b174..de3456a8ba5f 100644 --- a/clippy_lints/src/types/redundant_allocation.rs +++ b/clippy_lints/src/types/redundant_allocation.rs @@ -60,7 +60,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'tcx>, qpath: // here because `mod.rs` guarantees this lint is only run on types outside of bodies and // is not run on locals. let ty = lower_ty(cx.tcx, hir_ty); - if ty.has_escaping_bound_vars() || !ty.is_sized(cx.tcx, cx.param_env) { + if ty.has_escaping_bound_vars() || !ty.is_sized(cx.tcx, cx.typing_env()) { return false; } hir_ty.span diff --git a/clippy_lints/src/types/vec_box.rs b/clippy_lints/src/types/vec_box.rs index 230239335c65..9b236d3bda55 100644 --- a/clippy_lints/src/types/vec_box.rs +++ b/clippy_lints/src/types/vec_box.rs @@ -37,7 +37,7 @@ pub(super) fn check<'tcx>( && let boxed_alloc_ty = last.args.get(1) && let ty_ty = lower_ty(cx.tcx, boxed_ty) && !ty_ty.has_escaping_bound_vars() - && ty_ty.is_sized(cx.tcx, cx.param_env) + && ty_ty.is_sized(cx.tcx, cx.typing_env()) && let Ok(ty_ty_size) = cx.layout_of(ty_ty).map(|l| l.size.bytes()) && ty_ty_size < box_size_threshold // https://github.com/rust-lang/rust-clippy/issues/7114 diff --git a/clippy_lints/src/unnecessary_box_returns.rs b/clippy_lints/src/unnecessary_box_returns.rs index 14f4aa6676b6..34df1d5560a1 100644 --- a/clippy_lints/src/unnecessary_box_returns.rs +++ b/clippy_lints/src/unnecessary_box_returns.rs @@ -82,7 +82,7 @@ fn check_fn_item(&mut self, cx: &LateContext<'_>, decl: &FnDecl<'_>, def_id: Loc // It's sometimes useful to return Box if T is unsized, so don't lint those. // Also, don't lint if we know that T is very large, in which case returning // a Box may be beneficial. - if boxed_ty.is_sized(cx.tcx, cx.param_env) && approx_ty_size(cx, boxed_ty) <= self.maximum_size { + if boxed_ty.is_sized(cx.tcx, cx.typing_env()) && approx_ty_size(cx, boxed_ty) <= self.maximum_size { span_lint_and_then( cx, UNNECESSARY_BOX_RETURNS, diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 2aad867dc0d6..fff516b730dd 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -38,7 +38,7 @@ /// Checks if the given type implements copy. pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { - ty.is_copy_modulo_regions(cx.tcx, cx.param_env) + ty.is_copy_modulo_regions(cx.tcx, cx.typing_env()) } /// This checks whether a given type is known to implement Debug. From d8e5f7ad8a10824a0de614d6f1276ebe2d84322e Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 19 Nov 2024 19:31:02 +0100 Subject: [PATCH 05/12] remove `TypingMode::from_param_env` in clippy --- clippy_lints/src/dereference.rs | 15 +++--- clippy_lints/src/derive.rs | 14 +++-- clippy_lints/src/loops/explicit_iter_loop.rs | 8 +-- .../src/needless_borrows_for_generic_args.rs | 6 +-- clippy_lints/src/needless_pass_by_value.rs | 11 ++-- clippy_utils/src/lib.rs | 54 ++++++++++--------- clippy_utils/src/ty.rs | 37 +++++-------- 7 files changed, 77 insertions(+), 68 deletions(-) diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 9049739dddb4..fce7f9985ea1 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -15,6 +15,7 @@ self as hir, BindingMode, Body, BodyId, BorrowKind, Expr, ExprKind, HirId, MatchSource, Mutability, Node, Pat, PatKind, Path, QPath, TyKind, UnOp, }; +use rustc_hir::def_id::DefId; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, TypeckResults}; @@ -753,11 +754,10 @@ fn is_reborrow_stable(self) -> bool { fn for_defined_ty<'tcx>(cx: &LateContext<'tcx>, ty: DefinedTy<'tcx>, for_return: bool) -> Self { match ty { DefinedTy::Hir(ty) => Self::for_hir_ty(ty), - DefinedTy::Mir(ty) => Self::for_mir_ty( + DefinedTy::Mir { def_site_def_id, ty } => Self::for_mir_ty( cx.tcx, - // FIXME(#132279): convert `DefinedTy` to use `TypingEnv` instead. - ty::TypingEnv::from_param_env(ty.param_env), - cx.tcx.instantiate_bound_regions_with_erased(ty.value), + def_site_def_id, + cx.tcx.instantiate_bound_regions_with_erased(ty), for_return, ), } @@ -824,12 +824,15 @@ fn for_hir_ty<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> Self { } } - fn for_mir_ty<'tcx>(tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>, ty: Ty<'tcx>, for_return: bool) -> Self { + fn for_mir_ty<'tcx>(tcx: TyCtxt<'tcx>, def_site_def_id: Option, ty: Ty<'tcx>, for_return: bool) -> Self { let ty::Ref(_, mut ty, _) = *ty.kind() else { return Self::None; }; - ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty); + if let Some(def_id) = def_site_def_id { + let typing_env = ty::TypingEnv::non_body_analysis(tcx, def_id); + ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty); + } loop { break match *ty.kind() { ty::Ref(_, ref_ty, _) => { diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 0db6a822ec05..3b6b3c89858b 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -454,13 +454,13 @@ fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_r && cx.tcx.is_diagnostic_item(sym::PartialEq, def_id) && !has_non_exhaustive_attr(cx.tcx, *adt) && !ty_implements_eq_trait(cx.tcx, ty, eq_trait_def_id) - && let param_env = param_env_for_derived_eq(cx.tcx, adt.did(), eq_trait_def_id) + && let typing_env = typing_env_env_for_derived_eq(cx.tcx, adt.did(), eq_trait_def_id) && let Some(local_def_id) = adt.did().as_local() // If all of our fields implement `Eq`, we can implement `Eq` too && adt .all_fields() .map(|f| f.ty(cx.tcx, args)) - .all(|ty| implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, None, &[])) + .all(|ty| implements_trait_with_env(cx.tcx, typing_env, ty, eq_trait_def_id, None, &[])) { span_lint_hir_and_then( cx, @@ -485,7 +485,7 @@ fn ty_implements_eq_trait<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, eq_trait_id: De } /// Creates the `ParamEnv` used for the give type's derived `Eq` impl. -fn param_env_for_derived_eq(tcx: TyCtxt<'_>, did: DefId, eq_trait_id: DefId) -> ParamEnv<'_> { +fn typing_env_env_for_derived_eq(tcx: TyCtxt<'_>, did: DefId, eq_trait_id: DefId) -> ty::TypingEnv<'_> { // Initial map from generic index to param def. // Vec<(param_def, needs_eq)> let mut params = tcx @@ -506,7 +506,7 @@ fn param_env_for_derived_eq(tcx: TyCtxt<'_>, did: DefId, eq_trait_id: DefId) -> } } - ParamEnv::new( + let param_env = ParamEnv::new( tcx.mk_clauses_from_iter(ty_predicates.iter().map(|&(p, _)| p).chain( params.iter().filter(|&&(_, needs_eq)| needs_eq).map(|&(param, _)| { ClauseKind::Trait(TraitPredicate { @@ -517,5 +517,9 @@ fn param_env_for_derived_eq(tcx: TyCtxt<'_>, did: DefId, eq_trait_id: DefId) -> }), )), Reveal::UserFacing, - ) + ); + ty::TypingEnv { + typing_mode: ty::TypingMode::non_body_analysis(), + param_env, + } } diff --git a/clippy_lints/src/loops/explicit_iter_loop.rs b/clippy_lints/src/loops/explicit_iter_loop.rs index 48318682f33c..d999e1a05857 100644 --- a/clippy_lints/src/loops/explicit_iter_loop.rs +++ b/clippy_lints/src/loops/explicit_iter_loop.rs @@ -115,11 +115,11 @@ fn is_ref_iterable<'tcx>( .tcx .liberate_late_bound_regions(fn_id, cx.tcx.fn_sig(fn_id).skip_binder()) && let &[req_self_ty, req_res_ty] = &**sig.inputs_and_output - && let param_env = cx.tcx.param_env(fn_id) - && implements_trait_with_env(cx.tcx, param_env, req_self_ty, trait_id, Some(fn_id), &[]) + && let typing_env = ty::TypingEnv::non_body_analysis(cx.tcx, fn_id) + && implements_trait_with_env(cx.tcx, typing_env, req_self_ty, trait_id, Some(fn_id), &[]) && let Some(into_iter_ty) = - make_normalized_projection_with_regions(cx.tcx, param_env, trait_id, sym!(IntoIter), [req_self_ty]) - && let req_res_ty = normalize_with_regions(cx.tcx, param_env, req_res_ty) + make_normalized_projection_with_regions(cx.tcx, typing_env, trait_id, sym!(IntoIter), [req_self_ty]) + && let req_res_ty = normalize_with_regions(cx.tcx, typing_env, req_res_ty) && into_iter_ty == req_res_ty { let adjustments = typeck.expr_adjustments(self_arg); diff --git a/clippy_lints/src/needless_borrows_for_generic_args.rs b/clippy_lints/src/needless_borrows_for_generic_args.rs index 43b885fbd2c9..9ebef531bc5c 100644 --- a/clippy_lints/src/needless_borrows_for_generic_args.rs +++ b/clippy_lints/src/needless_borrows_for_generic_args.rs @@ -85,8 +85,8 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { && use_cx.same_ctxt && !use_cx.is_ty_unified && let use_node = use_cx.use_node(cx) - && let Some(DefinedTy::Mir(ty)) = use_node.defined_ty(cx) - && let ty::Param(ty) = *ty.value.skip_binder().kind() + && let Some(DefinedTy::Mir { def_site_def_id: _, ty }) = use_node.defined_ty(cx) + && let ty::Param(param_ty) = *ty.skip_binder().kind() && let Some((hir_id, fn_id, i)) = match use_node { ExprUseNode::MethodArg(_, _, 0) => None, ExprUseNode::MethodArg(hir_id, None, i) => cx @@ -112,7 +112,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { fn_id, cx.typeck_results().node_args(hir_id), i, - ty, + param_ty, expr, &self.msrv, ) diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index ad0ab1485302..b65ec8c3c482 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -182,9 +182,14 @@ fn check_fn( && !is_copy(cx, ty) && ty.is_sized(cx.tcx, cx.typing_env()) && !allowed_traits.iter().any(|&t| { - implements_trait_with_env_from_iter(cx.tcx, cx.param_env, ty, t, None, [Option::< - ty::GenericArg<'tcx>, - >::None]) + implements_trait_with_env_from_iter( + cx.tcx, + cx.typing_env(), + ty, + t, + None, + [None::>] + ) }) && !implements_borrow_trait && !all_borrowable_trait diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index f28e5c9ed0e3..6408cc938cb8 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -116,8 +116,8 @@ use rustc_middle::ty::fast_reject::SimplifiedType; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::{ - self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, FloatTy, GenericArgsRef, IntTy, ParamEnv, - ParamEnvAnd, Ty, TyCtxt, TypeVisitableExt, UintTy, UpvarCapture, + self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, FloatTy, GenericArgsRef, IntTy, + Ty, TyCtxt, TypeVisitableExt, UintTy, UpvarCapture, }; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::SourceMap; @@ -2712,9 +2712,17 @@ pub fn walk_to_expr_usage<'tcx, T>( pub enum DefinedTy<'tcx> { // Used for locals and closures defined within the function. Hir(&'tcx hir::Ty<'tcx>), - /// Used for function signatures, and constant and static values. This includes the `ParamEnv` - /// from the definition site. - Mir(ParamEnvAnd<'tcx, Binder<'tcx, Ty<'tcx>>>), + /// Used for function signatures, and constant and static values. The type is + /// in the context of its definition site. We also track the `def_id` of its + /// definition site. + /// + /// WARNING: As the `ty` in in the scope of the definition, not of the function + /// using it, you must be very careful with how you use it. Using it in the wrong + /// scope easily results in ICEs. + Mir { + def_site_def_id: Option, + ty: Binder<'tcx, Ty<'tcx>>, + }, } /// The context an expressions value is used in. @@ -2833,10 +2841,10 @@ pub fn is_recv(&self) -> bool { pub fn defined_ty(&self, cx: &LateContext<'tcx>) -> Option> { match *self { Self::LetStmt(LetStmt { ty: Some(ty), .. }) => Some(DefinedTy::Hir(ty)), - Self::ConstStatic(id) => Some(DefinedTy::Mir( - cx.param_env - .and(Binder::dummy(cx.tcx.type_of(id).instantiate_identity())), - )), + Self::ConstStatic(id) => Some(DefinedTy::Mir { + def_site_def_id: Some(id.def_id.to_def_id()), + ty: Binder::dummy(cx.tcx.type_of(id).instantiate_identity()), + }), Self::Return(id) => { if let Node::Expr(Expr { kind: ExprKind::Closure(c), @@ -2848,9 +2856,8 @@ pub fn defined_ty(&self, cx: &LateContext<'tcx>) -> Option> { FnRetTy::Return(ty) => Some(DefinedTy::Hir(ty)), } } else { - Some(DefinedTy::Mir( - cx.param_env.and(cx.tcx.fn_sig(id).instantiate_identity().output()), - )) + let ty = cx.tcx.fn_sig(id).instantiate_identity().output(); + Some(DefinedTy::Mir { def_site_def_id: Some(id.def_id.to_def_id()), ty }) } }, Self::Field(field) => match get_parent_expr_for_hir(cx, field.hir_id) { @@ -2866,12 +2873,9 @@ pub fn defined_ty(&self, cx: &LateContext<'tcx>) -> Option> { .find(|f| f.name == field.ident.name) .map(|f| (adt, f)) }) - .map(|(adt, field_def)| { - DefinedTy::Mir( - cx.tcx - .param_env(adt.did()) - .and(Binder::dummy(cx.tcx.type_of(field_def.did).instantiate_identity())), - ) + .map(|(adt, field_def)| DefinedTy::Mir { + def_site_def_id: Some(adt.did()), + ty: Binder::dummy(cx.tcx.type_of(field_def.did).instantiate_identity()), }), _ => None, }, @@ -2880,17 +2884,19 @@ pub fn defined_ty(&self, cx: &LateContext<'tcx>) -> Option> { let (hir_ty, ty) = sig.input_with_hir(i)?; Some(match hir_ty { Some(hir_ty) => DefinedTy::Hir(hir_ty), - None => DefinedTy::Mir( - sig.predicates_id() - .map_or(ParamEnv::empty(), |id| cx.tcx.param_env(id)) - .and(ty), - ), + None => DefinedTy::Mir { + def_site_def_id: sig.predicates_id(), + ty, + } }) }, Self::MethodArg(id, _, i) => { let id = cx.typeck_results().type_dependent_def_id(id)?; let sig = cx.tcx.fn_sig(id).skip_binder(); - Some(DefinedTy::Mir(cx.tcx.param_env(id).and(sig.input(i)))) + Some(DefinedTy::Mir { + def_site_def_id: Some(id), + ty: sig.input(i), + }) }, Self::LetStmt(_) | Self::FieldAccess(..) | Self::Callee | Self::Other | Self::AddrOf(..) => None, } diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index fff516b730dd..3498606dfd39 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -19,7 +19,7 @@ use rustc_middle::ty::{ self, AdtDef, AliasTy, AssocItem, AssocKind, Binder, BoundRegion, FnSig, GenericArg, GenericArgKind, GenericArgsRef, GenericParamDefKind, IntTy, ParamEnv, Region, RegionKind, TraitRef, Ty, TyCtxt, TypeSuperVisitable, - TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, UintTy, Upcast, VariantDef, VariantDiscr, + TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr, }; use rustc_span::symbol::Ident; use rustc_span::{DUMMY_SP, Span, Symbol, sym}; @@ -226,7 +226,7 @@ pub fn implements_trait<'tcx>( trait_id: DefId, args: &[GenericArg<'tcx>], ) -> bool { - implements_trait_with_env_from_iter(cx.tcx, cx.param_env, ty, trait_id, None, args.iter().map(|&x| Some(x))) + implements_trait_with_env_from_iter(cx.tcx, cx.typing_env(), ty, trait_id, None, args.iter().map(|&x| Some(x))) } /// Same as `implements_trait` but allows using a `ParamEnv` different from the lint context. @@ -235,19 +235,19 @@ pub fn implements_trait<'tcx>( /// environment, used for checking const traits. pub fn implements_trait_with_env<'tcx>( tcx: TyCtxt<'tcx>, - param_env: ParamEnv<'tcx>, + typing_env: ty::TypingEnv<'tcx>, ty: Ty<'tcx>, trait_id: DefId, callee_id: Option, args: &[GenericArg<'tcx>], ) -> bool { - implements_trait_with_env_from_iter(tcx, param_env, ty, trait_id, callee_id, args.iter().map(|&x| Some(x))) + implements_trait_with_env_from_iter(tcx, typing_env, ty, trait_id, callee_id, args.iter().map(|&x| Some(x))) } /// Same as `implements_trait_from_env` but takes the arguments as an iterator. pub fn implements_trait_with_env_from_iter<'tcx>( tcx: TyCtxt<'tcx>, - param_env: ParamEnv<'tcx>, + typing_env: ty::TypingEnv<'tcx>, ty: Ty<'tcx>, trait_id: DefId, callee_id: Option, @@ -268,7 +268,7 @@ pub fn implements_trait_with_env_from_iter<'tcx>( return false; } - let infcx = tcx.infer_ctxt().build(TypingMode::from_param_env(param_env)); + let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env); let args = args .into_iter() .map(|arg| arg.into().unwrap_or_else(|| infcx.next_ty_var(DUMMY_SP).into())) @@ -1239,12 +1239,12 @@ pub fn is_interior_mut_ty(&mut self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bo pub fn make_normalized_projection_with_regions<'tcx>( tcx: TyCtxt<'tcx>, - param_env: ParamEnv<'tcx>, + typing_env: ty::TypingEnv<'tcx>, container_id: DefId, assoc_ty: Symbol, args: impl IntoIterator>>, ) -> Option> { - fn helper<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: AliasTy<'tcx>) -> Option> { + fn helper<'tcx>(tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>, ty: AliasTy<'tcx>) -> Option> { #[cfg(debug_assertions)] if let Some((i, arg)) = ty .args @@ -1261,10 +1261,8 @@ fn helper<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: AliasTy<'tcx>) return None; } let cause = ObligationCause::dummy(); - match tcx - .infer_ctxt() - .build(TypingMode::from_param_env(param_env)) - .at(&cause, param_env) + let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env); + match infcx.at(&cause, param_env) .query_normalize(Ty::new_projection_from_args(tcx, ty.def_id, ty.args)) { Ok(ty) => Some(ty.value), @@ -1274,20 +1272,13 @@ fn helper<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: AliasTy<'tcx>) }, } } - helper(tcx, param_env, make_projection(tcx, container_id, assoc_ty, args)?) + helper(tcx, typing_env, make_projection(tcx, container_id, assoc_ty, args)?) } -pub fn normalize_with_regions<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { +pub fn normalize_with_regions<'tcx>(tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { let cause = ObligationCause::dummy(); - match tcx - .infer_ctxt() - .build(TypingMode::from_param_env(param_env)) - .at(&cause, param_env) - .query_normalize(ty) - { - Ok(ty) => ty.value, - Err(_) => ty, - } + let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env); + infcx.at(&cause, param_env).query_normalize(ty).map_or(ty, |ty| ty.value) } /// Checks if the type is `core::mem::ManuallyDrop<_>` From c783d1e387d0fbf35c19d76d5ea357c5d4a5d607 Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 19 Nov 2024 20:10:42 +0100 Subject: [PATCH 06/12] `InterpCx` store `TypingEnv` instead of a `ParamEnv` --- clippy_lints/src/non_copy_const.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index 57fa4797c5e5..5416e00fe0cb 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -270,8 +270,8 @@ fn is_value_unfrozen_poly(cx: &LateContext<'tcx>, body_id: BodyId, ty: Ty<'tcx>) instance, promoted: None, }; - let param_env = cx.tcx.param_env(def_id).with_reveal_all_normalized(cx.tcx); - let result = cx.tcx.const_eval_global_id_for_typeck(param_env, cid, DUMMY_SP); + let typing_env = ty::TypingEnv::post_analysis(cx.tcx, def_id); + let result = cx.tcx.const_eval_global_id_for_typeck(typing_env, cid, DUMMY_SP); Self::is_value_unfrozen_raw(cx, result, ty) } @@ -294,7 +294,7 @@ pub fn const_eval_resolve( instance, promoted: None, }; - tcx.const_eval_global_id_for_typeck(typing_env.param_env, cid, span) + tcx.const_eval_global_id_for_typeck(typing_env, cid, span) }, Ok(None) => Err(ErrorHandled::TooGeneric(span)), Err(err) => Err(ErrorHandled::Reported(err.into(), span)), From ff6c4b731bc1df7f2a02c7dd6e94028371590db3 Mon Sep 17 00:00:00 2001 From: Ding Xiang Fei Date: Mon, 2 Sep 2024 01:13:07 +0800 Subject: [PATCH 07/12] reduce false positives of tail-expr-drop-order from consumed values take 2 open up coroutines tweak the wordings the lint works up until 2021 We were missing one case, for ADTs, which was causing `Result` to yield incorrect results. only include field spans with significant types deduplicate and eliminate field spans switch to emit spans to impl Drops Co-authored-by: Niko Matsakis collect drops instead of taking liveness diff apply some suggestions and add explantory notes small fix on the cache let the query recurse through coroutine new suggestion format with extracted variable name fine-tune the drop span and messages bugfix on runtime borrows tweak message wording filter out ecosystem types earlier apply suggestions clippy check lint level at session level further restrict applicability of the lint translate bid into nop for stable mir detect cycle in type structure --- clippy_utils/src/qualify_min_const_fn.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index abadca714001..345c46f944a5 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -233,6 +233,7 @@ fn check_statement<'tcx>( | StatementKind::PlaceMention(..) | StatementKind::Coverage(..) | StatementKind::ConstEvalCounter + | StatementKind::BackwardIncompatibleDropHint { .. } | StatementKind::Nop => Ok(()), } } From 1931fb825bb18a4bf6f2af6e0249dcf72119c263 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 23 Nov 2024 08:05:19 +0100 Subject: [PATCH 08/12] remove is_trivially_const_drop --- clippy_utils/src/qualify_min_const_fn.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index 345c46f944a5..3c9ea4bfaf44 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -404,12 +404,11 @@ fn is_ty_const_destruct<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, body: &Body<'tcx> // FIXME(const_trait_impl, fee1-dead) revert to const destruct once it works again #[expect(unused)] fn is_ty_const_destruct_unused<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, body: &Body<'tcx>) -> bool { - // Avoid selecting for simple cases, such as builtin types. - if ty::util::is_trivially_const_drop(ty) { - return true; + // If this doesn't need drop at all, then don't select `~const Destruct`. + if !ty.needs_drop(tcx, body.typing_env(tcx)) { + return false; } - let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(body.typing_env(tcx)); // FIXME(const_trait_impl) constness From 78fa111a48001abf372911b50d7d7ed25424d489 Mon Sep 17 00:00:00 2001 From: lcnr Date: Wed, 20 Nov 2024 11:31:49 +0100 Subject: [PATCH 09/12] no more Reveal :( --- clippy_lints/src/derive.rs | 2 -- clippy_utils/src/ty.rs | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 3b6b3c89858b..1a34b87e42a2 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -11,7 +11,6 @@ }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; -use rustc_middle::traits::Reveal; use rustc_middle::ty::{ self, ClauseKind, GenericArgKind, GenericParamDefKind, ParamEnv, TraitPredicate, Ty, TyCtxt, Upcast, }; @@ -516,7 +515,6 @@ fn typing_env_env_for_derived_eq(tcx: TyCtxt<'_>, did: DefId, eq_trait_id: DefId .upcast(tcx) }), )), - Reveal::UserFacing, ); ty::TypingEnv { typing_mode: ty::TypingMode::non_body_analysis(), diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 3498606dfd39..03e1a814a860 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -575,7 +575,7 @@ pub fn same_type_and_consts<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool { /// Checks if a given type looks safe to be uninitialized. pub fn is_uninit_value_valid_for_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { - let typing_env = cx.typing_env().with_reveal_all_normalized(cx.tcx); + let typing_env = cx.typing_env().with_post_analysis_normalized(cx.tcx); cx.tcx .check_validity_requirement((ValidityRequirement::Uninit, typing_env.as_query_input(ty))) .unwrap_or_else(|_| is_uninit_value_valid_for_ty_fallback(cx, ty)) From 945ccbd0634b727f2b872f0ffd2872f8b9d7d173 Mon Sep 17 00:00:00 2001 From: Frank King Date: Mon, 25 Nov 2024 16:38:35 +0800 Subject: [PATCH 10/12] Refactor `where` predicates, and reserve for attributes support --- .../src/extra_unused_type_parameters.rs | 7 +++--- clippy_lints/src/implied_bounds_in_impls.rs | 4 ++-- clippy_lints/src/lifetimes.rs | 10 ++++----- clippy_lints/src/multiple_bound_locations.rs | 10 ++++----- clippy_lints/src/needless_maybe_sized.rs | 4 ++-- clippy_lints/src/trait_bounds.rs | 22 +++++++++---------- clippy_utils/src/ast_utils.rs | 4 ++-- 7 files changed, 31 insertions(+), 30 deletions(-) diff --git a/clippy_lints/src/extra_unused_type_parameters.rs b/clippy_lints/src/extra_unused_type_parameters.rs index 6ad879b9fe7a..81152da8c852 100644 --- a/clippy_lints/src/extra_unused_type_parameters.rs +++ b/clippy_lints/src/extra_unused_type_parameters.rs @@ -6,7 +6,7 @@ use rustc_hir::intravisit::{Visitor, walk_impl_item, walk_item, walk_param_bound, walk_ty}; use rustc_hir::{ BodyId, ExprKind, GenericBound, GenericParam, GenericParamKind, Generics, ImplItem, ImplItemKind, Item, ItemKind, - PredicateOrigin, Ty, WherePredicate, + PredicateOrigin, Ty, WherePredicate, WherePredicateKind }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::nested_filter; @@ -205,12 +205,13 @@ fn visit_ty(&mut self, t: &'tcx Ty<'tcx>) { } fn visit_where_predicate(&mut self, predicate: &'tcx WherePredicate<'tcx>) { - if let WherePredicate::BoundPredicate(predicate) = predicate { + let span = predicate.span; + if let WherePredicateKind::BoundPredicate(predicate) = predicate.kind { // Collect spans for any bounds on type parameters. if let Some((def_id, _)) = predicate.bounded_ty.peel_refs().as_generic_param() { match predicate.origin { PredicateOrigin::GenericParam => { - self.inline_bounds.insert(def_id, predicate.span); + self.inline_bounds.insert(def_id, span); }, PredicateOrigin::WhereClause => { self.where_bounds.insert(def_id); diff --git a/clippy_lints/src/implied_bounds_in_impls.rs b/clippy_lints/src/implied_bounds_in_impls.rs index 65fdc93e0ed9..4427edb752e0 100644 --- a/clippy_lints/src/implied_bounds_in_impls.rs +++ b/clippy_lints/src/implied_bounds_in_impls.rs @@ -4,7 +4,7 @@ use rustc_hir::def_id::DefId; use rustc_hir::{ AssocItemConstraint, GenericArg, GenericBound, GenericBounds, PredicateOrigin, TraitBoundModifiers, TyKind, - WherePredicate, + WherePredicateKind, }; use rustc_hir_analysis::lower_ty; use rustc_lint::{LateContext, LateLintPass}; @@ -324,7 +324,7 @@ fn check<'tcx>(cx: &LateContext<'tcx>, bounds: GenericBounds<'tcx>) { impl<'tcx> LateLintPass<'tcx> for ImpliedBoundsInImpls { fn check_generics(&mut self, cx: &LateContext<'tcx>, generics: &rustc_hir::Generics<'tcx>) { for predicate in generics.predicates { - if let WherePredicate::BoundPredicate(predicate) = predicate + if let WherePredicateKind::BoundPredicate(predicate) = predicate.kind // In theory, the origin doesn't really matter, // we *could* also lint on explicit where clauses written out by the user, // not just impl trait desugared ones, but that contradicts with the lint name... diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index ce0e1a24a7b5..6ff1a1e5ec74 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -12,7 +12,7 @@ use rustc_hir::{ BareFnTy, BodyId, FnDecl, FnSig, GenericArg, GenericArgs, GenericBound, GenericParam, GenericParamKind, Generics, Impl, ImplItem, ImplItemKind, Item, ItemKind, Lifetime, LifetimeName, LifetimeParamKind, Node, PolyTraitRef, - PredicateOrigin, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WherePredicate, lang_items, + PredicateOrigin, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WherePredicate, WherePredicateKind, lang_items, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::map::Map; @@ -442,9 +442,9 @@ fn visit_ty(&mut self, ty: &'tcx Ty<'_>) { /// reason about elision. fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, generics: &'tcx Generics<'_>) -> bool { for predicate in generics.predicates { - match *predicate { - WherePredicate::RegionPredicate(..) => return true, - WherePredicate::BoundPredicate(ref pred) => { + match *predicate.kind { + WherePredicateKind::RegionPredicate(..) => return true, + WherePredicateKind::BoundPredicate(ref pred) => { // a predicate like F: Trait or F: for<'a> Trait<'a> let mut visitor = RefVisitor::new(cx); // walk the type F, it may not contain LT refs @@ -467,7 +467,7 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, generics: &'tcx Generics<'_ } } }, - WherePredicate::EqPredicate(ref pred) => { + WherePredicateKind::EqPredicate(ref pred) => { let mut visitor = RefVisitor::new(cx); walk_ty(&mut visitor, pred.lhs_ty); walk_ty(&mut visitor, pred.rhs_ty); diff --git a/clippy_lints/src/multiple_bound_locations.rs b/clippy_lints/src/multiple_bound_locations.rs index d276e29bacec..882ab2dda7aa 100644 --- a/clippy_lints/src/multiple_bound_locations.rs +++ b/clippy_lints/src/multiple_bound_locations.rs @@ -1,5 +1,5 @@ use rustc_ast::visit::FnKind; -use rustc_ast::{NodeId, WherePredicate}; +use rustc_ast::{NodeId, WherePredicateKind}; use rustc_data_structures::fx::FxHashMap; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::declare_lint_pass; @@ -51,8 +51,8 @@ fn check_fn(&mut self, cx: &EarlyContext<'_>, kind: FnKind<'_>, _: Span, _: Node } } for clause in &generics.where_clause.predicates { - match clause { - WherePredicate::BoundPredicate(pred) => { + match &clause.kind { + WherePredicateKind::BoundPredicate(pred) => { if (!pred.bound_generic_params.is_empty() || !pred.bounds.is_empty()) && let Some(Some(bound_span)) = pred .bounded_ty @@ -62,14 +62,14 @@ fn check_fn(&mut self, cx: &EarlyContext<'_>, kind: FnKind<'_>, _: Span, _: Node emit_lint(cx, *bound_span, pred.bounded_ty.span); } }, - WherePredicate::RegionPredicate(pred) => { + WherePredicateKind::RegionPredicate(pred) => { if !pred.bounds.is_empty() && let Some(bound_span) = generic_params_with_bounds.get(&pred.lifetime.ident.name.as_str()) { emit_lint(cx, *bound_span, pred.lifetime.ident.span); } }, - WherePredicate::EqPredicate(_) => {}, + WherePredicateKind::EqPredicate(_) => {}, } } } diff --git a/clippy_lints/src/needless_maybe_sized.rs b/clippy_lints/src/needless_maybe_sized.rs index 852a0ce8c6d5..ad6313e391bd 100644 --- a/clippy_lints/src/needless_maybe_sized.rs +++ b/clippy_lints/src/needless_maybe_sized.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use rustc_errors::Applicability; use rustc_hir::def_id::{DefId, DefIdMap}; -use rustc_hir::{BoundPolarity, GenericBound, Generics, PolyTraitRef, TraitBoundModifiers, WherePredicate}; +use rustc_hir::{BoundPolarity, GenericBound, Generics, PolyTraitRef, TraitBoundModifiers, WherePredicateKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{ClauseKind, PredicatePolarity}; use rustc_session::declare_lint_pass; @@ -52,7 +52,7 @@ fn type_param_bounds<'tcx>(generics: &'tcx Generics<'tcx>) -> impl Iterator, item: &'tcx TraitItem<'tc let mut self_bounds_map = FxHashMap::default(); for predicate in item.generics.predicates { - if let WherePredicate::BoundPredicate(bound_predicate) = predicate + if let WherePredicateKind::BoundPredicate(bound_predicate) = predicate.kind && bound_predicate.origin != PredicateOrigin::ImplTrait - && !bound_predicate.span.from_expansion() + && !predicate.span.from_expansion() && let TyKind::Path(QPath::Resolved(_, Path { segments, .. })) = bound_predicate.bounded_ty.kind && let Some(PathSegment { res: Res::SelfTyParam { trait_: def_id }, @@ -268,10 +268,10 @@ impl Eq for SpanlessTy<'_, '_> {} let mut map: UnhashMap, Vec<&GenericBound<'_>>> = UnhashMap::default(); let mut applicability = Applicability::MaybeIncorrect; for bound in generics.predicates { - if let WherePredicate::BoundPredicate(p) = bound + if let WherePredicateKind::BoundPredicate(p) = bound.kind && p.origin != PredicateOrigin::ImplTrait && p.bounds.len() as u64 <= self.max_trait_bounds - && !p.span.from_expansion() + && !bound.span.from_expansion() && let bounds = p .bounds .iter() @@ -295,7 +295,7 @@ impl Eq for SpanlessTy<'_, '_> {} span_lint_and_help( cx, TYPE_REPETITION_IN_BOUNDS, - p.span, + bound.span, "this type has already been used as a bound predicate", None, hint_string, @@ -322,8 +322,8 @@ fn check_trait_bound_duplication<'tcx>(cx: &LateContext<'tcx>, generics: &'_ Gen .predicates .iter() .filter_map(|pred| { - if pred.in_where_clause() - && let WherePredicate::BoundPredicate(bound_predicate) = pred + if pred.kind.in_where_clause() + && let WherePredicateKind::BoundPredicate(bound_predicate) = pred.kind && let TyKind::Path(QPath::Resolved(_, path)) = bound_predicate.bounded_ty.kind { return Some( @@ -347,10 +347,10 @@ fn check_trait_bound_duplication<'tcx>(cx: &LateContext<'tcx>, generics: &'_ Gen // | // compare trait bounds keyed by generic name and comparable trait to collected where // predicates eg. (T, Clone) - for predicate in generics.predicates.iter().filter(|pred| !pred.in_where_clause()) { - if let WherePredicate::BoundPredicate(bound_predicate) = predicate + for predicate in generics.predicates.iter().filter(|pred| !pred.kind.in_where_clause()) { + if let WherePredicateKind::BoundPredicate(bound_predicate) = predicate.kind && bound_predicate.origin != PredicateOrigin::ImplTrait - && !bound_predicate.span.from_expansion() + && !predicate.span.from_expansion() && let TyKind::Path(QPath::Resolved(_, path)) = bound_predicate.bounded_ty.kind { let traits = rollup_traits(cx, bound_predicate.bounds, "these bounds contain repeated elements"); diff --git a/clippy_utils/src/ast_utils.rs b/clippy_utils/src/ast_utils.rs index 0be6dc15a8e1..c90f4a6ebfe6 100644 --- a/clippy_utils/src/ast_utils.rs +++ b/clippy_utils/src/ast_utils.rs @@ -661,8 +661,8 @@ pub fn eq_generics(l: &Generics, r: &Generics) -> bool { } pub fn eq_where_predicate(l: &WherePredicate, r: &WherePredicate) -> bool { - use WherePredicate::*; - match (l, r) { + use WherePredicateKind::*; + match (&l.kind, &r.kind) { (BoundPredicate(l), BoundPredicate(r)) => { over(&l.bound_generic_params, &r.bound_generic_params, |l, r| { eq_generic_param(l, r) From 3c9daca1d159930adada5ac843b00b24e7bd0d24 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 28 Nov 2024 18:57:00 +0100 Subject: [PATCH 11/12] Bump nightly version -> 2024-11-28 --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index 0a2e9d89b6e8..fb159ca2ae03 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2024-11-14" +channel = "nightly-2024-11-28" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" From 5fb924f3340cdacb4e67ff3e6e0ab42857c0ab5f Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 28 Nov 2024 18:57:52 +0100 Subject: [PATCH 12/12] Bump Clippy version -> 0.1.85 --- Cargo.toml | 4 ++-- clippy_config/Cargo.toml | 2 +- clippy_lints/Cargo.toml | 2 +- clippy_utils/Cargo.toml | 2 +- clippy_utils/README.md | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 79b9af7b0bf3..bb259c77ee37 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clippy" # begin autogenerated version -version = "0.1.84" +version = "0.1.85" # end autogenerated version description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" @@ -29,7 +29,7 @@ rustc_tools_util = "0.4.0" tempfile = { version = "3.3", optional = true } termize = "0.1" color-print = "0.3.4" -anstream = "0.6.0" +anstream = "0.6.18" [dev-dependencies] cargo_metadata = "0.18.1" diff --git a/clippy_config/Cargo.toml b/clippy_config/Cargo.toml index d1158b48e921..3f18a0bc7d25 100644 --- a/clippy_config/Cargo.toml +++ b/clippy_config/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clippy_config" # begin autogenerated version -version = "0.1.84" +version = "0.1.85" # end autogenerated version edition = "2021" publish = false diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index be99dcc2c7c5..c1f8e82f6988 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clippy_lints" # begin autogenerated version -version = "0.1.84" +version = "0.1.85" # end autogenerated version description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index 4f95889a53a7..945827c98c17 100644 --- a/clippy_utils/Cargo.toml +++ b/clippy_utils/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clippy_utils" # begin autogenerated version -version = "0.1.84" +version = "0.1.85" # end autogenerated version edition = "2021" description = "Helpful tools for writing lints, provided as they are used in Clippy" diff --git a/clippy_utils/README.md b/clippy_utils/README.md index fb1a3f13f8cb..61476a82ba00 100644 --- a/clippy_utils/README.md +++ b/clippy_utils/README.md @@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain: ``` -nightly-2024-11-14 +nightly-2024-11-28 ```