From d232d094b8e2776a6a1e88f478442fd98e2e70b4 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Wed, 18 Sep 2024 10:34:27 -0700 Subject: [PATCH 01/21] Remove the `control_flow_enum` feature from clippy --- clippy_lints/src/lib.rs | 1 - clippy_utils/src/lib.rs | 1 - 2 files changed, 2 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 1d41f568f..54c375845 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1,7 +1,6 @@ #![feature(array_windows)] #![feature(binary_heap_into_iter_sorted)] #![feature(box_patterns)] -#![feature(control_flow_enum)] #![feature(f128)] #![feature(f16)] #![feature(if_let_guard)] diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 6dbc33341..6c0413c18 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1,6 +1,5 @@ #![feature(array_chunks)] #![feature(box_patterns)] -#![feature(control_flow_enum)] #![feature(f128)] #![feature(f16)] #![feature(if_let_guard)] From 3ac3c3495173d2b93c585e0df37667f7633b9ab5 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 29 Sep 2024 22:23:44 -0400 Subject: [PATCH 02/21] Remove crashes and fix tests --- tests/ui/crashes/ice-12284.rs | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 tests/ui/crashes/ice-12284.rs diff --git a/tests/ui/crashes/ice-12284.rs b/tests/ui/crashes/ice-12284.rs deleted file mode 100644 index 8d1dbface..000000000 --- a/tests/ui/crashes/ice-12284.rs +++ /dev/null @@ -1,10 +0,0 @@ -#![allow(incomplete_features)] -#![feature(unnamed_fields)] - -#[repr(C)] -struct Foo { - _: struct { - }, -} - -fn main() {} From ce22fd34d9c35ad08da74d82bea4227f8dba77f7 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 2 Oct 2024 21:52:44 -0400 Subject: [PATCH 03/21] Remove redundant in_trait from hir::TyKind::OpaqueDef --- clippy_lints/src/extra_unused_type_parameters.rs | 2 +- clippy_lints/src/lifetimes.rs | 2 +- clippy_lints/src/manual_async_fn.rs | 2 +- clippy_utils/src/hir_utils.rs | 3 +-- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/extra_unused_type_parameters.rs b/clippy_lints/src/extra_unused_type_parameters.rs index bf9388b4a..a7fb9535e 100644 --- a/clippy_lints/src/extra_unused_type_parameters.rs +++ b/clippy_lints/src/extra_unused_type_parameters.rs @@ -199,7 +199,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for TypeWalker<'cx, 'tcx> { fn visit_ty(&mut self, t: &'tcx Ty<'tcx>) { if let Some((def_id, _)) = t.peel_refs().as_generic_param() { self.ty_params.remove(&def_id); - } else if let TyKind::OpaqueDef(id, _, _) = t.kind { + } else if let TyKind::OpaqueDef(id, _) = t.kind { // Explicitly walk OpaqueDef. Normally `walk_ty` would do the job, but it calls // `visit_nested_item`, which checks that `Self::NestedFilter::INTER` is set. We're // using `OnlyBodies`, so the check ends up failing and the type isn't fully walked. diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 755afe959..d3cc5ea62 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -523,7 +523,7 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> { fn visit_ty(&mut self, ty: &'tcx Ty<'_>) { match ty.kind { - TyKind::OpaqueDef(item, bounds, _) => { + TyKind::OpaqueDef(item, bounds) => { let map = self.cx.tcx.hir(); let item = map.item(item); let len = self.lts.len(); diff --git a/clippy_lints/src/manual_async_fn.rs b/clippy_lints/src/manual_async_fn.rs index fc3bba9e5..7097c8515 100644 --- a/clippy_lints/src/manual_async_fn.rs +++ b/clippy_lints/src/manual_async_fn.rs @@ -105,7 +105,7 @@ fn future_trait_ref<'tcx>( cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>, ) -> Option<(&'tcx TraitRef<'tcx>, Vec)> { - if let TyKind::OpaqueDef(item_id, bounds, false) = ty.kind + if let TyKind::OpaqueDef(item_id, bounds) = ty.kind && let item = cx.tcx.hir().item(item_id) && let ItemKind::OpaqueTy(opaque) = &item.kind && let Some(trait_ref) = opaque.bounds.iter().find_map(|bound| { diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index 76900379a..b2d75125c 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -1115,9 +1115,8 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { } }, TyKind::Path(ref qpath) => self.hash_qpath(qpath), - TyKind::OpaqueDef(_, arg_list, in_trait) => { + TyKind::OpaqueDef(_, arg_list) => { self.hash_generic_args(arg_list); - in_trait.hash(&mut self.s); }, TyKind::TraitObject(_, lifetime, _) => { self.hash_lifetime(lifetime); From cbd64a4868a84e980101be7779b7b17156dff383 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 2 Oct 2024 22:04:18 -0400 Subject: [PATCH 04/21] Use named fields for OpaqueTyOrigin --- clippy_lints/src/len_zero.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 8bc2a56af..0cbcdbeb9 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -313,7 +313,7 @@ fn extract_future_output<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<& kind: ItemKind::OpaqueTy(opaque), .. } = item - && let OpaqueTyOrigin::AsyncFn(_) = opaque.origin + && let OpaqueTyOrigin::AsyncFn { .. } = opaque.origin && let [GenericBound::Trait(trait_ref, _)] = &opaque.bounds && let Some(segment) = trait_ref.trait_ref.path.segments.last() && let Some(generic_args) = segment.args From 277c4e4baff50ff0df4b63e86404b8eea70e61ec Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 3 Oct 2024 16:32:51 +0200 Subject: [PATCH 05/21] Merge commit 'aa0d551351a9c15d8a95fdb3e2946b505893dda8' into clippy-subtree-update --- .github/workflows/remark.yml | 2 +- CHANGELOG.md | 1 + book/src/lint_configuration.md | 1 + clippy_config/src/conf.rs | 1 + clippy_config/src/msrvs.rs | 1 + clippy_lints/src/booleans.rs | 86 +++- clippy_lints/src/box_default.rs | 2 +- clippy_lints/src/cargo/common_metadata.rs | 16 +- clippy_lints/src/casts/borrow_as_ptr.rs | 6 +- clippy_lints/src/casts/ref_as_ptr.rs | 6 +- clippy_lints/src/checked_conversions.rs | 2 +- clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/default_numeric_fallback.rs | 2 +- clippy_lints/src/derive.rs | 2 +- clippy_lints/src/doc/mod.rs | 2 +- clippy_lints/src/empty_enum.rs | 2 +- clippy_lints/src/escape.rs | 4 +- clippy_lints/src/excessive_nesting.rs | 2 +- .../src/extra_unused_type_parameters.rs | 2 +- clippy_lints/src/fallible_impl_from.rs | 2 +- clippy_lints/src/format_args.rs | 2 +- clippy_lints/src/format_impl.rs | 2 +- clippy_lints/src/from_over_into.rs | 2 +- clippy_lints/src/functions/mod.rs | 60 +++ clippy_lints/src/functions/ref_option.rs | 122 +++++ clippy_lints/src/implicit_hasher.rs | 4 +- clippy_lints/src/index_refutable_slice.rs | 2 +- clippy_lints/src/len_zero.rs | 4 +- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/lifetimes.rs | 439 ++++++++++-------- clippy_lints/src/loops/manual_memcpy.rs | 2 +- clippy_lints/src/loops/missing_spin_loop.rs | 10 +- clippy_lints/src/loops/needless_range_loop.rs | 4 +- clippy_lints/src/loops/same_item_push.rs | 2 +- clippy_lints/src/loops/utils.rs | 4 +- .../src/loops/while_immutable_condition.rs | 4 +- .../src/loops/while_let_on_iterator.rs | 2 +- clippy_lints/src/macro_metavars_in_unsafe.rs | 2 +- clippy_lints/src/macro_use.rs | 2 +- clippy_lints/src/manual_clamp.rs | 2 +- clippy_lints/src/manual_strip.rs | 2 +- clippy_lints/src/map_unit_fn.rs | 2 +- .../src/matches/match_str_case_mismatch.rs | 4 +- clippy_lints/src/matches/mod.rs | 2 +- clippy_lints/src/matches/overlapping_arms.rs | 4 +- .../matches/significant_drop_in_scrutinee.rs | 4 +- clippy_lints/src/matches/wild_in_or_pats.rs | 14 +- clippy_lints/src/methods/mut_mutex_lock.rs | 6 +- clippy_lints/src/methods/needless_collect.rs | 2 +- .../src/methods/option_map_unwrap_or.rs | 4 +- clippy_lints/src/methods/or_fun_call.rs | 2 +- clippy_lints/src/methods/utils.rs | 4 +- .../src/mixed_read_write_in_expression.rs | 6 +- clippy_lints/src/mut_mut.rs | 2 +- clippy_lints/src/mutable_debug_assertion.rs | 2 +- clippy_lints/src/needless_for_each.rs | 2 +- clippy_lints/src/needless_pass_by_ref_mut.rs | 2 +- clippy_lints/src/non_expressive_names.rs | 10 +- .../src/non_send_fields_in_send_ty.rs | 2 +- clippy_lints/src/pass_by_ref_or_value.rs | 4 +- clippy_lints/src/pathbuf_init_then_push.rs | 2 +- clippy_lints/src/ptr.rs | 8 +- clippy_lints/src/redundant_closure_call.rs | 2 +- clippy_lints/src/returns.rs | 6 +- .../src/significant_drop_tightening.rs | 2 +- .../src/single_component_path_imports.rs | 2 +- .../src/slow_vector_initialization.rs | 4 +- clippy_lints/src/swap.rs | 2 +- clippy_lints/src/unnested_or_patterns.rs | 11 +- clippy_lints/src/unused_async.rs | 25 +- clippy_lints/src/unwrap.rs | 4 +- clippy_lints/src/use_self.rs | 2 +- .../interning_defined_symbol.rs | 2 +- .../src/utils/internal_lints/invalid_paths.rs | 3 +- .../internal_lints/lint_without_lint_pass.rs | 2 +- clippy_lints/src/zombie_processes.rs | 11 +- clippy_utils/src/ast_utils.rs | 109 +++-- clippy_utils/src/consts.rs | 6 +- clippy_utils/src/eager_or_lazy.rs | 2 +- clippy_utils/src/hir_utils.rs | 49 +- clippy_utils/src/lib.rs | 8 +- clippy_utils/src/mir/mod.rs | 2 +- clippy_utils/src/mir/possible_borrower.rs | 6 +- clippy_utils/src/mir/possible_origin.rs | 2 +- clippy_utils/src/qualify_min_const_fn.rs | 3 +- clippy_utils/src/ty.rs | 2 +- clippy_utils/src/ty/type_certainty/mod.rs | 2 +- clippy_utils/src/usage.rs | 6 +- clippy_utils/src/visitors.rs | 4 +- lintcheck/src/main.rs | 4 +- rust-toolchain | 2 +- src/driver.rs | 61 ++- tests/ui-toml/suppress_lint_in_const/test.rs | 3 +- .../suppress_lint_in_const/test.stderr | 12 +- tests/ui/as_ptr_cast_mut.rs | 2 +- tests/ui/borrow_box.fixed | 133 ++++++ tests/ui/borrow_box.rs | 20 +- tests/ui/borrow_box.stderr | 14 +- tests/ui/boxed_local.rs | 3 +- tests/ui/boxed_local.stderr | 8 +- tests/ui/bytecount.rs | 2 +- .../needless_lifetimes_impl_trait.fixed | 2 +- .../needless_lifetimes_impl_trait.stderr | 20 +- tests/ui/deref_addrof_double_trigger.rs | 4 +- tests/ui/derive.rs | 7 +- tests/ui/derive.stderr | 20 +- tests/ui/eta.fixed | 3 +- tests/ui/eta.rs | 3 +- tests/ui/eta.stderr | 68 +-- tests/ui/explicit_auto_deref.fixed | 3 +- tests/ui/explicit_auto_deref.rs | 3 +- tests/ui/explicit_auto_deref.stderr | 92 ++-- tests/ui/extra_unused_lifetimes.rs | 8 + tests/ui/extra_unused_lifetimes.stderr | 8 +- tests/ui/float_cmp.rs | 2 +- tests/ui/float_cmp_const.rs | 3 +- tests/ui/float_cmp_const.stderr | 16 +- tests/ui/float_equality_without_abs.rs | 2 +- .../if_let_slice_binding.rs | 2 +- tests/ui/invalid_null_ptr_usage.fixed | 7 +- tests/ui/invalid_null_ptr_usage.rs | 5 +- tests/ui/invalid_null_ptr_usage.stderr | 36 +- tests/ui/iter_without_into_iter.rs | 1 + tests/ui/iter_without_into_iter.stderr | 16 +- tests/ui/mem_replace.fixed | 3 +- tests/ui/mem_replace.rs | 3 +- tests/ui/mem_replace.stderr | 48 +- tests/ui/mem_replace_no_std.fixed | 3 +- tests/ui/mem_replace_no_std.rs | 3 +- tests/ui/mem_replace_no_std.stderr | 14 +- tests/ui/mismatching_type_param_order.rs | 2 +- tests/ui/mut_mutex_lock.fixed | 12 + tests/ui/mut_mutex_lock.rs | 12 + tests/ui/mut_mutex_lock.stderr | 8 +- tests/ui/needless_borrow.fixed | 3 +- tests/ui/needless_borrow.rs | 3 +- tests/ui/needless_borrow.stderr | 56 +-- tests/ui/needless_lifetimes.fixed | 23 +- tests/ui/needless_lifetimes.rs | 21 +- tests/ui/needless_lifetimes.stderr | 24 +- tests/ui/needless_pass_by_value.rs | 3 +- tests/ui/needless_pass_by_value.stderr | 52 +-- tests/ui/needless_return.fixed | 20 + tests/ui/needless_return.rs | 20 + tests/ui/new_without_default.fixed | 3 +- tests/ui/new_without_default.rs | 3 +- tests/ui/new_without_default.stderr | 18 +- tests/ui/nonminimal_bool_methods.fixed | 62 +++ tests/ui/nonminimal_bool_methods.rs | 62 +++ tests/ui/nonminimal_bool_methods.stderr | 110 ++++- tests/ui/nonminimal_bool_methods_unfixable.rs | 9 + .../nonminimal_bool_methods_unfixable.stderr | 17 + tests/ui/ref_as_ptr.fixed | 2 +- tests/ui/ref_as_ptr.rs | 2 +- tests/ui/ref_option/all/clippy.toml | 1 + tests/ui/ref_option/private/clippy.toml | 1 + tests/ui/ref_option/ref_option.all.fixed | 62 +++ tests/ui/ref_option/ref_option.all.stderr | 162 +++++++ tests/ui/ref_option/ref_option.private.fixed | 62 +++ tests/ui/ref_option/ref_option.private.stderr | 108 +++++ tests/ui/ref_option/ref_option.rs | 62 +++ .../ref_option/ref_option_traits.all.stderr | 37 ++ .../ref_option_traits.private.stderr | 21 + tests/ui/ref_option/ref_option_traits.rs | 37 ++ tests/ui/serde.rs | 2 +- tests/ui/significant_drop_in_scrutinee.rs | 7 +- tests/ui/significant_drop_in_scrutinee.stderr | 58 +-- tests/ui/str_split.fixed | 1 + tests/ui/str_split.rs | 1 + tests/ui/str_split.stderr | 20 +- tests/ui/temporary_assignment.rs | 2 +- tests/ui/unconditional_recursion.rs | 3 +- tests/ui/unconditional_recursion.stderr | 90 ++-- tests/ui/unused_async.rs | 16 + tests/ui/unused_async.stderr | 4 +- tests/ui/unused_format_specs.1.fixed | 35 ++ tests/ui/unused_format_specs.2.fixed | 35 ++ ...cs_unfixable.rs => unused_format_specs.rs} | 2 +- ...able.stderr => unused_format_specs.stderr} | 8 +- tests/ui/useful_asref.rs | 1 + tests/ui/wild_in_or_pats.rs | 68 +++ tests/ui/wild_in_or_pats.stderr | 18 +- tests/ui/zombie_processes.rs | 7 + 183 files changed, 2484 insertions(+), 848 deletions(-) create mode 100644 clippy_lints/src/functions/ref_option.rs create mode 100644 tests/ui/borrow_box.fixed create mode 100644 tests/ui/nonminimal_bool_methods_unfixable.rs create mode 100644 tests/ui/nonminimal_bool_methods_unfixable.stderr create mode 100644 tests/ui/ref_option/all/clippy.toml create mode 100644 tests/ui/ref_option/private/clippy.toml create mode 100644 tests/ui/ref_option/ref_option.all.fixed create mode 100644 tests/ui/ref_option/ref_option.all.stderr create mode 100644 tests/ui/ref_option/ref_option.private.fixed create mode 100644 tests/ui/ref_option/ref_option.private.stderr create mode 100644 tests/ui/ref_option/ref_option.rs create mode 100644 tests/ui/ref_option/ref_option_traits.all.stderr create mode 100644 tests/ui/ref_option/ref_option_traits.private.stderr create mode 100644 tests/ui/ref_option/ref_option_traits.rs create mode 100644 tests/ui/unused_format_specs.1.fixed create mode 100644 tests/ui/unused_format_specs.2.fixed rename tests/ui/{unused_format_specs_unfixable.rs => unused_format_specs.rs} (98%) rename tests/ui/{unused_format_specs_unfixable.stderr => unused_format_specs.stderr} (89%) diff --git a/.github/workflows/remark.yml b/.github/workflows/remark.yml index 348d52020..a1b011dc3 100644 --- a/.github/workflows/remark.yml +++ b/.github/workflows/remark.yml @@ -19,7 +19,7 @@ jobs: uses: actions/checkout@v4 - name: Setup Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: '18.x' diff --git a/CHANGELOG.md b/CHANGELOG.md index 41a86e8ce..5d253d525 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5871,6 +5871,7 @@ Released 2018-09-13 [`ref_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_as_ptr [`ref_binding_to_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_binding_to_reference [`ref_in_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_in_deref +[`ref_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option [`ref_option_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option_ref [`ref_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_patterns [`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index 91159bc79..07a56fb33 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -353,6 +353,7 @@ Suppress lints whenever the suggested change would cause breakage for other crat * [`rc_buffer`](https://rust-lang.github.io/rust-clippy/master/index.html#rc_buffer) * [`rc_mutex`](https://rust-lang.github.io/rust-clippy/master/index.html#rc_mutex) * [`redundant_allocation`](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation) +* [`ref_option`](https://rust-lang.github.io/rust-clippy/master/index.html#ref_option) * [`single_call_fn`](https://rust-lang.github.io/rust-clippy/master/index.html#single_call_fn) * [`trivially_copy_pass_by_ref`](https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref) * [`unnecessary_box_returns`](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_box_returns) diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 757620341..e4e2c97fd 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -378,6 +378,7 @@ define_Conf! { rc_buffer, rc_mutex, redundant_allocation, + ref_option, single_call_fn, trivially_copy_pass_by_ref, unnecessary_box_returns, diff --git a/clippy_config/src/msrvs.rs b/clippy_config/src/msrvs.rs index e30df3d32..68a3b11d3 100644 --- a/clippy_config/src/msrvs.rs +++ b/clippy_config/src/msrvs.rs @@ -19,6 +19,7 @@ macro_rules! msrv_aliases { msrv_aliases! { 1,83,0 { CONST_EXTERN_FN } 1,83,0 { CONST_FLOAT_BITS_CONV } + 1,82,0 { IS_NONE_OR } 1,81,0 { LINT_REASONS_STABILIZATION } 1,80,0 { BOX_INTO_ITER} 1,77,0 { C_STR_LITERALS } diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index 3c2af7262..26888f7e3 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -1,3 +1,5 @@ +use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::eq_expr_value; use clippy_utils::source::SpanRangeExt; @@ -7,7 +9,7 @@ use rustc_errors::Applicability; use rustc_hir::intravisit::{FnKind, Visitor, walk_expr}; use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, UnOp}; use rustc_lint::{LateContext, LateLintPass, Level}; -use rustc_session::declare_lint_pass; +use rustc_session::{RustcVersion, impl_lint_pass}; use rustc_span::def_id::LocalDefId; use rustc_span::{Span, sym}; @@ -69,9 +71,25 @@ declare_clippy_lint! { } // For each pairs, both orders are considered. -const METHODS_WITH_NEGATION: [(&str, &str); 2] = [("is_some", "is_none"), ("is_err", "is_ok")]; +const METHODS_WITH_NEGATION: [(Option, &str, &str); 3] = [ + (None, "is_some", "is_none"), + (None, "is_err", "is_ok"), + (Some(msrvs::IS_NONE_OR), "is_some_and", "is_none_or"), +]; -declare_lint_pass!(NonminimalBool => [NONMINIMAL_BOOL, OVERLY_COMPLEX_BOOL_EXPR]); +pub struct NonminimalBool { + msrv: Msrv, +} + +impl NonminimalBool { + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } + } +} + +impl_lint_pass!(NonminimalBool => [NONMINIMAL_BOOL, OVERLY_COMPLEX_BOOL_EXPR]); impl<'tcx> LateLintPass<'tcx> for NonminimalBool { fn check_fn( @@ -83,7 +101,7 @@ impl<'tcx> LateLintPass<'tcx> for NonminimalBool { _: Span, _: LocalDefId, ) { - NonminimalBoolVisitor { cx }.visit_body(body); + NonminimalBoolVisitor { cx, msrv: &self.msrv }.visit_body(body); } fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { @@ -100,6 +118,8 @@ impl<'tcx> LateLintPass<'tcx> for NonminimalBool { _ => {}, } } + + extract_msrv_attr!(LateContext); } fn inverted_bin_op_eq_str(op: BinOpKind) -> Option<&'static str> { @@ -176,11 +196,11 @@ fn check_inverted_bool_in_condition( ); } -fn check_simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) { +fn check_simplify_not(cx: &LateContext<'_>, msrv: &Msrv, expr: &Expr<'_>) { if let ExprKind::Unary(UnOp::Not, inner) = &expr.kind && !expr.span.from_expansion() && !inner.span.from_expansion() - && let Some(suggestion) = simplify_not(cx, inner) + && let Some(suggestion) = simplify_not(cx, msrv, inner) && cx.tcx.lint_level_at_node(NONMINIMAL_BOOL, expr.hir_id).0 != Level::Allow { span_lint_and_sugg( @@ -197,6 +217,7 @@ fn check_simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) { struct NonminimalBoolVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, + msrv: &'a Msrv, } use quine_mc_cluskey::Bool; @@ -205,7 +226,7 @@ struct Hir2Qmm<'a, 'tcx, 'v> { cx: &'a LateContext<'tcx>, } -impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, 'v> { +impl<'v> Hir2Qmm<'_, '_, 'v> { fn extract(&mut self, op: BinOpKind, a: &[&'v Expr<'_>], mut v: Vec) -> Result, String> { for a in a { if let ExprKind::Binary(binop, lhs, rhs) = &a.kind { @@ -289,10 +310,11 @@ impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, 'v> { struct SuggestContext<'a, 'tcx, 'v> { terminals: &'v [&'v Expr<'v>], cx: &'a LateContext<'tcx>, + msrv: &'a Msrv, output: String, } -impl<'a, 'tcx, 'v> SuggestContext<'a, 'tcx, 'v> { +impl SuggestContext<'_, '_, '_> { fn recurse(&mut self, suggestion: &Bool) -> Option<()> { use quine_mc_cluskey::Bool::{And, False, Not, Or, Term, True}; match suggestion { @@ -311,7 +333,7 @@ impl<'a, 'tcx, 'v> SuggestContext<'a, 'tcx, 'v> { }, Term(n) => { let terminal = self.terminals[n as usize]; - if let Some(str) = simplify_not(self.cx, terminal) { + if let Some(str) = simplify_not(self.cx, self.msrv, terminal) { self.output.push_str(&str); } else { self.output.push('!'); @@ -358,7 +380,7 @@ impl<'a, 'tcx, 'v> SuggestContext<'a, 'tcx, 'v> { } } -fn simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { +fn simplify_not(cx: &LateContext<'_>, curr_msrv: &Msrv, expr: &Expr<'_>) -> Option { match &expr.kind { ExprKind::Binary(binop, lhs, rhs) => { if !implements_ord(cx, lhs) { @@ -389,7 +411,7 @@ fn simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { Some(format!("{lhs_snippet}{op}{rhs_snippet}")) }) }, - ExprKind::MethodCall(path, receiver, [], _) => { + ExprKind::MethodCall(path, receiver, args, _) => { let type_of_receiver = cx.typeck_results().expr_ty(receiver); if !is_type_diagnostic_item(cx, type_of_receiver, sym::Option) && !is_type_diagnostic_item(cx, type_of_receiver, sym::Result) @@ -399,21 +421,41 @@ fn simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { METHODS_WITH_NEGATION .iter() .copied() - .flat_map(|(a, b)| vec![(a, b), (b, a)]) - .find(|&(a, _)| { - let path: &str = path.ident.name.as_str(); - a == path + .flat_map(|(msrv, a, b)| vec![(msrv, a, b), (msrv, b, a)]) + .find(|&(msrv, a, _)| msrv.is_none_or(|msrv| curr_msrv.meets(msrv)) && a == path.ident.name.as_str()) + .and_then(|(_, _, neg_method)| { + let negated_args = args + .iter() + .map(|arg| simplify_not(cx, curr_msrv, arg)) + .collect::>>()? + .join(", "); + Some(format!( + "{}.{neg_method}({negated_args})", + receiver.span.get_source_text(cx)? + )) }) - .and_then(|(_, neg_method)| Some(format!("{}.{neg_method}()", receiver.span.get_source_text(cx)?))) }, + ExprKind::Closure(closure) => { + let body = cx.tcx.hir().body(closure.body); + let params = body + .params + .iter() + .map(|param| param.span.get_source_text(cx).map(|t| t.to_string())) + .collect::>>()? + .join(", "); + let negated = simplify_not(cx, curr_msrv, body.value)?; + Some(format!("|{params}| {negated}")) + }, + ExprKind::Unary(UnOp::Not, expr) => expr.span.get_source_text(cx).map(|t| t.to_string()), _ => None, } } -fn suggest(cx: &LateContext<'_>, suggestion: &Bool, terminals: &[&Expr<'_>]) -> String { +fn suggest(cx: &LateContext<'_>, msrv: &Msrv, suggestion: &Bool, terminals: &[&Expr<'_>]) -> String { let mut suggest_context = SuggestContext { terminals, cx, + msrv, output: String::new(), }; suggest_context.recurse(suggestion); @@ -475,7 +517,7 @@ fn terminal_stats(b: &Bool) -> Stats { stats } -impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> { +impl<'tcx> NonminimalBoolVisitor<'_, 'tcx> { fn bool_expr(&self, e: &'tcx Expr<'_>) { let mut h2q = Hir2Qmm { terminals: Vec::new(), @@ -526,7 +568,7 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> { diag.span_suggestion( e.span, "it would look like the following", - suggest(self.cx, suggestion, &h2q.terminals), + suggest(self.cx, self.msrv, suggestion, &h2q.terminals), // nonminimal_bool can produce minimal but // not human readable expressions (#3141) Applicability::Unspecified, @@ -569,12 +611,12 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> { } }; if improvements.is_empty() { - check_simplify_not(self.cx, e); + check_simplify_not(self.cx, self.msrv, e); } else { nonminimal_bool_lint( improvements .into_iter() - .map(|suggestion| suggest(self.cx, suggestion, &h2q.terminals)) + .map(|suggestion| suggest(self.cx, self.msrv, suggestion, &h2q.terminals)) .collect(), ); } @@ -582,7 +624,7 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> { } } -impl<'a, 'tcx> Visitor<'tcx> for NonminimalBoolVisitor<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for NonminimalBoolVisitor<'_, 'tcx> { fn visit_expr(&mut self, e: &'tcx Expr<'_>) { if !e.span.from_expansion() { match &e.kind { diff --git a/clippy_lints/src/box_default.rs b/clippy_lints/src/box_default.rs index 8261c6535..40d154c0b 100644 --- a/clippy_lints/src/box_default.rs +++ b/clippy_lints/src/box_default.rs @@ -91,7 +91,7 @@ fn is_local_vec_expn(cx: &LateContext<'_>, expr: &Expr<'_>, ref_expr: &Expr<'_>) #[derive(Default)] struct InferVisitor(bool); -impl<'tcx> Visitor<'tcx> for InferVisitor { +impl Visitor<'_> for InferVisitor { fn visit_ty(&mut self, t: &Ty<'_>) { self.0 |= matches!(t.kind, TyKind::Infer | TyKind::OpaqueDef(..) | TyKind::TraitObject(..)); if !self.0 { diff --git a/clippy_lints/src/cargo/common_metadata.rs b/clippy_lints/src/cargo/common_metadata.rs index fed0aa8b2..6714c0539 100644 --- a/clippy_lints/src/cargo/common_metadata.rs +++ b/clippy_lints/src/cargo/common_metadata.rs @@ -10,27 +10,27 @@ pub(super) fn check(cx: &LateContext<'_>, metadata: &Metadata, ignore_publish: b // only run the lint if publish is `None` (`publish = true` or skipped entirely) // or if the vector isn't empty (`publish = ["something"]`) if package.publish.as_ref().filter(|publish| publish.is_empty()).is_none() || ignore_publish { - if is_empty_str(&package.description) { + if is_empty_str(package.description.as_ref()) { missing_warning(cx, package, "package.description"); } - if is_empty_str(&package.license) && is_empty_str(&package.license_file) { + if is_empty_str(package.license.as_ref()) && is_empty_str(package.license_file.as_ref()) { missing_warning(cx, package, "either package.license or package.license_file"); } - if is_empty_str(&package.repository) { + if is_empty_str(package.repository.as_ref()) { missing_warning(cx, package, "package.repository"); } - if is_empty_str(&package.readme) { + if is_empty_str(package.readme.as_ref()) { missing_warning(cx, package, "package.readme"); } - if is_empty_vec(&package.keywords) { + if is_empty_vec(package.keywords.as_ref()) { missing_warning(cx, package, "package.keywords"); } - if is_empty_vec(&package.categories) { + if is_empty_vec(package.categories.as_ref()) { missing_warning(cx, package, "package.categories"); } } @@ -42,8 +42,8 @@ fn missing_warning(cx: &LateContext<'_>, package: &cargo_metadata::Package, fiel span_lint(cx, CARGO_COMMON_METADATA, DUMMY_SP, message); } -fn is_empty_str>(value: &Option) -> bool { - value.as_ref().map_or(true, |s| s.as_ref().is_empty()) +fn is_empty_str>(value: Option<&T>) -> bool { + value.map_or(true, |s| s.as_ref().is_empty()) } fn is_empty_vec(value: &[String]) -> bool { diff --git a/clippy_lints/src/casts/borrow_as_ptr.rs b/clippy_lints/src/casts/borrow_as_ptr.rs index b7256dd2e..4dd51dcbc 100644 --- a/clippy_lints/src/casts/borrow_as_ptr.rs +++ b/clippy_lints/src/casts/borrow_as_ptr.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_no_std_crate; use clippy_utils::source::snippet_with_context; +use clippy_utils::std_or_core; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Ty, TyKind}; use rustc_lint::LateContext; @@ -16,8 +16,8 @@ pub(super) fn check<'tcx>( ) { if matches!(cast_to.kind, TyKind::Ptr(_)) && let ExprKind::AddrOf(BorrowKind::Ref, mutability, e) = cast_expr.kind + && let Some(std_or_core) = std_or_core(cx) { - let core_or_std = if is_no_std_crate(cx) { "core" } else { "std" }; let macro_name = match mutability { Mutability::Not => "addr_of", Mutability::Mut => "addr_of_mut", @@ -40,7 +40,7 @@ pub(super) fn check<'tcx>( expr.span, "borrow as raw pointer", "try", - format!("{core_or_std}::ptr::{macro_name}!({snip})"), + format!("{std_or_core}::ptr::{macro_name}!({snip})"), Applicability::MachineApplicable, ); } diff --git a/clippy_lints/src/casts/ref_as_ptr.rs b/clippy_lints/src/casts/ref_as_ptr.rs index dfa240cce..f699bba20 100644 --- a/clippy_lints/src/casts/ref_as_ptr.rs +++ b/clippy_lints/src/casts/ref_as_ptr.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; -use clippy_utils::{ExprUseNode, expr_use_ctxt, is_no_std_crate}; +use clippy_utils::{ExprUseNode, expr_use_ctxt, std_or_core}; use rustc_errors::Applicability; use rustc_hir::{Expr, Mutability, Ty, TyKind}; use rustc_lint::LateContext; @@ -25,8 +25,8 @@ pub(super) fn check<'tcx>( && let use_cx = expr_use_ctxt(cx, expr) // TODO: only block the lint if `cast_expr` is a temporary && !matches!(use_cx.use_node(cx), ExprUseNode::LetStmt(_) | ExprUseNode::ConstStatic(_)) + && let Some(std_or_core) = std_or_core(cx) { - let core_or_std = if is_no_std_crate(cx) { "core" } else { "std" }; let fn_name = match to_mutbl { Mutability::Not => "from_ref", Mutability::Mut => "from_mut", @@ -56,7 +56,7 @@ pub(super) fn check<'tcx>( expr.span, "reference as raw pointer", "try", - format!("{core_or_std}::ptr::{fn_name}{turbofish}({cast_expr_sugg})"), + format!("{std_or_core}::ptr::{fn_name}{turbofish}({cast_expr_sugg})"), app, ); } diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs index d3aa2fd1e..2e7f91a84 100644 --- a/clippy_lints/src/checked_conversions.rs +++ b/clippy_lints/src/checked_conversions.rs @@ -48,7 +48,7 @@ impl CheckedConversions { impl_lint_pass!(CheckedConversions => [CHECKED_CONVERSIONS]); -impl<'tcx> LateLintPass<'tcx> for CheckedConversions { +impl LateLintPass<'_> for CheckedConversions { fn check_expr(&mut self, cx: &LateContext<'_>, item: &Expr<'_>) { if let ExprKind::Binary(op, lhs, rhs) = item.kind && let (lt1, gt1, op2) = match op.node { diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 2b229d2fe..9cec672be 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -202,6 +202,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::functions::MUST_USE_CANDIDATE_INFO, crate::functions::MUST_USE_UNIT_INFO, crate::functions::NOT_UNSAFE_PTR_ARG_DEREF_INFO, + crate::functions::REF_OPTION_INFO, crate::functions::RENAMED_FUNCTION_PARAMS_INFO, crate::functions::RESULT_LARGE_ERR_INFO, crate::functions::RESULT_UNIT_ERR_INFO, diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs index a065dc2cf..4808c3727 100644 --- a/clippy_lints/src/default_numeric_fallback.rs +++ b/clippy_lints/src/default_numeric_fallback.rs @@ -119,7 +119,7 @@ impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> { } } -impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for NumericFallbackVisitor<'_, 'tcx> { fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { match &expr.kind { ExprKind::Block( diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 35a97adfb..82a66cc92 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -385,7 +385,7 @@ fn check_unsafe_derive_deserialize<'tcx>( && cx .tcx .inherent_impls(def.did()) - .into_iter() + .iter() .map(|imp_did| cx.tcx.hir().expect_item(imp_did.expect_local())) .any(|imp| has_unsafe(cx, imp)) { diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index 0ae1fad56..e090644ae 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -970,7 +970,7 @@ impl<'a, 'tcx> FindPanicUnwrap<'a, 'tcx> { } } -impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for FindPanicUnwrap<'_, 'tcx> { type NestedFilter = nested_filter::OnlyBodies; fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { diff --git a/clippy_lints/src/empty_enum.rs b/clippy_lints/src/empty_enum.rs index f4c55738c..70eb81fa0 100644 --- a/clippy_lints/src/empty_enum.rs +++ b/clippy_lints/src/empty_enum.rs @@ -60,7 +60,7 @@ declare_clippy_lint! { declare_lint_pass!(EmptyEnum => [EMPTY_ENUM]); -impl<'tcx> LateLintPass<'tcx> for EmptyEnum { +impl LateLintPass<'_> for EmptyEnum { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { if let ItemKind::Enum(..) = item.kind // Only suggest the `never_type` if the feature is enabled diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index 5588124e7..a89f0d9c4 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -141,7 +141,7 @@ fn is_argument(tcx: TyCtxt<'_>, id: HirId) -> bool { matches!(tcx.parent_hir_node(id), Node::Param(_)) } -impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> { +impl<'tcx> Delegate<'tcx> for EscapeDelegate<'_, 'tcx> { fn consume(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId) { if cmt.place.projections.is_empty() { if let PlaceBase::Local(lid) = cmt.place.base { @@ -188,7 +188,7 @@ impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> { fn fake_read(&mut self, _: &PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} } -impl<'a, 'tcx> EscapeDelegate<'a, 'tcx> { +impl<'tcx> EscapeDelegate<'_, 'tcx> { fn is_large_box(&self, ty: Ty<'tcx>) -> bool { // Large types need to be boxed to avoid stack overflows. if let Some(boxed_ty) = ty.boxed_ty() { diff --git a/clippy_lints/src/excessive_nesting.rs b/clippy_lints/src/excessive_nesting.rs index ce0e0faa0..ffc763669 100644 --- a/clippy_lints/src/excessive_nesting.rs +++ b/clippy_lints/src/excessive_nesting.rs @@ -135,7 +135,7 @@ impl NestingVisitor<'_, '_> { } } -impl<'conf, 'cx> Visitor<'_> for NestingVisitor<'conf, 'cx> { +impl Visitor<'_> for NestingVisitor<'_, '_> { fn visit_block(&mut self, block: &Block) { if block.span.from_expansion() { return; diff --git a/clippy_lints/src/extra_unused_type_parameters.rs b/clippy_lints/src/extra_unused_type_parameters.rs index bf9388b4a..3b93d3ff9 100644 --- a/clippy_lints/src/extra_unused_type_parameters.rs +++ b/clippy_lints/src/extra_unused_type_parameters.rs @@ -193,7 +193,7 @@ fn bound_to_trait_def_id(bound: &GenericBound<'_>) -> Option { bound.trait_ref()?.trait_def_id()?.as_local() } -impl<'cx, 'tcx> Visitor<'tcx> for TypeWalker<'cx, 'tcx> { +impl<'tcx> Visitor<'tcx> for TypeWalker<'_, 'tcx> { type NestedFilter = nested_filter::OnlyBodies; fn visit_ty(&mut self, t: &'tcx Ty<'tcx>) { diff --git a/clippy_lints/src/fallible_impl_from.rs b/clippy_lints/src/fallible_impl_from.rs index 747ea9a43..f822432cc 100644 --- a/clippy_lints/src/fallible_impl_from.rs +++ b/clippy_lints/src/fallible_impl_from.rs @@ -73,7 +73,7 @@ fn lint_impl_body(cx: &LateContext<'_>, impl_span: Span, impl_items: &[hir::Impl result: Vec, } - impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> { + impl<'tcx> Visitor<'tcx> for FindPanicUnwrap<'_, 'tcx> { fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { if let Some(macro_call) = root_macro_call_first_node(self.lcx, expr) { if is_panic(self.lcx, macro_call.def_id) { diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index 83ab9f655..4c043f8dc 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -219,7 +219,7 @@ struct FormatArgsExpr<'a, 'tcx> { ignore_mixed: bool, } -impl<'a, 'tcx> FormatArgsExpr<'a, 'tcx> { +impl FormatArgsExpr<'_, '_> { fn check_templates(&self) { for piece in &self.format_args.template { if let FormatArgsPiece::Placeholder(placeholder) = piece diff --git a/clippy_lints/src/format_impl.rs b/clippy_lints/src/format_impl.rs index c196f404c..7c0515b8c 100644 --- a/clippy_lints/src/format_impl.rs +++ b/clippy_lints/src/format_impl.rs @@ -148,7 +148,7 @@ struct FormatImplExpr<'a, 'tcx> { format_trait_impl: FormatTraitNames, } -impl<'a, 'tcx> FormatImplExpr<'a, 'tcx> { +impl FormatImplExpr<'_, '_> { fn check_to_string_in_display(&self) { if self.format_trait_impl.name == sym::Display && let ExprKind::MethodCall(path, self_arg, ..) = self.expr.kind diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs index 8832cd42d..d5a2e0686 100644 --- a/clippy_lints/src/from_over_into.rs +++ b/clippy_lints/src/from_over_into.rs @@ -126,7 +126,7 @@ struct SelfFinder<'a, 'tcx> { invalid: bool, } -impl<'a, 'tcx> Visitor<'tcx> for SelfFinder<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for SelfFinder<'_, 'tcx> { type NestedFilter = OnlyBodies; fn nested_visit_map(&mut self) -> Self::Map { diff --git a/clippy_lints/src/functions/mod.rs b/clippy_lints/src/functions/mod.rs index ba8a459c9..91d73c2a9 100644 --- a/clippy_lints/src/functions/mod.rs +++ b/clippy_lints/src/functions/mod.rs @@ -2,6 +2,7 @@ mod impl_trait_in_params; mod misnamed_getters; mod must_use; mod not_unsafe_ptr_arg_deref; +mod ref_option; mod renamed_function_params; mod result; mod too_many_arguments; @@ -399,6 +400,53 @@ declare_clippy_lint! { "renamed function parameters in trait implementation" } +declare_clippy_lint! { + /// ### What it does + /// Warns when a function signature uses `&Option` instead of `Option<&T>`. + /// + /// ### Why is this bad? + /// More flexibility, better memory optimization, and more idiomatic Rust code. + /// + /// `&Option` in a function signature breaks encapsulation because the caller must own T + /// and move it into an Option to call with it. When returned, the owner must internally store + /// it as `Option` in order to return it. + /// At a lower level, `&Option` points to memory with the `presence` bit flag plus the `T` value, + /// whereas `Option<&T>` is usually [optimized](https://doc.rust-lang.org/1.81.0/std/option/index.html#representation) + /// to a single pointer, so it may be more optimal. + /// + /// See this [YouTube video](https://www.youtube.com/watch?v=6c7pZYP_iIE) by + /// Logan Smith for an in-depth explanation of why this is important. + /// + /// ### Known problems + /// This lint recommends changing the function signatures, but it cannot + /// automatically change the function calls or the function implementations. + /// + /// ### Example + /// ```no_run + /// // caller uses foo(&opt) + /// fn foo(a: &Option) {} + /// # struct Unit {} + /// # impl Unit { + /// fn bar(&self) -> &Option { &None } + /// # } + /// ``` + /// Use instead: + /// ```no_run + /// // caller should use `foo1(opt.as_ref())` + /// fn foo1(a: Option<&String>) {} + /// // better yet, use string slice `foo2(opt.as_deref())` + /// fn foo2(a: Option<&str>) {} + /// # struct Unit {} + /// # impl Unit { + /// fn bar(&self) -> Option<&String> { None } + /// # } + /// ``` + #[clippy::version = "1.82.0"] + pub REF_OPTION, + pedantic, + "function signature uses `&Option` instead of `Option<&T>`" +} + pub struct Functions { too_many_arguments_threshold: u64, too_many_lines_threshold: u64, @@ -437,6 +485,7 @@ impl_lint_pass!(Functions => [ MISNAMED_GETTERS, IMPL_TRAIT_IN_PARAMS, RENAMED_FUNCTION_PARAMS, + REF_OPTION, ]); impl<'tcx> LateLintPass<'tcx> for Functions { @@ -455,6 +504,16 @@ impl<'tcx> LateLintPass<'tcx> for Functions { not_unsafe_ptr_arg_deref::check_fn(cx, kind, decl, body, def_id); misnamed_getters::check_fn(cx, kind, decl, body, span); impl_trait_in_params::check_fn(cx, &kind, body, hir_id); + ref_option::check_fn( + cx, + kind, + decl, + span, + hir_id, + def_id, + body, + self.avoid_breaking_exported_api, + ); } fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { @@ -475,5 +534,6 @@ impl<'tcx> LateLintPass<'tcx> for Functions { must_use::check_trait_item(cx, item); result::check_trait_item(cx, item, self.large_error_threshold); impl_trait_in_params::check_trait_item(cx, item, self.avoid_breaking_exported_api); + ref_option::check_trait_item(cx, item, self.avoid_breaking_exported_api); } } diff --git a/clippy_lints/src/functions/ref_option.rs b/clippy_lints/src/functions/ref_option.rs new file mode 100644 index 000000000..373ecd74c --- /dev/null +++ b/clippy_lints/src/functions/ref_option.rs @@ -0,0 +1,122 @@ +use crate::functions::REF_OPTION; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::is_trait_impl_item; +use clippy_utils::source::snippet; +use clippy_utils::ty::is_type_diagnostic_item; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::intravisit::FnKind; +use rustc_hir::{FnDecl, HirId}; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, GenericArgKind, Mutability, Ty}; +use rustc_span::def_id::LocalDefId; +use rustc_span::{Span, sym}; + +fn check_ty<'a>(cx: &LateContext<'a>, param: &rustc_hir::Ty<'a>, param_ty: Ty<'a>, fixes: &mut Vec<(Span, String)>) { + if let ty::Ref(_, opt_ty, Mutability::Not) = param_ty.kind() + && is_type_diagnostic_item(cx, *opt_ty, sym::Option) + && let ty::Adt(_, opt_gen) = opt_ty.kind() + && let [gen] = opt_gen.as_slice() + && let GenericArgKind::Type(gen_ty) = gen.unpack() + && !gen_ty.is_ref() + // Need to gen the original spans, so first parsing mid, and hir parsing afterward + && let hir::TyKind::Ref(lifetime, hir::MutTy { ty, .. }) = param.kind + && let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = ty.kind + && let (Some(first), Some(last)) = (path.segments.first(), path.segments.last()) + && let Some(hir::GenericArgs { + args: [hir::GenericArg::Type(opt_ty)], + .. + }) = last.args + { + let lifetime = snippet(cx, lifetime.ident.span, ".."); + fixes.push(( + param.span, + format!( + "{}<&{lifetime}{}{}>", + snippet(cx, first.ident.span.to(last.ident.span), ".."), + if lifetime.is_empty() { "" } else { " " }, + snippet(cx, opt_ty.span, "..") + ), + )); + } +} + +fn check_fn_sig<'a>(cx: &LateContext<'a>, decl: &FnDecl<'a>, span: Span, sig: ty::FnSig<'a>) { + let mut fixes = Vec::new(); + // Check function arguments' types + for (param, param_ty) in decl.inputs.iter().zip(sig.inputs()) { + check_ty(cx, param, *param_ty, &mut fixes); + } + // Check return type + if let hir::FnRetTy::Return(ty) = &decl.output { + check_ty(cx, ty, sig.output(), &mut fixes); + } + if !fixes.is_empty() { + span_lint_and_then( + cx, + REF_OPTION, + span, + "it is more idiomatic to use `Option<&T>` instead of `&Option`", + |diag| { + diag.multipart_suggestion("change this to", fixes, Applicability::Unspecified); + }, + ); + } +} + +#[allow(clippy::too_many_arguments)] +pub(crate) fn check_fn<'a>( + cx: &LateContext<'a>, + kind: FnKind<'_>, + decl: &FnDecl<'a>, + span: Span, + hir_id: HirId, + def_id: LocalDefId, + body: &hir::Body<'_>, + avoid_breaking_exported_api: bool, +) { + if avoid_breaking_exported_api && cx.effective_visibilities.is_exported(def_id) { + return; + } + + if let FnKind::Closure = kind { + // Compute the span of the closure parameters + return type if set + let span = if let hir::FnRetTy::Return(out_ty) = &decl.output { + if decl.inputs.is_empty() { + out_ty.span + } else { + span.with_hi(out_ty.span.hi()) + } + } else if let (Some(first), Some(last)) = (decl.inputs.first(), decl.inputs.last()) { + first.span.to(last.span) + } else { + // No parameters - no point in checking + return; + }; + + // Figure out the signature of the closure + let ty::Closure(_, args) = cx.typeck_results().expr_ty(body.value).kind() else { + return; + }; + let sig = args.as_closure().sig().skip_binder(); + + check_fn_sig(cx, decl, span, sig); + } else if !is_trait_impl_item(cx, hir_id) { + let sig = cx.tcx.fn_sig(def_id).instantiate_identity().skip_binder(); + check_fn_sig(cx, decl, span, sig); + } +} + +pub(super) fn check_trait_item<'a>( + cx: &LateContext<'a>, + trait_item: &hir::TraitItem<'a>, + avoid_breaking_exported_api: bool, +) { + if let hir::TraitItemKind::Fn(ref sig, _) = trait_item.kind + && !(avoid_breaking_exported_api && cx.effective_visibilities.is_exported(trait_item.owner_id.def_id)) + { + let def_id = trait_item.owner_id.def_id; + let ty_sig = cx.tcx.fn_sig(def_id).instantiate_identity().skip_binder(); + check_fn_sig(cx, sig.decl, sig.span, ty_sig); + } +} diff --git a/clippy_lints/src/implicit_hasher.rs b/clippy_lints/src/implicit_hasher.rs index f68392514..4c5375730 100644 --- a/clippy_lints/src/implicit_hasher.rs +++ b/clippy_lints/src/implicit_hasher.rs @@ -281,7 +281,7 @@ impl<'a, 'tcx> ImplicitHasherTypeVisitor<'a, 'tcx> { } } -impl<'a, 'tcx> Visitor<'tcx> for ImplicitHasherTypeVisitor<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for ImplicitHasherTypeVisitor<'_, 'tcx> { fn visit_ty(&mut self, t: &'tcx hir::Ty<'_>) { if let Some(target) = ImplicitHasherType::new(self.cx, t) { self.found.push(target); @@ -318,7 +318,7 @@ impl<'a, 'b, 'tcx> ImplicitHasherConstructorVisitor<'a, 'b, 'tcx> { } } -impl<'a, 'b, 'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'a, 'b, 'tcx> { +impl<'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'_, '_, 'tcx> { type NestedFilter = nested_filter::OnlyBodies; fn visit_body(&mut self, body: &Body<'tcx>) { diff --git a/clippy_lints/src/index_refutable_slice.rs b/clippy_lints/src/index_refutable_slice.rs index 39afb6810..73ebe6aec 100644 --- a/clippy_lints/src/index_refutable_slice.rs +++ b/clippy_lints/src/index_refutable_slice.rs @@ -226,7 +226,7 @@ struct SliceIndexLintingVisitor<'a, 'tcx> { max_suggested_slice: u64, } -impl<'a, 'tcx> Visitor<'tcx> for SliceIndexLintingVisitor<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for SliceIndexLintingVisitor<'_, 'tcx> { type NestedFilter = nested_filter::OnlyBodies; fn nested_visit_map(&mut self) -> Self::Map { diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 8bc2a56af..c88eb7038 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -460,7 +460,7 @@ fn check_for_is_empty( let is_empty = cx .tcx .inherent_impls(impl_ty) - .into_iter() + .iter() .flat_map(|&id| cx.tcx.associated_items(id).filter_by_name_unhygienic(is_empty)) .find(|item| item.kind == AssocKind::Fn); @@ -628,7 +628,7 @@ fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { /// Checks the inherent impl's items for an `is_empty(self)` method. fn has_is_empty_impl(cx: &LateContext<'_>, id: DefId) -> bool { let is_empty = sym!(is_empty); - cx.tcx.inherent_impls(id).into_iter().any(|imp| { + cx.tcx.inherent_impls(id).iter().any(|imp| { cx.tcx .associated_items(*imp) .filter_by_name_unhygienic(is_empty) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 1d41f568f..2eb6d99b7 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -609,7 +609,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(move |tcx| Box::new(await_holding_invalid::AwaitHolding::new(tcx, conf))); store.register_late_pass(|_| Box::new(serde_api::SerdeApi)); store.register_late_pass(move |_| Box::new(types::Types::new(conf))); - store.register_late_pass(|_| Box::new(booleans::NonminimalBool)); + store.register_late_pass(move |_| Box::new(booleans::NonminimalBool::new(conf))); store.register_late_pass(|_| Box::new(enum_clike::UnportableVariant)); store.register_late_pass(|_| Box::new(float_literal::FloatLiteral)); store.register_late_pass(|_| Box::new(ptr::Ptr)); diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 755afe959..034cab720 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -6,12 +6,12 @@ use rustc_errors::Applicability; use rustc_hir::FnRetTy::Return; use rustc_hir::intravisit::nested_filter::{self as hir_nested_filter, NestedFilter}; use rustc_hir::intravisit::{ - Visitor, walk_fn_decl, walk_generic_param, walk_generics, walk_impl_item_ref, walk_item, walk_param_bound, - walk_poly_trait_ref, walk_trait_ref, walk_ty, + Visitor, walk_fn_decl, walk_generic_args, walk_generics, walk_impl_item_ref, walk_item, walk_param_bound, + walk_poly_trait_ref, walk_trait_ref, walk_ty, walk_where_predicate, }; use rustc_hir::{ - BareFnTy, BodyId, FnDecl, FnSig, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, Impl, - ImplItem, ImplItemKind, Item, ItemKind, Lifetime, LifetimeName, LifetimeParamKind, Node, PolyTraitRef, + 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, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -21,7 +21,7 @@ use rustc_middle::lint::in_external_macro; use rustc_session::declare_lint_pass; use rustc_span::Span; use rustc_span::def_id::LocalDefId; -use rustc_span::symbol::{Ident, Symbol, kw}; +use rustc_span::symbol::{Ident, kw}; use std::ops::ControlFlow; declare_clippy_lint! { @@ -191,45 +191,10 @@ fn check_fn_inner<'tcx>( if usages.iter().any(|usage| !usage.ident.span.eq_ctxt(span)) { return; } - - let lts = elidable_lts - .iter() - // In principle, the result of the call to `Node::ident` could be `unwrap`ped, as `DefId` should refer to a - // `Node::GenericParam`. - .filter_map(|&def_id| cx.tcx.hir_node_by_def_id(def_id).ident()) - .map(|ident| ident.to_string()) - .collect::>() - .join(", "); - - span_lint_and_then( - cx, - NEEDLESS_LIFETIMES, - elidable_lts - .iter() - .map(|<| cx.tcx.def_span(lt)) - .chain(usages.iter().filter_map(|usage| { - if let LifetimeName::Param(def_id) = usage.res - && elidable_lts.contains(&def_id) - { - return Some(usage.ident.span); - } - - None - })) - .collect_vec(), - format!("the following explicit lifetimes could be elided: {lts}"), - |diag| { - if sig.header.is_async() { - // async functions have usages whose spans point at the lifetime declaration which messes up - // suggestions - return; - }; - - if let Some(suggestions) = elision_suggestions(cx, generics, &elidable_lts, &usages) { - diag.multipart_suggestion("elide the lifetimes", suggestions, Applicability::MachineApplicable); - } - }, - ); + // async functions have usages whose spans point at the lifetime declaration which messes up + // suggestions + let include_suggestions = !sig.header.is_async(); + report_elidable_lifetimes(cx, generics, &elidable_lts, &usages, include_suggestions); } if report_extra_lifetimes { @@ -237,97 +202,6 @@ fn check_fn_inner<'tcx>( } } -fn elision_suggestions( - cx: &LateContext<'_>, - generics: &Generics<'_>, - elidable_lts: &[LocalDefId], - usages: &[Lifetime], -) -> Option> { - let explicit_params = generics - .params - .iter() - .filter(|param| !param.is_elided_lifetime() && !param.is_impl_trait()) - .collect::>(); - - let mut suggestions = if elidable_lts.len() == explicit_params.len() { - // if all the params are elided remove the whole generic block - // - // fn x<'a>() {} - // ^^^^ - vec![(generics.span, String::new())] - } else { - elidable_lts - .iter() - .map(|&id| { - let pos = explicit_params.iter().position(|param| param.def_id == id)?; - let param = explicit_params.get(pos)?; - - let span = if let Some(next) = explicit_params.get(pos + 1) { - // fn x<'prev, 'a, 'next>() {} - // ^^^^ - param.span.until(next.span) - } else { - // `pos` should be at least 1 here, because the param in position 0 would either have a `next` - // param or would have taken the `elidable_lts.len() == explicit_params.len()` branch. - let prev = explicit_params.get(pos - 1)?; - - // fn x<'prev, 'a>() {} - // ^^^^ - param.span.with_lo(prev.span.hi()) - }; - - Some((span, String::new())) - }) - .collect::>>()? - }; - - suggestions.extend( - usages - .iter() - .filter(|usage| named_lifetime(usage).map_or(false, |id| elidable_lts.contains(&id))) - .map(|usage| { - match cx.tcx.parent_hir_node(usage.hir_id) { - Node::Ty(Ty { - kind: TyKind::Ref(..), .. - }) => { - // expand `&'a T` to `&'a T` - // ^^ ^^^ - let span = cx.sess().source_map().span_extend_while_whitespace(usage.ident.span); - - (span, String::new()) - }, - // `T<'a>` and `impl Foo + 'a` should be replaced by `'_` - _ => (usage.ident.span, String::from("'_")), - } - }), - ); - - Some(suggestions) -} - -// elision doesn't work for explicit self types, see rust-lang/rust#69064 -fn explicit_self_type<'tcx>(cx: &LateContext<'tcx>, func: &FnDecl<'tcx>, ident: Option) -> bool { - if let Some(ident) = ident - && ident.name == kw::SelfLower - && !func.implicit_self.has_implicit_self() - && let Some(self_ty) = func.inputs.first() - { - let mut visitor = RefVisitor::new(cx); - visitor.visit_ty(self_ty); - - !visitor.all_lts().is_empty() - } else { - false - } -} - -fn named_lifetime(lt: &Lifetime) -> Option { - match lt.res { - LifetimeName::Param(id) if !lt.is_anonymous() => Some(id), - _ => None, - } -} - fn could_use_elision<'tcx>( cx: &LateContext<'tcx>, func: &'tcx FnDecl<'_>, @@ -450,6 +324,22 @@ fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxHashSet(cx: &LateContext<'tcx>, func: &FnDecl<'tcx>, ident: Option) -> bool { + if let Some(ident) = ident + && ident.name == kw::SelfLower + && !func.implicit_self.has_implicit_self() + && let Some(self_ty) = func.inputs.first() + { + let mut visitor = RefVisitor::new(cx); + visitor.visit_ty(self_ty); + + !visitor.all_lts().is_empty() + } else { + false + } +} + /// Number of times each named lifetime occurs in the given slice. Returns a vector to preserve /// relative order. #[must_use] @@ -470,6 +360,13 @@ fn named_lifetime_occurrences(lts: &[Lifetime]) -> Vec<(LocalDefId, usize)> { occurrences } +fn named_lifetime(lt: &Lifetime) -> Option { + match lt.res { + LifetimeName::Param(id) if !lt.is_anonymous() => Some(id), + _ => None, + } +} + struct RefVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, lts: Vec, @@ -500,7 +397,7 @@ impl<'a, 'tcx> RefVisitor<'a, 'tcx> { } } -impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for RefVisitor<'_, 'tcx> { // for lifetimes as parameters of generics fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) { self.lts.push(*lifetime); @@ -594,23 +491,43 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, generics: &'tcx Generics<'_ false } +struct Usage { + lifetime: Lifetime, + in_where_predicate: bool, + in_generics_arg: bool, +} + struct LifetimeChecker<'cx, 'tcx, F> { cx: &'cx LateContext<'tcx>, - map: FxHashMap, + map: FxHashMap>, + where_predicate_depth: usize, + generic_args_depth: usize, phantom: std::marker::PhantomData, } impl<'cx, 'tcx, F> LifetimeChecker<'cx, 'tcx, F> { - fn new(cx: &'cx LateContext<'tcx>, map: FxHashMap) -> LifetimeChecker<'cx, 'tcx, F> { + fn new(cx: &'cx LateContext<'tcx>, generics: &'tcx Generics<'_>) -> LifetimeChecker<'cx, 'tcx, F> { + let map = generics + .params + .iter() + .filter_map(|par| match par.kind { + GenericParamKind::Lifetime { + kind: LifetimeParamKind::Explicit, + } => Some((par.def_id, Vec::new())), + _ => None, + }) + .collect(); Self { cx, map, + where_predicate_depth: 0, + generic_args_depth: 0, phantom: std::marker::PhantomData, } } } -impl<'cx, 'tcx, F> Visitor<'tcx> for LifetimeChecker<'cx, 'tcx, F> +impl<'tcx, F> Visitor<'tcx> for LifetimeChecker<'_, 'tcx, F> where F: NestedFilter<'tcx>, { @@ -619,18 +536,27 @@ where // for lifetimes as parameters of generics fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) { - self.map.remove(&lifetime.ident.name); + if let LifetimeName::Param(def_id) = lifetime.res + && let Some(usages) = self.map.get_mut(&def_id) + { + usages.push(Usage { + lifetime: *lifetime, + in_where_predicate: self.where_predicate_depth != 0, + in_generics_arg: self.generic_args_depth != 0, + }); + } } - fn visit_generic_param(&mut self, param: &'tcx GenericParam<'_>) { - // don't actually visit `<'a>` or `<'a: 'b>` - // we've already visited the `'a` declarations and - // don't want to spuriously remove them - // `'b` in `'a: 'b` is useless unless used elsewhere in - // a non-lifetime bound - if let GenericParamKind::Type { .. } = param.kind { - walk_generic_param(self, param); - } + fn visit_where_predicate(&mut self, predicate: &'tcx WherePredicate<'tcx>) { + self.where_predicate_depth += 1; + walk_where_predicate(self, predicate); + self.where_predicate_depth -= 1; + } + + fn visit_generic_args(&mut self, generic_args: &'tcx GenericArgs<'tcx>) -> Self::Result { + self.generic_args_depth += 1; + walk_generic_args(self, generic_args); + self.generic_args_depth -= 1; } fn nested_visit_map(&mut self) -> Self::Map { @@ -639,44 +565,28 @@ where } fn report_extra_lifetimes<'tcx>(cx: &LateContext<'tcx>, func: &'tcx FnDecl<'_>, generics: &'tcx Generics<'_>) { - let hs = generics - .params - .iter() - .filter_map(|par| match par.kind { - GenericParamKind::Lifetime { - kind: LifetimeParamKind::Explicit, - } => Some((par.name.ident().name, par.span)), - _ => None, - }) - .collect(); - let mut checker = LifetimeChecker::::new(cx, hs); + let mut checker = LifetimeChecker::::new(cx, generics); walk_generics(&mut checker, generics); walk_fn_decl(&mut checker, func); - for &v in checker.map.values() { - span_lint( - cx, - EXTRA_UNUSED_LIFETIMES, - v, - "this lifetime isn't used in the function definition", - ); + for (def_id, usages) in checker.map { + if usages + .iter() + .all(|usage| usage.in_where_predicate && !usage.in_generics_arg) + { + span_lint( + cx, + EXTRA_UNUSED_LIFETIMES, + cx.tcx.def_span(def_id), + "this lifetime isn't used in the function definition", + ); + } } } fn report_extra_impl_lifetimes<'tcx>(cx: &LateContext<'tcx>, impl_: &'tcx Impl<'_>) { - let hs = impl_ - .generics - .params - .iter() - .filter_map(|par| match par.kind { - GenericParamKind::Lifetime { - kind: LifetimeParamKind::Explicit, - } => Some((par.name.ident().name, par.span)), - _ => None, - }) - .collect(); - let mut checker = LifetimeChecker::::new(cx, hs); + let mut checker = LifetimeChecker::::new(cx, impl_.generics); walk_generics(&mut checker, impl_.generics); if let Some(ref trait_ref) = impl_.of_trait { @@ -687,9 +597,176 @@ fn report_extra_impl_lifetimes<'tcx>(cx: &LateContext<'tcx>, impl_: &'tcx Impl<' walk_impl_item_ref(&mut checker, item); } - for &v in checker.map.values() { - span_lint(cx, EXTRA_UNUSED_LIFETIMES, v, "this lifetime isn't used in the impl"); + for (&def_id, usages) in &checker.map { + if usages + .iter() + .all(|usage| usage.in_where_predicate && !usage.in_generics_arg) + { + span_lint( + cx, + EXTRA_UNUSED_LIFETIMES, + cx.tcx.def_span(def_id), + "this lifetime isn't used in the impl", + ); + } } + + report_elidable_impl_lifetimes(cx, impl_, &checker.map); +} + +// An `impl` lifetime is elidable if it satisfies the following conditions: +// - It is used exactly once. +// - That single use is not in `GenericArgs` in a `WherePredicate`. (Note that `GenericArgs` are +// different from `GenericParam`s.) +fn report_elidable_impl_lifetimes<'tcx>( + cx: &LateContext<'tcx>, + impl_: &'tcx Impl<'_>, + map: &FxHashMap>, +) { + let single_usages = map + .iter() + .filter_map(|(def_id, usages)| { + if let [ + Usage { + lifetime, + in_where_predicate: false, + .. + } + | Usage { + lifetime, + in_generics_arg: false, + .. + }, + ] = usages.as_slice() + { + Some((def_id, lifetime)) + } else { + None + } + }) + .collect::>(); + + if single_usages.is_empty() { + return; + } + + let (elidable_lts, usages): (Vec<_>, Vec<_>) = single_usages.into_iter().unzip(); + + report_elidable_lifetimes(cx, impl_.generics, &elidable_lts, &usages, true); +} + +/// Generate diagnostic messages for elidable lifetimes. +fn report_elidable_lifetimes( + cx: &LateContext<'_>, + generics: &Generics<'_>, + elidable_lts: &[LocalDefId], + usages: &[Lifetime], + include_suggestions: bool, +) { + let lts = elidable_lts + .iter() + // In principle, the result of the call to `Node::ident` could be `unwrap`ped, as `DefId` should refer to a + // `Node::GenericParam`. + .filter_map(|&def_id| cx.tcx.hir_node_by_def_id(def_id).ident()) + .map(|ident| ident.to_string()) + .collect::>() + .join(", "); + + span_lint_and_then( + cx, + NEEDLESS_LIFETIMES, + elidable_lts + .iter() + .map(|<| cx.tcx.def_span(lt)) + .chain(usages.iter().filter_map(|usage| { + if let LifetimeName::Param(def_id) = usage.res + && elidable_lts.contains(&def_id) + { + return Some(usage.ident.span); + } + + None + })) + .collect_vec(), + format!("the following explicit lifetimes could be elided: {lts}"), + |diag| { + if !include_suggestions { + return; + }; + + if let Some(suggestions) = elision_suggestions(cx, generics, elidable_lts, usages) { + diag.multipart_suggestion("elide the lifetimes", suggestions, Applicability::MachineApplicable); + } + }, + ); +} + +fn elision_suggestions( + cx: &LateContext<'_>, + generics: &Generics<'_>, + elidable_lts: &[LocalDefId], + usages: &[Lifetime], +) -> Option> { + let explicit_params = generics + .params + .iter() + .filter(|param| !param.is_elided_lifetime() && !param.is_impl_trait()) + .collect::>(); + + let mut suggestions = if elidable_lts.len() == explicit_params.len() { + // if all the params are elided remove the whole generic block + // + // fn x<'a>() {} + // ^^^^ + vec![(generics.span, String::new())] + } else { + elidable_lts + .iter() + .map(|&id| { + let pos = explicit_params.iter().position(|param| param.def_id == id)?; + let param = explicit_params.get(pos)?; + + let span = if let Some(next) = explicit_params.get(pos + 1) { + // fn x<'prev, 'a, 'next>() {} + // ^^^^ + param.span.until(next.span) + } else { + // `pos` should be at least 1 here, because the param in position 0 would either have a `next` + // param or would have taken the `elidable_lts.len() == explicit_params.len()` branch. + let prev = explicit_params.get(pos - 1)?; + + // fn x<'prev, 'a>() {} + // ^^^^ + param.span.with_lo(prev.span.hi()) + }; + + Some((span, String::new())) + }) + .collect::>>()? + }; + + suggestions.extend( + usages + .iter() + .filter(|usage| named_lifetime(usage).map_or(false, |id| elidable_lts.contains(&id))) + .map(|usage| { + match cx.tcx.parent_hir_node(usage.hir_id) { + Node::Ty(Ty { + kind: TyKind::Ref(..), .. + }) => { + // expand `&'a T` to `&'a T` + // ^^ ^^^ + let span = cx.sess().source_map().span_extend_while_whitespace(usage.ident.span); + + (span, String::new()) + }, + // `T<'a>` and `impl Foo + 'a` should be replaced by `'_` + _ => (usage.ident.span, String::from("'_")), + } + }), + ); + + Some(suggestions) } struct BodyLifetimeChecker; diff --git a/clippy_lints/src/loops/manual_memcpy.rs b/clippy_lints/src/loops/manual_memcpy.rs index a7c1d1bd6..68d063ad5 100644 --- a/clippy_lints/src/loops/manual_memcpy.rs +++ b/clippy_lints/src/loops/manual_memcpy.rs @@ -209,7 +209,7 @@ fn build_manual_memcpy_suggestion<'tcx>( #[derive(Clone)] struct MinifyingSugg<'a>(Sugg<'a>); -impl<'a> Display for MinifyingSugg<'a> { +impl Display for MinifyingSugg<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.0.fmt(f) } diff --git a/clippy_lints/src/loops/missing_spin_loop.rs b/clippy_lints/src/loops/missing_spin_loop.rs index e405829b2..a9944d64c 100644 --- a/clippy_lints/src/loops/missing_spin_loop.rs +++ b/clippy_lints/src/loops/missing_spin_loop.rs @@ -1,6 +1,6 @@ use super::MISSING_SPIN_LOOP; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_no_std_crate; +use clippy_utils::std_or_core; use rustc_errors::Applicability; use rustc_hir::{Block, Expr, ExprKind}; use rustc_lint::LateContext; @@ -41,6 +41,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, body: &' && [sym::load, sym::compare_exchange, sym::compare_exchange_weak].contains(&method.ident.name) && let ty::Adt(def, _args) = cx.typeck_results().expr_ty(callee).kind() && cx.tcx.is_diagnostic_item(sym::AtomicBool, def.did()) + && let Some(std_or_core) = std_or_core(cx) { span_lint_and_sugg( cx, @@ -48,12 +49,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, body: &' body.span, "busy-waiting loop should at least have a spin loop hint", "try", - (if is_no_std_crate(cx) { - "{ core::hint::spin_loop() }" - } else { - "{ std::hint::spin_loop() }" - }) - .into(), + format!("{{ {std_or_core}::hint::spin_loop() }}"), Applicability::MachineApplicable, ); } diff --git a/clippy_lints/src/loops/needless_range_loop.rs b/clippy_lints/src/loops/needless_range_loop.rs index 20dd5a311..745f070a5 100644 --- a/clippy_lints/src/loops/needless_range_loop.rs +++ b/clippy_lints/src/loops/needless_range_loop.rs @@ -241,7 +241,7 @@ struct VarVisitor<'a, 'tcx> { prefer_mutable: bool, } -impl<'a, 'tcx> VarVisitor<'a, 'tcx> { +impl<'tcx> VarVisitor<'_, 'tcx> { fn check(&mut self, idx: &'tcx Expr<'_>, seqexpr: &'tcx Expr<'_>, expr: &'tcx Expr<'_>) -> bool { if let ExprKind::Path(ref seqpath) = seqexpr.kind // the indexed container is referenced by a name @@ -292,7 +292,7 @@ impl<'a, 'tcx> VarVisitor<'a, 'tcx> { } } -impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for VarVisitor<'_, 'tcx> { fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { if let ExprKind::MethodCall(meth, args_0, [args_1, ..], _) = &expr.kind // a range index op diff --git a/clippy_lints/src/loops/same_item_push.rs b/clippy_lints/src/loops/same_item_push.rs index 5662d3013..f8659897f 100644 --- a/clippy_lints/src/loops/same_item_push.rs +++ b/clippy_lints/src/loops/same_item_push.rs @@ -123,7 +123,7 @@ impl<'a, 'tcx> SameItemPushVisitor<'a, 'tcx> { } } -impl<'a, 'tcx> Visitor<'tcx> for SameItemPushVisitor<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for SameItemPushVisitor<'_, 'tcx> { fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { match &expr.kind { // Non-determinism may occur ... don't give a lint diff --git a/clippy_lints/src/loops/utils.rs b/clippy_lints/src/loops/utils.rs index 9a89a41d2..c4c504e1a 100644 --- a/clippy_lints/src/loops/utils.rs +++ b/clippy_lints/src/loops/utils.rs @@ -44,7 +44,7 @@ impl<'a, 'tcx> IncrementVisitor<'a, 'tcx> { } } -impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for IncrementVisitor<'_, 'tcx> { fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { // If node is a variable if let Some(def_id) = path_to_local(expr) { @@ -138,7 +138,7 @@ impl<'a, 'tcx> InitializeVisitor<'a, 'tcx> { } } -impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for InitializeVisitor<'_, 'tcx> { type NestedFilter = nested_filter::OnlyBodies; fn visit_local(&mut self, l: &'tcx LetStmt<'_>) { diff --git a/clippy_lints/src/loops/while_immutable_condition.rs b/clippy_lints/src/loops/while_immutable_condition.rs index eab096e9a..1a1cde3c5 100644 --- a/clippy_lints/src/loops/while_immutable_condition.rs +++ b/clippy_lints/src/loops/while_immutable_condition.rs @@ -84,7 +84,7 @@ struct VarCollectorVisitor<'a, 'tcx> { skip: bool, } -impl<'a, 'tcx> VarCollectorVisitor<'a, 'tcx> { +impl<'tcx> VarCollectorVisitor<'_, 'tcx> { fn insert_def_id(&mut self, ex: &'tcx Expr<'_>) { if let ExprKind::Path(ref qpath) = ex.kind && let QPath::Resolved(None, _) = *qpath @@ -103,7 +103,7 @@ impl<'a, 'tcx> VarCollectorVisitor<'a, 'tcx> { } } -impl<'a, 'tcx> Visitor<'tcx> for VarCollectorVisitor<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for VarCollectorVisitor<'_, 'tcx> { fn visit_expr(&mut self, ex: &'tcx Expr<'_>) { match ex.kind { ExprKind::Path(_) => self.insert_def_id(ex), diff --git a/clippy_lints/src/loops/while_let_on_iterator.rs b/clippy_lints/src/loops/while_let_on_iterator.rs index 7d9fbaf3c..744675226 100644 --- a/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/clippy_lints/src/loops/while_let_on_iterator.rs @@ -283,7 +283,7 @@ fn needs_mutable_borrow(cx: &LateContext<'_>, iter_expr: &IterExpr, loop_expr: & found_local: bool, used_after: bool, } - impl<'a, 'b, 'tcx> Visitor<'tcx> for NestedLoopVisitor<'a, 'b, 'tcx> { + impl<'tcx> Visitor<'tcx> for NestedLoopVisitor<'_, '_, 'tcx> { type NestedFilter = OnlyBodies; fn nested_visit_map(&mut self) -> Self::Map { self.cx.tcx.hir() diff --git a/clippy_lints/src/macro_metavars_in_unsafe.rs b/clippy_lints/src/macro_metavars_in_unsafe.rs index ccc554042..312bcb55a 100644 --- a/clippy_lints/src/macro_metavars_in_unsafe.rs +++ b/clippy_lints/src/macro_metavars_in_unsafe.rs @@ -149,7 +149,7 @@ fn is_public_macro(cx: &LateContext<'_>, def_id: LocalDefId) -> bool { && !cx.tcx.is_doc_hidden(def_id) } -impl<'a, 'tcx> Visitor<'tcx> for BodyVisitor<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for BodyVisitor<'_, 'tcx> { fn visit_stmt(&mut self, s: &'tcx Stmt<'tcx>) { let from_expn = s.span.from_expansion(); if from_expn { diff --git a/clippy_lints/src/macro_use.rs b/clippy_lints/src/macro_use.rs index bd6b3f1a4..50680331f 100644 --- a/clippy_lints/src/macro_use.rs +++ b/clippy_lints/src/macro_use.rs @@ -81,7 +81,7 @@ impl MacroUseImports { } } -impl<'tcx> LateLintPass<'tcx> for MacroUseImports { +impl LateLintPass<'_> for MacroUseImports { fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { if cx.sess().opts.edition >= Edition::Edition2018 && let hir::ItemKind::Use(path, _kind) = &item.kind diff --git a/clippy_lints/src/manual_clamp.rs b/clippy_lints/src/manual_clamp.rs index 788649fd4..fd66cacdf 100644 --- a/clippy_lints/src/manual_clamp.rs +++ b/clippy_lints/src/manual_clamp.rs @@ -741,7 +741,7 @@ enum MaybeBorrowedStmtKind<'a> { Owned(StmtKind<'a>), } -impl<'a> Clone for MaybeBorrowedStmtKind<'a> { +impl Clone for MaybeBorrowedStmtKind<'_> { fn clone(&self) -> Self { match self { Self::Borrowed(t) => Self::Borrowed(t), diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index 9aceca66b..828c5a3f6 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -203,7 +203,7 @@ fn find_stripping<'tcx>( results: Vec, } - impl<'a, 'tcx> Visitor<'tcx> for StrippingFinder<'a, 'tcx> { + impl<'tcx> Visitor<'tcx> for StrippingFinder<'_, 'tcx> { fn visit_expr(&mut self, ex: &'tcx Expr<'_>) { if is_ref_str(self.cx, ex) && let unref = peel_ref(ex) diff --git a/clippy_lints/src/map_unit_fn.rs b/clippy_lints/src/map_unit_fn.rs index a97dbbbc3..3221a04d2 100644 --- a/clippy_lints/src/map_unit_fn.rs +++ b/clippy_lints/src/map_unit_fn.rs @@ -251,7 +251,7 @@ fn lint_map_unit_fn( } } -impl<'tcx> LateLintPass<'tcx> for MapUnit { +impl LateLintPass<'_> for MapUnit { fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &hir::Stmt<'_>) { if let hir::StmtKind::Semi(expr) = stmt.kind && !stmt.span.from_expansion() diff --git a/clippy_lints/src/matches/match_str_case_mismatch.rs b/clippy_lints/src/matches/match_str_case_mismatch.rs index 40518ce2c..463aa602b 100644 --- a/clippy_lints/src/matches/match_str_case_mismatch.rs +++ b/clippy_lints/src/matches/match_str_case_mismatch.rs @@ -40,7 +40,7 @@ struct MatchExprVisitor<'a, 'tcx> { case_method: Option, } -impl<'a, 'tcx> Visitor<'tcx> for MatchExprVisitor<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for MatchExprVisitor<'_, 'tcx> { fn visit_expr(&mut self, ex: &'tcx Expr<'_>) { match ex.kind { ExprKind::MethodCall(segment, receiver, [], _) if self.case_altered(segment.ident.as_str(), receiver) => {}, @@ -49,7 +49,7 @@ impl<'a, 'tcx> Visitor<'tcx> for MatchExprVisitor<'a, 'tcx> { } } -impl<'a, 'tcx> MatchExprVisitor<'a, 'tcx> { +impl MatchExprVisitor<'_, '_> { fn case_altered(&mut self, segment_ident: &str, receiver: &Expr<'_>) -> bool { if let Some(case_method) = get_case_method(segment_ident) { let ty = self.cx.typeck_results().expr_ty(receiver).peel_refs(); diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index 686fc4a0f..28adcc2f2 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -1045,7 +1045,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { if !from_expansion { // These don't depend on a relationship between multiple arms match_wild_err_arm::check(cx, ex, arms); - wild_in_or_pats::check(cx, arms); + wild_in_or_pats::check(cx, ex, arms); } if let MatchSource::TryDesugar(_) = source { diff --git a/clippy_lints/src/matches/overlapping_arms.rs b/clippy_lints/src/matches/overlapping_arms.rs index 6a4c553ce..856311899 100644 --- a/clippy_lints/src/matches/overlapping_arms.rs +++ b/clippy_lints/src/matches/overlapping_arms.rs @@ -96,13 +96,13 @@ where #[derive(Copy, Clone, Debug, Eq, PartialEq)] struct RangeBound<'a, T>(T, BoundKind, &'a SpannedRange); - impl<'a, T: Copy + Ord> PartialOrd for RangeBound<'a, T> { + impl PartialOrd for RangeBound<'_, T> { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } - impl<'a, T: Copy + Ord> Ord for RangeBound<'a, T> { + impl Ord for RangeBound<'_, T> { fn cmp(&self, RangeBound(other_value, other_kind, _): &Self) -> Ordering { let RangeBound(self_value, self_kind, _) = *self; (self_value, self_kind).cmp(&(*other_value, *other_kind)) diff --git a/clippy_lints/src/matches/significant_drop_in_scrutinee.rs b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs index 537f7272f..7372f52e1 100644 --- a/clippy_lints/src/matches/significant_drop_in_scrutinee.rs +++ b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs @@ -424,7 +424,7 @@ fn ty_has_erased_regions(ty: Ty<'_>) -> bool { ty.visit_with(&mut V).is_break() } -impl<'a, 'tcx> Visitor<'tcx> for SigDropHelper<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for SigDropHelper<'_, 'tcx> { fn visit_expr(&mut self, ex: &'tcx Expr<'_>) { // We've emitted a lint on some neighborhood expression. That lint will suggest to move out the // _parent_ expression (not the expression itself). Since we decide to move out the parent @@ -495,7 +495,7 @@ fn has_significant_drop_in_arms<'tcx>(cx: &LateContext<'tcx>, arms: &[&'tcx Expr helper.found_sig_drop_spans } -impl<'a, 'tcx> Visitor<'tcx> for ArmSigDropHelper<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for ArmSigDropHelper<'_, 'tcx> { fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { if self.sig_drop_checker.is_sig_drop_expr(ex) { self.found_sig_drop_spans.insert(ex.span); diff --git a/clippy_lints/src/matches/wild_in_or_pats.rs b/clippy_lints/src/matches/wild_in_or_pats.rs index 459513e65..390ba889f 100644 --- a/clippy_lints/src/matches/wild_in_or_pats.rs +++ b/clippy_lints/src/matches/wild_in_or_pats.rs @@ -1,11 +1,19 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::is_wild; -use rustc_hir::{Arm, PatKind}; +use clippy_utils::{has_non_exhaustive_attr, is_wild}; +use rustc_hir::{Arm, Expr, PatKind}; use rustc_lint::LateContext; +use rustc_middle::ty; use super::WILDCARD_IN_OR_PATTERNS; -pub(crate) fn check(cx: &LateContext<'_>, arms: &[Arm<'_>]) { +pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arms: &[Arm<'_>]) { + // first check if we are matching on an enum that has the non_exhaustive attribute + let ty = cx.typeck_results().expr_ty(expr).peel_refs(); + if let ty::Adt(adt_def, _) = ty.kind() + && has_non_exhaustive_attr(cx.tcx, *adt_def) + { + return; + }; for arm in arms { if let PatKind::Or(fields) = arm.pat.kind { // look for multiple fields in this arm that contains at least one Wild pattern diff --git a/clippy_lints/src/methods/mut_mutex_lock.rs b/clippy_lints/src/methods/mut_mutex_lock.rs index 83e8370f9..320523ace 100644 --- a/clippy_lints/src/methods/mut_mutex_lock.rs +++ b/clippy_lints/src/methods/mut_mutex_lock.rs @@ -1,17 +1,17 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::expr_custom_deref_adjustment; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable}; use rustc_errors::Applicability; use rustc_hir::{Expr, Mutability}; use rustc_lint::LateContext; -use rustc_middle::ty; use rustc_span::{Span, sym}; use super::MUT_MUTEX_LOCK; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'tcx>, recv: &'tcx Expr<'tcx>, name_span: Span) { if matches!(expr_custom_deref_adjustment(cx, recv), None | Some(Mutability::Mut)) - && let ty::Ref(_, _, Mutability::Mut) = cx.typeck_results().expr_ty(recv).kind() + && let (_, ref_depth, Mutability::Mut) = peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(recv)) + && ref_depth >= 1 && let Some(method_id) = cx.typeck_results().type_dependent_def_id(ex.hir_id) && let Some(impl_id) = cx.tcx.impl_of_method(method_id) && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::Mutex) diff --git a/clippy_lints/src/methods/needless_collect.rs b/clippy_lints/src/methods/needless_collect.rs index 421c7a5e0..c58e27e37 100644 --- a/clippy_lints/src/methods/needless_collect.rs +++ b/clippy_lints/src/methods/needless_collect.rs @@ -441,7 +441,7 @@ struct UsedCountVisitor<'a, 'tcx> { count: usize, } -impl<'a, 'tcx> Visitor<'tcx> for UsedCountVisitor<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for UsedCountVisitor<'_, 'tcx> { type NestedFilter = nested_filter::OnlyBodies; fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { diff --git a/clippy_lints/src/methods/option_map_unwrap_or.rs b/clippy_lints/src/methods/option_map_unwrap_or.rs index b160ab6de..528e2204c 100644 --- a/clippy_lints/src/methods/option_map_unwrap_or.rs +++ b/clippy_lints/src/methods/option_map_unwrap_or.rs @@ -130,7 +130,7 @@ struct UnwrapVisitor<'a, 'tcx> { identifiers: FxHashSet, } -impl<'a, 'tcx> Visitor<'tcx> for UnwrapVisitor<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for UnwrapVisitor<'_, 'tcx> { type NestedFilter = nested_filter::All; fn visit_path(&mut self, path: &Path<'tcx>, _: HirId) { @@ -154,7 +154,7 @@ struct ReferenceVisitor<'a, 'tcx> { unwrap_or_span: Span, } -impl<'a, 'tcx> Visitor<'tcx> for ReferenceVisitor<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for ReferenceVisitor<'_, 'tcx> { type NestedFilter = nested_filter::All; type Result = ControlFlow<()>; fn visit_expr(&mut self, expr: &'tcx rustc_hir::Expr<'_>) -> ControlFlow<()> { diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index c60a83943..b971f60d4 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -77,7 +77,7 @@ pub(super) fn check<'tcx>( let Some(suggested_method_def_id) = receiver_ty.ty_adt_def().and_then(|adt_def| { cx.tcx .inherent_impls(adt_def.did()) - .into_iter() + .iter() .flat_map(|impl_id| cx.tcx.associated_items(impl_id).filter_by_name_unhygienic(sugg)) .find_map(|assoc| { if assoc.fn_has_self_parameter diff --git a/clippy_lints/src/methods/utils.rs b/clippy_lints/src/methods/utils.rs index 4e33dc1df..cf0ee569f 100644 --- a/clippy_lints/src/methods/utils.rs +++ b/clippy_lints/src/methods/utils.rs @@ -86,7 +86,7 @@ struct CloneOrCopyVisitor<'cx, 'tcx> { references_to_binding: Vec<(Span, String)>, } -impl<'cx, 'tcx> Visitor<'tcx> for CloneOrCopyVisitor<'cx, 'tcx> { +impl<'tcx> Visitor<'tcx> for CloneOrCopyVisitor<'_, 'tcx> { type NestedFilter = nested_filter::OnlyBodies; fn nested_visit_map(&mut self) -> Self::Map { @@ -123,7 +123,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for CloneOrCopyVisitor<'cx, 'tcx> { } } -impl<'cx, 'tcx> CloneOrCopyVisitor<'cx, 'tcx> { +impl<'tcx> CloneOrCopyVisitor<'_, 'tcx> { fn is_binding(&self, expr: &Expr<'tcx>) -> bool { self.binding_hir_ids .iter() diff --git a/clippy_lints/src/mixed_read_write_in_expression.rs b/clippy_lints/src/mixed_read_write_in_expression.rs index d333b71ed..a7452c8a3 100644 --- a/clippy_lints/src/mixed_read_write_in_expression.rs +++ b/clippy_lints/src/mixed_read_write_in_expression.rs @@ -116,7 +116,7 @@ struct DivergenceVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, } -impl<'a, 'tcx> DivergenceVisitor<'a, 'tcx> { +impl<'tcx> DivergenceVisitor<'_, 'tcx> { fn maybe_walk_expr(&mut self, e: &'tcx Expr<'_>) { match e.kind { ExprKind::Closure(..) | ExprKind::If(..) | ExprKind::Loop(..) => {}, @@ -148,7 +148,7 @@ fn stmt_might_diverge(stmt: &Stmt<'_>) -> bool { !matches!(stmt.kind, StmtKind::Item(..)) } -impl<'a, 'tcx> Visitor<'tcx> for DivergenceVisitor<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for DivergenceVisitor<'_, 'tcx> { fn visit_expr(&mut self, e: &'tcx Expr<'_>) { match e.kind { // fix #10776 @@ -321,7 +321,7 @@ struct ReadVisitor<'a, 'tcx> { last_expr: &'tcx Expr<'tcx>, } -impl<'a, 'tcx> Visitor<'tcx> for ReadVisitor<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for ReadVisitor<'_, 'tcx> { fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { if expr.hir_id == self.last_expr.hir_id { return; diff --git a/clippy_lints/src/mut_mut.rs b/clippy_lints/src/mut_mut.rs index 60372121a..6cddd7ea8 100644 --- a/clippy_lints/src/mut_mut.rs +++ b/clippy_lints/src/mut_mut.rs @@ -55,7 +55,7 @@ pub struct MutVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, } -impl<'a, 'tcx> intravisit::Visitor<'tcx> for MutVisitor<'a, 'tcx> { +impl<'tcx> intravisit::Visitor<'tcx> for MutVisitor<'_, 'tcx> { fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { if in_external_macro(self.cx.sess(), expr.span) { return; diff --git a/clippy_lints/src/mutable_debug_assertion.rs b/clippy_lints/src/mutable_debug_assertion.rs index 785bf70a3..152635a5c 100644 --- a/clippy_lints/src/mutable_debug_assertion.rs +++ b/clippy_lints/src/mutable_debug_assertion.rs @@ -87,7 +87,7 @@ impl<'a, 'tcx> MutArgVisitor<'a, 'tcx> { } } -impl<'a, 'tcx> Visitor<'tcx> for MutArgVisitor<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for MutArgVisitor<'_, 'tcx> { type NestedFilter = nested_filter::OnlyBodies; fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { diff --git a/clippy_lints/src/needless_for_each.rs b/clippy_lints/src/needless_for_each.rs index b54eb164e..93e20f37e 100644 --- a/clippy_lints/src/needless_for_each.rs +++ b/clippy_lints/src/needless_for_each.rs @@ -133,7 +133,7 @@ struct RetCollector { loop_depth: u16, } -impl<'tcx> Visitor<'tcx> for RetCollector { +impl Visitor<'_> for RetCollector { fn visit_expr(&mut self, expr: &Expr<'_>) { match expr.kind { ExprKind::Ret(..) => { diff --git a/clippy_lints/src/needless_pass_by_ref_mut.rs b/clippy_lints/src/needless_pass_by_ref_mut.rs index 19cbf5959..c2facb2fc 100644 --- a/clippy_lints/src/needless_pass_by_ref_mut.rs +++ b/clippy_lints/src/needless_pass_by_ref_mut.rs @@ -311,7 +311,7 @@ struct MutablyUsedVariablesCtxt<'tcx> { tcx: TyCtxt<'tcx>, } -impl<'tcx> MutablyUsedVariablesCtxt<'tcx> { +impl MutablyUsedVariablesCtxt<'_> { fn add_mutably_used_var(&mut self, used_id: HirId) { self.mutably_used_vars.insert(used_id); } diff --git a/clippy_lints/src/non_expressive_names.rs b/clippy_lints/src/non_expressive_names.rs index d85032e9e..2fee1c72a 100644 --- a/clippy_lints/src/non_expressive_names.rs +++ b/clippy_lints/src/non_expressive_names.rs @@ -104,7 +104,7 @@ struct SimilarNamesLocalVisitor<'a, 'tcx> { single_char_names: Vec>, } -impl<'a, 'tcx> SimilarNamesLocalVisitor<'a, 'tcx> { +impl SimilarNamesLocalVisitor<'_, '_> { fn check_single_char_names(&self) { if self.single_char_names.last().map(Vec::len) == Some(0) { return; @@ -152,7 +152,7 @@ fn chars_are_similar(a: char, b: char) -> bool { struct SimilarNamesNameVisitor<'a, 'tcx, 'b>(&'b mut SimilarNamesLocalVisitor<'a, 'tcx>); -impl<'a, 'tcx, 'b> Visitor<'tcx> for SimilarNamesNameVisitor<'a, 'tcx, 'b> { +impl<'tcx> Visitor<'tcx> for SimilarNamesNameVisitor<'_, 'tcx, '_> { fn visit_pat(&mut self, pat: &'tcx Pat) { match pat.kind { PatKind::Ident(_, ident, _) => { @@ -189,7 +189,7 @@ fn allowed_to_be_similar(interned_name: &str, list: &[&str]) -> bool { .any(|&name| interned_name.starts_with(name) || interned_name.ends_with(name)) } -impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> { +impl SimilarNamesNameVisitor<'_, '_, '_> { fn check_short_ident(&mut self, ident: Ident) { // Ignore shadowing if self @@ -329,7 +329,7 @@ impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> { } } -impl<'a, 'b> SimilarNamesLocalVisitor<'a, 'b> { +impl SimilarNamesLocalVisitor<'_, '_> { /// ensure scoping rules work fn apply Fn(&'c mut Self)>(&mut self, f: F) { let n = self.names.len(); @@ -340,7 +340,7 @@ impl<'a, 'b> SimilarNamesLocalVisitor<'a, 'b> { } } -impl<'a, 'tcx> Visitor<'tcx> for SimilarNamesLocalVisitor<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for SimilarNamesLocalVisitor<'_, 'tcx> { fn visit_local(&mut self, local: &'tcx Local) { if let Some((init, els)) = &local.kind.init_else_opt() { self.apply(|this| walk_expr(this, init)); diff --git a/clippy_lints/src/non_send_fields_in_send_ty.rs b/clippy_lints/src/non_send_fields_in_send_ty.rs index a60988ac5..793eb5d94 100644 --- a/clippy_lints/src/non_send_fields_in_send_ty.rs +++ b/clippy_lints/src/non_send_fields_in_send_ty.rs @@ -159,7 +159,7 @@ struct NonSendField<'tcx> { generic_params: Vec>, } -impl<'tcx> NonSendField<'tcx> { +impl NonSendField<'_> { fn generic_params_string(&self) -> String { self.generic_params .iter() diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs index 75d8c09f2..1bddfab39 100644 --- a/clippy_lints/src/pass_by_ref_or_value.rs +++ b/clippy_lints/src/pass_by_ref_or_value.rs @@ -110,7 +110,7 @@ pub struct PassByRefOrValue { avoid_breaking_exported_api: bool, } -impl<'tcx> PassByRefOrValue { +impl PassByRefOrValue { pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { let ref_min_size = conf.trivial_copy_size_limit.unwrap_or_else(|| { let bit_width = u64::from(tcx.sess.target.pointer_width); @@ -130,7 +130,7 @@ impl<'tcx> PassByRefOrValue { } } - fn check_poly_fn(&mut self, cx: &LateContext<'tcx>, def_id: LocalDefId, decl: &FnDecl<'_>, span: Option) { + fn check_poly_fn(&mut self, cx: &LateContext<'_>, def_id: LocalDefId, decl: &FnDecl<'_>, span: Option) { if self.avoid_breaking_exported_api && cx.effective_visibilities.is_exported(def_id) { return; } diff --git a/clippy_lints/src/pathbuf_init_then_push.rs b/clippy_lints/src/pathbuf_init_then_push.rs index 1b9a5a443..9f84686a0 100644 --- a/clippy_lints/src/pathbuf_init_then_push.rs +++ b/clippy_lints/src/pathbuf_init_then_push.rs @@ -60,7 +60,7 @@ struct PathbufPushSearcher<'tcx> { err_span: Span, } -impl<'tcx> PathbufPushSearcher<'tcx> { +impl PathbufPushSearcher<'_> { /// Try to generate a suggestion with `PathBuf::from`. /// Returns `None` if the suggestion would be invalid. fn gen_pathbuf_from(&self, cx: &LateContext<'_>) -> Option { diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 807636bb6..bb8a9b6fc 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -271,14 +271,18 @@ fn check_invalid_ptr_usage<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { && let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id() && let Some(name) = cx.tcx.get_diagnostic_name(fun_def_id) { + // TODO: `ptr_slice_from_raw_parts` and its mutable variant should probably still be linted + // conditionally based on how the return value is used, but not universally like the other + // functions since there are valid uses for null slice pointers. + // + // See: https://github.com/rust-lang/rust-clippy/pull/13452/files#r1773772034 + // `arg` positions where null would cause U.B. let arg_indices: &[_] = match name { sym::ptr_read | sym::ptr_read_unaligned | sym::ptr_read_volatile | sym::ptr_replace - | sym::ptr_slice_from_raw_parts - | sym::ptr_slice_from_raw_parts_mut | sym::ptr_write | sym::ptr_write_bytes | sym::ptr_write_unaligned diff --git a/clippy_lints/src/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs index 6930a01d4..41a44de53 100644 --- a/clippy_lints/src/redundant_closure_call.rs +++ b/clippy_lints/src/redundant_closure_call.rs @@ -228,7 +228,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { path: &'tcx hir::Path<'tcx>, count: usize, } - impl<'a, 'tcx> Visitor<'tcx> for ClosureUsageCount<'a, 'tcx> { + impl<'tcx> Visitor<'tcx> for ClosureUsageCount<'_, 'tcx> { type NestedFilter = nested_filter::OnlyBodies; fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 3754fddde..1f223048c 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -140,7 +140,7 @@ enum RetReplacement<'tcx> { Expr(Cow<'tcx, str>, Applicability), } -impl<'tcx> RetReplacement<'tcx> { +impl RetReplacement<'_> { fn sugg_help(&self) -> &'static str { match self { Self::Empty | Self::Expr(..) => "remove `return`", @@ -158,7 +158,7 @@ impl<'tcx> RetReplacement<'tcx> { } } -impl<'tcx> Display for RetReplacement<'tcx> { +impl Display for RetReplacement<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Empty => write!(f, ""), @@ -421,7 +421,7 @@ fn check_final_expr<'tcx>( if matches!(Level::from_attr(attr), Some(Level::Expect(_))) && let metas = attr.meta_item_list() && let Some(lst) = metas - && let [NestedMetaItem::MetaItem(meta_item)] = lst.as_slice() + && let [NestedMetaItem::MetaItem(meta_item), ..] = lst.as_slice() && let [tool, lint_name] = meta_item.path.segments.as_slice() && tool.ident.name == sym::clippy && matches!( diff --git a/clippy_lints/src/significant_drop_tightening.rs b/clippy_lints/src/significant_drop_tightening.rs index d1114cb29..0eece9221 100644 --- a/clippy_lints/src/significant_drop_tightening.rs +++ b/clippy_lints/src/significant_drop_tightening.rs @@ -249,7 +249,7 @@ impl<'ap, 'lc, 'others, 'stmt, 'tcx> StmtsChecker<'ap, 'lc, 'others, 'stmt, 'tcx } } -impl<'ap, 'lc, 'others, 'stmt, 'tcx> Visitor<'tcx> for StmtsChecker<'ap, 'lc, 'others, 'stmt, 'tcx> { +impl<'tcx> Visitor<'tcx> for StmtsChecker<'_, '_, '_, '_, 'tcx> { fn visit_block(&mut self, block: &'tcx hir::Block<'tcx>) { self.ap.curr_block_hir_id = block.hir_id; self.ap.curr_block_span = block.span; diff --git a/clippy_lints/src/single_component_path_imports.rs b/clippy_lints/src/single_component_path_imports.rs index c986c3e8a..9fdee8543 100644 --- a/clippy_lints/src/single_component_path_imports.rs +++ b/clippy_lints/src/single_component_path_imports.rs @@ -102,7 +102,7 @@ struct ImportUsageVisitor { imports_referenced_with_self: Vec, } -impl<'tcx> Visitor<'tcx> for ImportUsageVisitor { +impl Visitor<'_> for ImportUsageVisitor { fn visit_expr(&mut self, expr: &Expr) { if let ExprKind::Path(_, path) = &expr.kind && path.segments.len() > 1 diff --git a/clippy_lints/src/slow_vector_initialization.rs b/clippy_lints/src/slow_vector_initialization.rs index 5129bbf26..fc799cad6 100644 --- a/clippy_lints/src/slow_vector_initialization.rs +++ b/clippy_lints/src/slow_vector_initialization.rs @@ -229,7 +229,7 @@ struct VectorInitializationVisitor<'a, 'tcx> { initialization_found: bool, } -impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> { +impl<'tcx> VectorInitializationVisitor<'_, 'tcx> { /// Checks if the given expression is extending a vector with `repeat(0).take(..)` fn search_slow_extend_filling(&mut self, expr: &'tcx Expr<'_>) { if self.initialization_found @@ -299,7 +299,7 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> { } } -impl<'a, 'tcx> Visitor<'tcx> for VectorInitializationVisitor<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for VectorInitializationVisitor<'_, 'tcx> { fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) { if self.initialization_found { match stmt.kind { diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index e05fa4095..a3145c464 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -332,7 +332,7 @@ struct IndexBinding<'a, 'tcx> { applicability: &'a mut Applicability, } -impl<'a, 'tcx> IndexBinding<'a, 'tcx> { +impl<'tcx> IndexBinding<'_, 'tcx> { fn snippet_index_bindings(&mut self, exprs: &[&'tcx Expr<'tcx>]) -> String { let mut bindings = FxHashSet::default(); for expr in exprs { diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs index 104be63bb..c7c837de5 100644 --- a/clippy_lints/src/unnested_or_patterns.rs +++ b/clippy_lints/src/unnested_or_patterns.rs @@ -275,12 +275,15 @@ fn transform_with_focus_on_idx(alternatives: &mut ThinVec>, focus_idx: us |k, ps1, idx| matches!( k, TupleStruct(qself2, path2, ps2) - if eq_maybe_qself(qself1, qself2) && eq_path(path1, path2) && eq_pre_post(ps1, ps2, idx) + if eq_maybe_qself(qself1.as_ref(), qself2.as_ref()) + && eq_path(path1, path2) && eq_pre_post(ps1, ps2, idx) ), |k| always_pat!(k, TupleStruct(_, _, ps) => ps), ), // Transform a record pattern `S { fp_0, ..., fp_n }`. - Struct(qself1, path1, fps1, rest1) => extend_with_struct_pat(qself1, path1, fps1, *rest1, start, alternatives), + Struct(qself1, path1, fps1, rest1) => { + extend_with_struct_pat(qself1.as_ref(), path1, fps1, *rest1, start, alternatives) + }, }; alternatives[focus_idx].kind = focus_kind; @@ -292,7 +295,7 @@ fn transform_with_focus_on_idx(alternatives: &mut ThinVec>, focus_idx: us /// So when we fixate on some `ident_k: pat_k`, we try to find `ident_k` in the other pattern /// and check that all `fp_i` where `i ∈ ((0...n) \ k)` between two patterns are equal. fn extend_with_struct_pat( - qself1: &Option>, + qself1: Option<&P>, path1: &ast::Path, fps1: &mut [ast::PatField], rest1: ast::PatFieldsRest, @@ -307,7 +310,7 @@ fn extend_with_struct_pat( |k| { matches!(k, Struct(qself2, path2, fps2, rest2) if rest1 == *rest2 // If one struct pattern has `..` so must the other. - && eq_maybe_qself(qself1, qself2) + && eq_maybe_qself(qself1, qself2.as_ref()) && eq_path(path1, path2) && fps1.len() == fps2.len() && fps1.iter().enumerate().all(|(idx_1, fp1)| { diff --git a/clippy_lints/src/unused_async.rs b/clippy_lints/src/unused_async.rs index a1f08cf66..c899b1868 100644 --- a/clippy_lints/src/unused_async.rs +++ b/clippy_lints/src/unused_async.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::is_def_id_trait_method; use rustc_hir::def::DefKind; use rustc_hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn}; -use rustc_hir::{Body, Expr, ExprKind, FnDecl, Node, YieldSource}; +use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId, Node, YieldSource}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; use rustc_session::impl_lint_pass; @@ -67,7 +67,7 @@ struct AsyncFnVisitor<'a, 'tcx> { async_depth: usize, } -impl<'a, 'tcx> Visitor<'tcx> for AsyncFnVisitor<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for AsyncFnVisitor<'_, 'tcx> { type NestedFilter = nested_filter::OnlyBodies; fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { @@ -137,17 +137,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync { } } - fn check_path(&mut self, cx: &LateContext<'tcx>, path: &rustc_hir::Path<'tcx>, hir_id: rustc_hir::HirId) { - fn is_node_func_call(node: Node<'_>, expected_receiver: Span) -> bool { - matches!( - node, - Node::Expr(Expr { - kind: ExprKind::Call(Expr { span, .. }, _) | ExprKind::MethodCall(_, Expr { span, .. }, ..), - .. - }) if *span == expected_receiver - ) - } - + fn check_path(&mut self, cx: &LateContext<'tcx>, path: &rustc_hir::Path<'tcx>, hir_id: HirId) { // Find paths to local async functions that aren't immediately called. // E.g. `async fn f() {}; let x = f;` // Depending on how `x` is used, f's asyncness might be required despite not having any `await` @@ -156,7 +146,14 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync { && let Some(local_def_id) = def_id.as_local() && cx.tcx.def_kind(def_id) == DefKind::Fn && cx.tcx.asyncness(def_id).is_async() - && !is_node_func_call(cx.tcx.parent_hir_node(hir_id), path.span) + && let parent = cx.tcx.parent_hir_node(hir_id) + && !matches!( + parent, + Node::Expr(Expr { + kind: ExprKind::Call(Expr { span, .. }, _), + .. + }) if *span == path.span + ) { self.async_fns_as_value.insert(local_def_id); } diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index 596f0fd9c..6fe660b6a 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -235,7 +235,7 @@ impl<'tcx> Delegate<'tcx> for MutationVisitor<'tcx> { fn fake_read(&mut self, _: &PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} } -impl<'a, 'tcx> UnwrappableVariablesVisitor<'a, 'tcx> { +impl<'tcx> UnwrappableVariablesVisitor<'_, 'tcx> { fn visit_branch( &mut self, if_expr: &'tcx Expr<'_>, @@ -288,7 +288,7 @@ fn consume_option_as_ref<'tcx>(expr: &'tcx Expr<'tcx>) -> (&'tcx Expr<'tcx>, Opt } } -impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'_, 'tcx> { type NestedFilter = nested_filter::OnlyBodies; fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index e340b419b..08449de79 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -280,7 +280,7 @@ struct SkipTyCollector { types_to_skip: Vec, } -impl<'tcx> Visitor<'tcx> for SkipTyCollector { +impl Visitor<'_> for SkipTyCollector { fn visit_infer(&mut self, inf: &hir::InferArg) { self.types_to_skip.push(inf.hir_id); diff --git a/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs b/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs index 7c2e23995..9e400d239 100644 --- a/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs +++ b/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs @@ -80,7 +80,7 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { && let ty = cx.tcx.type_of(item_def_id).instantiate_identity() && match_type(cx, ty, &paths::SYMBOL) && let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id) - && let Ok(value) = value.to_u32() + && let Some(value) = value.to_u32().discard_err() { self.symbol_map.insert(value, item_def_id); } diff --git a/clippy_lints/src/utils/internal_lints/invalid_paths.rs b/clippy_lints/src/utils/internal_lints/invalid_paths.rs index d444ad450..96397375d 100644 --- a/clippy_lints/src/utils/internal_lints/invalid_paths.rs +++ b/clippy_lints/src/utils/internal_lints/invalid_paths.rs @@ -72,8 +72,7 @@ pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool { SimplifiedType::Bool, ] .iter() - .flat_map(|&ty| cx.tcx.incoherent_impls(ty).into_iter()) - .flatten() + .flat_map(|&ty| cx.tcx.incoherent_impls(ty).iter()) .copied(); for item_def_id in lang_items.iter().map(|(_, def_id)| def_id).chain(incoherent_impls) { let lang_item_path = cx.get_def_path(item_def_id); diff --git a/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs b/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs index 7c45a5b2f..dd4560222 100644 --- a/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs +++ b/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs @@ -270,7 +270,7 @@ struct LintCollector<'a, 'tcx> { cx: &'a LateContext<'tcx>, } -impl<'a, 'tcx> Visitor<'tcx> for LintCollector<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for LintCollector<'_, 'tcx> { type NestedFilter = nested_filter::All; fn visit_path(&mut self, path: &Path<'_>, _: HirId) { diff --git a/clippy_lints/src/zombie_processes.rs b/clippy_lints/src/zombie_processes.rs index ba2a80ee6..8d9241cc7 100644 --- a/clippy_lints/src/zombie_processes.rs +++ b/clippy_lints/src/zombie_processes.rs @@ -6,6 +6,7 @@ use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_local}; use rustc_hir::{Expr, ExprKind, HirId, LetStmt, Node, PatKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::nested_filter; use rustc_session::declare_lint_pass; use rustc_span::sym; use std::ops::ControlFlow; @@ -118,7 +119,8 @@ enum WaitFinder<'a, 'tcx> { Found(&'a LateContext<'tcx>, HirId), } -impl<'a, 'tcx> Visitor<'tcx> for WaitFinder<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for WaitFinder<'_, 'tcx> { + type NestedFilter = nested_filter::OnlyBodies; type Result = ControlFlow; fn visit_local(&mut self, l: &'tcx LetStmt<'tcx>) -> Self::Result { @@ -204,6 +206,11 @@ impl<'a, 'tcx> Visitor<'tcx> for WaitFinder<'a, 'tcx> { walk_expr(self, ex) } + + fn nested_visit_map(&mut self) -> Self::Map { + let (Self::Found(cx, _) | Self::WalkUpTo(cx, _)) = self; + cx.tcx.hir() + } } /// This function has shared logic between the different kinds of nodes that can trigger the lint. @@ -300,7 +307,7 @@ struct ExitPointFinder<'a, 'tcx> { struct ExitCallFound; -impl<'a, 'tcx> Visitor<'tcx> for ExitPointFinder<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for ExitPointFinder<'_, 'tcx> { type Result = ControlFlow; fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) -> Self::Result { diff --git a/clippy_utils/src/ast_utils.rs b/clippy_utils/src/ast_utils.rs index 49323492e..68f74e52e 100644 --- a/clippy_utils/src/ast_utils.rs +++ b/clippy_utils/src/ast_utils.rs @@ -37,20 +37,27 @@ pub fn eq_pat(l: &Pat, r: &Pat) -> bool { (_, Paren(r)) => eq_pat(l, r), (Wild, Wild) | (Rest, Rest) => true, (Lit(l), Lit(r)) => eq_expr(l, r), - (Ident(b1, i1, s1), Ident(b2, i2, s2)) => b1 == b2 && eq_id(*i1, *i2) && both(s1, s2, |l, r| eq_pat(l, r)), + (Ident(b1, i1, s1), Ident(b2, i2, s2)) => { + b1 == b2 && eq_id(*i1, *i2) && both(s1.as_deref(), s2.as_deref(), eq_pat) + }, (Range(lf, lt, le), Range(rf, rt, re)) => { - eq_expr_opt(lf, rf) && eq_expr_opt(lt, rt) && eq_range_end(&le.node, &re.node) + eq_expr_opt(lf.as_ref(), rf.as_ref()) + && eq_expr_opt(lt.as_ref(), rt.as_ref()) + && eq_range_end(&le.node, &re.node) }, (Box(l), Box(r)) | (Ref(l, Mutability::Not), Ref(r, Mutability::Not)) | (Ref(l, Mutability::Mut), Ref(r, Mutability::Mut)) => eq_pat(l, r), (Tuple(l), Tuple(r)) | (Slice(l), Slice(r)) => over(l, r, |l, r| eq_pat(l, r)), - (Path(lq, lp), Path(rq, rp)) => both(lq, rq, eq_qself) && eq_path(lp, rp), + (Path(lq, lp), Path(rq, rp)) => both(lq.as_ref(), rq.as_ref(), eq_qself) && eq_path(lp, rp), (TupleStruct(lqself, lp, lfs), TupleStruct(rqself, rp, rfs)) => { - eq_maybe_qself(lqself, rqself) && eq_path(lp, rp) && over(lfs, rfs, |l, r| eq_pat(l, r)) + eq_maybe_qself(lqself.as_ref(), rqself.as_ref()) && eq_path(lp, rp) && over(lfs, rfs, |l, r| eq_pat(l, r)) }, (Struct(lqself, lp, lfs, lr), Struct(rqself, rp, rfs, rr)) => { - lr == rr && eq_maybe_qself(lqself, rqself) && eq_path(lp, rp) && unordered_over(lfs, rfs, eq_field_pat) + lr == rr + && eq_maybe_qself(lqself.as_ref(), rqself.as_ref()) + && eq_path(lp, rp) + && unordered_over(lfs, rfs, eq_field_pat) }, (Or(ls), Or(rs)) => unordered_over(ls, rs, |l, r| eq_pat(l, r)), (MacCall(l), MacCall(r)) => eq_mac_call(l, r), @@ -79,7 +86,7 @@ pub fn eq_qself(l: &P, r: &P) -> bool { l.position == r.position && eq_ty(&l.ty, &r.ty) } -pub fn eq_maybe_qself(l: &Option>, r: &Option>) -> bool { +pub fn eq_maybe_qself(l: Option<&P>, r: Option<&P>) -> bool { match (l, r) { (Some(l), Some(r)) => eq_qself(l, r), (None, None) => true, @@ -92,7 +99,7 @@ pub fn eq_path(l: &Path, r: &Path) -> bool { } pub fn eq_path_seg(l: &PathSegment, r: &PathSegment) -> bool { - eq_id(l.ident, r.ident) && both(&l.args, &r.args, |l, r| eq_generic_args(l, r)) + eq_id(l.ident, r.ident) && both(l.args.as_ref(), r.args.as_ref(), |l, r| eq_generic_args(l, r)) } pub fn eq_generic_args(l: &GenericArgs, r: &GenericArgs) -> bool { @@ -122,7 +129,7 @@ pub fn eq_generic_arg(l: &GenericArg, r: &GenericArg) -> bool { } } -pub fn eq_expr_opt(l: &Option>, r: &Option>) -> bool { +pub fn eq_expr_opt(l: Option<&P>, r: Option<&P>) -> bool { both(l, r, |l, r| eq_expr(l, r)) } @@ -169,8 +176,12 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool { (Lit(l), Lit(r)) => l == r, (Cast(l, lt), Cast(r, rt)) | (Type(l, lt), Type(r, rt)) => eq_expr(l, r) && eq_ty(lt, rt), (Let(lp, le, _, _), Let(rp, re, _, _)) => eq_pat(lp, rp) && eq_expr(le, re), - (If(lc, lt, le), If(rc, rt, re)) => eq_expr(lc, rc) && eq_block(lt, rt) && eq_expr_opt(le, re), - (While(lc, lt, ll), While(rc, rt, rl)) => eq_label(ll, rl) && eq_expr(lc, rc) && eq_block(lt, rt), + (If(lc, lt, le), If(rc, rt, re)) => { + eq_expr(lc, rc) && eq_block(lt, rt) && eq_expr_opt(le.as_ref(), re.as_ref()) + }, + (While(lc, lt, ll), While(rc, rt, rl)) => { + eq_label(ll.as_ref(), rl.as_ref()) && eq_expr(lc, rc) && eq_block(lt, rt) + }, ( ForLoop { pat: lp, @@ -186,13 +197,13 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool { label: rl, kind: rk, }, - ) => eq_label(ll, rl) && eq_pat(lp, rp) && eq_expr(li, ri) && eq_block(lt, rt) && lk == rk, - (Loop(lt, ll, _), Loop(rt, rl, _)) => eq_label(ll, rl) && eq_block(lt, rt), - (Block(lb, ll), Block(rb, rl)) => eq_label(ll, rl) && eq_block(lb, rb), + ) => eq_label(ll.as_ref(), rl.as_ref()) && eq_pat(lp, rp) && eq_expr(li, ri) && eq_block(lt, rt) && lk == rk, + (Loop(lt, ll, _), Loop(rt, rl, _)) => eq_label(ll.as_ref(), rl.as_ref()) && eq_block(lt, rt), + (Block(lb, ll), Block(rb, rl)) => eq_label(ll.as_ref(), rl.as_ref()) && eq_block(lb, rb), (TryBlock(l), TryBlock(r)) => eq_block(l, r), - (Yield(l), Yield(r)) | (Ret(l), Ret(r)) => eq_expr_opt(l, r), - (Break(ll, le), Break(rl, re)) => eq_label(ll, rl) && eq_expr_opt(le, re), - (Continue(ll), Continue(rl)) => eq_label(ll, rl), + (Yield(l), Yield(r)) | (Ret(l), Ret(r)) => eq_expr_opt(l.as_ref(), r.as_ref()), + (Break(ll, le), Break(rl, re)) => eq_label(ll.as_ref(), rl.as_ref()) && eq_expr_opt(le.as_ref(), re.as_ref()), + (Continue(ll), Continue(rl)) => eq_label(ll.as_ref(), rl.as_ref()), (Assign(l1, l2, _), Assign(r1, r2, _)) | (Index(l1, l2, _), Index(r1, r2, _)) => { eq_expr(l1, r1) && eq_expr(l2, r2) }, @@ -227,12 +238,14 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool { && eq_expr(le, re) }, (Gen(lc, lb, lk, _), Gen(rc, rb, rk, _)) => lc == rc && eq_block(lb, rb) && lk == rk, - (Range(lf, lt, ll), Range(rf, rt, rl)) => ll == rl && eq_expr_opt(lf, rf) && eq_expr_opt(lt, rt), + (Range(lf, lt, ll), Range(rf, rt, rl)) => { + ll == rl && eq_expr_opt(lf.as_ref(), rf.as_ref()) && eq_expr_opt(lt.as_ref(), rt.as_ref()) + }, (AddrOf(lbk, lm, le), AddrOf(rbk, rm, re)) => lbk == rbk && lm == rm && eq_expr(le, re), - (Path(lq, lp), Path(rq, rp)) => both(lq, rq, eq_qself) && eq_path(lp, rp), + (Path(lq, lp), Path(rq, rp)) => both(lq.as_ref(), rq.as_ref(), eq_qself) && eq_path(lp, rp), (MacCall(l), MacCall(r)) => eq_mac_call(l, r), (Struct(lse), Struct(rse)) => { - eq_maybe_qself(&lse.qself, &rse.qself) + eq_maybe_qself(lse.qself.as_ref(), rse.qself.as_ref()) && eq_path(&lse.path, &rse.path) && eq_struct_rest(&lse.rest, &rse.rest) && unordered_over(&lse.fields, &rse.fields, eq_field) @@ -264,12 +277,12 @@ pub fn eq_field(l: &ExprField, r: &ExprField) -> bool { pub fn eq_arm(l: &Arm, r: &Arm) -> bool { l.is_placeholder == r.is_placeholder && eq_pat(&l.pat, &r.pat) - && eq_expr_opt(&l.body, &r.body) - && eq_expr_opt(&l.guard, &r.guard) + && eq_expr_opt(l.body.as_ref(), r.body.as_ref()) + && eq_expr_opt(l.guard.as_ref(), r.guard.as_ref()) && over(&l.attrs, &r.attrs, eq_attr) } -pub fn eq_label(l: &Option