From 10cf3cb945b413c0fb81cf13c4bf4db762ba5377 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maja=20K=C4=85dzio=C5=82ka?= Date: Mon, 3 Mar 2025 12:54:26 +0100 Subject: [PATCH 01/19] Add helper methods checking for "#[non_exhaustive] that's active" A check for `#[non_exhaustive]` is often done in combination with checking whether the type is local to the crate, in a variety of ways. Create a helper method and standardize on it as the way to check for this. --- clippy_lints/src/default.rs | 2 +- clippy_lints/src/needless_update.rs | 5 +++-- clippy_lints/src/unneeded_struct_pattern.rs | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/default.rs b/clippy_lints/src/default.rs index ffdd946aadb8..886c325b355f 100644 --- a/clippy_lints/src/default.rs +++ b/clippy_lints/src/default.rs @@ -134,7 +134,7 @@ impl<'tcx> LateLintPass<'tcx> for Default { && let ty::Adt(adt, args) = *binding_type.kind() && adt.is_struct() && let variant = adt.non_enum_variant() - && (adt.did().is_local() || !variant.is_field_list_non_exhaustive()) + && !variant.field_list_has_applicable_non_exhaustive() && let module_did = cx.tcx.parent_module(stmt.hir_id) && variant .fields diff --git a/clippy_lints/src/needless_update.rs b/clippy_lints/src/needless_update.rs index 0cba72bd2c6a..cce0617ba392 100644 --- a/clippy_lints/src/needless_update.rs +++ b/clippy_lints/src/needless_update.rs @@ -54,8 +54,9 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessUpdate { if let ExprKind::Struct(_, fields, StructTailExpr::Base(base)) = expr.kind { let ty = cx.typeck_results().expr_ty(expr); if let ty::Adt(def, _) = ty.kind() { - if fields.len() == def.non_enum_variant().fields.len() - && !def.variant(0_usize.into()).is_field_list_non_exhaustive() + let variant = def.non_enum_variant(); + if fields.len() == variant.fields.len() + && !variant.is_field_list_non_exhaustive() { span_lint( cx, diff --git a/clippy_lints/src/unneeded_struct_pattern.rs b/clippy_lints/src/unneeded_struct_pattern.rs index a74eab8b6ae5..3326dea8c5df 100644 --- a/clippy_lints/src/unneeded_struct_pattern.rs +++ b/clippy_lints/src/unneeded_struct_pattern.rs @@ -51,7 +51,7 @@ impl LateLintPass<'_> for UnneededStructPattern { let variant = cx.tcx.adt_def(enum_did).variant_with_id(did); let has_only_fields_brackets = variant.ctor.is_some() && variant.fields.is_empty(); - let non_exhaustive_activated = !variant.def_id.is_local() && variant.is_field_list_non_exhaustive(); + let non_exhaustive_activated = variant.field_list_has_applicable_non_exhaustive(); if !has_only_fields_brackets || non_exhaustive_activated { return; } From 9576c712ccc22ce09be4317b90813600d2af2533 Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Thu, 13 Mar 2025 14:36:02 -0700 Subject: [PATCH 02/19] Teach rustfmt to handle postfix yield --- clippy_utils/src/ast_utils/mod.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/clippy_utils/src/ast_utils/mod.rs b/clippy_utils/src/ast_utils/mod.rs index 707312a97f3b..deda6030831e 100644 --- a/clippy_utils/src/ast_utils/mod.rs +++ b/clippy_utils/src/ast_utils/mod.rs @@ -201,7 +201,8 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool { (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.as_ref(), r.as_ref()), + (Yield(l, lk), Yield(r, rk)) => eq_expr_opt(l.as_ref(), r.as_ref()) && lk == rk, + (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, _)) => { @@ -688,7 +689,7 @@ pub fn eq_generics(l: &Generics, r: &Generics) -> bool { pub fn eq_where_predicate(l: &WherePredicate, r: &WherePredicate) -> bool { use WherePredicateKind::*; - over(&l.attrs, &r.attrs, eq_attr) + over(&l.attrs, &r.attrs, eq_attr) && match (&l.kind, &r.kind) { (BoundPredicate(l), BoundPredicate(r)) => { over(&l.bound_generic_params, &r.bound_generic_params, |l, r| { From ed1778c226591425b31f93ae01a2997fcbbcc738 Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Thu, 13 Mar 2025 16:14:31 -0700 Subject: [PATCH 03/19] Fix clippy --- clippy_lints/src/suspicious_operation_groupings.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/suspicious_operation_groupings.rs b/clippy_lints/src/suspicious_operation_groupings.rs index 0d809c17989d..206912d8de40 100644 --- a/clippy_lints/src/suspicious_operation_groupings.rs +++ b/clippy_lints/src/suspicious_operation_groupings.rs @@ -528,7 +528,7 @@ fn ident_difference_expr_with_base_location( &strip_non_ident_wrappers(left).kind, &strip_non_ident_wrappers(right).kind, ) { - (Yield(_), Yield(_)) + (Yield(_, _), Yield(_, _)) | (Try(_), Try(_)) | (Paren(_), Paren(_)) | (Repeat(_, _), Repeat(_, _)) From 6cf2d721508ab8879be65bbfc5ca74ed64c87d05 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Fri, 14 Mar 2025 20:34:43 +0300 Subject: [PATCH 04/19] expand: Leave traces when expanding `cfg_attr` attributes --- clippy_lints/src/attrs/duplicated_attributes.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/attrs/duplicated_attributes.rs b/clippy_lints/src/attrs/duplicated_attributes.rs index 2ddbc7a6a76d..5c486eb90cc2 100644 --- a/clippy_lints/src/attrs/duplicated_attributes.rs +++ b/clippy_lints/src/attrs/duplicated_attributes.rs @@ -36,7 +36,11 @@ fn check_duplicated_attr( } let Some(ident) = attr.ident() else { return }; let name = ident.name; - if name == sym::doc || name == sym::cfg_attr || name == sym::rustc_on_unimplemented || name == sym::reason { + if name == sym::doc + || name == sym::cfg_attr + || name == sym::cfg_attr_trace + || name == sym::rustc_on_unimplemented + || name == sym::reason { // FIXME: Would be nice to handle `cfg_attr` as well. Only problem is to check that cfg // conditions are the same. // `#[rustc_on_unimplemented]` contains duplicated subattributes, that's expected. From e3f1bc8b06a1fe671bb21d17328584e5fc3c871c Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Tue, 18 Mar 2025 12:19:43 -0700 Subject: [PATCH 05/19] Refactor YieldKind so postfix yield must have an expression --- clippy_lints/src/suspicious_operation_groupings.rs | 2 +- clippy_utils/src/ast_utils/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/suspicious_operation_groupings.rs b/clippy_lints/src/suspicious_operation_groupings.rs index 206912d8de40..0d809c17989d 100644 --- a/clippy_lints/src/suspicious_operation_groupings.rs +++ b/clippy_lints/src/suspicious_operation_groupings.rs @@ -528,7 +528,7 @@ fn ident_difference_expr_with_base_location( &strip_non_ident_wrappers(left).kind, &strip_non_ident_wrappers(right).kind, ) { - (Yield(_, _), Yield(_, _)) + (Yield(_), Yield(_)) | (Try(_), Try(_)) | (Paren(_), Paren(_)) | (Repeat(_, _), Repeat(_, _)) diff --git a/clippy_utils/src/ast_utils/mod.rs b/clippy_utils/src/ast_utils/mod.rs index deda6030831e..54261079fcad 100644 --- a/clippy_utils/src/ast_utils/mod.rs +++ b/clippy_utils/src/ast_utils/mod.rs @@ -201,7 +201,7 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool { (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, lk), Yield(r, rk)) => eq_expr_opt(l.as_ref(), r.as_ref()) && lk == rk, + (Yield(l), Yield(r)) => eq_expr_opt(l.expr(), r.expr()) && l.same_kind(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()), From dd92647f33e153b7a1feed1afbcd2c658c32b334 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 14 Mar 2025 09:03:23 +1100 Subject: [PATCH 06/19] Use `Option` for lowered param names. Parameter patterns are lowered to an `Ident` by `lower_fn_params_to_names`, which is used when lowering bare function types, trait methods, and foreign functions. Currently, there are two exceptional cases where the lowered param can become an empty `Ident`. - If the incoming pattern is an empty `Ident`. This occurs if the parameter is anonymous, e.g. in a bare function type. - If the incoming pattern is neither an ident nor an underscore. Any such parameter will have triggered a compile error (hence the `span_delayed_bug`), but lowering still occurs. This commit replaces these empty `Ident` results with `None`, which eliminates a number of `kw::Empty` uses, and makes it impossible to fail to check for these exceptional cases. Note: the `FIXME` comment in `is_unwrap_or_empty_symbol` is removed. It actually should have been removed in #138482, the precursor to this PR. That PR changed the lowering of wild patterns to `_` symbols instead of empty symbols, which made the mentioned underscore check load-bearing. --- .../src/functions/renamed_function_params.rs | 45 ++++++++++--------- clippy_lints/src/lifetimes.rs | 10 ++--- 2 files changed, 29 insertions(+), 26 deletions(-) diff --git a/clippy_lints/src/functions/renamed_function_params.rs b/clippy_lints/src/functions/renamed_function_params.rs index 5ad83f886e2e..041f6228fba2 100644 --- a/clippy_lints/src/functions/renamed_function_params.rs +++ b/clippy_lints/src/functions/renamed_function_params.rs @@ -5,7 +5,7 @@ use rustc_hir::hir_id::OwnerId; use rustc_hir::{Impl, ImplItem, ImplItemKind, ImplItemRef, ItemKind, Node, TraitRef}; use rustc_lint::LateContext; use rustc_span::Span; -use rustc_span::symbol::{Ident, Symbol, kw}; +use rustc_span::symbol::{Ident, kw}; use super::RENAMED_FUNCTION_PARAMS; @@ -51,22 +51,33 @@ struct RenamedFnArgs(Vec<(Span, String)>); impl RenamedFnArgs { /// Comparing between an iterator of default names and one with current names, /// then collect the ones that got renamed. - fn new(default_names: &mut I, current_names: &mut T) -> Self + fn new(default_idents: &mut I1, current_idents: &mut I2) -> Self where - I: Iterator, - T: Iterator, + I1: Iterator>, + I2: Iterator>, { let mut renamed: Vec<(Span, String)> = vec![]; - debug_assert!(default_names.size_hint() == current_names.size_hint()); - while let (Some(def_name), Some(cur_name)) = (default_names.next(), current_names.next()) { - let current_name = cur_name.name; - let default_name = def_name.name; - if is_unused_or_empty_symbol(current_name) || is_unused_or_empty_symbol(default_name) { - continue; - } - if current_name != default_name { - renamed.push((cur_name.span, default_name.to_string())); + debug_assert!(default_idents.size_hint() == current_idents.size_hint()); + while let (Some(default_ident), Some(current_ident)) = + (default_idents.next(), current_idents.next()) + { + let has_name_to_check = |ident: Option| { + if let Some(ident) = ident + && ident.name != kw::Underscore + && !ident.name.as_str().starts_with('_') + { + Some(ident) + } else { + None + } + }; + + if let Some(default_ident) = has_name_to_check(default_ident) + && let Some(current_ident) = has_name_to_check(current_ident) + && default_ident.name != current_ident.name + { + renamed.push((current_ident.span, default_ident.to_string())); } } @@ -83,14 +94,6 @@ impl RenamedFnArgs { } } -fn is_unused_or_empty_symbol(symbol: Symbol) -> bool { - // FIXME: `body_param_names` currently returning empty symbols for `wild` as well, - // so we need to check if the symbol is empty first. - // Therefore the check of whether it's equal to [`kw::Underscore`] has no use for now, - // but it would be nice to keep it here just to be future-proof. - symbol.is_empty() || symbol == kw::Underscore || symbol.as_str().starts_with('_') -} - /// Get the [`trait_item_def_id`](ImplItemRef::trait_item_def_id) of a relevant impl item. fn trait_item_def_id_of_impl(items: &[ImplItemRef], target: OwnerId) -> Option { items.iter().find_map(|item| { diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 3dd2de1fafc7..8d47c756fc53 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -189,7 +189,7 @@ fn check_fn_inner<'tcx>( cx: &LateContext<'tcx>, sig: &'tcx FnSig<'_>, body: Option, - trait_sig: Option<&[Ident]>, + trait_sig: Option<&[Option]>, generics: &'tcx Generics<'_>, span: Span, report_extra_lifetimes: bool, @@ -264,7 +264,7 @@ fn could_use_elision<'tcx>( cx: &LateContext<'tcx>, func: &'tcx FnDecl<'_>, body: Option, - trait_sig: Option<&[Ident]>, + trait_sig: Option<&[Option]>, named_generics: &'tcx [GenericParam<'_>], msrv: Msrv, ) -> Option<(Vec, Vec)> { @@ -310,7 +310,7 @@ fn could_use_elision<'tcx>( let body = cx.tcx.hir_body(body_id); let first_ident = body.params.first().and_then(|param| param.pat.simple_ident()); - if non_elidable_self_type(cx, func, first_ident, msrv) { + if non_elidable_self_type(cx, func, Some(first_ident), msrv) { return None; } @@ -384,8 +384,8 @@ fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxIndexSet(cx: &LateContext<'tcx>, func: &FnDecl<'tcx>, ident: Option, msrv: Msrv) -> bool { - if let Some(ident) = ident +fn non_elidable_self_type<'tcx>(cx: &LateContext<'tcx>, func: &FnDecl<'tcx>, ident: Option>, msrv: Msrv) -> bool { + if let Some(Some(ident)) = ident && ident.name == kw::SelfLower && !func.implicit_self.has_implicit_self() && let Some(self_ty) = func.inputs.first() From c86216ebe708d88dec9772e1f84556f5f529c6c6 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 20 Mar 2025 22:34:29 +0100 Subject: [PATCH 07/19] Merge commit '1e5237f4a56ae958af7e5824343eacf737b67083' into clippy-subtree-update --- .github/workflows/clippy_mq.yml | 2 + .github/workflows/remark.yml | 1 + CHANGELOG.md | 2 + COPYRIGHT | 2 +- Cargo.toml | 2 +- LICENSE-APACHE | 2 +- LICENSE-MIT | 2 +- README.md | 2 +- book/src/development/macro_expansions.md | 76 +++++ book/src/development/the_team.md | 3 +- book/src/lint_configuration.md | 14 + clippy_config/src/conf.rs | 79 ++++- clippy_config/src/types.rs | 108 ++++++- .../src/arbitrary_source_item_ordering.rs | 85 +++-- clippy_lints/src/attrs/mod.rs | 4 +- clippy_lints/src/blocks_in_conditions.rs | 9 +- clippy_lints/src/casts/borrow_as_ptr.rs | 10 +- clippy_lints/src/comparison_chain.rs | 2 +- clippy_lints/src/declared_lints.rs | 3 +- .../doc_comment_double_space_linebreaks.rs | 33 ++ clippy_lints/src/doc/mod.rs | 59 +++- clippy_lints/src/entry.rs | 47 ++- clippy_lints/src/escape.rs | 30 +- clippy_lints/src/excessive_bools.rs | 17 +- clippy_lints/src/format_args.rs | 5 +- clippy_lints/src/from_over_into.rs | 14 +- clippy_lints/src/ignored_unit_patterns.rs | 8 +- clippy_lints/src/implicit_saturating_sub.rs | 65 ++-- clippy_lints/src/incompatible_msrv.rs | 26 +- clippy_lints/src/inline_fn_without_body.rs | 6 +- clippy_lints/src/item_name_repetitions.rs | 291 ++++++++++-------- .../src/iter_not_returning_iterator.rs | 6 +- clippy_lints/src/legacy_numeric_constants.rs | 4 +- clippy_lints/src/loops/mod.rs | 2 +- clippy_lints/src/loops/never_loop.rs | 25 +- clippy_lints/src/manual_bits.rs | 6 +- clippy_lints/src/manual_div_ceil.rs | 2 +- clippy_lints/src/manual_ignore_case_cmp.rs | 2 +- clippy_lints/src/manual_let_else.rs | 37 +-- .../src/manual_slice_size_calculation.rs | 4 +- clippy_lints/src/matches/mod.rs | 8 +- clippy_lints/src/matches/single_match.rs | 38 ++- clippy_lints/src/methods/io_other_error.rs | 2 +- clippy_lints/src/methods/mod.rs | 4 +- clippy_lints/src/methods/needless_collect.rs | 78 ++++- .../src/methods/unnecessary_to_owned.rs | 5 +- .../src/misc_early/mixed_case_hex_literals.rs | 33 +- clippy_lints/src/missing_inline.rs | 10 +- clippy_lints/src/needless_late_init.rs | 9 +- clippy_lints/src/needless_pass_by_value.rs | 8 +- clippy_lints/src/needless_question_mark.rs | 2 +- clippy_lints/src/operators/mod.rs | 32 -- clippy_lints/src/operators/ptr_eq.rs | 62 ---- clippy_lints/src/option_if_let_else.rs | 86 +++++- clippy_lints/src/partialeq_ne_impl.rs | 4 +- clippy_lints/src/ptr.rs | 111 ++++++- clippy_lints/src/question_mark.rs | 55 +++- clippy_lints/src/redundant_async_block.rs | 4 +- clippy_lints/src/redundant_locals.rs | 2 +- clippy_lints/src/redundant_pub_crate.rs | 4 +- clippy_lints/src/size_of_in_element_count.rs | 1 - clippy_lints/src/size_of_ref.rs | 12 +- clippy_lints/src/strings.rs | 99 +++++- clippy_lints/src/types/mod.rs | 4 - .../src/undocumented_unsafe_blocks.rs | 151 ++++++--- clippy_lints/src/unnecessary_semicolon.rs | 2 +- clippy_lints/src/useless_conversion.rs | 28 +- .../almost_standard_lint_formulation.rs | 5 +- .../internal_lints/lint_without_lint_pass.rs | 6 +- .../src/utils/internal_lints/produce_ice.rs | 10 +- clippy_utils/README.md | 4 +- clippy_utils/src/ast_utils/mod.rs | 2 +- clippy_utils/src/diagnostics.rs | 10 +- clippy_utils/src/lib.rs | 45 ++- clippy_utils/src/mir/possible_borrower.rs | 3 +- clippy_utils/src/msrvs.rs | 3 +- clippy_utils/src/qualify_min_const_fn.rs | 40 ++- clippy_utils/src/sugg.rs | 41 ++- lintcheck/src/json.rs | 22 ++ rust-toolchain | 2 +- rustc_tools_util/README.md | 2 +- tests/compile-test.rs | 6 +- tests/lint_message_convention.rs | 1 + .../check_clippy_version_attribute.rs | 4 + .../check_clippy_version_attribute.stderr | 19 +- tests/ui-internal/check_formulation.rs | 2 + tests/ui-internal/check_formulation.stderr | 2 +- .../collapsible_span_lint_calls.rs | 5 + .../collapsible_span_lint_calls.stderr | 13 +- tests/ui-internal/custom_ice_message.rs | 1 + tests/ui-internal/custom_ice_message.stderr | 21 +- tests/ui-internal/default_lint.rs | 1 + tests/ui-internal/default_lint.stderr | 1 + tests/ui-internal/disallow_span_lint.rs | 2 + tests/ui-internal/disallow_span_lint.stderr | 2 +- .../interning_defined_symbol.fixed | 4 + tests/ui-internal/interning_defined_symbol.rs | 4 + .../interning_defined_symbol.stderr | 6 +- .../ui-internal/invalid_msrv_attr_impl.fixed | 1 + tests/ui-internal/invalid_msrv_attr_impl.rs | 1 + tests/ui-internal/invalid_paths.rs | 3 + tests/ui-internal/invalid_paths.stderr | 4 +- tests/ui-internal/lint_without_lint_pass.rs | 1 + .../ui-internal/lint_without_lint_pass.stderr | 1 + tests/ui-internal/outer_expn_data.fixed | 1 + tests/ui-internal/outer_expn_data.rs | 1 + tests/ui-internal/unnecessary_def_path.fixed | 15 + tests/ui-internal/unnecessary_def_path.rs | 15 + tests/ui-internal/unnecessary_def_path.stderr | 28 +- .../unnecessary_def_path_hardcoded_path.rs | 3 + ...unnecessary_def_path_hardcoded_path.stderr | 4 +- .../ui-internal/unnecessary_symbol_str.fixed | 5 + tests/ui-internal/unnecessary_symbol_str.rs | 5 + .../ui-internal/unnecessary_symbol_str.stderr | 8 +- .../bad_conf_4/clippy.toml | 1 + .../bad_conf_5/clippy.toml | 1 + .../bad_conf_6/clippy.toml | 1 + .../default_exp/clippy.toml | 2 +- .../ord_in_2/clippy.toml | 1 + .../ord_in_3/clippy.toml | 2 + .../ord_within/clippy.toml | 1 + .../ordering_good.bad_conf_4.stderr | 8 + .../ordering_good.bad_conf_5.stderr | 4 + .../ordering_good.bad_conf_6.stderr | 4 + .../ordering_good.rs | 8 +- .../ordering_mixed.default.stderr | 125 ++------ .../ordering_mixed.default_exp.stderr | 160 ++++++++++ .../ordering_mixed.ord_within.stderr | 235 ++++++++++++++ .../ordering_mixed.rs | 14 +- .../selective_ordering.default.stderr | 19 ++ .../selective_ordering.ord_in_2.stderr | 36 +++ .../selective_ordering.ord_in_3.stderr | 19 ++ .../selective_ordering.ord_within.stderr | 48 +++ .../selective_ordering.rs | 46 +++ .../toml_unknown_key/conf_unknown_key.stderr | 3 + .../undocumented_unsafe_blocks.default.stderr | 46 ++- ...undocumented_unsafe_blocks.disabled.stderr | 62 +++- .../undocumented_unsafe_blocks.rs | 91 ++++++ tests/ui/blocks_in_conditions.fixed | 7 + tests/ui/blocks_in_conditions.rs | 7 + tests/ui/crashes/if_same_then_else.rs | 1 - ...needless_pass_by_value-w-late-bound.stderr | 6 +- .../doc_comment_double_space_linebreaks.fixed | 98 ++++++ .../doc_comment_double_space_linebreaks.rs | 98 ++++++ ...doc_comment_double_space_linebreaks.stderr | 50 +++ tests/ui/entry_unfixable.rs | 94 ++++++ tests/ui/entry_unfixable.stderr | 41 +++ tests/ui/from_over_into.fixed | 11 + tests/ui/from_over_into.rs | 11 + tests/ui/from_over_into.stderr | 18 +- tests/ui/ifs_same_cond.rs | 7 +- tests/ui/ifs_same_cond.stderr | 16 +- tests/ui/implicit_saturating_sub.fixed | 24 ++ tests/ui/implicit_saturating_sub.rs | 24 ++ tests/ui/implicit_saturating_sub.stderr | 20 +- tests/ui/incompatible_msrv.rs | 39 +++ tests/ui/incompatible_msrv.stderr | 37 ++- tests/ui/io_other_error.fixed | 5 + tests/ui/io_other_error.rs | 5 + tests/ui/io_other_error.stderr | 14 +- tests/ui/literals.rs | 4 + tests/ui/literals.stderr | 45 ++- tests/ui/manual_arithmetic_check-2.rs | 9 + tests/ui/manual_arithmetic_check-2.stderr | 38 ++- tests/ui/manual_bits.stderr | 58 ++-- tests/ui/manual_let_else.rs | 34 ++ tests/ui/manual_let_else.stderr | 42 ++- .../ui/missing_const_for_fn/cant_be_const.rs | 88 ++++++ tests/ui/needless_collect.fixed | 23 ++ tests/ui/needless_collect.rs | 23 ++ tests/ui/needless_late_init.fixed | 6 + tests/ui/needless_late_init.rs | 6 + tests/ui/needless_late_init.stderr | 18 +- tests/ui/needless_pass_by_value.rs | 36 +++ tests/ui/needless_pass_by_value.stderr | 172 +++++++++-- tests/ui/needless_return.fixed | 12 +- tests/ui/needless_return.rs | 12 +- tests/ui/needless_return.stderr | 122 ++++---- tests/ui/never_loop.rs | 28 ++ tests/ui/never_loop.stderr | 70 ++++- tests/ui/never_loop_fixable.fixed | 20 ++ tests/ui/never_loop_fixable.rs | 20 ++ tests/ui/never_loop_fixable.stderr | 35 +++ tests/ui/option_if_let_else.fixed | 20 ++ tests/ui/option_if_let_else.rs | 20 ++ tests/ui/ptr_eq.fixed | 23 +- tests/ui/ptr_eq.rs | 15 + tests/ui/ptr_eq.stderr | 44 ++- tests/ui/ptr_eq_no_std.fixed | 12 +- tests/ui/ptr_eq_no_std.rs | 4 + tests/ui/ptr_eq_no_std.stderr | 26 +- tests/ui/question_mark.fixed | 55 ++++ tests/ui/question_mark.rs | 72 +++++ tests/ui/question_mark.stderr | 62 +++- tests/ui/same_functions_in_if_condition.rs | 7 +- .../ui/same_functions_in_if_condition.stderr | 24 +- tests/ui/single_match.fixed | 34 +- tests/ui/single_match.rs | 10 +- tests/ui/single_match.stderr | 97 ++++-- tests/ui/single_match_else.fixed | 8 + tests/ui/single_match_else.rs | 12 + tests/ui/single_match_else.stderr | 43 ++- tests/ui/size_of_ref.stderr | 12 +- tests/ui/string_to_string.rs | 15 +- tests/ui/string_to_string.stderr | 18 +- tests/ui/string_to_string_in_map.fixed | 20 ++ tests/ui/string_to_string_in_map.rs | 20 ++ tests/ui/string_to_string_in_map.stderr | 38 +++ tests/ui/struct_fields.rs | 27 ++ tests/ui/struct_fields.stderr | 21 +- tests/ui/unnecessary_path_debug_formatting.rs | 6 + tests/ui/unnecessary_to_owned.fixed | 36 +-- tests/ui/unnecessary_to_owned.rs | 36 +-- tests/ui/unnecessary_to_owned.stderr | 86 ++---- triagebot.toml | 1 - util/gh-pages/index_template.html | 8 +- 216 files changed, 4613 insertions(+), 1146 deletions(-) create mode 100644 clippy_lints/src/doc/doc_comment_double_space_linebreaks.rs delete mode 100644 clippy_lints/src/operators/ptr_eq.rs create mode 100644 tests/ui-toml/arbitrary_source_item_ordering/bad_conf_4/clippy.toml create mode 100644 tests/ui-toml/arbitrary_source_item_ordering/bad_conf_5/clippy.toml create mode 100644 tests/ui-toml/arbitrary_source_item_ordering/bad_conf_6/clippy.toml create mode 100644 tests/ui-toml/arbitrary_source_item_ordering/ord_in_2/clippy.toml create mode 100644 tests/ui-toml/arbitrary_source_item_ordering/ord_in_3/clippy.toml create mode 100644 tests/ui-toml/arbitrary_source_item_ordering/ord_within/clippy.toml create mode 100644 tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_4.stderr create mode 100644 tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_5.stderr create mode 100644 tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_6.stderr create mode 100644 tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default_exp.stderr create mode 100644 tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.ord_within.stderr create mode 100644 tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.default.stderr create mode 100644 tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.ord_in_2.stderr create mode 100644 tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.ord_in_3.stderr create mode 100644 tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.ord_within.stderr create mode 100644 tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs create mode 100644 tests/ui/doc/doc_comment_double_space_linebreaks.fixed create mode 100644 tests/ui/doc/doc_comment_double_space_linebreaks.rs create mode 100644 tests/ui/doc/doc_comment_double_space_linebreaks.stderr create mode 100644 tests/ui/entry_unfixable.rs create mode 100644 tests/ui/entry_unfixable.stderr create mode 100644 tests/ui/never_loop_fixable.fixed create mode 100644 tests/ui/never_loop_fixable.rs create mode 100644 tests/ui/never_loop_fixable.stderr create mode 100644 tests/ui/string_to_string_in_map.fixed create mode 100644 tests/ui/string_to_string_in_map.rs create mode 100644 tests/ui/string_to_string_in_map.stderr diff --git a/.github/workflows/clippy_mq.yml b/.github/workflows/clippy_mq.yml index c337a96bdac5..741e74573317 100644 --- a/.github/workflows/clippy_mq.yml +++ b/.github/workflows/clippy_mq.yml @@ -27,6 +27,8 @@ jobs: host: x86_64-pc-windows-msvc - os: macos-13 host: x86_64-apple-darwin + - os: macos-latest + host: aarch64-apple-darwin runs-on: ${{ matrix.os }} diff --git a/.github/workflows/remark.yml b/.github/workflows/remark.yml index 13902f78b541..7e7e26818c09 100644 --- a/.github/workflows/remark.yml +++ b/.github/workflows/remark.yml @@ -37,6 +37,7 @@ jobs: - name: Linkcheck book run: | rustup toolchain install nightly --component rust-docs + rustup override set nightly curl https://raw.githubusercontent.com/rust-lang/rust/master/src/tools/linkchecker/linkcheck.sh -o linkcheck.sh sh linkcheck.sh clippy --path ./book diff --git a/CHANGELOG.md b/CHANGELOG.md index 51441ab9fc0d..1bf4b51ff0fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5570,6 +5570,7 @@ Released 2018-09-13 [`disallowed_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_type [`disallowed_types`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_types [`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression +[`doc_comment_double_space_linebreaks`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_comment_double_space_linebreaks [`doc_include_without_cfg`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_include_without_cfg [`doc_lazy_continuation`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_lazy_continuation [`doc_link_code`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_link_code @@ -6372,6 +6373,7 @@ Released 2018-09-13 [`min-ident-chars-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#min-ident-chars-threshold [`missing-docs-in-crate-items`]: https://doc.rust-lang.org/clippy/lint_configuration.html#missing-docs-in-crate-items [`module-item-order-groupings`]: https://doc.rust-lang.org/clippy/lint_configuration.html#module-item-order-groupings +[`module-items-ordered-within-groupings`]: https://doc.rust-lang.org/clippy/lint_configuration.html#module-items-ordered-within-groupings [`msrv`]: https://doc.rust-lang.org/clippy/lint_configuration.html#msrv [`pass-by-value-size-limit`]: https://doc.rust-lang.org/clippy/lint_configuration.html#pass-by-value-size-limit [`pub-underscore-fields-behavior`]: https://doc.rust-lang.org/clippy/lint_configuration.html#pub-underscore-fields-behavior diff --git a/COPYRIGHT b/COPYRIGHT index 219693d63d97..f402dcf465a3 100644 --- a/COPYRIGHT +++ b/COPYRIGHT @@ -1,6 +1,6 @@ // REUSE-IgnoreStart -Copyright 2014-2024 The Rust Project Developers +Copyright 2014-2025 The Rust Project Developers Licensed under the Apache License, Version 2.0 or the MIT license diff --git a/Cargo.toml b/Cargo.toml index c4588002dc99..94c170d73af3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,7 @@ anstream = "0.6.18" [dev-dependencies] cargo_metadata = "0.18.1" -ui_test = "0.26.4" +ui_test = "0.29.2" regex = "1.5.5" serde = { version = "1.0.145", features = ["derive"] } serde_json = "1.0.122" diff --git a/LICENSE-APACHE b/LICENSE-APACHE index 506582c31d6d..9990a0cec474 100644 --- a/LICENSE-APACHE +++ b/LICENSE-APACHE @@ -186,7 +186,7 @@ APPENDIX: How to apply the Apache License to your work. same "printed page" as the copyright notice for easier identification within third-party archives. -Copyright 2014-2024 The Rust Project Developers +Copyright 2014-2025 The Rust Project Developers Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/LICENSE-MIT b/LICENSE-MIT index 6d8ee9afb616..5d6e36ef6bfc 100644 --- a/LICENSE-MIT +++ b/LICENSE-MIT @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2014-2024 The Rust Project Developers +Copyright (c) 2014-2025 The Rust Project Developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated diff --git a/README.md b/README.md index 32c1d33e2ed3..20a5e997e629 100644 --- a/README.md +++ b/README.md @@ -277,7 +277,7 @@ If you want to contribute to Clippy, you can find more information in [CONTRIBUT -Copyright 2014-2024 The Rust Project Developers +Copyright 2014-2025 The Rust Project Developers Licensed under the Apache License, Version 2.0 or the MIT license diff --git a/book/src/development/macro_expansions.md b/book/src/development/macro_expansions.md index 36092f82e260..ed547130b358 100644 --- a/book/src/development/macro_expansions.md +++ b/book/src/development/macro_expansions.md @@ -150,9 +150,85 @@ if foo_span.in_external_macro(cx.sess().source_map()) { } ``` +### The `is_from_proc_macro` function +A common point of confusion is the existence of [`is_from_proc_macro`] +and how it differs from the other [`in_external_macro`]/[`from_expansion`] functions. + +While [`in_external_macro`] and [`from_expansion`] both work perfectly fine for detecting expanded code +from *declarative* macros (i.e. `macro_rules!` and macros 2.0), +detecting *proc macro*-generated code is a bit more tricky, as proc macros can (and often do) +freely manipulate the span of returned tokens. + +In practice, this often happens through the use of [`quote::quote_spanned!`] with a span from the input tokens. + +In those cases, there is no *reliable* way for the compiler (and tools like Clippy) +to distinguish code that comes from such a proc macro from code that the user wrote directly, +and [`in_external_macro`] will return `false`. + +This is usually not an issue for the compiler and actually helps proc macro authors create better error messages, +as it allows associating parts of the expansion with parts of the macro input and lets the compiler +point the user to the relevant code in case of a compile error. + +However, for Clippy this is inconvenient, because most of the time *we don't* want +to lint proc macro-generated code and this makes it impossible to tell what is and isn't proc macro code. + +> NOTE: this is specifically only an issue when a proc macro explicitly sets the span to that of an **input span**. +> +> For example, other common ways of creating `TokenStream`s, such as `"fn foo() {...}".parse::()`, +> sets each token's span to `Span::call_site()`, which already marks the span as coming from a proc macro +> and the usual span methods have no problem detecting that as a macro span. + +As such, Clippy has its own `is_from_proc_macro` function which tries to *approximate* +whether a span comes from a proc macro, by checking whether the source text at the given span +lines up with the given AST node. + +This function is typically used in combination with the other mentioned macro span functions, +but is usually called much later into the condition chain as it's a bit heavier than most other conditions, +so that the other cheaper conditions can fail faster. For example, the `borrow_deref_ref` lint: +```rs +impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef { + fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &rustc_hir::Expr<'tcx>) { + if let ... = ... + && ... + && !e.span.from_expansion() + && ... + && ... + && !is_from_proc_macro(cx, e) + && ... + { + ... + } + } +} +``` + +### Testing lints with macro expansions +To test that all of these cases are handled correctly in your lint, +we have a helper auxiliary crate that exposes various macros, used by tests like so: +```rust +//@aux-build:proc_macros.rs + +extern crate proc_macros; + +fn main() { + proc_macros::external!{ code_that_should_trigger_your_lint } + proc_macros::with_span!{ span code_that_should_trigger_your_lint } +} +``` +This exercises two cases: +- `proc_macros::external!` is a simple proc macro that echos the input tokens back but with a macro span: +this represents the usual, common case where an external macro expands to code that your lint would trigger, +and is correctly handled by `in_external_macro` and `Span::from_expansion`. + +- `proc_macros::with_span!` echos back the input tokens starting from the second token +with the span of the first token: this is where the other functions will fail and `is_from_proc_macro` is needed + + [`ctxt`]: https://doc.rust-lang.org/stable/nightly-rustc/rustc_span/struct.Span.html#method.ctxt [expansion]: https://rustc-dev-guide.rust-lang.org/macro-expansion.html#expansion-and-ast-integration [`from_expansion`]: https://doc.rust-lang.org/stable/nightly-rustc/rustc_span/struct.Span.html#method.from_expansion [`in_external_macro`]: https://doc.rust-lang.org/stable/nightly-rustc/rustc_span/struct.Span.html#method.in_external_macro [Span]: https://doc.rust-lang.org/stable/nightly-rustc/rustc_span/struct.Span.html [SyntaxContext]: https://doc.rust-lang.org/stable/nightly-rustc/rustc_span/hygiene/struct.SyntaxContext.html +[`is_from_proc_macro`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/fn.is_from_proc_macro.html +[`quote::quote_spanned!`]: https://docs.rs/quote/latest/quote/macro.quote_spanned.html diff --git a/book/src/development/the_team.md b/book/src/development/the_team.md index 6bc0783b166a..da5d084ed97f 100644 --- a/book/src/development/the_team.md +++ b/book/src/development/the_team.md @@ -102,8 +102,7 @@ is responsible for maintaining Clippy. 5. **Update the changelog** - This needs to be done for every release, every six weeks. This is usually - done by @xFrednet. + This needs to be done for every release, every six weeks. ### Membership diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index 28b613ea3295..3726d6e8a869 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -744,6 +744,19 @@ The named groupings of different source item kinds within modules. * [`arbitrary_source_item_ordering`](https://rust-lang.github.io/rust-clippy/master/index.html#arbitrary_source_item_ordering) +## `module-items-ordered-within-groupings` +Whether the items within module groups should be ordered alphabetically or not. + +This option can be configured to "all", "none", or a list of specific grouping names that should be checked +(e.g. only "enums"). + +**Default Value:** `"none"` + +--- +**Affected lints:** +* [`arbitrary_source_item_ordering`](https://rust-lang.github.io/rust-clippy/master/index.html#arbitrary_source_item_ordering) + + ## `msrv` The minimum rust version that the project supports. Defaults to the `rust-version` field in `Cargo.toml` @@ -806,6 +819,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`option_as_ref_deref`](https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref) * [`option_map_unwrap_or`](https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unwrap_or) * [`ptr_as_ptr`](https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr) +* [`question_mark`](https://rust-lang.github.io/rust-clippy/master/index.html#question_mark) * [`redundant_field_names`](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names) * [`redundant_static_lifetimes`](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes) * [`repeat_vec_with_capacity`](https://rust-lang.github.io/rust-clippy/master/index.html#repeat_vec_with_capacity) diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index a61acbaa96bc..798f8b3aa5a9 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -3,14 +3,17 @@ use crate::types::{ DisallowedPath, DisallowedPathWithoutReplacement, MacroMatcher, MatchLintBehaviour, PubUnderscoreFieldsBehaviour, Rename, SourceItemOrdering, SourceItemOrderingCategory, SourceItemOrderingModuleItemGroupings, SourceItemOrderingModuleItemKind, SourceItemOrderingTraitAssocItemKind, SourceItemOrderingTraitAssocItemKinds, + SourceItemOrderingWithinModuleItemGroupings, }; use clippy_utils::msrvs::Msrv; +use itertools::Itertools; use rustc_errors::Applicability; use rustc_session::Session; use rustc_span::edit_distance::edit_distance; use rustc_span::{BytePos, Pos, SourceFile, Span, SyntaxContext}; use serde::de::{IgnoredAny, IntoDeserializer, MapAccess, Visitor}; use serde::{Deserialize, Deserializer, Serialize}; +use std::collections::HashMap; use std::fmt::{Debug, Display, Formatter}; use std::ops::Range; use std::path::PathBuf; @@ -79,6 +82,7 @@ const DEFAULT_SOURCE_ITEM_ORDERING: &[SourceItemOrderingCategory] = { #[derive(Default)] struct TryConf { conf: Conf, + value_spans: HashMap>, errors: Vec, warnings: Vec, } @@ -87,6 +91,7 @@ impl TryConf { fn from_toml_error(file: &SourceFile, error: &toml::de::Error) -> Self { Self { conf: Conf::default(), + value_spans: HashMap::default(), errors: vec![ConfError::from_toml(file, error)], warnings: vec![], } @@ -210,6 +215,7 @@ macro_rules! define_Conf { } fn visit_map(self, mut map: V) -> Result where V: MapAccess<'de> { + let mut value_spans = HashMap::new(); let mut errors = Vec::new(); let mut warnings = Vec::new(); $(let mut $name = None;)* @@ -232,6 +238,7 @@ macro_rules! define_Conf { } None => { $name = Some(value); + value_spans.insert(name.get_ref().as_str().to_string(), value_span); // $new_conf is the same as one of the defined `$name`s, so // this variable is defined in line 2 of this function. $(match $new_conf { @@ -250,7 +257,7 @@ macro_rules! define_Conf { } } let conf = Conf { $($name: $name.unwrap_or_else(defaults::$name),)* }; - Ok(TryConf { conf, errors, warnings }) + Ok(TryConf { conf, value_spans, errors, warnings }) } } @@ -596,6 +603,13 @@ define_Conf! { /// The named groupings of different source item kinds within modules. #[lints(arbitrary_source_item_ordering)] module_item_order_groupings: SourceItemOrderingModuleItemGroupings = DEFAULT_MODULE_ITEM_ORDERING_GROUPS.into(), + /// Whether the items within module groups should be ordered alphabetically or not. + /// + /// This option can be configured to "all", "none", or a list of specific grouping names that should be checked + /// (e.g. only "enums"). + #[lints(arbitrary_source_item_ordering)] + module_items_ordered_within_groupings: SourceItemOrderingWithinModuleItemGroupings = + SourceItemOrderingWithinModuleItemGroupings::None, /// The minimum rust version that the project supports. Defaults to the `rust-version` field in `Cargo.toml` #[default_text = "current version"] #[lints( @@ -654,6 +668,7 @@ define_Conf! { option_as_ref_deref, option_map_unwrap_or, ptr_as_ptr, + question_mark, redundant_field_names, redundant_static_lifetimes, repeat_vec_with_capacity, @@ -815,6 +830,36 @@ fn deserialize(file: &SourceFile) -> TryConf { &mut conf.conf.allow_renamed_params_for, DEFAULT_ALLOWED_TRAITS_WITH_RENAMED_PARAMS, ); + + // Confirms that the user has not accidentally configured ordering requirements for groups that + // aren't configured. + if let SourceItemOrderingWithinModuleItemGroupings::Custom(groupings) = + &conf.conf.module_items_ordered_within_groupings + { + for grouping in groupings { + if !conf.conf.module_item_order_groupings.is_grouping(grouping) { + // Since this isn't fixable by rustfix, don't emit a `Suggestion`. This just adds some useful + // info for the user instead. + + let names = conf.conf.module_item_order_groupings.grouping_names(); + let suggestion = suggest_candidate(grouping, names.iter().map(String::as_str)) + .map(|s| format!(" perhaps you meant `{s}`?")) + .unwrap_or_default(); + let names = names.iter().map(|s| format!("`{s}`")).join(", "); + let message = format!( + "unknown ordering group: `{grouping}` was not specified in `module-items-ordered-within-groupings`,{suggestion} expected one of: {names}" + ); + + let span = conf + .value_spans + .get("module_item_order_groupings") + .cloned() + .unwrap_or_default(); + conf.errors.push(ConfError::spanned(file, message, None, span)); + } + } + } + // TODO: THIS SHOULD BE TESTED, this comment will be gone soon if conf.conf.allowed_idents_below_min_chars.iter().any(|e| e == "..") { conf.conf @@ -860,6 +905,7 @@ impl Conf { let TryConf { mut conf, + value_spans: _, errors, warnings, } = match path { @@ -950,17 +996,10 @@ impl serde::de::Error for FieldError { } } - let suggestion = expected - .iter() - .filter_map(|expected| { - let dist = edit_distance(field, expected, 4)?; - Some((dist, expected)) - }) - .min_by_key(|&(dist, _)| dist) - .map(|(_, suggestion)| Suggestion { - message: "perhaps you meant", - suggestion, - }); + let suggestion = suggest_candidate(field, expected).map(|suggestion| Suggestion { + message: "perhaps you meant", + suggestion, + }); Self { error: msg, suggestion } } @@ -998,6 +1037,22 @@ fn calculate_dimensions(fields: &[&str]) -> (usize, Vec) { (rows, column_widths) } +/// Given a user-provided value that couldn't be matched to a known option, finds the most likely +/// candidate among candidates that the user might have meant. +fn suggest_candidate<'a, I>(value: &str, candidates: I) -> Option<&'a str> +where + I: IntoIterator, +{ + candidates + .into_iter() + .filter_map(|expected| { + let dist = edit_distance(value, expected, 4)?; + Some((dist, expected)) + }) + .min_by_key(|&(dist, _)| dist) + .map(|(_, suggestion)| suggestion) +} + #[cfg(test)] mod tests { use serde::de::IgnoredAny; diff --git a/clippy_config/src/types.rs b/clippy_config/src/types.rs index c72291e9799f..8faac9ecffea 100644 --- a/clippy_config/src/types.rs +++ b/clippy_config/src/types.rs @@ -305,6 +305,7 @@ impl SourceItemOrderingModuleItemKind { pub struct SourceItemOrderingModuleItemGroupings { groups: Vec<(String, Vec)>, lut: HashMap, + back_lut: HashMap, } impl SourceItemOrderingModuleItemGroupings { @@ -320,6 +321,30 @@ impl SourceItemOrderingModuleItemGroupings { lut } + fn build_back_lut( + groups: &[(String, Vec)], + ) -> HashMap { + let mut lut = HashMap::new(); + for (group_name, items) in groups { + for item in items { + lut.insert(item.clone(), group_name.clone()); + } + } + lut + } + + pub fn grouping_name_of(&self, item: &SourceItemOrderingModuleItemKind) -> Option<&String> { + self.back_lut.get(item) + } + + pub fn grouping_names(&self) -> Vec { + self.groups.iter().map(|(name, _)| name.clone()).collect() + } + + pub fn is_grouping(&self, grouping: &str) -> bool { + self.groups.iter().any(|(g, _)| g == grouping) + } + pub fn module_level_order_of(&self, item: &SourceItemOrderingModuleItemKind) -> Option { self.lut.get(item).copied() } @@ -330,7 +355,8 @@ impl From<&[(&str, &[SourceItemOrderingModuleItemKind])]> for SourceItemOrdering let groups: Vec<(String, Vec)> = value.iter().map(|item| (item.0.to_string(), item.1.to_vec())).collect(); let lut = Self::build_lut(&groups); - Self { groups, lut } + let back_lut = Self::build_back_lut(&groups); + Self { groups, lut, back_lut } } } @@ -348,6 +374,7 @@ impl<'de> Deserialize<'de> for SourceItemOrderingModuleItemGroupings { let groups = Vec::<(String, Vec)>::deserialize(deserializer)?; let items_total: usize = groups.iter().map(|(_, v)| v.len()).sum(); let lut = Self::build_lut(&groups); + let back_lut = Self::build_back_lut(&groups); let mut expected_items = SourceItemOrderingModuleItemKind::all_variants(); for item in lut.keys() { @@ -370,7 +397,7 @@ impl<'de> Deserialize<'de> for SourceItemOrderingModuleItemGroupings { )); } - Ok(Self { groups, lut }) + Ok(Self { groups, lut, back_lut }) } else if items_total != all_items.len() { Err(de::Error::custom(format!( "Some module item kinds were configured more than once, or were missing, in the source ordering configuration. \ @@ -482,6 +509,83 @@ impl Serialize for SourceItemOrderingTraitAssocItemKinds { } } +/// Describes which specific groupings should have their items ordered +/// alphabetically. +/// +/// This is separate from defining and enforcing groupings. For example, +/// defining enums are grouped before structs still allows for an enum B to be +/// placed before an enum A. Only when enforcing ordering within the grouping, +/// will it be checked if A is placed before B. +#[derive(Clone, Debug)] +pub enum SourceItemOrderingWithinModuleItemGroupings { + /// All groupings should have their items ordered. + All, + + /// None of the groupings should have their order checked. + None, + + /// Only the specified groupings should have their order checked. + Custom(Vec), +} + +impl SourceItemOrderingWithinModuleItemGroupings { + pub fn ordered_within(&self, grouping_name: &String) -> bool { + match self { + SourceItemOrderingWithinModuleItemGroupings::All => true, + SourceItemOrderingWithinModuleItemGroupings::None => false, + SourceItemOrderingWithinModuleItemGroupings::Custom(groups) => groups.contains(grouping_name), + } + } +} + +/// Helper struct for deserializing the [`SourceItemOrderingWithinModuleItemGroupings`]. +#[derive(Deserialize)] +#[serde(untagged)] +enum StringOrVecOfString { + String(String), + Vec(Vec), +} + +impl<'de> Deserialize<'de> for SourceItemOrderingWithinModuleItemGroupings { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let description = "The available options for configuring an ordering within module item groups are: \ + \"all\", \"none\", or a list of module item group names \ + (as configured with the `module-item-order-groupings` configuration option)."; + + match StringOrVecOfString::deserialize(deserializer) { + Ok(StringOrVecOfString::String(preset)) if preset == "all" => { + Ok(SourceItemOrderingWithinModuleItemGroupings::All) + }, + Ok(StringOrVecOfString::String(preset)) if preset == "none" => { + Ok(SourceItemOrderingWithinModuleItemGroupings::None) + }, + Ok(StringOrVecOfString::String(preset)) => Err(de::Error::custom(format!( + "Unknown configuration option: {preset}.\n{description}" + ))), + Ok(StringOrVecOfString::Vec(groupings)) => { + Ok(SourceItemOrderingWithinModuleItemGroupings::Custom(groupings)) + }, + Err(e) => Err(de::Error::custom(format!("{e}\n{description}"))), + } + } +} + +impl Serialize for SourceItemOrderingWithinModuleItemGroupings { + fn serialize(&self, serializer: S) -> Result + where + S: ser::Serializer, + { + match self { + SourceItemOrderingWithinModuleItemGroupings::All => serializer.serialize_str("all"), + SourceItemOrderingWithinModuleItemGroupings::None => serializer.serialize_str("none"), + SourceItemOrderingWithinModuleItemGroupings::Custom(vec) => vec.serialize(serializer), + } + } +} + // these impls are never actually called but are used by the various config options that default to // empty lists macro_rules! unimplemented_serialize { diff --git a/clippy_lints/src/arbitrary_source_item_ordering.rs b/clippy_lints/src/arbitrary_source_item_ordering.rs index 39528a8f55c3..57cabe437034 100644 --- a/clippy_lints/src/arbitrary_source_item_ordering.rs +++ b/clippy_lints/src/arbitrary_source_item_ordering.rs @@ -2,11 +2,12 @@ use clippy_config::Conf; use clippy_config::types::{ SourceItemOrderingCategory, SourceItemOrderingModuleItemGroupings, SourceItemOrderingModuleItemKind, SourceItemOrderingTraitAssocItemKind, SourceItemOrderingTraitAssocItemKinds, + SourceItemOrderingWithinModuleItemGroupings, }; use clippy_utils::diagnostics::span_lint_and_note; use rustc_hir::{ - AssocItemKind, FieldDef, HirId, ImplItemRef, IsAuto, Item, ItemKind, Mod, QPath, TraitItemRef, TyKind, - Variant, VariantData, + AssocItemKind, FieldDef, HirId, ImplItemRef, IsAuto, Item, ItemKind, Mod, QPath, TraitItemRef, TyKind, Variant, + VariantData, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; @@ -36,7 +37,7 @@ declare_clippy_lint! { /// 2. Individual ordering rules per item kind. /// /// The item kinds that can be linted are: - /// - Module (with customized groupings, alphabetical within) + /// - Module (with customized groupings, alphabetical within - configurable) /// - Trait (with customized order of associated items, alphabetical within) /// - Enum, Impl, Struct (purely alphabetical) /// @@ -57,8 +58,31 @@ declare_clippy_lint! { /// | `PascalCase` | "ty_alias", "opaque_ty", "enum", "struct", "union", "trait", "trait_alias", "impl" | /// | `lower_snake_case` | "fn" | /// + /// The groups' names are arbitrary and can be changed to suit the + /// conventions that should be enforced for a specific project. + /// /// All item kinds must be accounted for to create an enforceable linting - /// rule set. + /// rule set. Following are some example configurations that may be useful. + /// + /// Example: *module inclusions and use statements to be at the top* + /// + /// ```toml + /// module-item-order-groupings = [ + /// [ "modules", [ "extern_crate", "mod", "foreign_mod" ], ], + /// [ "use", [ "use", ], ], + /// [ "everything_else", [ "macro", "global_asm", "static", "const", "ty_alias", "enum", "struct", "union", "trait", "trait_alias", "impl", "fn", ], ], + /// ] + /// ``` + /// + /// Example: *only consts and statics should be alphabetically ordered* + /// + /// It is also possible to configure a selection of module item groups that + /// should be ordered alphabetically. This may be useful if for example + /// statics and consts should be ordered, but the rest should be left open. + /// + /// ```toml + /// module-items-ordered-within-groupings = ["UPPER_SNAKE_CASE"] + /// ``` /// /// ### Known Problems /// @@ -143,6 +167,7 @@ pub struct ArbitrarySourceItemOrdering { enable_ordering_for_struct: bool, enable_ordering_for_trait: bool, module_item_order_groupings: SourceItemOrderingModuleItemGroupings, + module_items_ordered_within_groupings: SourceItemOrderingWithinModuleItemGroupings, } impl ArbitrarySourceItemOrdering { @@ -157,6 +182,7 @@ impl ArbitrarySourceItemOrdering { enable_ordering_for_struct: conf.source_item_ordering.contains(&Struct), enable_ordering_for_trait: conf.source_item_ordering.contains(&Trait), module_item_order_groupings: conf.module_item_order_groupings.clone(), + module_items_ordered_within_groupings: conf.module_items_ordered_within_groupings.clone(), } } @@ -176,11 +202,7 @@ impl ArbitrarySourceItemOrdering { } /// Produces a linting warning for incorrectly ordered item members. - fn lint_member_name( - cx: &T, - ident: &rustc_span::Ident, - before_ident: &rustc_span::Ident, - ) { + fn lint_member_name(cx: &T, ident: &rustc_span::Ident, before_ident: &rustc_span::Ident) { span_lint_and_note( cx, ARBITRARY_SOURCE_ITEM_ORDERING, @@ -191,7 +213,7 @@ impl ArbitrarySourceItemOrdering { ); } - fn lint_member_item(cx: &T, item: &Item<'_>, before_item: &Item<'_>) { + fn lint_member_item(cx: &T, item: &Item<'_>, before_item: &Item<'_>, msg: &'static str) { let span = if let Some(ident) = item.kind.ident() { ident.span } else { @@ -199,10 +221,7 @@ impl ArbitrarySourceItemOrdering { }; let (before_span, note) = if let Some(ident) = before_item.kind.ident() { - ( - ident.span, - format!("should be placed before `{}`", ident.as_str(),), - ) + (ident.span, format!("should be placed before `{}`", ident.as_str(),)) } else { ( before_item.span, @@ -215,14 +234,7 @@ impl ArbitrarySourceItemOrdering { return; } - span_lint_and_note( - cx, - ARBITRARY_SOURCE_ITEM_ORDERING, - span, - "incorrect ordering of items (must be alphabetically ordered)", - Some(before_span), - note, - ); + span_lint_and_note(cx, ARBITRARY_SOURCE_ITEM_ORDERING, span, msg, Some(before_span), note); } /// Produces a linting warning for incorrectly ordered trait items. @@ -376,6 +388,7 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering { } let item_kind = convert_module_item_kind(&item.kind); + let grouping_name = self.module_item_order_groupings.grouping_name_of(&item_kind); let module_level_order = self .module_item_order_groupings .module_level_order_of(&item_kind) @@ -385,13 +398,27 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering { use std::cmp::Ordering; // Better legibility. match module_level_order.cmp(&cur_t.order) { Ordering::Less => { - Self::lint_member_item(cx, item, cur_t.item); + Self::lint_member_item( + cx, + item, + cur_t.item, + "incorrect ordering of items (module item groupings specify another order)", + ); }, Ordering::Equal if item_kind == SourceItemOrderingModuleItemKind::Use => { // Skip ordering use statements, as these should be ordered by rustfmt. }, - Ordering::Equal if cur_t.name > get_item_name(item) => { - Self::lint_member_item(cx, item, cur_t.item); + Ordering::Equal + if (grouping_name.is_some_and(|grouping_name| { + self.module_items_ordered_within_groupings.ordered_within(grouping_name) + }) && cur_t.name > get_item_name(item)) => + { + Self::lint_member_item( + cx, + item, + cur_t.item, + "incorrect ordering of items (must be alphabetically ordered)", + ); }, Ordering::Equal | Ordering::Greater => { // Nothing to do in this case, they're already in the right order. @@ -501,6 +528,12 @@ fn get_item_name(item: &Item<'_>) -> String { }, // FIXME: `Ident::empty` for anonymous items is a bit strange, is there // a better way to do it? - _ => item.kind.ident().unwrap_or(rustc_span::Ident::empty()).name.as_str().to_owned(), + _ => item + .kind + .ident() + .unwrap_or(rustc_span::Ident::empty()) + .name + .as_str() + .to_owned(), } } diff --git a/clippy_lints/src/attrs/mod.rs b/clippy_lints/src/attrs/mod.rs index 54ec8c7b2758..e04d2ad5d13b 100644 --- a/clippy_lints/src/attrs/mod.rs +++ b/clippy_lints/src/attrs/mod.rs @@ -466,7 +466,9 @@ impl Attributes { impl<'tcx> LateLintPass<'tcx> for Attributes { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { let attrs = cx.tcx.hir_attrs(item.hir_id()); - if let ItemKind::Fn { ident, .. } = item.kind && is_relevant_item(cx, item) { + if let ItemKind::Fn { ident, .. } = item.kind + && is_relevant_item(cx, item) + { inline_always::check(cx, item.span, ident.name, attrs); } repr_attributes::check(cx, item.span, attrs, self.msrv); diff --git a/clippy_lints/src/blocks_in_conditions.rs b/clippy_lints/src/blocks_in_conditions.rs index aab0af0d743b..011962846cb3 100644 --- a/clippy_lints/src/blocks_in_conditions.rs +++ b/clippy_lints/src/blocks_in_conditions.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_block_with_applicability; -use clippy_utils::{higher, is_from_proc_macro}; +use clippy_utils::{contains_return, higher, is_from_proc_macro}; use rustc_errors::Applicability; use rustc_hir::{BlockCheckMode, Expr, ExprKind, MatchSource}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -86,6 +86,13 @@ impl<'tcx> LateLintPass<'tcx> for BlocksInConditions { if expr.span.from_expansion() || ex.span.from_expansion() { return; } + + // Linting should not be triggered to cases where `return` is included in the condition. + // #9911 + if contains_return(block.expr) { + return; + } + let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( cx, diff --git a/clippy_lints/src/casts/borrow_as_ptr.rs b/clippy_lints/src/casts/borrow_as_ptr.rs index d143629da3a0..64345c81a248 100644 --- a/clippy_lints/src/casts/borrow_as_ptr.rs +++ b/clippy_lints/src/casts/borrow_as_ptr.rs @@ -2,11 +2,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::Msrv; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; -use clippy_utils::{is_lint_allowed, msrvs, std_or_core}; +use clippy_utils::{is_expr_temporary_value, is_lint_allowed, msrvs, std_or_core}; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Ty, TyKind}; use rustc_lint::LateContext; -use rustc_middle::ty::adjustment::Adjust; use rustc_span::BytePos; use super::BORROW_AS_PTR; @@ -25,12 +24,7 @@ pub(super) fn check<'tcx>( let mut app = Applicability::MachineApplicable; let snip = snippet_with_context(cx, e.span, cast_expr.span.ctxt(), "..", &mut app).0; // Fix #9884 - if !e.is_place_expr(|base| { - cx.typeck_results() - .adjustments() - .get(base.hir_id) - .is_some_and(|x| x.iter().any(|adj| matches!(adj.kind, Adjust::Deref(_)))) - }) { + if is_expr_temporary_value(cx, e) { return false; } diff --git a/clippy_lints/src/comparison_chain.rs b/clippy_lints/src/comparison_chain.rs index 61c92d441d09..0e7f01e44b04 100644 --- a/clippy_lints/src/comparison_chain.rs +++ b/clippy_lints/src/comparison_chain.rs @@ -53,7 +53,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.40.0"] pub COMPARISON_CHAIN, - style, + pedantic, "`if`s that can be rewritten with `match` and `cmp`" } diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 0834618499c7..7fa23dad6981 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -137,6 +137,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::disallowed_names::DISALLOWED_NAMES_INFO, crate::disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS_INFO, crate::disallowed_types::DISALLOWED_TYPES_INFO, + crate::doc::DOC_COMMENT_DOUBLE_SPACE_LINEBREAKS_INFO, crate::doc::DOC_INCLUDE_WITHOUT_CFG_INFO, crate::doc::DOC_LAZY_CONTINUATION_INFO, crate::doc::DOC_LINK_CODE_INFO, @@ -614,7 +615,6 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::operators::MODULO_ONE_INFO, crate::operators::NEEDLESS_BITWISE_BOOL_INFO, crate::operators::OP_REF_INFO, - crate::operators::PTR_EQ_INFO, crate::operators::REDUNDANT_COMPARISONS_INFO, crate::operators::SELF_ASSIGNMENT_INFO, crate::operators::VERBOSE_BIT_MASK_INFO, @@ -641,6 +641,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::ptr::INVALID_NULL_PTR_USAGE_INFO, crate::ptr::MUT_FROM_REF_INFO, crate::ptr::PTR_ARG_INFO, + crate::ptr::PTR_EQ_INFO, crate::ptr_offset_with_cast::PTR_OFFSET_WITH_CAST_INFO, crate::pub_underscore_fields::PUB_UNDERSCORE_FIELDS_INFO, crate::pub_use::PUB_USE_INFO, diff --git a/clippy_lints/src/doc/doc_comment_double_space_linebreaks.rs b/clippy_lints/src/doc/doc_comment_double_space_linebreaks.rs new file mode 100644 index 000000000000..4cc027022333 --- /dev/null +++ b/clippy_lints/src/doc/doc_comment_double_space_linebreaks.rs @@ -0,0 +1,33 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use rustc_errors::Applicability; +use rustc_lint::LateContext; +use rustc_span::{BytePos, Span}; + +use super::DOC_COMMENT_DOUBLE_SPACE_LINEBREAKS; + +pub fn check(cx: &LateContext<'_>, collected_breaks: &[Span]) { + if collected_breaks.is_empty() { + return; + } + + let breaks: Vec<_> = collected_breaks + .iter() + .map(|span| span.with_hi(span.lo() + BytePos(2))) + .collect(); + + span_lint_and_then( + cx, + DOC_COMMENT_DOUBLE_SPACE_LINEBREAKS, + breaks.clone(), + "doc comment uses two spaces for a hard line break", + |diag| { + let suggs: Vec<_> = breaks.iter().map(|span| (*span, "\\".to_string())).collect(); + diag.tool_only_multipart_suggestion( + "replace this double space with a backslash:", + suggs, + Applicability::MachineApplicable, + ); + diag.help("replace this double space with a backslash: `\\`"); + }, + ); +} diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index 0296ff13112f..36fd396cc1df 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -1,12 +1,10 @@ #![allow(clippy::lint_without_lint_pass)] -mod lazy_continuation; -mod too_long_first_doc_paragraph; - use clippy_config::Conf; use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_then}; use clippy_utils::macros::{is_panic, root_macro_call_first_node}; +use clippy_utils::source::snippet_opt; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::Visitable; use clippy_utils::{is_entrypoint_fn, is_trait_impl_item, method_chain_args}; @@ -33,12 +31,15 @@ use rustc_span::{Span, sym}; use std::ops::Range; use url::Url; +mod doc_comment_double_space_linebreaks; mod include_in_doc_without_cfg; +mod lazy_continuation; mod link_with_quotes; mod markdown; mod missing_headers; mod needless_doctest_main; mod suspicious_doc_comments; +mod too_long_first_doc_paragraph; declare_clippy_lint! { /// ### What it does @@ -567,6 +568,39 @@ declare_clippy_lint! { "link reference defined in list item or quote" } +declare_clippy_lint! { + /// ### What it does + /// Detects doc comment linebreaks that use double spaces to separate lines, instead of back-slash (`\`). + /// + /// ### Why is this bad? + /// Double spaces, when used as doc comment linebreaks, can be difficult to see, and may + /// accidentally be removed during automatic formatting or manual refactoring. The use of a back-slash (`\`) + /// is clearer in this regard. + /// + /// ### Example + /// The two replacement dots in this example represent a double space. + /// ```no_run + /// /// This command takes two numbers as inputs and·· + /// /// adds them together, and then returns the result. + /// fn add(l: i32, r: i32) -> i32 { + /// l + r + /// } + /// ``` + /// + /// Use instead: + /// ```no_run + /// /// This command takes two numbers as inputs and\ + /// /// adds them together, and then returns the result. + /// fn add(l: i32, r: i32) -> i32 { + /// l + r + /// } + /// ``` + #[clippy::version = "1.87.0"] + pub DOC_COMMENT_DOUBLE_SPACE_LINEBREAKS, + pedantic, + "double-space used for doc comment linebreak instead of `\\`" +} + pub struct Documentation { valid_idents: FxHashSet, check_private_items: bool, @@ -598,6 +632,7 @@ impl_lint_pass!(Documentation => [ DOC_OVERINDENTED_LIST_ITEMS, TOO_LONG_FIRST_DOC_PARAGRAPH, DOC_INCLUDE_WITHOUT_CFG, + DOC_COMMENT_DOUBLE_SPACE_LINEBREAKS ]); impl EarlyLintPass for Documentation { @@ -894,6 +929,7 @@ fn check_doc<'a, Events: Iterator, Range = Vec::new(); let mut is_first_paragraph = true; let mut containers = Vec::new(); @@ -1010,11 +1046,7 @@ fn check_doc<'a, Events: Iterator, Range range.start { - start - range.start - } else { - 0 - } + start.saturating_sub(range.start) } } else { 0 @@ -1073,6 +1105,14 @@ fn check_doc<'a, Events: Iterator, Range { paragraph_range.end = range.end; @@ -1123,6 +1163,9 @@ fn check_doc<'a, Events: Iterator, Range {} } } + + doc_comment_double_space_linebreaks::check(cx, &collected_breaks); + headers } diff --git a/clippy_lints/src/entry.rs b/clippy_lints/src/entry.rs index f404bc59b3b8..dcfee0b6d3c6 100644 --- a/clippy_lints/src/entry.rs +++ b/clippy_lints/src/entry.rs @@ -1,5 +1,6 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::source::{reindent_multiline, snippet_indent, snippet_with_applicability, snippet_with_context}; +use clippy_utils::ty::is_copy; use clippy_utils::visitors::for_each_expr; use clippy_utils::{ SpanlessEq, can_move_expr_to_closure_no_visit, higher, is_expr_final_block_expr, is_expr_used_or_unified, @@ -84,14 +85,21 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass { return; }; + let lint_msg = format!("usage of `contains_key` followed by `insert` on a `{}`", map_ty.name()); let mut app = Applicability::MachineApplicable; let map_str = snippet_with_context(cx, contains_expr.map.span, contains_expr.call_ctxt, "..", &mut app).0; let key_str = snippet_with_context(cx, contains_expr.key.span, contains_expr.call_ctxt, "..", &mut app).0; + let sugg = if let Some(else_expr) = else_expr { let Some(else_search) = find_insert_calls(cx, &contains_expr, else_expr) else { return; }; + if then_search.is_key_used_and_no_copy || else_search.is_key_used_and_no_copy { + span_lint(cx, MAP_ENTRY, expr.span, lint_msg); + return; + } + if then_search.edits.is_empty() && else_search.edits.is_empty() { // No insertions return; @@ -184,15 +192,7 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass { } }; - span_lint_and_sugg( - cx, - MAP_ENTRY, - expr.span, - format!("usage of `contains_key` followed by `insert` on a `{}`", map_ty.name()), - "try", - sugg, - app, - ); + span_lint_and_sugg(cx, MAP_ENTRY, expr.span, lint_msg, "try", sugg, app); } } @@ -354,6 +354,8 @@ struct InsertSearcher<'cx, 'tcx> { key: &'tcx Expr<'tcx>, /// The context of the top level block. All insert calls must be in the same context. ctxt: SyntaxContext, + /// The spanless equality utility used to compare expressions. + spanless_eq: SpanlessEq<'cx, 'tcx>, /// Whether this expression can be safely moved into a closure. allow_insert_closure: bool, /// Whether this expression can use the entry api. @@ -364,6 +366,8 @@ struct InsertSearcher<'cx, 'tcx> { is_single_insert: bool, /// If the visitor has seen the map being used. is_map_used: bool, + /// If the visitor has seen the key being used. + is_key_used: bool, /// The locations where changes need to be made for the suggestion. edits: Vec>, /// A stack of loops the visitor is currently in. @@ -479,11 +483,11 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> { } match try_parse_insert(self.cx, expr) { - Some(insert_expr) if SpanlessEq::new(self.cx).eq_expr(self.map, insert_expr.map) => { + Some(insert_expr) if self.spanless_eq.eq_expr(self.map, insert_expr.map) => { self.visit_insert_expr_arguments(&insert_expr); // Multiple inserts, inserts with a different key, and inserts from a macro can't use the entry api. if self.is_map_used - || !SpanlessEq::new(self.cx).eq_expr(self.key, insert_expr.key) + || !self.spanless_eq.eq_expr(self.key, insert_expr.key) || expr.span.ctxt() != self.ctxt { self.can_use_entry = false; @@ -502,9 +506,12 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> { self.visit_non_tail_expr(insert_expr.value); self.is_single_insert = is_single_insert; }, - _ if is_any_expr_in_map_used(self.cx, self.map, expr) => { + _ if is_any_expr_in_map_used(self.cx, &mut self.spanless_eq, self.map, expr) => { self.is_map_used = true; }, + _ if self.spanless_eq.eq_expr(self.key, expr) => { + self.is_key_used = true; + }, _ => match expr.kind { ExprKind::If(cond_expr, then_expr, Some(else_expr)) => { self.is_single_insert = false; @@ -568,9 +575,14 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> { /// Check if the given expression is used for each sub-expression in the given map. /// For example, in map `a.b.c.my_map`, The expression `a.b.c.my_map`, `a.b.c`, `a.b`, and `a` are /// all checked. -fn is_any_expr_in_map_used<'tcx>(cx: &LateContext<'tcx>, map: &'tcx Expr<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { +fn is_any_expr_in_map_used<'tcx>( + cx: &LateContext<'tcx>, + spanless_eq: &mut SpanlessEq<'_, 'tcx>, + map: &'tcx Expr<'tcx>, + expr: &'tcx Expr<'tcx>, +) -> bool { for_each_expr(cx, map, |e| { - if SpanlessEq::new(cx).eq_expr(e, expr) { + if spanless_eq.eq_expr(e, expr) { return ControlFlow::Break(()); } ControlFlow::Continue(()) @@ -582,6 +594,7 @@ struct InsertSearchResults<'tcx> { edits: Vec>, allow_insert_closure: bool, is_single_insert: bool, + is_key_used_and_no_copy: bool, } impl<'tcx> InsertSearchResults<'tcx> { fn as_single_insertion(&self) -> Option> { @@ -694,11 +707,13 @@ fn find_insert_calls<'tcx>( map: contains_expr.map, key: contains_expr.key, ctxt: expr.span.ctxt(), + spanless_eq: SpanlessEq::new(cx), allow_insert_closure: true, can_use_entry: true, in_tail_pos: true, is_single_insert: true, is_map_used: false, + is_key_used: false, edits: Vec::new(), loops: Vec::new(), locals: HirIdSet::default(), @@ -706,10 +721,12 @@ fn find_insert_calls<'tcx>( s.visit_expr(expr); let allow_insert_closure = s.allow_insert_closure; let is_single_insert = s.is_single_insert; + let is_key_used_and_no_copy = s.is_key_used && !is_copy(cx, cx.typeck_results().expr_ty(contains_expr.key)); let edits = s.edits; s.can_use_entry.then_some(InsertSearchResults { edits, allow_insert_closure, is_single_insert, + is_key_used_and_no_copy, }) } diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index 3433b2cd857a..de0fc2b1bf4b 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -162,25 +162,23 @@ impl<'tcx> Delegate<'tcx> for EscapeDelegate<'_, 'tcx> { } fn mutate(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId) { - if cmt.place.projections.is_empty() { - if is_argument(self.cx.tcx, cmt.hir_id) { - // Skip closure arguments - let parent_id = self.cx.tcx.parent_hir_id(cmt.hir_id); - if let Node::Expr(..) = self.cx.tcx.parent_hir_node(parent_id) { + if cmt.place.projections.is_empty() && is_argument(self.cx.tcx, cmt.hir_id) { + // Skip closure arguments + let parent_id = self.cx.tcx.parent_hir_id(cmt.hir_id); + if let Node::Expr(..) = self.cx.tcx.parent_hir_node(parent_id) { + return; + } + + // skip if there is a `self` parameter binding to a type + // that contains `Self` (i.e.: `self: Box`), see #4804 + if let Some(trait_self_ty) = self.trait_self_ty { + if self.cx.tcx.hir_name(cmt.hir_id) == kw::SelfLower && cmt.place.ty().contains(trait_self_ty) { return; } + } - // skip if there is a `self` parameter binding to a type - // that contains `Self` (i.e.: `self: Box`), see #4804 - if let Some(trait_self_ty) = self.trait_self_ty { - if self.cx.tcx.hir_name(cmt.hir_id) == kw::SelfLower && cmt.place.ty().contains(trait_self_ty) { - return; - } - } - - if is_non_trait_box(cmt.place.ty()) && !self.is_large_box(cmt.place.ty()) { - self.set.insert(cmt.hir_id); - } + if is_non_trait_box(cmt.place.ty()) && !self.is_large_box(cmt.place.ty()) { + self.set.insert(cmt.hir_id); } } } diff --git a/clippy_lints/src/excessive_bools.rs b/clippy_lints/src/excessive_bools.rs index 2509c04cd86d..38d115b878c7 100644 --- a/clippy_lints/src/excessive_bools.rs +++ b/clippy_lints/src/excessive_bools.rs @@ -15,12 +15,17 @@ declare_clippy_lint! { /// use of bools in structs. /// /// ### Why is this bad? - /// Excessive bools in a struct - /// is often a sign that it's used as a state machine, - /// which is much better implemented as an enum. - /// If it's not the case, excessive bools usually benefit - /// from refactoring into two-variant enums for better - /// readability and API. + /// Excessive bools in a struct is often a sign that + /// the type is being used to represent a state + /// machine, which is much better implemented as an + /// enum. + /// + /// The reason an enum is better for state machines + /// over structs is that enums more easily forbid + /// invalid states. + /// + /// Structs with too many booleans may benefit from refactoring + /// into multi variant enums for better readability and API. /// /// ### Example /// ```no_run diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index fc5f76179f90..3862ff7921db 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -9,7 +9,7 @@ use clippy_utils::macros::{ use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{SpanRangeExt, snippet}; use clippy_utils::ty::{implements_trait, is_type_lang_item}; -use clippy_utils::{is_diag_trait_item, is_from_proc_macro}; +use clippy_utils::{is_diag_trait_item, is_from_proc_macro, is_in_test}; use itertools::Itertools; use rustc_ast::{ FormatArgPosition, FormatArgPositionKind, FormatArgsPiece, FormatArgumentKind, FormatCount, FormatOptions, @@ -484,7 +484,8 @@ impl<'tcx> FormatArgsExpr<'_, 'tcx> { fn check_unnecessary_debug_formatting(&self, name: Symbol, value: &Expr<'tcx>) { let cx = self.cx; - if !value.span.from_expansion() + if !is_in_test(cx.tcx, value.hir_id) + && !value.span.from_expansion() && !is_from_proc_macro(cx, value) && let ty = cx.typeck_results().expr_ty(value) && self.can_display_format(ty) diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs index 6da5567d9c70..be887b03ae4b 100644 --- a/clippy_lints/src/from_over_into.rs +++ b/clippy_lints/src/from_over_into.rs @@ -176,8 +176,8 @@ fn convert_to_from( return None; }; let body = cx.tcx.hir_body(body_id); - let [input] = body.params else { return None }; - let PatKind::Binding(.., self_ident, None) = input.pat.kind else { + let [self_param] = body.params else { return None }; + let PatKind::Binding(.., self_ident, None) = self_param.pat.kind else { return None; }; @@ -194,12 +194,12 @@ fn convert_to_from( // impl Into for U -> impl Into for T // ~ ~ (self_ty.span, into.to_owned()), - // fn into(self) -> T -> fn from(self) -> T - // ~~~~ ~~~~ + // fn into(self: U) -> T -> fn from(self) -> T + // ~~~~ ~~~~ (impl_item.ident.span, String::from("from")), - // fn into([mut] self) -> T -> fn into([mut] v: T) -> T - // ~~~~ ~~~~ - (self_ident.span, format!("val: {from}")), + // fn into([mut] self: U) -> T -> fn into([mut] val: T) -> T + // ~~~~~~~ ~~~~~~ + (self_ident.span.to(self_param.ty_span), format!("val: {from}")), ]; if let FnRetTy::Return(_) = sig.decl.output { diff --git a/clippy_lints/src/ignored_unit_patterns.rs b/clippy_lints/src/ignored_unit_patterns.rs index 54b8adbc8ac7..e4ace3bdabf0 100644 --- a/clippy_lints/src/ignored_unit_patterns.rs +++ b/clippy_lints/src/ignored_unit_patterns.rs @@ -17,15 +17,15 @@ declare_clippy_lint! { /// ### Example /// ```no_run /// match std::fs::create_dir("tmp-work-dir") { - /// Ok(_) => println!("Working directory created"), - /// Err(s) => eprintln!("Could not create directory: {s}"), + /// Ok(_) => println!("Working directory created"), + /// Err(s) => eprintln!("Could not create directory: {s}"), /// } /// ``` /// Use instead: /// ```no_run /// match std::fs::create_dir("tmp-work-dir") { - /// Ok(()) => println!("Working directory created"), - /// Err(s) => eprintln!("Could not create directory: {s}"), + /// Ok(()) => println!("Working directory created"), + /// Err(s) => eprintln!("Could not create directory: {s}"), /// } /// ``` #[clippy::version = "1.73.0"] diff --git a/clippy_lints/src/implicit_saturating_sub.rs b/clippy_lints/src/implicit_saturating_sub.rs index f2d16ff2e564..cbc3e2ccd5b8 100644 --- a/clippy_lints/src/implicit_saturating_sub.rs +++ b/clippy_lints/src/implicit_saturating_sub.rs @@ -1,14 +1,14 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::snippet_opt; +use clippy_utils::sugg::{Sugg, make_binop}; use clippy_utils::{ - SpanlessEq, higher, is_in_const_context, is_integer_literal, path_to_local, peel_blocks, peel_blocks_with_stmt, + SpanlessEq, eq_expr_value, higher, is_in_const_context, is_integer_literal, peel_blocks, peel_blocks_with_stmt, }; use rustc_ast::ast::LitKind; use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; -use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, HirId, QPath}; +use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; use rustc_span::Span; @@ -170,22 +170,20 @@ fn check_gt( cx: &LateContext<'_>, condition_span: Span, expr_span: Span, - big_var: &Expr<'_>, - little_var: &Expr<'_>, + big_expr: &Expr<'_>, + little_expr: &Expr<'_>, if_block: &Expr<'_>, else_block: &Expr<'_>, msrv: Msrv, is_composited: bool, ) { - if let Some(big_var) = Var::new(big_var) - && let Some(little_var) = Var::new(little_var) - { + if is_side_effect_free(cx, big_expr) && is_side_effect_free(cx, little_expr) { check_subtraction( cx, condition_span, expr_span, - big_var, - little_var, + big_expr, + little_expr, if_block, else_block, msrv, @@ -194,18 +192,8 @@ fn check_gt( } } -struct Var { - span: Span, - hir_id: HirId, -} - -impl Var { - fn new(expr: &Expr<'_>) -> Option { - path_to_local(expr).map(|hir_id| Self { - span: expr.span, - hir_id, - }) - } +fn is_side_effect_free(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + eq_expr_value(cx, expr, expr) } #[allow(clippy::too_many_arguments)] @@ -213,8 +201,8 @@ fn check_subtraction( cx: &LateContext<'_>, condition_span: Span, expr_span: Span, - big_var: Var, - little_var: Var, + big_expr: &Expr<'_>, + little_expr: &Expr<'_>, if_block: &Expr<'_>, else_block: &Expr<'_>, msrv: Msrv, @@ -234,8 +222,8 @@ fn check_subtraction( cx, condition_span, expr_span, - little_var, - big_var, + little_expr, + big_expr, else_block, if_block, msrv, @@ -247,17 +235,15 @@ fn check_subtraction( && let ExprKind::Binary(op, left, right) = if_block.kind && let BinOpKind::Sub = op.node { - let local_left = path_to_local(left); - let local_right = path_to_local(right); - if Some(big_var.hir_id) == local_left && Some(little_var.hir_id) == local_right { + if eq_expr_value(cx, left, big_expr) && eq_expr_value(cx, right, little_expr) { // This part of the condition is voluntarily split from the one before to ensure that // if `snippet_opt` fails, it won't try the next conditions. - if let Some(big_var_snippet) = snippet_opt(cx, big_var.span) - && let Some(little_var_snippet) = snippet_opt(cx, little_var.span) - && (!is_in_const_context(cx) || msrv.meets(cx, msrvs::SATURATING_SUB_CONST)) + if (!is_in_const_context(cx) || msrv.meets(cx, msrvs::SATURATING_SUB_CONST)) + && let Some(big_expr_sugg) = Sugg::hir_opt(cx, big_expr).map(Sugg::maybe_par) + && let Some(little_expr_sugg) = Sugg::hir_opt(cx, little_expr) { let sugg = format!( - "{}{big_var_snippet}.saturating_sub({little_var_snippet}){}", + "{}{big_expr_sugg}.saturating_sub({little_expr_sugg}){}", if is_composited { "{ " } else { "" }, if is_composited { " }" } else { "" } ); @@ -271,11 +257,12 @@ fn check_subtraction( Applicability::MachineApplicable, ); } - } else if Some(little_var.hir_id) == local_left - && Some(big_var.hir_id) == local_right - && let Some(big_var_snippet) = snippet_opt(cx, big_var.span) - && let Some(little_var_snippet) = snippet_opt(cx, little_var.span) + } else if eq_expr_value(cx, left, little_expr) + && eq_expr_value(cx, right, big_expr) + && let Some(big_expr_sugg) = Sugg::hir_opt(cx, big_expr) + && let Some(little_expr_sugg) = Sugg::hir_opt(cx, little_expr) { + let sugg = make_binop(BinOpKind::Sub, &big_expr_sugg, &little_expr_sugg); span_lint_and_then( cx, INVERTED_SATURATING_SUB, @@ -284,12 +271,12 @@ fn check_subtraction( |diag| { diag.span_note( if_block.span, - format!("this subtraction underflows when `{little_var_snippet} < {big_var_snippet}`"), + format!("this subtraction underflows when `{little_expr_sugg} < {big_expr_sugg}`"), ); diag.span_suggestion( if_block.span, "try replacing it with", - format!("{big_var_snippet} - {little_var_snippet}"), + format!("{sugg}"), Applicability::MaybeIncorrect, ); }, diff --git a/clippy_lints/src/incompatible_msrv.rs b/clippy_lints/src/incompatible_msrv.rs index 12dfb14c454d..e55edb1fcaa8 100644 --- a/clippy_lints/src/incompatible_msrv.rs +++ b/clippy_lints/src/incompatible_msrv.rs @@ -4,12 +4,12 @@ use clippy_utils::is_in_test; use clippy_utils::msrvs::Msrv; use rustc_attr_parsing::{RustcVersion, StabilityLevel, StableSince}; use rustc_data_structures::fx::FxHashMap; -use rustc_hir::{Expr, ExprKind, HirId}; +use rustc_hir::{Expr, ExprKind, HirId, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::TyCtxt; use rustc_session::impl_lint_pass; use rustc_span::def_id::DefId; -use rustc_span::{ExpnKind, Span}; +use rustc_span::{ExpnKind, Span, sym}; declare_clippy_lint! { /// ### What it does @@ -93,6 +93,21 @@ impl IncompatibleMsrv { // Intentionally not using `.from_expansion()`, since we do still care about macro expansions return; } + + // Functions coming from `core` while expanding a macro such as `assert*!()` get to cheat too: the + // macros may have existed prior to the checked MSRV, but their expansion with a recent compiler + // might use recent functions or methods. Compiling with an older compiler would not use those. + if span.from_expansion() + && cx.tcx.crate_name(def_id.krate) == sym::core + && span + .ctxt() + .outer_expn_data() + .macro_def_id + .is_some_and(|def_id| cx.tcx.crate_name(def_id.krate) == sym::core) + { + return; + } + if (self.check_in_tests || !is_in_test(cx.tcx, node)) && let Some(current) = self.msrv.current(cx) && let version = self.get_def_id_version(cx.tcx, def_id) @@ -118,8 +133,11 @@ impl<'tcx> LateLintPass<'tcx> for IncompatibleMsrv { self.emit_lint_if_under_msrv(cx, method_did, expr.hir_id, span); } }, - ExprKind::Call(call, [_]) => { - if let ExprKind::Path(qpath) = call.kind + ExprKind::Call(call, _) => { + // Desugaring into function calls by the compiler will use `QPath::LangItem` variants. Those should + // not be linted as they will not be generated in older compilers if the function is not available, + // and the compiler is allowed to call unstable functions. + if let ExprKind::Path(qpath @ (QPath::Resolved(..) | QPath::TypeRelative(..))) = call.kind && let Some(path_def_id) = cx.qpath_res(&qpath, call.hir_id).opt_def_id() { self.emit_lint_if_under_msrv(cx, path_def_id, expr.hir_id, call.span); diff --git a/clippy_lints/src/inline_fn_without_body.rs b/clippy_lints/src/inline_fn_without_body.rs index 6a436fb4a9d1..da5ca5e67721 100644 --- a/clippy_lints/src/inline_fn_without_body.rs +++ b/clippy_lints/src/inline_fn_without_body.rs @@ -32,11 +32,7 @@ declare_lint_pass!(InlineFnWithoutBody => [INLINE_FN_WITHOUT_BODY]); impl<'tcx> LateLintPass<'tcx> for InlineFnWithoutBody { fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { if let TraitItemKind::Fn(_, TraitFn::Required(_)) = item.kind - && let Some(attr) = cx - .tcx - .hir_attrs(item.hir_id()) - .iter() - .find(|a| a.has_name(sym::inline)) + && let Some(attr) = cx.tcx.hir_attrs(item.hir_id()).iter().find(|a| a.has_name(sym::inline)) { span_lint_and_then( cx, diff --git a/clippy_lints/src/item_name_repetitions.rs b/clippy_lints/src/item_name_repetitions.rs index 8de6125d1f2b..977fd5fce15b 100644 --- a/clippy_lints/src/item_name_repetitions.rs +++ b/clippy_lints/src/item_name_repetitions.rs @@ -8,7 +8,6 @@ use rustc_data_structures::fx::FxHashSet; use rustc_hir::{EnumDef, FieldDef, Item, ItemKind, OwnerId, Variant, VariantData}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; -use rustc_span::{Ident, Span}; use rustc_span::symbol::Symbol; declare_clippy_lint! { @@ -196,80 +195,176 @@ fn have_no_extra_prefix(prefixes: &[&str]) -> bool { prefixes.iter().all(|p| p == &"" || p == &"_") } -fn check_fields(cx: &LateContext<'_>, threshold: u64, ident: Ident, span: Span, fields: &[FieldDef<'_>]) { - if (fields.len() as u64) < threshold { - return; - } - - check_struct_name_repetition(cx, ident, fields); - - // if the SyntaxContext of the identifiers of the fields and struct differ dont lint them. - // this prevents linting in macros in which the location of the field identifier names differ - if !fields.iter().all(|field| ident.span.eq_ctxt(field.ident.span)) { - return; - } - - let mut pre: Vec<&str> = match fields.first() { - Some(first_field) => first_field.ident.name.as_str().split('_').collect(), - None => return, - }; - let mut post = pre.clone(); - post.reverse(); - for field in fields { - let field_split: Vec<&str> = field.ident.name.as_str().split('_').collect(); - if field_split.len() == 1 { +impl ItemNameRepetitions { + /// Lint the names of enum variants against the name of the enum. + fn check_variants(&self, cx: &LateContext<'_>, item: &Item<'_>, def: &EnumDef<'_>) { + if self.avoid_breaking_exported_api && cx.effective_visibilities.is_exported(item.owner_id.def_id) { return; } - pre = pre - .into_iter() - .zip(field_split.iter()) - .take_while(|(a, b)| &a == b) - .map(|e| e.0) - .collect(); - post = post - .into_iter() - .zip(field_split.iter().rev()) - .take_while(|(a, b)| &a == b) - .map(|e| e.0) - .collect(); - } - let prefix = pre.join("_"); - post.reverse(); - let postfix = match post.last() { - Some(&"") => post.join("_") + "_", - Some(_) | None => post.join("_"), - }; - if fields.len() > 1 { - let (what, value) = match ( - prefix.is_empty() || prefix.chars().all(|c| c == '_'), - postfix.is_empty(), - ) { - (true, true) => return, - (false, _) => ("pre", prefix), - (true, false) => ("post", postfix), + if (def.variants.len() as u64) < self.enum_threshold { + return; + } + + let Some(ident) = item.kind.ident() else { + return; }; - if fields.iter().all(|field| is_bool(field.ty)) { - // If all fields are booleans, we don't want to emit this lint. + let item_name = ident.name.as_str(); + for var in def.variants { + check_enum_start(cx, item_name, var); + check_enum_end(cx, item_name, var); + } + + Self::check_enum_common_affix(cx, item, def); + } + + /// Lint the names of struct fields against the name of the struct. + fn check_fields(&self, cx: &LateContext<'_>, item: &Item<'_>, fields: &[FieldDef<'_>]) { + if (fields.len() as u64) < self.struct_threshold { return; } + + self.check_struct_name_repetition(cx, item, fields); + self.check_struct_common_affix(cx, item, fields); + } + + fn check_enum_common_affix(cx: &LateContext<'_>, item: &Item<'_>, def: &EnumDef<'_>) { + let first = match def.variants.first() { + Some(variant) => variant.ident.name.as_str(), + None => return, + }; + let mut pre = camel_case_split(first); + let mut post = pre.clone(); + post.reverse(); + for var in def.variants { + let name = var.ident.name.as_str(); + + let variant_split = camel_case_split(name); + if variant_split.len() == 1 { + return; + } + + pre = pre + .iter() + .zip(variant_split.iter()) + .take_while(|(a, b)| a == b) + .map(|e| *e.0) + .collect(); + post = post + .iter() + .zip(variant_split.iter().rev()) + .take_while(|(a, b)| a == b) + .map(|e| *e.0) + .collect(); + } + let (what, value) = match (have_no_extra_prefix(&pre), post.is_empty()) { + (true, true) => return, + (false, _) => ("pre", pre.join("")), + (true, false) => { + post.reverse(); + ("post", post.join("")) + }, + }; span_lint_and_help( cx, - STRUCT_FIELD_NAMES, - span, - format!("all fields have the same {what}fix: `{value}`"), + ENUM_VARIANT_NAMES, + item.span, + format!("all variants have the same {what}fix: `{value}`"), None, - format!("remove the {what}fixes"), + format!( + "remove the {what}fixes and use full paths to \ + the variants instead of glob imports" + ), ); } -} -fn check_struct_name_repetition(cx: &LateContext<'_>, ident: Ident, fields: &[FieldDef<'_>]) { - let snake_name = to_snake_case(ident.name.as_str()); - let item_name_words: Vec<&str> = snake_name.split('_').collect(); - for field in fields { - if field.ident.span.eq_ctxt(ident.span) { - //consider linting only if the field identifier has the same SyntaxContext as the item(struct) + fn check_struct_common_affix(&self, cx: &LateContext<'_>, item: &Item<'_>, fields: &[FieldDef<'_>]) { + // if the SyntaxContext of the identifiers of the fields and struct differ dont lint them. + // this prevents linting in macros in which the location of the field identifier names differ + if !fields + .iter() + .all(|field| item.kind.ident().is_some_and(|i| i.span.eq_ctxt(field.ident.span))) + { + return; + } + + if self.avoid_breaking_exported_api + && fields + .iter() + .any(|field| cx.effective_visibilities.is_exported(field.def_id)) + { + return; + } + + let mut pre: Vec<&str> = match fields.first() { + Some(first_field) => first_field.ident.name.as_str().split('_').collect(), + None => return, + }; + let mut post = pre.clone(); + post.reverse(); + for field in fields { + let field_split: Vec<&str> = field.ident.name.as_str().split('_').collect(); + if field_split.len() == 1 { + return; + } + + pre = pre + .into_iter() + .zip(field_split.iter()) + .take_while(|(a, b)| &a == b) + .map(|e| e.0) + .collect(); + post = post + .into_iter() + .zip(field_split.iter().rev()) + .take_while(|(a, b)| &a == b) + .map(|e| e.0) + .collect(); + } + let prefix = pre.join("_"); + post.reverse(); + let postfix = match post.last() { + Some(&"") => post.join("_") + "_", + Some(_) | None => post.join("_"), + }; + if fields.len() > 1 { + let (what, value) = match ( + prefix.is_empty() || prefix.chars().all(|c| c == '_'), + postfix.is_empty(), + ) { + (true, true) => return, + (false, _) => ("pre", prefix), + (true, false) => ("post", postfix), + }; + if fields.iter().all(|field| is_bool(field.ty)) { + // If all fields are booleans, we don't want to emit this lint. + return; + } + span_lint_and_help( + cx, + STRUCT_FIELD_NAMES, + item.span, + format!("all fields have the same {what}fix: `{value}`"), + None, + format!("remove the {what}fixes"), + ); + } + } + + fn check_struct_name_repetition(&self, cx: &LateContext<'_>, item: &Item<'_>, fields: &[FieldDef<'_>]) { + let Some(ident) = item.kind.ident() else { return }; + let snake_name = to_snake_case(ident.name.as_str()); + let item_name_words: Vec<&str> = snake_name.split('_').collect(); + for field in fields { + if self.avoid_breaking_exported_api && cx.effective_visibilities.is_exported(field.def_id) { + continue; + } + + if !field.ident.span.eq_ctxt(ident.span) { + // consider linting only if the field identifier has the same SyntaxContext as the item(struct) + continue; + } + let field_words: Vec<&str> = field.ident.name.as_str().split('_').collect(); if field_words.len() >= item_name_words.len() { // if the field name is shorter than the struct name it cannot contain it @@ -337,65 +432,6 @@ fn check_enum_end(cx: &LateContext<'_>, item_name: &str, variant: &Variant<'_>) } } -fn check_variant(cx: &LateContext<'_>, threshold: u64, def: &EnumDef<'_>, item_name: &str, span: Span) { - if (def.variants.len() as u64) < threshold { - return; - } - - for var in def.variants { - check_enum_start(cx, item_name, var); - check_enum_end(cx, item_name, var); - } - - let first = match def.variants.first() { - Some(variant) => variant.ident.name.as_str(), - None => return, - }; - let mut pre = camel_case_split(first); - let mut post = pre.clone(); - post.reverse(); - for var in def.variants { - let name = var.ident.name.as_str(); - - let variant_split = camel_case_split(name); - if variant_split.len() == 1 { - return; - } - - pre = pre - .iter() - .zip(variant_split.iter()) - .take_while(|(a, b)| a == b) - .map(|e| *e.0) - .collect(); - post = post - .iter() - .zip(variant_split.iter().rev()) - .take_while(|(a, b)| a == b) - .map(|e| *e.0) - .collect(); - } - let (what, value) = match (have_no_extra_prefix(&pre), post.is_empty()) { - (true, true) => return, - (false, _) => ("pre", pre.join("")), - (true, false) => { - post.reverse(); - ("post", post.join("")) - }, - }; - span_lint_and_help( - cx, - ENUM_VARIANT_NAMES, - span, - format!("all variants have the same {what}fix: `{value}`"), - None, - format!( - "remove the {what}fixes and use full paths to \ - the variants instead of glob imports" - ), - ); -} - impl LateLintPass<'_> for ItemNameRepetitions { fn check_item_post(&mut self, _cx: &LateContext<'_>, item: &Item<'_>) { let Some(_ident) = item.kind.ident() else { return }; @@ -462,13 +498,14 @@ impl LateLintPass<'_> for ItemNameRepetitions { } } } - if !(self.avoid_breaking_exported_api && cx.effective_visibilities.is_exported(item.owner_id.def_id)) - && span_is_local(item.span) - { + + if span_is_local(item.span) { match item.kind { - ItemKind::Enum(_, def, _) => check_variant(cx, self.enum_threshold, &def, item_name, item.span), + ItemKind::Enum(_, def, _) => { + self.check_variants(cx, item, &def); + }, ItemKind::Struct(_, VariantData::Struct { fields, .. }, _) => { - check_fields(cx, self.struct_threshold, ident, item.span, fields); + self.check_fields(cx, item, fields); }, _ => (), } diff --git a/clippy_lints/src/iter_not_returning_iterator.rs b/clippy_lints/src/iter_not_returning_iterator.rs index 4bc6ad0798c9..753360906d66 100644 --- a/clippy_lints/src/iter_not_returning_iterator.rs +++ b/clippy_lints/src/iter_not_returning_iterator.rs @@ -28,9 +28,9 @@ declare_clippy_lint! { /// use std::str::Chars; /// struct Data {} /// impl Data { - /// fn iter(&self) -> Chars<'static> { - /// todo!() - /// } + /// fn iter(&self) -> Chars<'static> { + /// todo!() + /// } /// } /// ``` #[clippy::version = "1.57.0"] diff --git a/clippy_lints/src/legacy_numeric_constants.rs b/clippy_lints/src/legacy_numeric_constants.rs index ca51d8b618ee..01b49403cac8 100644 --- a/clippy_lints/src/legacy_numeric_constants.rs +++ b/clippy_lints/src/legacy_numeric_constants.rs @@ -72,7 +72,9 @@ impl<'tcx> LateLintPass<'tcx> for LegacyNumericConstants { "importing a legacy numeric constant" }, |diag| { - if let UseKind::Single(ident) = kind && ident.name == kw::Underscore { + if let UseKind::Single(ident) = kind + && ident.name == kw::Underscore + { diag.help("remove this import"); return; } diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index ed725a039891..4b0bf5a4b3c9 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -469,7 +469,7 @@ declare_clippy_lint! { /// let item2 = 3; /// let mut vec: Vec = Vec::new(); /// for _ in 0..20 { - /// vec.push(item1); + /// vec.push(item1); /// } /// for _ in 0..30 { /// vec.push(item2); diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index dd7a6f77acff..c3a2a38b5ec2 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -4,11 +4,13 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::ForLoop; use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::source::snippet; +use clippy_utils::visitors::{Descend, for_each_expr_without_closures}; use rustc_errors::Applicability; use rustc_hir::{Block, Destination, Expr, ExprKind, HirId, InlineAsmOperand, Pat, Stmt, StmtKind, StructTailExpr}; use rustc_lint::LateContext; use rustc_span::{Span, sym}; use std::iter::once; +use std::ops::ControlFlow; pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, @@ -24,17 +26,23 @@ pub(super) fn check<'tcx>( arg: iterator, pat, span: for_span, + label, .. }) = for_loop { - // Suggests using an `if let` instead. This is `Unspecified` because the - // loop may (probably) contain `break` statements which would be invalid - // in an `if let`. + // If the block contains a break or continue, or if the loop has a label, `MachineApplicable` is not + // appropriate. + let app = if !contains_any_break_or_continue(block) && label.is_none() { + Applicability::MachineApplicable + } else { + Applicability::Unspecified + }; + diag.span_suggestion_verbose( for_span.with_hi(iterator.span.hi()), "if you need the first element of the iterator, try writing", for_to_if_let_sugg(cx, iterator, pat), - Applicability::Unspecified, + app, ); } }); @@ -43,6 +51,15 @@ pub(super) fn check<'tcx>( } } +fn contains_any_break_or_continue(block: &Block<'_>) -> bool { + for_each_expr_without_closures(block, |e| match e.kind { + ExprKind::Break(..) | ExprKind::Continue(..) => ControlFlow::Break(()), + ExprKind::Loop(..) => ControlFlow::Continue(Descend::No), + _ => ControlFlow::Continue(Descend::Yes), + }) + .is_some() +} + /// The `never_loop` analysis keeps track of three things: /// /// * Has any (reachable) code path hit a `continue` of the main loop? diff --git a/clippy_lints/src/manual_bits.rs b/clippy_lints/src/manual_bits.rs index 39c4857b3e87..40fe88532729 100644 --- a/clippy_lints/src/manual_bits.rs +++ b/clippy_lints/src/manual_bits.rs @@ -14,7 +14,7 @@ use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does - /// Checks for usage of `std::mem::size_of::() * 8` when + /// Checks for usage of `size_of::() * 8` when /// `T::BITS` is available. /// /// ### Why is this bad? @@ -22,7 +22,7 @@ declare_clippy_lint! { /// /// ### Example /// ```no_run - /// std::mem::size_of::() * 8; + /// size_of::() * 8; /// ``` /// Use instead: /// ```no_run @@ -68,7 +68,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualBits { cx, MANUAL_BITS, expr.span, - "usage of `mem::size_of::()` to obtain the size of `T` in bits", + "usage of `size_of::()` to obtain the size of `T` in bits", "consider using", sugg, app, diff --git a/clippy_lints/src/manual_div_ceil.rs b/clippy_lints/src/manual_div_ceil.rs index 9c1419175d55..9944c4f88048 100644 --- a/clippy_lints/src/manual_div_ceil.rs +++ b/clippy_lints/src/manual_div_ceil.rs @@ -64,7 +64,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualDivCeil { && check_int_ty_and_feature(cx, div_lhs) && check_int_ty_and_feature(cx, div_rhs) && let ExprKind::Binary(inner_op, inner_lhs, inner_rhs) = div_lhs.kind - && self.msrv.meets(cx, msrvs::MANUAL_DIV_CEIL) + && self.msrv.meets(cx, msrvs::DIV_CEIL) { // (x + (y - 1)) / y if let ExprKind::Binary(sub_op, sub_lhs, sub_rhs) = inner_rhs.kind diff --git a/clippy_lints/src/manual_ignore_case_cmp.rs b/clippy_lints/src/manual_ignore_case_cmp.rs index 506f4f6d9de1..d92069edb6d0 100644 --- a/clippy_lints/src/manual_ignore_case_cmp.rs +++ b/clippy_lints/src/manual_ignore_case_cmp.rs @@ -29,7 +29,7 @@ declare_clippy_lint! { /// Use instead: /// ```no_run /// fn compare(a: &str, b: &str) -> bool { - /// a.eq_ignore_ascii_case(b) || a.eq_ignore_ascii_case("abc") + /// a.eq_ignore_ascii_case(b) || a.eq_ignore_ascii_case("abc") /// } /// ``` #[clippy::version = "1.84.0"] diff --git a/clippy_lints/src/manual_let_else.rs b/clippy_lints/src/manual_let_else.rs index 47939767212e..d6ac6e106b4b 100644 --- a/clippy_lints/src/manual_let_else.rs +++ b/clippy_lints/src/manual_let_else.rs @@ -5,7 +5,8 @@ use clippy_utils::higher::IfLetOrMatch; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{is_lint_allowed, is_never_expr, msrvs, pat_and_expr_can_be_question_mark, peel_blocks}; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_ast::BindingMode; +use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatExpr, PatExprKind, PatKind, QPath, Stmt, StmtKind}; use rustc_lint::{LateContext, LintContext}; @@ -113,7 +114,7 @@ fn emit_manual_let_else( cx: &LateContext<'_>, span: Span, expr: &Expr<'_>, - ident_map: &FxHashMap>, + ident_map: &FxHashMap, BindingMode)>, pat: &Pat<'_>, else_body: &Expr<'_>, ) { @@ -167,7 +168,7 @@ fn emit_manual_let_else( fn replace_in_pattern( cx: &LateContext<'_>, span: Span, - ident_map: &FxHashMap>, + ident_map: &FxHashMap, BindingMode)>, pat: &Pat<'_>, app: &mut Applicability, top_level: bool, @@ -185,15 +186,16 @@ fn replace_in_pattern( match pat.kind { PatKind::Binding(_ann, _id, binding_name, opt_subpt) => { - let Some(pat_to_put) = ident_map.get(&binding_name.name) else { + let Some((pat_to_put, binding_mode)) = ident_map.get(&binding_name.name) else { break 'a; }; + let sn_pfx = binding_mode.prefix_str(); let (sn_ptp, _) = snippet_with_context(cx, pat_to_put.span, span.ctxt(), "", app); if let Some(subpt) = opt_subpt { let subpt = replace_in_pattern(cx, span, ident_map, subpt, app, false); - return format!("{sn_ptp} @ {subpt}"); + return format!("{sn_pfx}{sn_ptp} @ {subpt}"); } - return sn_ptp.to_string(); + return format!("{sn_pfx}{sn_ptp}"); }, PatKind::Or(pats) => { let patterns = pats @@ -211,17 +213,18 @@ fn replace_in_pattern( .iter() .map(|fld| { if let PatKind::Binding(_, _, name, None) = fld.pat.kind - && let Some(pat_to_put) = ident_map.get(&name.name) + && let Some((pat_to_put, binding_mode)) = ident_map.get(&name.name) { + let sn_pfx = binding_mode.prefix_str(); let (sn_fld_name, _) = snippet_with_context(cx, fld.ident.span, span.ctxt(), "", app); let (sn_ptp, _) = snippet_with_context(cx, pat_to_put.span, span.ctxt(), "", app); // TODO: this is a bit of a hack, but it does its job. Ideally, we'd check if pat_to_put is // a PatKind::Binding but that is also hard to get right. if sn_fld_name == sn_ptp { // Field init shorthand - return format!("{sn_fld_name}"); + return format!("{sn_pfx}{sn_fld_name}"); } - return format!("{sn_fld_name}: {sn_ptp}"); + return format!("{sn_fld_name}: {sn_pfx}{sn_ptp}"); } let (sn_fld, _) = snippet_with_context(cx, fld.span, span.ctxt(), "", app); sn_fld.into_owned() @@ -334,7 +337,7 @@ fn expr_simple_identity_map<'a, 'hir>( local_pat: &'a Pat<'hir>, let_pat: &'_ Pat<'hir>, expr: &'_ Expr<'hir>, -) -> Option>> { +) -> Option, BindingMode)>> { let peeled = peel_blocks(expr); let (sub_pats, paths) = match (local_pat.kind, peeled.kind) { (PatKind::Tuple(pats, _), ExprKind::Tup(exprs)) | (PatKind::Slice(pats, ..), ExprKind::Array(exprs)) => { @@ -351,9 +354,9 @@ fn expr_simple_identity_map<'a, 'hir>( return None; } - let mut pat_bindings = FxHashSet::default(); - let_pat.each_binding_or_first(&mut |_ann, _hir_id, _sp, ident| { - pat_bindings.insert(ident); + let mut pat_bindings = FxHashMap::default(); + let_pat.each_binding_or_first(&mut |binding_mode, _hir_id, _sp, ident| { + pat_bindings.insert(ident, binding_mode); }); if pat_bindings.len() < paths.len() { // This rebinds some bindings from the outer scope, or it repeats some copy-able bindings multiple @@ -366,12 +369,10 @@ fn expr_simple_identity_map<'a, 'hir>( for (sub_pat, path) in sub_pats.iter().zip(paths.iter()) { if let ExprKind::Path(QPath::Resolved(_ty, path)) = path.kind && let [path_seg] = path.segments + && let ident = path_seg.ident + && let Some(let_binding_mode) = pat_bindings.remove(&ident) { - let ident = path_seg.ident; - if !pat_bindings.remove(&ident) { - return None; - } - ident_map.insert(ident.name, sub_pat); + ident_map.insert(ident.name, (sub_pat, let_binding_mode)); } else { return None; } diff --git a/clippy_lints/src/manual_slice_size_calculation.rs b/clippy_lints/src/manual_slice_size_calculation.rs index 18901f7399d9..2dad0fa4925e 100644 --- a/clippy_lints/src/manual_slice_size_calculation.rs +++ b/clippy_lints/src/manual_slice_size_calculation.rs @@ -24,12 +24,12 @@ declare_clippy_lint! { /// ### Example /// ```no_run /// # let data : &[i32] = &[1, 2, 3]; - /// let newlen = data.len() * std::mem::size_of::(); + /// let newlen = data.len() * size_of::(); /// ``` /// Use instead: /// ```no_run /// # let data : &[i32] = &[1, 2, 3]; - /// let newlen = std::mem::size_of_val(data); + /// let newlen = size_of_val(data); /// ``` #[clippy::version = "1.70.0"] pub MANUAL_SLICE_SIZE_CALCULATION, diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index 35caa7d1f3a6..2b9173e6f412 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -1110,11 +1110,9 @@ impl<'tcx> LateLintPass<'tcx> for Matches { } } } - // If there are still comments, it means they are outside of the arms, therefore - // we should not lint. - if match_comments.is_empty() { - single_match::check(cx, ex, arms, expr); - } + // If there are still comments, it means they are outside of the arms. Tell the lint + // code about it. + single_match::check(cx, ex, arms, expr, !match_comments.is_empty()); match_bool::check(cx, ex, arms, expr); overlapping_arms::check(cx, ex, arms); match_wild_enum::check(cx, ex, arms); diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index 2f46eaaabb36..56fbd626eefc 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{SpanRangeExt, expr_block, snippet, snippet_block_with_context}; use clippy_utils::ty::implements_trait; use clippy_utils::{ @@ -6,7 +6,7 @@ use clippy_utils::{ }; use core::ops::ControlFlow; use rustc_arena::DroplessArena; -use rustc_errors::Applicability; +use rustc_errors::{Applicability, Diag}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{Visitor, walk_pat}; use rustc_hir::{Arm, Expr, ExprKind, HirId, Node, Pat, PatExpr, PatExprKind, PatKind, QPath, StmtKind}; @@ -32,7 +32,7 @@ fn empty_arm_has_comment(cx: &LateContext<'_>, span: Span) -> bool { } #[rustfmt::skip] -pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>], expr: &'tcx Expr<'_>) { +pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>], expr: &'tcx Expr<'_>, contains_comments: bool) { if let [arm1, arm2] = arms && arm1.guard.is_none() && arm2.guard.is_none() @@ -77,15 +77,31 @@ pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'_>, arms: &'tc } } - report_single_pattern(cx, ex, arm1, expr, els); + report_single_pattern(cx, ex, arm1, expr, els, contains_comments); } } } -fn report_single_pattern(cx: &LateContext<'_>, ex: &Expr<'_>, arm: &Arm<'_>, expr: &Expr<'_>, els: Option<&Expr<'_>>) { +fn report_single_pattern( + cx: &LateContext<'_>, + ex: &Expr<'_>, + arm: &Arm<'_>, + expr: &Expr<'_>, + els: Option<&Expr<'_>>, + contains_comments: bool, +) { let lint = if els.is_some() { SINGLE_MATCH_ELSE } else { SINGLE_MATCH }; let ctxt = expr.span.ctxt(); - let mut app = Applicability::MachineApplicable; + let note = |diag: &mut Diag<'_, ()>| { + if contains_comments { + diag.note("you might want to preserve the comments from inside the `match`"); + } + }; + let mut app = if contains_comments { + Applicability::MaybeIncorrect + } else { + Applicability::MachineApplicable + }; let els_str = els.map_or(String::new(), |els| { format!(" else {}", expr_block(cx, els, ctxt, "..", Some(expr.span), &mut app)) }); @@ -109,7 +125,10 @@ fn report_single_pattern(cx: &LateContext<'_>, ex: &Expr<'_>, arm: &Arm<'_>, exp } (sugg, "try") }; - span_lint_and_sugg(cx, lint, expr.span, msg, help, sugg.to_string(), app); + span_lint_and_then(cx, lint, expr.span, msg, |diag| { + diag.span_suggestion(expr.span, help, sugg.to_string(), app); + note(diag); + }); return; } @@ -162,7 +181,10 @@ fn report_single_pattern(cx: &LateContext<'_>, ex: &Expr<'_>, arm: &Arm<'_>, exp (msg, sugg) }; - span_lint_and_sugg(cx, lint, expr.span, msg, "try", sugg, app); + span_lint_and_then(cx, lint, expr.span, msg, |diag| { + diag.span_suggestion(expr.span, "try", sugg.to_string(), app); + note(diag); + }); } struct PatVisitor<'tcx> { diff --git a/clippy_lints/src/methods/io_other_error.rs b/clippy_lints/src/methods/io_other_error.rs index 4659e9e163fe..bdc834bd47a5 100644 --- a/clippy_lints/src/methods/io_other_error.rs +++ b/clippy_lints/src/methods/io_other_error.rs @@ -27,7 +27,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, path: &Expr<'_>, args "use `std::io::Error::other`", vec![ (new_segment.ident.span, "other".to_owned()), - (error_kind.span.until(error.span), String::new()), + (error_kind.span.until(error.span.source_callsite()), String::new()), ], Applicability::MachineApplicable, ); diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 7dde21d3edb1..1d9296016e25 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -4447,13 +4447,13 @@ declare_clippy_lint! { /// ### Example /// ```no_run /// fn foo(values: &[u8]) -> bool { - /// values.iter().any(|&v| v == 10) + /// values.iter().any(|&v| v == 10) /// } /// ``` /// Use instead: /// ```no_run /// fn foo(values: &[u8]) -> bool { - /// values.contains(&10) + /// values.contains(&10) /// } /// ``` #[clippy::version = "1.86.0"] diff --git a/clippy_lints/src/methods/needless_collect.rs b/clippy_lints/src/methods/needless_collect.rs index 45f79dd44f2a..56ff7e2c61b2 100644 --- a/clippy_lints/src/methods/needless_collect.rs +++ b/clippy_lints/src/methods/needless_collect.rs @@ -1,3 +1,5 @@ +use std::ops::ControlFlow; + use super::NEEDLESS_COLLECT; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::source::{snippet, snippet_with_applicability}; @@ -9,9 +11,9 @@ use clippy_utils::{ }; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, MultiSpan}; -use rustc_hir::intravisit::{Visitor, walk_block, walk_expr}; +use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_stmt}; use rustc_hir::{ - BindingMode, Block, Expr, ExprKind, HirId, HirIdSet, LetStmt, Mutability, Node, PatKind, Stmt, StmtKind, + BindingMode, Block, Expr, ExprKind, HirId, HirIdSet, LetStmt, Mutability, Node, Pat, PatKind, Stmt, StmtKind, }; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; @@ -103,6 +105,12 @@ pub(super) fn check<'tcx>( return; } + if let IterFunctionKind::IntoIter(hir_id) = iter_call.func + && !check_iter_expr_used_only_as_iterator(cx, hir_id, block) + { + return; + } + // Suggest replacing iter_call with iter_replacement, and removing stmt let mut span = MultiSpan::from_span(name_span); span.push_span_label(iter_call.span, "the iterator could be used here instead"); @@ -253,7 +261,7 @@ struct IterFunction { impl IterFunction { fn get_iter_method(&self, cx: &LateContext<'_>) -> String { match &self.func { - IterFunctionKind::IntoIter => String::new(), + IterFunctionKind::IntoIter(_) => String::new(), IterFunctionKind::Len => String::from(".count()"), IterFunctionKind::IsEmpty => String::from(".next().is_none()"), IterFunctionKind::Contains(span) => { @@ -268,7 +276,7 @@ impl IterFunction { } fn get_suggestion_text(&self) -> &'static str { match &self.func { - IterFunctionKind::IntoIter => { + IterFunctionKind::IntoIter(_) => { "use the original Iterator instead of collecting it and then producing a new one" }, IterFunctionKind::Len => { @@ -284,7 +292,7 @@ impl IterFunction { } } enum IterFunctionKind { - IntoIter, + IntoIter(HirId), Len, IsEmpty, Contains(Span), @@ -343,7 +351,7 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> { } match method_name.ident.name.as_str() { "into_iter" => self.uses.push(Some(IterFunction { - func: IterFunctionKind::IntoIter, + func: IterFunctionKind::IntoIter(expr.hir_id), span: expr.span, })), "len" => self.uses.push(Some(IterFunction { @@ -520,3 +528,61 @@ fn get_captured_ids(cx: &LateContext<'_>, ty: Ty<'_>) -> HirIdSet { set } + +struct IteratorMethodCheckVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + hir_id_of_expr: HirId, + hir_id_of_let_binding: Option, +} + +impl<'tcx> Visitor<'tcx> for IteratorMethodCheckVisitor<'_, 'tcx> { + type Result = ControlFlow<()>; + fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) -> ControlFlow<()> { + if let ExprKind::MethodCall(_method_name, recv, _args, _) = &expr.kind + && (recv.hir_id == self.hir_id_of_expr + || self + .hir_id_of_let_binding + .is_some_and(|hid| path_to_local_id(recv, hid))) + && !is_trait_method(self.cx, expr, sym::Iterator) + { + return ControlFlow::Break(()); + } else if let ExprKind::Assign(place, value, _span) = &expr.kind + && value.hir_id == self.hir_id_of_expr + && let Some(id) = path_to_local(place) + { + // our iterator was directly assigned to a variable + self.hir_id_of_let_binding = Some(id); + } + walk_expr(self, expr) + } + fn visit_stmt(&mut self, stmt: &'tcx Stmt<'tcx>) -> ControlFlow<()> { + if let StmtKind::Let(LetStmt { + init: Some(expr), + pat: + Pat { + kind: PatKind::Binding(BindingMode::NONE | BindingMode::MUT, id, _, None), + .. + }, + .. + }) = &stmt.kind + && expr.hir_id == self.hir_id_of_expr + { + // our iterator was directly assigned to a variable + self.hir_id_of_let_binding = Some(*id); + } + walk_stmt(self, stmt) + } +} + +fn check_iter_expr_used_only_as_iterator<'tcx>( + cx: &LateContext<'tcx>, + hir_id_of_expr: HirId, + block: &'tcx Block<'tcx>, +) -> bool { + let mut visitor = IteratorMethodCheckVisitor { + cx, + hir_id_of_expr, + hir_id_of_let_binding: None, + }; + visitor.visit_block(block).is_continue() +} diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index a71b3659fd24..62ba3012643c 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -6,7 +6,8 @@ use clippy_utils::source::{SpanRangeExt, snippet}; use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, is_type_diagnostic_item, is_type_lang_item}; use clippy_utils::visitors::find_all_ret_expressions; use clippy_utils::{ - fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, peel_middle_ty_refs, return_ty, + fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, is_expr_temporary_value, peel_middle_ty_refs, + return_ty, }; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -219,6 +220,8 @@ fn check_into_iter_call_arg( && let Some(receiver_snippet) = receiver.span.get_source_text(cx) // If the receiver is a `Cow`, we can't remove the `into_owned` generally, see https://github.com/rust-lang/rust-clippy/issues/13624. && !is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(receiver), sym::Cow) + // Calling `iter()` on a temporary object can lead to false positives. #14242 + && !is_expr_temporary_value(cx, receiver) { if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver, true) { return true; diff --git a/clippy_lints/src/misc_early/mixed_case_hex_literals.rs b/clippy_lints/src/misc_early/mixed_case_hex_literals.rs index 9151cc633200..4fbd3c9874db 100644 --- a/clippy_lints/src/misc_early/mixed_case_hex_literals.rs +++ b/clippy_lints/src/misc_early/mixed_case_hex_literals.rs @@ -1,32 +1,51 @@ -use clippy_utils::diagnostics::span_lint; +use clippy_utils::diagnostics::span_lint_and_help; use rustc_lint::EarlyContext; use rustc_span::Span; use super::MIXED_CASE_HEX_LITERALS; pub(super) fn check(cx: &EarlyContext<'_>, lit_span: Span, suffix: &str, lit_snip: &str) { - let Some(maybe_last_sep_idx) = lit_snip.len().checked_sub(suffix.len() + 1) else { - return; // It's useless so shouldn't lint. + let num_end_idx = match lit_snip.strip_suffix(suffix) { + Some(p) if p.ends_with('_') => lit_snip.len() - (suffix.len() + 1), + Some(_) => lit_snip.len() - suffix.len(), + None => lit_snip.len(), }; - if maybe_last_sep_idx <= 2 { + + if num_end_idx <= 2 { // It's meaningless or causes range error. return; } + let mut seen = (false, false); - for ch in &lit_snip.as_bytes()[2..=maybe_last_sep_idx] { + for ch in &lit_snip.as_bytes()[2..num_end_idx] { match ch { b'a'..=b'f' => seen.0 = true, b'A'..=b'F' => seen.1 = true, _ => {}, } if seen.0 && seen.1 { - span_lint( + let raw_digits = &lit_snip[2..num_end_idx]; + let (sugg_lower, sugg_upper) = if suffix.is_empty() { + ( + format!("0x{}", raw_digits.to_lowercase()), + format!("0x{}", raw_digits.to_uppercase()), + ) + } else { + ( + format!("0x{}_{}", raw_digits.to_lowercase(), suffix), + format!("0x{}_{}", raw_digits.to_uppercase(), suffix), + ) + }; + + span_lint_and_help( cx, MIXED_CASE_HEX_LITERALS, lit_span, "inconsistent casing in hexadecimal literal", + None, + format!("consider using `{sugg_lower}` or `{sugg_upper}`"), ); - break; + return; } } } diff --git a/clippy_lints/src/missing_inline.rs b/clippy_lints/src/missing_inline.rs index 8045ab97d384..f49e03ea7652 100644 --- a/clippy_lints/src/missing_inline.rs +++ b/clippy_lints/src/missing_inline.rs @@ -37,7 +37,7 @@ declare_clippy_lint! { /// /// struct Baz; /// impl Baz { - /// fn private() {} // ok + /// fn private() {} // ok /// } /// /// impl Bar for Baz { @@ -46,13 +46,13 @@ declare_clippy_lint! { /// /// pub struct PubBaz; /// impl PubBaz { - /// fn private() {} // ok - /// pub fn not_private() {} // missing #[inline] + /// fn private() {} // ok + /// pub fn not_private() {} // missing #[inline] /// } /// /// impl Bar for PubBaz { - /// fn bar() {} // missing #[inline] - /// fn def_bar() {} // missing #[inline] + /// fn bar() {} // missing #[inline] + /// fn def_bar() {} // missing #[inline] /// } /// ``` /// diff --git a/clippy_lints/src/needless_late_init.rs b/clippy_lints/src/needless_late_init.rs index 3efbed0c2365..a914267cf500 100644 --- a/clippy_lints/src/needless_late_init.rs +++ b/clippy_lints/src/needless_late_init.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::path_to_local; -use clippy_utils::source::{SourceText, SpanRangeExt}; +use clippy_utils::source::{SourceText, SpanRangeExt, snippet}; use clippy_utils::ty::needs_ordered_drop; use clippy_utils::visitors::{for_each_expr, for_each_expr_without_closures, is_local_used}; use core::ops::ControlFlow; @@ -100,7 +100,6 @@ fn stmt_needs_ordered_drop(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool { #[derive(Debug)] struct LocalAssign { lhs_id: HirId, - lhs_span: Span, rhs_span: Span, span: Span, } @@ -118,7 +117,6 @@ impl LocalAssign { Some(Self { lhs_id: path_to_local(lhs)?, - lhs_span: lhs.span, rhs_span: rhs.span.source_callsite(), span, }) @@ -281,7 +279,10 @@ fn check<'tcx>( format!("move the declaration `{binding_name}` here"), vec![ (local_stmt.span, String::new()), - (assign.lhs_span, let_snippet.to_owned()), + ( + assign.span, + let_snippet.to_owned() + " = " + &snippet(cx, assign.rhs_span, ".."), + ), ], Applicability::MachineApplicable, ); diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 7bee89086b80..55ca875edcee 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::is_self; use clippy_utils::ptr::get_spans; use clippy_utils::source::{SpanRangeExt, snippet}; use clippy_utils::ty::{ implements_trait, implements_trait_with_env_from_iter, is_copy, is_type_diagnostic_item, is_type_lang_item, }; +use clippy_utils::{is_self, peel_hir_ty_options}; use rustc_abi::ExternAbi; use rustc_errors::{Applicability, Diag}; use rustc_hir::intravisit::FnKind; @@ -279,10 +279,10 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { } } - diag.span_suggestion( - input.span, + diag.span_suggestion_verbose( + peel_hir_ty_options(cx, input).span.shrink_to_lo(), "consider taking a reference instead", - format!("&{}", snippet(cx, input.span, "_")), + '&', Applicability::MaybeIncorrect, ); }; diff --git a/clippy_lints/src/needless_question_mark.rs b/clippy_lints/src/needless_question_mark.rs index 37463cfec9a2..72b0a80260e9 100644 --- a/clippy_lints/src/needless_question_mark.rs +++ b/clippy_lints/src/needless_question_mark.rs @@ -40,7 +40,7 @@ declare_clippy_lint! { /// } /// /// fn f(to: TO) -> Option { - /// to.magic + /// to.magic /// } /// /// struct TR { diff --git a/clippy_lints/src/operators/mod.rs b/clippy_lints/src/operators/mod.rs index 80459945094e..f758d08d3663 100644 --- a/clippy_lints/src/operators/mod.rs +++ b/clippy_lints/src/operators/mod.rs @@ -18,7 +18,6 @@ mod modulo_one; mod needless_bitwise_bool; mod numeric_arithmetic; mod op_ref; -mod ptr_eq; mod self_assignment; mod verbose_bit_mask; @@ -768,35 +767,6 @@ declare_clippy_lint! { "Boolean expressions that use bitwise rather than lazy operators" } -declare_clippy_lint! { - /// ### What it does - /// Use `std::ptr::eq` when applicable - /// - /// ### Why is this bad? - /// `ptr::eq` can be used to compare `&T` references - /// (which coerce to `*const T` implicitly) by their address rather than - /// comparing the values they point to. - /// - /// ### Example - /// ```no_run - /// let a = &[1, 2, 3]; - /// let b = &[1, 2, 3]; - /// - /// assert!(a as *const _ as usize == b as *const _ as usize); - /// ``` - /// Use instead: - /// ```no_run - /// let a = &[1, 2, 3]; - /// let b = &[1, 2, 3]; - /// - /// assert!(std::ptr::eq(a, b)); - /// ``` - #[clippy::version = "1.49.0"] - pub PTR_EQ, - style, - "use `std::ptr::eq` when comparing raw pointers" -} - declare_clippy_lint! { /// ### What it does /// Checks for explicit self-assignments. @@ -902,7 +872,6 @@ impl_lint_pass!(Operators => [ MODULO_ONE, MODULO_ARITHMETIC, NEEDLESS_BITWISE_BOOL, - PTR_EQ, SELF_ASSIGNMENT, MANUAL_MIDPOINT, ]); @@ -921,7 +890,6 @@ impl<'tcx> LateLintPass<'tcx> for Operators { erasing_op::check(cx, e, op.node, lhs, rhs); identity_op::check(cx, e, op.node, lhs, rhs); needless_bitwise_bool::check(cx, e, op.node, lhs, rhs); - ptr_eq::check(cx, e, op.node, lhs, rhs); manual_midpoint::check(cx, e, op.node, lhs, rhs, self.msrv); } self.arithmetic_context.check_binary(cx, e, op.node, lhs, rhs); diff --git a/clippy_lints/src/operators/ptr_eq.rs b/clippy_lints/src/operators/ptr_eq.rs deleted file mode 100644 index 8118ad59bb71..000000000000 --- a/clippy_lints/src/operators/ptr_eq.rs +++ /dev/null @@ -1,62 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::SpanRangeExt; -use clippy_utils::std_or_core; -use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Expr, ExprKind}; -use rustc_lint::LateContext; - -use super::PTR_EQ; - -pub(super) fn check<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'_>, - op: BinOpKind, - left: &'tcx Expr<'_>, - right: &'tcx Expr<'_>, -) { - if BinOpKind::Eq == op { - let (left, right) = match (expr_as_cast_to_usize(cx, left), expr_as_cast_to_usize(cx, right)) { - (Some(lhs), Some(rhs)) => (lhs, rhs), - _ => (left, right), - }; - - if let Some(left_var) = expr_as_cast_to_raw_pointer(cx, left) - && let Some(right_var) = expr_as_cast_to_raw_pointer(cx, right) - && let Some(left_snip) = left_var.span.get_source_text(cx) - && let Some(right_snip) = right_var.span.get_source_text(cx) - { - let Some(top_crate) = std_or_core(cx) else { return }; - span_lint_and_sugg( - cx, - PTR_EQ, - expr.span, - format!("use `{top_crate}::ptr::eq` when comparing raw pointers"), - "try", - format!("{top_crate}::ptr::eq({left_snip}, {right_snip})"), - Applicability::MachineApplicable, - ); - } - } -} - -// If the given expression is a cast to a usize, return the lhs of the cast -// E.g., `foo as *const _ as usize` returns `foo as *const _`. -fn expr_as_cast_to_usize<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { - if cx.typeck_results().expr_ty(cast_expr) == cx.tcx.types.usize { - if let ExprKind::Cast(expr, _) = cast_expr.kind { - return Some(expr); - } - } - None -} - -// If the given expression is a cast to a `*const` pointer, return the lhs of the cast -// E.g., `foo as *const _` returns `foo`. -fn expr_as_cast_to_raw_pointer<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { - if cx.typeck_results().expr_ty(cast_expr).is_raw_ptr() { - if let ExprKind::Cast(expr, _) = cast_expr.kind { - return Some(expr); - } - } - None -} diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 75b18bc651e2..6f302ea19621 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -1,16 +1,23 @@ +use std::ops::ControlFlow; + use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::sugg::Sugg; +use clippy_utils::ty::is_copy; use clippy_utils::{ CaptureKind, can_move_expr_to_closure, eager_or_lazy, higher, is_else_clause, is_in_const_context, is_res_lang_ctor, peel_blocks, peel_hir_expr_while, }; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; use rustc_hir::def::Res; +use rustc_hir::intravisit::{Visitor, walk_expr, walk_path}; use rustc_hir::{ - Arm, BindingMode, Expr, ExprKind, MatchSource, Mutability, Pat, PatExpr, PatExprKind, PatKind, Path, QPath, UnOp, + Arm, BindingMode, Expr, ExprKind, HirId, MatchSource, Mutability, Node, Pat, PatExpr, PatExprKind, PatKind, Path, + QPath, UnOp, }; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::nested_filter; use rustc_session::declare_lint_pass; use rustc_span::SyntaxContext; @@ -110,11 +117,12 @@ fn format_option_in_sugg(cond_sugg: Sugg<'_>, as_ref: bool, as_mut: bool) -> Str ) } +#[expect(clippy::too_many_lines)] fn try_get_option_occurrence<'tcx>( cx: &LateContext<'tcx>, ctxt: SyntaxContext, pat: &Pat<'tcx>, - expr: &Expr<'_>, + expr: &'tcx Expr<'_>, if_then: &'tcx Expr<'_>, if_else: &'tcx Expr<'_>, ) -> Option { @@ -182,6 +190,26 @@ fn try_get_option_occurrence<'tcx>( Some(CaptureKind::Ref(Mutability::Not)) | None => (), } } + } else if !is_copy(cx, cx.typeck_results().expr_ty(expr)) + // TODO: Cover more match cases + && matches!( + expr.kind, + ExprKind::Field(_, _) | ExprKind::Path(_) | ExprKind::Index(_, _, _) + ) + { + let mut condition_visitor = ConditionVisitor { + cx, + identifiers: FxHashSet::default(), + }; + condition_visitor.visit_expr(cond_expr); + + let mut reference_visitor = ReferenceVisitor { + cx, + identifiers: condition_visitor.identifiers, + }; + if reference_visitor.visit_expr(none_body).is_break() { + return None; + } } let mut app = Applicability::Unspecified; @@ -219,6 +247,60 @@ fn try_get_option_occurrence<'tcx>( None } +/// This visitor looks for bindings in the block that mention a local variable. Then gets the +/// identifiers. The list of identifiers will then be used to check if the block mentions the +/// same local. See [`ReferenceVisitor`] for more. +struct ConditionVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + identifiers: FxHashSet, +} + +impl<'tcx> Visitor<'tcx> for ConditionVisitor<'_, 'tcx> { + type NestedFilter = nested_filter::All; + + fn visit_path(&mut self, path: &Path<'tcx>, _: HirId) { + if let Res::Local(local_id) = path.res + && let Node::Pat(pat) = self.cx.tcx.hir_node(local_id) + && let PatKind::Binding(_, local_id, ..) = pat.kind + { + self.identifiers.insert(local_id); + } + walk_path(self, path); + } + + fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt { + self.cx.tcx + } +} + +/// This visitor checks if the block contains references to the local variables that are +/// used in the block. See [`ConditionVisitor`] for more. +struct ReferenceVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + identifiers: FxHashSet, +} + +impl<'tcx> Visitor<'tcx> for ReferenceVisitor<'_, 'tcx> { + type NestedFilter = nested_filter::All; + type Result = ControlFlow<()>; + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) -> ControlFlow<()> { + if let ExprKind::Path(ref path) = expr.kind + && let QPath::Resolved(_, path) = path + && let Res::Local(local_id) = path.res + && let Node::Pat(pat) = self.cx.tcx.hir_node(local_id) + && let PatKind::Binding(_, local_id, ..) = pat.kind + && self.identifiers.contains(&local_id) + { + return ControlFlow::Break(()); + } + walk_expr(self, expr) + } + + fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt { + self.cx.tcx + } +} + fn try_get_inner_pat_and_is_result<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>) -> Option<(&'tcx Pat<'tcx>, bool)> { if let PatKind::TupleStruct(ref qpath, [inner_pat], ..) = pat.kind { let res = cx.qpath_res(qpath, pat.hir_id); diff --git a/clippy_lints/src/partialeq_ne_impl.rs b/clippy_lints/src/partialeq_ne_impl.rs index 55676522419c..65671b478ba7 100644 --- a/clippy_lints/src/partialeq_ne_impl.rs +++ b/clippy_lints/src/partialeq_ne_impl.rs @@ -19,8 +19,8 @@ declare_clippy_lint! { /// struct Foo; /// /// impl PartialEq for Foo { - /// fn eq(&self, other: &Foo) -> bool { true } - /// fn ne(&self, other: &Foo) -> bool { !(self == other) } + /// fn eq(&self, other: &Foo) -> bool { true } + /// fn ne(&self, other: &Foo) -> bool { !(self == other) } /// } /// ``` #[clippy::version = "pre 1.29.0"] diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index dae0709a5404..55f1ece05593 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -148,7 +148,36 @@ declare_clippy_lint! { "invalid usage of a null pointer, suggesting `NonNull::dangling()` instead" } -declare_lint_pass!(Ptr => [PTR_ARG, CMP_NULL, MUT_FROM_REF, INVALID_NULL_PTR_USAGE]); +declare_clippy_lint! { + /// ### What it does + /// Use `std::ptr::eq` when applicable + /// + /// ### Why is this bad? + /// `ptr::eq` can be used to compare `&T` references + /// (which coerce to `*const T` implicitly) by their address rather than + /// comparing the values they point to. + /// + /// ### Example + /// ```no_run + /// let a = &[1, 2, 3]; + /// let b = &[1, 2, 3]; + /// + /// assert!(a as *const _ as usize == b as *const _ as usize); + /// ``` + /// Use instead: + /// ```no_run + /// let a = &[1, 2, 3]; + /// let b = &[1, 2, 3]; + /// + /// assert!(std::ptr::eq(a, b)); + /// ``` + #[clippy::version = "1.49.0"] + pub PTR_EQ, + style, + "use `std::ptr::eq` when comparing raw pointers" +} + +declare_lint_pass!(Ptr => [PTR_ARG, CMP_NULL, MUT_FROM_REF, INVALID_NULL_PTR_USAGE, PTR_EQ]); impl<'tcx> LateLintPass<'tcx> for Ptr { fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { @@ -253,10 +282,14 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { if let ExprKind::Binary(op, l, r) = expr.kind && (op.node == BinOpKind::Eq || op.node == BinOpKind::Ne) { - let non_null_path_snippet = match (is_null_path(cx, l), is_null_path(cx, r)) { - (true, false) if let Some(sugg) = Sugg::hir_opt(cx, r) => sugg.maybe_par(), - (false, true) if let Some(sugg) = Sugg::hir_opt(cx, l) => sugg.maybe_par(), - _ => return, + let non_null_path_snippet = match ( + is_lint_allowed(cx, CMP_NULL, expr.hir_id), + is_null_path(cx, l), + is_null_path(cx, r), + ) { + (false, true, false) if let Some(sugg) = Sugg::hir_opt(cx, r) => sugg.maybe_par(), + (false, false, true) if let Some(sugg) = Sugg::hir_opt(cx, l) => sugg.maybe_par(), + _ => return check_ptr_eq(cx, expr, op.node, l, r), }; span_lint_and_sugg( @@ -740,3 +773,71 @@ fn is_null_path(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { false } } + +fn check_ptr_eq<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + op: BinOpKind, + left: &'tcx Expr<'_>, + right: &'tcx Expr<'_>, +) { + if expr.span.from_expansion() { + return; + } + + // Remove one level of usize conversion if any + let (left, right) = match (expr_as_cast_to_usize(cx, left), expr_as_cast_to_usize(cx, right)) { + (Some(lhs), Some(rhs)) => (lhs, rhs), + _ => (left, right), + }; + + // This lint concerns raw pointers + let (left_ty, right_ty) = (cx.typeck_results().expr_ty(left), cx.typeck_results().expr_ty(right)); + if !left_ty.is_raw_ptr() || !right_ty.is_raw_ptr() { + return; + } + + let (left_var, right_var) = (peel_raw_casts(cx, left, left_ty), peel_raw_casts(cx, right, right_ty)); + + if let Some(left_snip) = left_var.span.get_source_text(cx) + && let Some(right_snip) = right_var.span.get_source_text(cx) + { + let Some(top_crate) = std_or_core(cx) else { return }; + let invert = if op == BinOpKind::Eq { "" } else { "!" }; + span_lint_and_sugg( + cx, + PTR_EQ, + expr.span, + format!("use `{top_crate}::ptr::eq` when comparing raw pointers"), + "try", + format!("{invert}{top_crate}::ptr::eq({left_snip}, {right_snip})"), + Applicability::MachineApplicable, + ); + } +} + +// If the given expression is a cast to a usize, return the lhs of the cast +// E.g., `foo as *const _ as usize` returns `foo as *const _`. +fn expr_as_cast_to_usize<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { + if cx.typeck_results().expr_ty(cast_expr) == cx.tcx.types.usize + && let ExprKind::Cast(expr, _) = cast_expr.kind + { + Some(expr) + } else { + None + } +} + +// Peel raw casts if the remaining expression can be coerced to it +fn peel_raw_casts<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, expr_ty: Ty<'tcx>) -> &'tcx Expr<'tcx> { + if let ExprKind::Cast(inner, _) = expr.kind + && let ty::RawPtr(target_ty, _) = expr_ty.kind() + && let inner_ty = cx.typeck_results().expr_ty(inner) + && let ty::RawPtr(inner_target_ty, _) | ty::Ref(_, inner_target_ty, _) = inner_ty.kind() + && target_ty == inner_target_ty + { + peel_raw_casts(cx, inner, inner_ty) + } else { + expr + } +} diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index 4f5f3eb6c15a..a80e1f79bbc7 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -3,7 +3,7 @@ use crate::question_mark_used::QUESTION_MARK_USED; use clippy_config::Conf; use clippy_config::types::MatchLintBehaviour; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::msrvs::Msrv; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use clippy_utils::{ @@ -145,8 +145,47 @@ fn check_let_some_else_return_none(cx: &LateContext<'_>, stmt: &Stmt<'_>) { { let mut applicability = Applicability::MaybeIncorrect; let init_expr_str = snippet_with_applicability(cx, init_expr.span, "..", &mut applicability); - let receiver_str = snippet_with_applicability(cx, inner_pat.span, "..", &mut applicability); - let sugg = format!("let {receiver_str} = {init_expr_str}?;",); + // Take care when binding is `ref` + let sugg = if let PatKind::Binding( + BindingMode(ByRef::Yes(ref_mutability), binding_mutability), + _hir_id, + ident, + subpattern, + ) = inner_pat.kind + { + let (from_method, replace_to) = match ref_mutability { + Mutability::Mut => (".as_mut()", "&mut "), + Mutability::Not => (".as_ref()", "&"), + }; + + let mutability_str = match binding_mutability { + Mutability::Mut => "mut ", + Mutability::Not => "", + }; + + // Handle subpattern (@ subpattern) + let maybe_subpattern = match subpattern { + Some(Pat { + kind: PatKind::Binding(BindingMode(ByRef::Yes(_), _), _, subident, None), + .. + }) => { + // avoid `&ref` + // note that, because you can't have aliased, mutable references, we don't have to worry about + // the outer and inner mutability being different + format!(" @ {subident}") + }, + Some(subpattern) => { + let substr = snippet_with_applicability(cx, subpattern.span, "..", &mut applicability); + format!(" @ {replace_to}{substr}") + }, + None => String::new(), + }; + + format!("let {mutability_str}{ident}{maybe_subpattern} = {init_expr_str}{from_method}?;") + } else { + let receiver_str = snippet_with_applicability(cx, inner_pat.span, "..", &mut applicability); + format!("let {receiver_str} = {init_expr_str}?;") + }; span_lint_and_sugg( cx, QUESTION_MARK, @@ -230,7 +269,7 @@ fn expr_return_none_or_err( /// /// ```ignore /// if option.is_none() { -/// return None; +/// return None; /// } /// ``` /// @@ -485,7 +524,8 @@ fn is_inferred_ret_closure(expr: &Expr<'_>) -> bool { impl<'tcx> LateLintPass<'tcx> for QuestionMark { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { - if !is_lint_allowed(cx, QUESTION_MARK_USED, stmt.hir_id) { + if !is_lint_allowed(cx, QUESTION_MARK_USED, stmt.hir_id) || !self.msrv.meets(cx, msrvs::QUESTION_MARK_OPERATOR) + { return; } @@ -501,7 +541,10 @@ impl<'tcx> LateLintPass<'tcx> for QuestionMark { return; } - if !self.inside_try_block() && !is_in_const_context(cx) && is_lint_allowed(cx, QUESTION_MARK_USED, expr.hir_id) + if !self.inside_try_block() + && !is_in_const_context(cx) + && is_lint_allowed(cx, QUESTION_MARK_USED, expr.hir_id) + && self.msrv.meets(cx, msrvs::QUESTION_MARK_OPERATOR) { check_is_none_or_err_and_early_return(cx, expr); check_if_let_some_or_err_and_early_return(cx, expr); diff --git a/clippy_lints/src/redundant_async_block.rs b/clippy_lints/src/redundant_async_block.rs index bc5e8fd2c258..8289ec47bc7e 100644 --- a/clippy_lints/src/redundant_async_block.rs +++ b/clippy_lints/src/redundant_async_block.rs @@ -23,7 +23,7 @@ declare_clippy_lint! { /// ### Example /// ```no_run /// let f = async { - /// 1 + 2 + /// 1 + 2 /// }; /// let fut = async { /// f.await @@ -32,7 +32,7 @@ declare_clippy_lint! { /// Use instead: /// ```no_run /// let f = async { - /// 1 + 2 + /// 1 + 2 /// }; /// let fut = f; /// ``` diff --git a/clippy_lints/src/redundant_locals.rs b/clippy_lints/src/redundant_locals.rs index defb6684cffb..8f33a47e2908 100644 --- a/clippy_lints/src/redundant_locals.rs +++ b/clippy_lints/src/redundant_locals.rs @@ -26,7 +26,7 @@ declare_clippy_lint! { /// let a = a; /// /// fn foo(b: i32) { - /// let b = b; + /// let b = b; /// } /// ``` /// Use instead: diff --git a/clippy_lints/src/redundant_pub_crate.rs b/clippy_lints/src/redundant_pub_crate.rs index 3a5f44db8720..f2fdac5a8afa 100644 --- a/clippy_lints/src/redundant_pub_crate.rs +++ b/clippy_lints/src/redundant_pub_crate.rs @@ -55,7 +55,9 @@ impl<'tcx> LateLintPass<'tcx> for RedundantPubCrate { // FIXME: `DUMMY_SP` isn't right here, because it causes the // resulting span to begin at the start of the file. let span = item.span.with_hi( - item.kind.ident().map(|ident| ident.span.hi()).unwrap_or(rustc_span::DUMMY_SP.hi()) + item.kind + .ident() + .map_or(rustc_span::DUMMY_SP.hi(), |ident| ident.span.hi()), ); let descr = cx.tcx.def_kind(item.owner_id).descr(item.owner_id.to_def_id()); span_lint_and_then( diff --git a/clippy_lints/src/size_of_in_element_count.rs b/clippy_lints/src/size_of_in_element_count.rs index dc19236011bd..835ec1e4ca1c 100644 --- a/clippy_lints/src/size_of_in_element_count.rs +++ b/clippy_lints/src/size_of_in_element_count.rs @@ -18,7 +18,6 @@ declare_clippy_lint! { /// ### Example /// ```rust,no_run /// # use std::ptr::copy_nonoverlapping; - /// # use std::mem::size_of; /// const SIZE: usize = 128; /// let x = [2u8; SIZE]; /// let mut y = [2u8; SIZE]; diff --git a/clippy_lints/src/size_of_ref.rs b/clippy_lints/src/size_of_ref.rs index b3d32a6d7d84..60d923bcd77e 100644 --- a/clippy_lints/src/size_of_ref.rs +++ b/clippy_lints/src/size_of_ref.rs @@ -8,7 +8,7 @@ use rustc_span::sym; declare_clippy_lint! { /// ### What it does /// - /// Checks for calls to `std::mem::size_of_val()` where the argument is + /// Checks for calls to `size_of_val()` where the argument is /// a reference to a reference. /// /// ### Why is this bad? @@ -29,7 +29,7 @@ declare_clippy_lint! { /// // is already a reference, `&self` is a double-reference. /// // The return value of `size_of_val()` therefore is the /// // size of the reference-type, not the size of `self`. - /// std::mem::size_of_val(&self) + /// size_of_val(&self) /// } /// } /// ``` @@ -42,14 +42,14 @@ declare_clippy_lint! { /// impl Foo { /// fn size(&self) -> usize { /// // Correct - /// std::mem::size_of_val(self) + /// size_of_val(self) /// } /// } /// ``` #[clippy::version = "1.68.0"] pub SIZE_OF_REF, suspicious, - "Argument to `std::mem::size_of_val()` is a double-reference, which is almost certainly unintended" + "Argument to `size_of_val()` is a double-reference, which is almost certainly unintended" } declare_lint_pass!(SizeOfRef => [SIZE_OF_REF]); @@ -65,9 +65,9 @@ impl LateLintPass<'_> for SizeOfRef { cx, SIZE_OF_REF, expr.span, - "argument to `std::mem::size_of_val()` is a reference to a reference", + "argument to `size_of_val()` is a reference to a reference", None, - "dereference the argument to `std::mem::size_of_val()` to get the size of the value instead of the size of the reference-type", + "dereference the argument to `size_of_val()` to get the size of the value instead of the size of the reference-type", ); } } diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 4a5f143a2d34..27c548bed9f6 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -14,6 +14,8 @@ use rustc_session::declare_lint_pass; use rustc_span::source_map::Spanned; use rustc_span::sym; +use std::ops::ControlFlow; + declare_clippy_lint! { /// ### What it does /// Checks for string appends of the form `x = x + y` (without @@ -438,27 +440,94 @@ declare_clippy_lint! { declare_lint_pass!(StringToString => [STRING_TO_STRING]); +fn is_parent_map_like(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { + if let Some(parent_expr) = get_parent_expr(cx, expr) + && let ExprKind::MethodCall(name, _, _, parent_span) = parent_expr.kind + && name.ident.name == sym::map + && let Some(caller_def_id) = cx.typeck_results().type_dependent_def_id(parent_expr.hir_id) + && (clippy_utils::is_diag_item_method(cx, caller_def_id, sym::Result) + || clippy_utils::is_diag_item_method(cx, caller_def_id, sym::Option) + || clippy_utils::is_diag_trait_item(cx, caller_def_id, sym::Iterator)) + { + Some(parent_span) + } else { + None + } +} + +fn is_called_from_map_like(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { + // Look for a closure as parent of `expr`, discarding simple blocks + let parent_closure = cx + .tcx + .hir_parent_iter(expr.hir_id) + .try_fold(expr.hir_id, |child_hir_id, (_, node)| match node { + // Check that the child expression is the only expression in the block + Node::Block(block) if block.stmts.is_empty() && block.expr.map(|e| e.hir_id) == Some(child_hir_id) => { + ControlFlow::Continue(block.hir_id) + }, + Node::Expr(expr) if matches!(expr.kind, ExprKind::Block(..)) => ControlFlow::Continue(expr.hir_id), + Node::Expr(expr) if matches!(expr.kind, ExprKind::Closure(_)) => ControlFlow::Break(Some(expr)), + _ => ControlFlow::Break(None), + }) + .break_value()?; + is_parent_map_like(cx, parent_closure?) +} + +fn suggest_cloned_string_to_string(cx: &LateContext<'_>, span: rustc_span::Span) { + span_lint_and_sugg( + cx, + STRING_TO_STRING, + span, + "`to_string()` called on a `String`", + "try", + "cloned()".to_string(), + Applicability::MachineApplicable, + ); +} + impl<'tcx> LateLintPass<'tcx> for StringToString { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { if expr.span.from_expansion() { return; } - if let ExprKind::MethodCall(path, self_arg, [], _) = &expr.kind - && path.ident.name == sym::to_string - && let ty = cx.typeck_results().expr_ty(self_arg) - && is_type_lang_item(cx, ty, LangItem::String) - { - #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] - span_lint_and_then( - cx, - STRING_TO_STRING, - expr.span, - "`to_string()` called on a `String`", - |diag| { - diag.help("consider using `.clone()`"); - }, - ); + match &expr.kind { + ExprKind::MethodCall(path, self_arg, [], _) => { + if path.ident.name == sym::to_string + && let ty = cx.typeck_results().expr_ty(self_arg) + && is_type_lang_item(cx, ty.peel_refs(), LangItem::String) + { + if let Some(parent_span) = is_called_from_map_like(cx, expr) { + suggest_cloned_string_to_string(cx, parent_span); + } else { + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( + cx, + STRING_TO_STRING, + expr.span, + "`to_string()` called on a `String`", + |diag| { + diag.help("consider using `.clone()`"); + }, + ); + } + } + }, + ExprKind::Path(QPath::TypeRelative(ty, segment)) => { + if segment.ident.name == sym::to_string + && let rustc_hir::TyKind::Path(QPath::Resolved(_, path)) = ty.peel_refs().kind + && let rustc_hir::def::Res::Def(_, def_id) = path.res + && cx + .tcx + .lang_items() + .get(LangItem::String) + .is_some_and(|lang_id| lang_id == def_id) + && let Some(parent_span) = is_parent_map_like(cx, expr) + { + suggest_cloned_string_to_string(cx, parent_span); + } + }, + _ => {}, } } } diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index 2e974374c99e..b6f4c4d7f0a4 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -61,10 +61,6 @@ declare_clippy_lint! { /// `Vec` already keeps its contents in a separate area on /// the heap. So if you `Box` its contents, you just add another level of indirection. /// - /// ### Known problems - /// Vec> makes sense if T is a large type (see [#3530](https://github.com/rust-lang/rust-clippy/issues/3530), - /// 1st comment). - /// /// ### Example /// ```no_run /// struct X { diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs index 4f1a017522e4..a2938c86c76a 100644 --- a/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -312,6 +312,25 @@ fn expr_has_unnecessary_safety_comment<'tcx>( }, _, ) => ControlFlow::Break(()), + // `_ = foo()` is desugared to `{ let _ = foo(); }` + hir::ExprKind::Block( + Block { + rules: BlockCheckMode::DefaultBlock, + stmts: + [ + hir::Stmt { + kind: + hir::StmtKind::Let(hir::LetStmt { + source: hir::LocalSource::AssignDesugar(_), + .. + }), + .. + }, + ], + .. + }, + _, + ) => ControlFlow::Continue(Descend::Yes), // statements will be handled by check_stmt itself again hir::ExprKind::Block(..) => ControlFlow::Continue(Descend::No), _ => ControlFlow::Continue(Descend::Yes), @@ -339,6 +358,33 @@ fn is_unsafe_from_proc_macro(cx: &LateContext<'_>, span: Span) -> bool { .is_none_or(|src| !src.starts_with("unsafe")) } +fn find_unsafe_block_parent_in_expr<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'tcx>, +) -> Option<(Span, HirId)> { + match cx.tcx.parent_hir_node(expr.hir_id) { + Node::LetStmt(hir::LetStmt { span, hir_id, .. }) + | Node::Expr(hir::Expr { + hir_id, + kind: hir::ExprKind::Assign(_, _, span), + .. + }) => Some((*span, *hir_id)), + Node::Expr(expr) => find_unsafe_block_parent_in_expr(cx, expr), + node if let Some((span, hir_id)) = span_and_hid_of_item_alike_node(&node) + && is_const_or_static(&node) => + { + Some((span, hir_id)) + }, + + _ => { + if is_branchy(expr) { + return None; + } + Some((expr.span, expr.hir_id)) + }, + } +} + // Checks if any parent {expression, statement, block, local, const, static} // has a safety comment fn block_parents_have_safety_comment( @@ -348,21 +394,7 @@ fn block_parents_have_safety_comment( id: HirId, ) -> bool { let (span, hir_id) = match cx.tcx.parent_hir_node(id) { - Node::Expr(expr) => match cx.tcx.parent_hir_node(expr.hir_id) { - Node::LetStmt(hir::LetStmt { span, hir_id, .. }) => (*span, *hir_id), - Node::Item(hir::Item { - kind: ItemKind::Const(..) | ItemKind::Static(..), - span, - owner_id, - .. - }) => (*span, cx.tcx.local_def_id_to_hir_id(owner_id.def_id)), - _ => { - if is_branchy(expr) { - return false; - } - (expr.span, expr.hir_id) - }, - }, + Node::Expr(expr) if let Some(inner) = find_unsafe_block_parent_in_expr(cx, expr) => inner, Node::Stmt(hir::Stmt { kind: hir::StmtKind::Let(hir::LetStmt { span, hir_id, .. }) @@ -371,12 +403,13 @@ fn block_parents_have_safety_comment( .. }) | Node::LetStmt(hir::LetStmt { span, hir_id, .. }) => (*span, *hir_id), - Node::Item(hir::Item { - kind: ItemKind::Const(..) | ItemKind::Static(..), - span, - owner_id, - .. - }) => (*span, cx.tcx.local_def_id_to_hir_id(owner_id.def_id)), + + node if let Some((span, hir_id)) = span_and_hid_of_item_alike_node(&node) + && is_const_or_static(&node) => + { + (span, hir_id) + }, + _ => return false, }; // if unsafe block is part of a let/const/static statement, @@ -427,11 +460,12 @@ fn block_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { } fn include_attrs_in_span(cx: &LateContext<'_>, hir_id: HirId, span: Span) -> Span { - span.to(cx - .tcx - .hir_attrs(hir_id) - .iter() - .fold(span, |acc, attr| acc.to(attr.span()))) + span.to(cx.tcx.hir_attrs(hir_id).iter().fold(span, |acc, attr| { + if attr.is_doc_comment() { + return acc; + } + acc.to(attr.span()) + })) } enum HasSafetyComment { @@ -603,31 +637,35 @@ fn span_from_macro_expansion_has_safety_comment(cx: &LateContext<'_>, span: Span fn get_body_search_span(cx: &LateContext<'_>) -> Option { let body = cx.enclosing_body?; - let mut span = cx.tcx.hir_body(body).value.span; - let mut maybe_global_var = false; - for (_, node) in cx.tcx.hir_parent_iter(body.hir_id) { - match node { - Node::Expr(e) => span = e.span, - Node::Block(_) | Node::Arm(_) | Node::Stmt(_) | Node::LetStmt(_) => (), + let mut maybe_mod_item = None; + + for (_, parent_node) in cx.tcx.hir_parent_iter(body.hir_id) { + match parent_node { + Node::Crate(mod_) => return Some(mod_.spans.inner_span), Node::Item(hir::Item { - kind: ItemKind::Const(..) | ItemKind::Static(..), - .. - }) => maybe_global_var = true, - Node::Item(hir::Item { - kind: ItemKind::Mod(..), - span: item_span, + kind: ItemKind::Mod(_, mod_), + span, .. }) => { - span = *item_span; - break; + return maybe_mod_item + .and_then(|item| comment_start_before_item_in_mod(cx, mod_, *span, &item)) + .map(|comment_start| mod_.spans.inner_span.with_lo(comment_start)) + .or(Some(*span)); }, - Node::Crate(mod_) if maybe_global_var => { - span = mod_.spans.inner_span; + node if let Some((span, _)) = span_and_hid_of_item_alike_node(&node) + && !is_const_or_static(&node) => + { + return Some(span); + }, + Node::Item(item) => { + maybe_mod_item = Some(*item); + }, + _ => { + maybe_mod_item = None; }, - _ => break, } } - Some(span) + None } fn span_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { @@ -716,3 +754,28 @@ fn text_has_safety_comment(src: &str, line_starts: &[RelativeBytePos], start_pos } } } + +fn span_and_hid_of_item_alike_node(node: &Node<'_>) -> Option<(Span, HirId)> { + match node { + Node::Item(item) => Some((item.span, item.owner_id.into())), + Node::TraitItem(ti) => Some((ti.span, ti.owner_id.into())), + Node::ImplItem(ii) => Some((ii.span, ii.owner_id.into())), + _ => None, + } +} + +fn is_const_or_static(node: &Node<'_>) -> bool { + matches!( + node, + Node::Item(hir::Item { + kind: ItemKind::Const(..) | ItemKind::Static(..), + .. + }) | Node::ImplItem(hir::ImplItem { + kind: hir::ImplItemKind::Const(..), + .. + }) | Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Const(..), + .. + }) + ) +} diff --git a/clippy_lints/src/unnecessary_semicolon.rs b/clippy_lints/src/unnecessary_semicolon.rs index e5267620c4fb..f1d1a76d0c2d 100644 --- a/clippy_lints/src/unnecessary_semicolon.rs +++ b/clippy_lints/src/unnecessary_semicolon.rs @@ -26,7 +26,7 @@ declare_clippy_lint! { /// ```no_run /// # let a: u32 = 42; /// if a > 10 { - /// println!("a is greater than 10"); + /// println!("a is greater than 10"); /// } /// ``` #[clippy::version = "1.86.0"] diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 5e452c6d2ac0..57bb2fc27f14 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{snippet, snippet_with_context}; use clippy_utils::sugg::{DiagExt as _, Sugg}; -use clippy_utils::ty::{is_copy, is_type_diagnostic_item, same_type_and_consts}; +use clippy_utils::ty::{get_type_diagnostic_name, is_copy, is_type_diagnostic_item, same_type_and_consts}; use clippy_utils::{ get_parent_expr, is_inherent_method_call, is_trait_item, is_trait_method, is_ty_alias, path_to_local, }; @@ -13,7 +13,7 @@ use rustc_infer::traits::Obligation; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::adjustment::{Adjust, AutoBorrow, AutoBorrowMutability}; -use rustc_middle::ty::{self, AdtDef, EarlyBinder, GenericArg, GenericArgsRef, Ty, TypeVisitableExt}; +use rustc_middle::ty::{self, EarlyBinder, GenericArg, GenericArgsRef, Ty, TypeVisitableExt}; use rustc_session::impl_lint_pass; use rustc_span::{Span, sym}; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; @@ -412,24 +412,14 @@ pub fn check_function_application(cx: &LateContext<'_>, expr: &Expr<'_>, recv: & } fn has_eligible_receiver(cx: &LateContext<'_>, recv: &Expr<'_>, expr: &Expr<'_>) -> bool { - let recv_ty = cx.typeck_results().expr_ty(recv); - if is_inherent_method_call(cx, expr) - && let Some(recv_ty_defid) = recv_ty.ty_adt_def().map(AdtDef::did) - { - if let Some(diag_name) = cx.tcx.get_diagnostic_name(recv_ty_defid) - && matches!(diag_name, sym::Option | sym::Result) - { - return true; - } - - if cx.tcx.is_diagnostic_item(sym::ControlFlow, recv_ty_defid) { - return true; - } + if is_inherent_method_call(cx, expr) { + matches!( + get_type_diagnostic_name(cx, cx.typeck_results().expr_ty(recv)), + Some(sym::Option | sym::Result | sym::ControlFlow) + ) + } else { + is_trait_method(cx, expr, sym::Iterator) } - if is_trait_method(cx, expr, sym::Iterator) { - return true; - } - false } fn adjustments(cx: &LateContext<'_>, expr: &Expr<'_>) -> String { diff --git a/clippy_lints/src/utils/internal_lints/almost_standard_lint_formulation.rs b/clippy_lints/src/utils/internal_lints/almost_standard_lint_formulation.rs index bfcce81c498a..0a01a364a75b 100644 --- a/clippy_lints/src/utils/internal_lints/almost_standard_lint_formulation.rs +++ b/clippy_lints/src/utils/internal_lints/almost_standard_lint_formulation.rs @@ -44,11 +44,10 @@ impl AlmostStandardFormulation { impl<'tcx> LateLintPass<'tcx> for AlmostStandardFormulation { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { let mut check_next = false; - if let ItemKind::Static(ty, Mutability::Not, _) = item.kind { + if let ItemKind::Static(_, ty, Mutability::Not, _) = item.kind { let lines = cx .tcx - .hir() - .attrs(item.hir_id()) + .hir_attrs(item.hir_id()) .iter() .filter_map(|attr| Attribute::doc_str(attr).map(|sym| (sym, attr))); if is_lint_ref_type(cx, ty) { 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 16d51fa09025..94a2e598522b 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 @@ -104,7 +104,7 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass { return; } - if let hir::ItemKind::Static(ty, Mutability::Not, body_id) = item.kind { + if let hir::ItemKind::Static(ident, ty, Mutability::Not, body_id) = item.kind { if is_lint_ref_type(cx, ty) { check_invalid_clippy_version_attribute(cx, item); @@ -133,10 +133,10 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass { cx, DEFAULT_LINT, item.span, - format!("the lint `{}` has the default lint description", item.ident.name), + format!("the lint `{}` has the default lint description", ident.name), ); } - self.declared_lints.insert(item.ident.name, item.span); + self.declared_lints.insert(ident.name, item.span); } } } else if let Some(macro_call) = root_macro_call_first_node(cx, item) { diff --git a/clippy_lints/src/utils/internal_lints/produce_ice.rs b/clippy_lints/src/utils/internal_lints/produce_ice.rs index 9169e2968eb7..0a07919d659f 100644 --- a/clippy_lints/src/utils/internal_lints/produce_ice.rs +++ b/clippy_lints/src/utils/internal_lints/produce_ice.rs @@ -1,6 +1,6 @@ use rustc_ast::ast::NodeId; use rustc_ast::visit::FnKind; -use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_session::declare_lint_pass; use rustc_span::Span; @@ -24,8 +24,12 @@ declare_clippy_lint! { declare_lint_pass!(ProduceIce => [PRODUCE_ICE]); impl EarlyLintPass for ProduceIce { - fn check_fn(&mut self, _: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId) { - assert!(!is_trigger_fn(fn_kind), "Would you like some help with that?"); + fn check_fn(&mut self, ctx: &EarlyContext<'_>, fn_kind: FnKind<'_>, span: Span, _: NodeId) { + if is_trigger_fn(fn_kind) { + ctx.sess() + .dcx() + .span_delayed_bug(span, "Would you like some help with that?"); + } } } diff --git a/clippy_utils/README.md b/clippy_utils/README.md index 5dd31b52f880..7c665b424977 100644 --- a/clippy_utils/README.md +++ b/clippy_utils/README.md @@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain: ``` -nightly-2025-02-27 +nightly-2025-03-20 ``` @@ -30,7 +30,7 @@ Function signatures can change or be removed without replacement without any pri -Copyright 2014-2024 The Rust Project Developers +Copyright 2014-2025 The Rust Project Developers Licensed under the Apache License, Version 2.0 <[https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0)> or the MIT license diff --git a/clippy_utils/src/ast_utils/mod.rs b/clippy_utils/src/ast_utils/mod.rs index 707312a97f3b..edebee289e1c 100644 --- a/clippy_utils/src/ast_utils/mod.rs +++ b/clippy_utils/src/ast_utils/mod.rs @@ -688,7 +688,7 @@ pub fn eq_generics(l: &Generics, r: &Generics) -> bool { pub fn eq_where_predicate(l: &WherePredicate, r: &WherePredicate) -> bool { use WherePredicateKind::*; - over(&l.attrs, &r.attrs, eq_attr) + over(&l.attrs, &r.attrs, eq_attr) && match (&l.kind, &r.kind) { (BoundPredicate(l), BoundPredicate(r)) => { over(&l.bound_generic_params, &r.bound_generic_params, |l, r| { diff --git a/clippy_utils/src/diagnostics.rs b/clippy_utils/src/diagnostics.rs index ddb7a6635e06..292792408c64 100644 --- a/clippy_utils/src/diagnostics.rs +++ b/clippy_utils/src/diagnostics.rs @@ -85,7 +85,7 @@ fn validate_diag(diag: &Diag<'_, impl EmissionGuarantee>) { /// This is needed for `#[allow]` and `#[expect]` attributes to work on the node /// highlighted in the displayed warning. /// -/// If you're unsure which function you should use, you can test if the `#[allow]` attribute works +/// If you're unsure which function you should use, you can test if the `#[expect]` attribute works /// where you would expect it to. /// If it doesn't, you likely need to use [`span_lint_hir`] instead. /// @@ -128,7 +128,7 @@ pub fn span_lint(cx: &T, lint: &'static Lint, sp: impl Into( /// This is needed for `#[allow]` and `#[expect]` attributes to work on the node /// highlighted in the displayed warning. /// -/// If you're unsure which function you should use, you can test if the `#[allow]` attribute works +/// If you're unsure which function you should use, you can test if the `#[expect]` attribute works /// where you would expect it to. /// If it doesn't, you likely need to use [`span_lint_hir_and_then`] instead. /// @@ -241,7 +241,7 @@ pub fn span_lint_and_note( /// This is needed for `#[allow]` and `#[expect]` attributes to work on the node /// highlighted in the displayed warning. /// -/// If you're unsure which function you should use, you can test if the `#[allow]` attribute works +/// If you're unsure which function you should use, you can test if the `#[expect]` attribute works /// where you would expect it to. /// If it doesn't, you likely need to use [`span_lint_hir_and_then`] instead. pub fn span_lint_and_then(cx: &C, lint: &'static Lint, sp: S, msg: M, f: F) @@ -358,7 +358,7 @@ pub fn span_lint_hir_and_then( /// This is needed for `#[allow]` and `#[expect]` attributes to work on the node /// highlighted in the displayed warning. /// -/// If you're unsure which function you should use, you can test if the `#[allow]` attribute works +/// If you're unsure which function you should use, you can test if the `#[expect]` attribute works /// where you would expect it to. /// If it doesn't, you likely need to use [`span_lint_hir_and_then`] instead. /// diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index eb4e1a7722f3..1307ff79bc5d 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -106,10 +106,10 @@ use rustc_hir::hir_id::{HirIdMap, HirIdSet}; use rustc_hir::intravisit::{FnKind, Visitor, walk_expr}; use rustc_hir::{ self as hir, Arm, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstArgKind, ConstContext, - Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, - ImplItemRef, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, OwnerNode, Param, Pat, - PatExpr, PatExprKind, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitFn, TraitItem, TraitItemKind, - TraitItemRef, TraitRef, TyKind, UnOp, def, + Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArg, GenericArgs, HirId, Impl, ImplItem, + ImplItemKind, ImplItemRef, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, OwnerNode, + Param, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitFn, TraitItem, + TraitItemKind, TraitItemRef, TraitRef, TyKind, UnOp, def, }; use rustc_lexer::{TokenKind, tokenize}; use rustc_lint::{LateContext, Level, Lint, LintContext}; @@ -434,7 +434,7 @@ pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator Some(ty.as_unambig_ty()), + GenericArg::Type(ty) => Some(ty.as_unambig_ty()), _ => None, }) } @@ -1420,8 +1420,7 @@ pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { let parent_id = cx.tcx.hir_get_parent_item(expr.hir_id).def_id; match cx.tcx.hir_node_by_def_id(parent_id) { Node::Item(item) => item.kind.ident().map(|ident| ident.name), - Node::TraitItem(TraitItem { ident, .. }) - | Node::ImplItem(ImplItem { ident, .. }) => Some(ident.name), + Node::TraitItem(TraitItem { ident, .. }) | Node::ImplItem(ImplItem { ident, .. }) => Some(ident.name), _ => None, } } @@ -2334,6 +2333,18 @@ pub fn is_expr_final_block_expr(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool { matches!(tcx.parent_hir_node(expr.hir_id), Node::Block(..)) } +/// Checks if the expression is a temporary value. +// This logic is the same as the one used in rustc's `check_named_place_expr function`. +// https://github.com/rust-lang/rust/blob/3ed2a10d173d6c2e0232776af338ca7d080b1cd4/compiler/rustc_hir_typeck/src/expr.rs#L482-L499 +pub fn is_expr_temporary_value(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + !expr.is_place_expr(|base| { + cx.typeck_results() + .adjustments() + .get(base.hir_id) + .is_some_and(|x| x.iter().any(|adj| matches!(adj.kind, Adjust::Deref(_)))) + }) +} + pub fn std_or_core(cx: &LateContext<'_>) -> Option<&'static str> { if !is_no_std_crate(cx) { Some("std") @@ -3548,7 +3559,7 @@ pub fn is_block_like(expr: &Expr<'_>) -> bool { pub fn binary_expr_needs_parentheses(expr: &Expr<'_>) -> bool { fn contains_block(expr: &Expr<'_>, is_operand: bool) -> bool { match expr.kind { - ExprKind::Binary(_, lhs, _) => contains_block(lhs, true), + ExprKind::Binary(_, lhs, _) | ExprKind::Cast(lhs, _) => contains_block(lhs, true), _ if is_block_like(expr) => is_operand, _ => false, } @@ -3695,3 +3706,21 @@ pub fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { true } } + +/// Peel `Option<…>` from `hir_ty` as long as the HIR name is `Option` and it corresponds to the +/// `core::Option<_>` type. +pub fn peel_hir_ty_options<'tcx>(cx: &LateContext<'tcx>, mut hir_ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> { + let Some(option_def_id) = cx.tcx.get_diagnostic_item(sym::Option) else { + return hir_ty; + }; + while let TyKind::Path(QPath::Resolved(None, path)) = hir_ty.kind + && let Some(segment) = path.segments.last() + && segment.ident.name == sym::Option + && let Res::Def(DefKind::Enum, def_id) = segment.res + && def_id == option_def_id + && let [GenericArg::Type(arg_ty)] = segment.args().args + { + hir_ty = arg_ty.as_unambig_ty(); + } + hir_ty +} diff --git a/clippy_utils/src/mir/possible_borrower.rs b/clippy_utils/src/mir/possible_borrower.rs index db07b6404169..152b4272c26c 100644 --- a/clippy_utils/src/mir/possible_borrower.rs +++ b/clippy_utils/src/mir/possible_borrower.rs @@ -6,8 +6,7 @@ use rustc_index::bit_set::DenseBitSet; use rustc_lint::LateContext; use rustc_middle::mir::visit::Visitor as _; use rustc_middle::mir::{self, Mutability}; -use rustc_middle::ty::TypeVisitor; -use rustc_middle::ty::{self, TyCtxt}; +use rustc_middle::ty::{self, TyCtxt, TypeVisitor}; use rustc_mir_dataflow::impls::MaybeStorageLive; use rustc_mir_dataflow::{Analysis, ResultsCursor}; use std::borrow::Cow; diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 0316de172de7..86f4f190b950 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -33,7 +33,7 @@ msrv_aliases! { 1,76,0 { PTR_FROM_REF, OPTION_RESULT_INSPECT } 1,75,0 { OPTION_AS_SLICE } 1,74,0 { REPR_RUST, IO_ERROR_OTHER } - 1,73,0 { MANUAL_DIV_CEIL } + 1,73,0 { DIV_CEIL } 1,71,0 { TUPLE_ARRAY_CONVERSIONS, BUILD_HASHER_HASH_ONE } 1,70,0 { OPTION_RESULT_IS_VARIANT_AND, BINARY_HEAP_RETAIN } 1,68,0 { PATH_MAIN_SEPARATOR_STR } @@ -74,6 +74,7 @@ msrv_aliases! { 1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST, EXPECT_ERR } 1,16,0 { STR_REPEAT } 1,15,0 { MAYBE_BOUND_IN_WHERE } + 1,13,0 { QUESTION_MARK_OPERATOR } } /// `#[clippy::msrv]` attributes are rarely used outside of Clippy's test suite, as a basic diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index 8e6f4d4a317e..5d0401010db6 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -395,24 +395,32 @@ fn check_terminator<'tcx>( fn is_stable_const_fn(cx: &LateContext<'_>, def_id: DefId, msrv: Msrv) -> bool { cx.tcx.is_const_fn(def_id) - && cx.tcx.lookup_const_stability(def_id).is_none_or(|const_stab| { - if let rustc_attr_parsing::StabilityLevel::Stable { since, .. } = const_stab.level { - // Checking MSRV is manually necessary because `rustc` has no such concept. This entire - // function could be removed if `rustc` provided a MSRV-aware version of `is_stable_const_fn`. - // as a part of an unimplemented MSRV check https://github.com/rust-lang/rust/issues/65262. + && cx + .tcx + .lookup_const_stability(def_id) + .or_else(|| { + cx.tcx + .trait_of_item(def_id) + .and_then(|trait_def_id| cx.tcx.lookup_const_stability(trait_def_id)) + }) + .is_none_or(|const_stab| { + if let rustc_attr_parsing::StabilityLevel::Stable { since, .. } = const_stab.level { + // Checking MSRV is manually necessary because `rustc` has no such concept. This entire + // function could be removed if `rustc` provided a MSRV-aware version of `is_stable_const_fn`. + // as a part of an unimplemented MSRV check https://github.com/rust-lang/rust/issues/65262. - let const_stab_rust_version = match since { - StableSince::Version(version) => version, - StableSince::Current => RustcVersion::CURRENT, - StableSince::Err => return false, - }; + let const_stab_rust_version = match since { + StableSince::Version(version) => version, + StableSince::Current => RustcVersion::CURRENT, + StableSince::Err => return false, + }; - msrv.meets(cx, const_stab_rust_version) - } else { - // Unstable const fn, check if the feature is enabled. - cx.tcx.features().enabled(const_stab.feature) && msrv.current(cx).is_none() - } - }) + msrv.meets(cx, const_stab_rust_version) + } else { + // Unstable const fn, check if the feature is enabled. + cx.tcx.features().enabled(const_stab.feature) && msrv.current(cx).is_none() + } + }) } fn is_ty_const_destruct<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, body: &Body<'tcx>) -> bool { diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index 9cc66593dcc3..68a1de96a351 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -4,8 +4,8 @@ use crate::source::{snippet, snippet_opt, snippet_with_applicability, snippet_with_context}; use crate::ty::expr_sig; use crate::{get_parent_expr_for_hir, higher}; -use rustc_ast::util::parser::AssocOp; use rustc_ast::ast; +use rustc_ast::util::parser::AssocOp; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::{Closure, ExprKind, HirId, MutTy, TyKind}; @@ -444,7 +444,7 @@ impl<'a> Not for Sugg<'a> { type Output = Sugg<'a>; fn not(self) -> Sugg<'a> { use AssocOp::Binary; - use ast::BinOpKind::{Eq, Gt, Ge, Lt, Le, Ne}; + use ast::BinOpKind::{Eq, Ge, Gt, Le, Lt, Ne}; if let Sugg::BinOp(op, lhs, rhs) = self { let to_op = match op { @@ -515,10 +515,10 @@ pub fn make_assoc(op: AssocOp, lhs: &Sugg<'_>, rhs: &Sugg<'_>) -> Sugg<'static> op, AssocOp::Binary( ast::BinOpKind::Add - | ast::BinOpKind::Sub - | ast::BinOpKind::Mul - | ast::BinOpKind::Div - | ast::BinOpKind::Rem + | ast::BinOpKind::Sub + | ast::BinOpKind::Mul + | ast::BinOpKind::Div + | ast::BinOpKind::Rem ) ) } @@ -578,10 +578,8 @@ enum Associativity { /// associative. #[must_use] fn associativity(op: AssocOp) -> Associativity { + use ast::BinOpKind::{Add, And, BitAnd, BitOr, BitXor, Div, Eq, Ge, Gt, Le, Lt, Mul, Ne, Or, Rem, Shl, Shr, Sub}; use rustc_ast::util::parser::AssocOp::{Assign, AssignOp, Binary, Cast, Range}; - use ast::BinOpKind::{ - Add, BitAnd, BitOr, BitXor, Div, Eq, Gt, Ge, And, Or, Lt, Le, Rem, Mul, Ne, Shl, Shr, Sub, - }; match op { Assign | AssignOp(_) => Associativity::Right, @@ -994,6 +992,7 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { mod test { use super::Sugg; + use rustc_ast as ast; use rustc_ast::util::parser::AssocOp; use std::borrow::Cow; @@ -1011,15 +1010,15 @@ mod test { #[test] fn binop_maybe_par() { - let sugg = Sugg::BinOp(AssocOp::Add, "1".into(), "1".into()); + let sugg = Sugg::BinOp(AssocOp::Binary(ast::BinOpKind::Add), "1".into(), "1".into()); assert_eq!("(1 + 1)", sugg.maybe_par().to_string()); - let sugg = Sugg::BinOp(AssocOp::Add, "(1 + 1)".into(), "(1 + 1)".into()); + let sugg = Sugg::BinOp(AssocOp::Binary(ast::BinOpKind::Add), "(1 + 1)".into(), "(1 + 1)".into()); assert_eq!("((1 + 1) + (1 + 1))", sugg.maybe_par().to_string()); } #[test] fn not_op() { - use AssocOp::{Add, Equal, Greater, GreaterEqual, LAnd, LOr, Less, LessEqual, NotEqual}; + use ast::BinOpKind::{Add, And, Eq, Ge, Gt, Le, Lt, Ne, Or}; fn test_not(op: AssocOp, correct: &str) { let sugg = Sugg::BinOp(op, "x".into(), "y".into()); @@ -1027,16 +1026,16 @@ mod test { } // Invert the comparison operator. - test_not(Equal, "x != y"); - test_not(NotEqual, "x == y"); - test_not(Less, "x >= y"); - test_not(LessEqual, "x > y"); - test_not(Greater, "x <= y"); - test_not(GreaterEqual, "x < y"); + test_not(AssocOp::Binary(Eq), "x != y"); + test_not(AssocOp::Binary(Ne), "x == y"); + test_not(AssocOp::Binary(Lt), "x >= y"); + test_not(AssocOp::Binary(Le), "x > y"); + test_not(AssocOp::Binary(Gt), "x <= y"); + test_not(AssocOp::Binary(Ge), "x < y"); // Other operators are inverted like !(..). - test_not(Add, "!(x + y)"); - test_not(LAnd, "!(x && y)"); - test_not(LOr, "!(x || y)"); + test_not(AssocOp::Binary(Add), "!(x + y)"); + test_not(AssocOp::Binary(And), "!(x && y)"); + test_not(AssocOp::Binary(Or), "!(x || y)"); } } diff --git a/lintcheck/src/json.rs b/lintcheck/src/json.rs index 3a68f2c92435..8ea0a41ed368 100644 --- a/lintcheck/src/json.rs +++ b/lintcheck/src/json.rs @@ -1,3 +1,9 @@ +//! JSON output and comparison functionality for Clippy warnings. +//! +//! This module handles serialization of Clippy warnings to JSON format, +//! loading warnings from JSON files, and generating human-readable diffs +//! between different linting runs. + use std::fs; use std::path::Path; @@ -8,8 +14,10 @@ use crate::ClippyWarning; /// This is the total number. 300 warnings results in 100 messages per section. const DEFAULT_LIMIT_PER_LINT: usize = 300; +/// Target for total warnings to display across all lints when truncating output. const TRUNCATION_TOTAL_TARGET: usize = 1000; +/// Representation of a single Clippy warning for JSON serialization. #[derive(Debug, Deserialize, Serialize)] struct LintJson { /// The lint name e.g. `clippy::bytes_nth` @@ -21,10 +29,12 @@ struct LintJson { } impl LintJson { + /// Returns a tuple of name and `file_line` for sorting and comparison. fn key(&self) -> impl Ord + '_ { (self.name.as_str(), self.file_line.as_str()) } + /// Formats the warning information with an action verb for display. fn info_text(&self, action: &str) -> String { format!("{action} `{}` at [`{}`]({})", self.name, self.file_line, self.file_url) } @@ -53,12 +63,17 @@ pub(crate) fn output(clippy_warnings: Vec) -> String { serde_json::to_string(&lints).unwrap() } +/// Loads lint warnings from a JSON file at the given path. fn load_warnings(path: &Path) -> Vec { let file = fs::read(path).unwrap_or_else(|e| panic!("failed to read {}: {e}", path.display())); serde_json::from_slice(&file).unwrap_or_else(|e| panic!("failed to deserialize {}: {e}", path.display())) } +/// Generates and prints a diff between two sets of lint warnings. +/// +/// Compares warnings from `old_path` and `new_path`, then displays a summary table +/// and detailed information about added, removed, and changed warnings. pub(crate) fn diff(old_path: &Path, new_path: &Path, truncate: bool) { let old_warnings = load_warnings(old_path); let new_warnings = load_warnings(new_path); @@ -116,6 +131,7 @@ pub(crate) fn diff(old_path: &Path, new_path: &Path, truncate: bool) { } } +/// Container for grouped lint warnings organized by status (added/removed/changed). #[derive(Debug)] struct LintWarnings { name: String, @@ -124,6 +140,7 @@ struct LintWarnings { changed: Vec<(LintJson, LintJson)>, } +/// Prints a formatted report for a single lint type with its warnings. fn print_lint_warnings(lint: &LintWarnings, truncate_after: usize) { let name = &lint.name; let html_id = to_html_id(name); @@ -145,6 +162,7 @@ fn print_lint_warnings(lint: &LintWarnings, truncate_after: usize) { print_changed_diff(&lint.changed, truncate_after / 3); } +/// Prints a summary table of all lints with counts of added, removed, and changed warnings. fn print_summary_table(lints: &[LintWarnings]) { println!("| Lint | Added | Removed | Changed |"); println!("| ------------------------------------------ | ------: | ------: | ------: |"); @@ -160,6 +178,7 @@ fn print_summary_table(lints: &[LintWarnings]) { } } +/// Prints a section of warnings with a header and formatted code blocks. fn print_warnings(title: &str, warnings: &[LintJson], truncate_after: usize) { if warnings.is_empty() { return; @@ -180,6 +199,7 @@ fn print_warnings(title: &str, warnings: &[LintJson], truncate_after: usize) { } } +/// Prints a section of changed warnings with unified diff format. fn print_changed_diff(changed: &[(LintJson, LintJson)], truncate_after: usize) { if changed.is_empty() { return; @@ -213,6 +233,7 @@ fn print_changed_diff(changed: &[(LintJson, LintJson)], truncate_after: usize) { } } +/// Truncates a list to a maximum number of items and prints a message about truncation. fn truncate(list: &[T], truncate_after: usize) -> &[T] { if list.len() > truncate_after { println!( @@ -227,6 +248,7 @@ fn truncate(list: &[T], truncate_after: usize) -> &[T] { } } +/// Prints a level 3 heading with an appropriate HTML ID for linking. fn print_h3(lint: &str, title: &str) { let html_id = to_html_id(lint); // We have to use HTML here to be able to manually add an id. diff --git a/rust-toolchain b/rust-toolchain index a4931499c802..fcaeedc9a66b 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2025-02-27" +channel = "nightly-2025-03-20" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" diff --git a/rustc_tools_util/README.md b/rustc_tools_util/README.md index ff4ca6f830e6..f47a4c69c2c3 100644 --- a/rustc_tools_util/README.md +++ b/rustc_tools_util/README.md @@ -51,7 +51,7 @@ The changelog for `rustc_tools_util` is available under: -Copyright 2014-2024 The Rust Project Developers +Copyright 2014-2025 The Rust Project Developers Licensed under the Apache License, Version 2.0 or the MIT license diff --git a/tests/compile-test.rs b/tests/compile-test.rs index f44cf7a7c25a..956a05288f35 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -16,7 +16,7 @@ use test_utils::IS_RUSTC_TEST_SUITE; use ui_test::custom_flags::Flag; use ui_test::custom_flags::rustfix::RustfixMode; use ui_test::spanned::Spanned; -use ui_test::{Args, CommandBuilder, Config, Match, OutputConflictHandling, status_emitter}; +use ui_test::{Args, CommandBuilder, Config, Match, error_on_output_conflict, status_emitter}; use std::collections::{BTreeMap, HashMap}; use std::env::{self, set_var, var_os}; @@ -142,7 +142,7 @@ impl TestContext { fn base_config(&self, test_dir: &str, mandatory_annotations: bool) -> Config { let target_dir = PathBuf::from(var_os("CARGO_TARGET_DIR").unwrap_or_else(|| "target".into())); let mut config = Config { - output_conflict_handling: OutputConflictHandling::Error, + output_conflict_handling: error_on_output_conflict, filter_files: env::var("TESTNAME") .map(|filters| filters.split(',').map(str::to_string).collect()) .unwrap_or_default(), @@ -220,7 +220,7 @@ fn run_internal_tests(cx: &TestContext) { if !RUN_INTERNAL_TESTS { return; } - let mut config = cx.base_config("ui-internal", false); + let mut config = cx.base_config("ui-internal", true); config.bless_command = Some("cargo uitest --features internal -- -- --bless".into()); ui_test::run_tests_generic( diff --git a/tests/lint_message_convention.rs b/tests/lint_message_convention.rs index 7ed1f485c1cf..9229e2e8c496 100644 --- a/tests/lint_message_convention.rs +++ b/tests/lint_message_convention.rs @@ -44,6 +44,7 @@ impl Message { ".*AT&T x86 assembly syntax used", "note: Clippy version: .*", "the compiler unexpectedly panicked. this is a bug.", + "internal compiler error:", ]) .unwrap() }); diff --git a/tests/ui-internal/check_clippy_version_attribute.rs b/tests/ui-internal/check_clippy_version_attribute.rs index 31acac89cc63..e5f6001b74d0 100644 --- a/tests/ui-internal/check_clippy_version_attribute.rs +++ b/tests/ui-internal/check_clippy_version_attribute.rs @@ -38,6 +38,7 @@ declare_tool_lint! { // Invalid attributes /////////////////////// declare_tool_lint! { +//~^ invalid_clippy_version_attribute #[clippy::version = "1.2.3.4.5.6"] pub clippy::INVALID_ONE, Warn, @@ -46,6 +47,7 @@ declare_tool_lint! { } declare_tool_lint! { +//~^ invalid_clippy_version_attribute #[clippy::version = "I'm a string"] pub clippy::INVALID_TWO, Warn, @@ -57,6 +59,7 @@ declare_tool_lint! { // Missing attribute test /////////////////////// declare_tool_lint! { +//~^ missing_clippy_version_attribute #[clippy::version] pub clippy::MISSING_ATTRIBUTE_ONE, Warn, @@ -65,6 +68,7 @@ declare_tool_lint! { } declare_tool_lint! { +//~^ missing_clippy_version_attribute pub clippy::MISSING_ATTRIBUTE_TWO, Warn, "Two", diff --git a/tests/ui-internal/check_clippy_version_attribute.stderr b/tests/ui-internal/check_clippy_version_attribute.stderr index 631c292f5249..1129c35d1d01 100644 --- a/tests/ui-internal/check_clippy_version_attribute.stderr +++ b/tests/ui-internal/check_clippy_version_attribute.stderr @@ -2,10 +2,10 @@ error: this item has an invalid `clippy::version` attribute --> tests/ui-internal/check_clippy_version_attribute.rs:40:1 | LL | / declare_tool_lint! { +LL | | LL | | #[clippy::version = "1.2.3.4.5.6"] LL | | pub clippy::INVALID_ONE, -LL | | Warn, -LL | | "One", +... | LL | | report_in_external_macro: true LL | | } | |_^ @@ -20,13 +20,13 @@ LL | #![deny(clippy::internal)] = note: this error originates in the macro `$crate::declare_tool_lint` which comes from the expansion of the macro `declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info) error: this item has an invalid `clippy::version` attribute - --> tests/ui-internal/check_clippy_version_attribute.rs:48:1 + --> tests/ui-internal/check_clippy_version_attribute.rs:49:1 | LL | / declare_tool_lint! { +LL | | LL | | #[clippy::version = "I'm a string"] LL | | pub clippy::INVALID_TWO, -LL | | Warn, -LL | | "Two", +... | LL | | report_in_external_macro: true LL | | } | |_^ @@ -35,13 +35,13 @@ LL | | } = note: this error originates in the macro `$crate::declare_tool_lint` which comes from the expansion of the macro `declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info) error: this lint is missing the `clippy::version` attribute or version value - --> tests/ui-internal/check_clippy_version_attribute.rs:59:1 + --> tests/ui-internal/check_clippy_version_attribute.rs:61:1 | LL | / declare_tool_lint! { +LL | | LL | | #[clippy::version] LL | | pub clippy::MISSING_ATTRIBUTE_ONE, -LL | | Warn, -LL | | "Two", +... | LL | | report_in_external_macro: true LL | | } | |_^ @@ -51,9 +51,10 @@ LL | | } = note: this error originates in the macro `$crate::declare_tool_lint` which comes from the expansion of the macro `declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info) error: this lint is missing the `clippy::version` attribute or version value - --> tests/ui-internal/check_clippy_version_attribute.rs:67:1 + --> tests/ui-internal/check_clippy_version_attribute.rs:70:1 | LL | / declare_tool_lint! { +LL | | LL | | pub clippy::MISSING_ATTRIBUTE_TWO, LL | | Warn, LL | | "Two", diff --git a/tests/ui-internal/check_formulation.rs b/tests/ui-internal/check_formulation.rs index 43fc996033ea..8265a78769d1 100644 --- a/tests/ui-internal/check_formulation.rs +++ b/tests/ui-internal/check_formulation.rs @@ -21,6 +21,7 @@ declare_tool_lint! { declare_tool_lint! { /// # What it does /// Check for lint formulations that are correct + //~^ almost_standard_lint_formulation #[clippy::version = "pre 1.29.0"] pub clippy::INVALID1, Warn, @@ -31,6 +32,7 @@ declare_tool_lint! { declare_tool_lint! { /// # What it does /// Detects uses of incorrect formulations + //~^ almost_standard_lint_formulation #[clippy::version = "pre 1.29.0"] pub clippy::INVALID2, Warn, diff --git a/tests/ui-internal/check_formulation.stderr b/tests/ui-internal/check_formulation.stderr index 12514370e6de..b16e1bf86873 100644 --- a/tests/ui-internal/check_formulation.stderr +++ b/tests/ui-internal/check_formulation.stderr @@ -9,7 +9,7 @@ LL | /// Check for lint formulations that are correct = help: to override `-D warnings` add `#[allow(clippy::almost_standard_lint_formulation)]` error: non-standard lint formulation - --> tests/ui-internal/check_formulation.rs:33:5 + --> tests/ui-internal/check_formulation.rs:34:5 | LL | /// Detects uses of incorrect formulations | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui-internal/collapsible_span_lint_calls.rs b/tests/ui-internal/collapsible_span_lint_calls.rs index 1baf6142b349..2f289ae2b481 100644 --- a/tests/ui-internal/collapsible_span_lint_calls.rs +++ b/tests/ui-internal/collapsible_span_lint_calls.rs @@ -33,18 +33,23 @@ impl EarlyLintPass for Pass { let predicate = true; span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { + //~^ collapsible_span_lint_calls db.span_suggestion(expr.span, help_msg, sugg.to_string(), Applicability::MachineApplicable); }); span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { + //~^ collapsible_span_lint_calls db.span_help(expr.span, help_msg); }); span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { + //~^ collapsible_span_lint_calls db.help(help_msg); }); span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { + //~^ collapsible_span_lint_calls db.span_note(expr.span, note_msg); }); span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { + //~^ collapsible_span_lint_calls db.note(note_msg); }); diff --git a/tests/ui-internal/collapsible_span_lint_calls.stderr b/tests/ui-internal/collapsible_span_lint_calls.stderr index 104995918de2..a2be1f1cd367 100644 --- a/tests/ui-internal/collapsible_span_lint_calls.stderr +++ b/tests/ui-internal/collapsible_span_lint_calls.stderr @@ -2,6 +2,7 @@ error: this call is collapsible --> tests/ui-internal/collapsible_span_lint_calls.rs:35:9 | LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { +LL | | LL | | db.span_suggestion(expr.span, help_msg, sugg.to_string(), Applicability::MachineApplicable); LL | | }); | |__________^ help: collapse into: `span_lint_and_sugg(cx, TEST_LINT, expr.span, lint_msg, help_msg, sugg.to_string(), Applicability::MachineApplicable)` @@ -14,33 +15,37 @@ LL | #![deny(clippy::internal)] = note: `#[deny(clippy::collapsible_span_lint_calls)]` implied by `#[deny(clippy::internal)]` error: this call is collapsible - --> tests/ui-internal/collapsible_span_lint_calls.rs:38:9 + --> tests/ui-internal/collapsible_span_lint_calls.rs:39:9 | LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { +LL | | LL | | db.span_help(expr.span, help_msg); LL | | }); | |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg)` error: this call is collapsible - --> tests/ui-internal/collapsible_span_lint_calls.rs:41:9 + --> tests/ui-internal/collapsible_span_lint_calls.rs:43:9 | LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { +LL | | LL | | db.help(help_msg); LL | | }); | |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg)` error: this call is collapsible - --> tests/ui-internal/collapsible_span_lint_calls.rs:44:9 + --> tests/ui-internal/collapsible_span_lint_calls.rs:47:9 | LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { +LL | | LL | | db.span_note(expr.span, note_msg); LL | | }); | |__________^ help: collapse into: `span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg)` error: this call is collapsible - --> tests/ui-internal/collapsible_span_lint_calls.rs:47:9 + --> tests/ui-internal/collapsible_span_lint_calls.rs:51:9 | LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { +LL | | LL | | db.note(note_msg); LL | | }); | |__________^ help: collapse into: `span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, None, note_msg)` diff --git a/tests/ui-internal/custom_ice_message.rs b/tests/ui-internal/custom_ice_message.rs index 9b0db660c997..71819fe37070 100644 --- a/tests/ui-internal/custom_ice_message.rs +++ b/tests/ui-internal/custom_ice_message.rs @@ -10,5 +10,6 @@ #![allow(clippy::missing_clippy_version_attribute)] fn it_looks_like_you_are_trying_to_kill_clippy() {} +//~^ ice: Would you like some help with that? fn main() {} diff --git a/tests/ui-internal/custom_ice_message.stderr b/tests/ui-internal/custom_ice_message.stderr index 801b0f340de9..589e1190a907 100644 --- a/tests/ui-internal/custom_ice_message.stderr +++ b/tests/ui-internal/custom_ice_message.stderr @@ -1,9 +1,18 @@ +note: no errors encountered even though delayed bugs were created -thread '' panicked at clippy_lints/src/utils/internal_lints/produce_ice.rs: -Would you like some help with that? -note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +note: those delayed bugs will now be shown as internal compiler errors -error: the compiler unexpectedly panicked. this is a bug. +error: internal compiler error: Would you like some help with that? + --> tests/ui-internal/custom_ice_message.rs:12:1 + | +LL | fn it_looks_like_you_are_trying_to_kill_clippy() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: delayed at clippy_lints/src/utils/internal_lints/produce_ice.rs - disabled backtrace + --> tests/ui-internal/custom_ice_message.rs:12:1 + | +LL | fn it_looks_like_you_are_trying_to_kill_clippy() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: we would appreciate a bug report: https://github.com/rust-lang/rust-clippy/issues/new?template=ice.yml @@ -13,9 +22,5 @@ note: rustc running on note: compiler flags: -Z ui-testing -Z deduplicate-diagnostics=no -query stack during panic: -#0 [early_lint_checks] perform lints prior to AST lowering -#1 [hir_crate] getting the crate HIR -... and 3 other queries... use `env RUST_BACKTRACE=1` to see the full query stack note: Clippy version: foo diff --git a/tests/ui-internal/default_lint.rs b/tests/ui-internal/default_lint.rs index da29aedb2a3a..959bfd27e389 100644 --- a/tests/ui-internal/default_lint.rs +++ b/tests/ui-internal/default_lint.rs @@ -16,6 +16,7 @@ declare_tool_lint! { } declare_tool_lint! { +//~^ default_lint pub clippy::TEST_LINT_DEFAULT, Warn, "default lint description", diff --git a/tests/ui-internal/default_lint.stderr b/tests/ui-internal/default_lint.stderr index c939125e875c..9d4c2e15349f 100644 --- a/tests/ui-internal/default_lint.stderr +++ b/tests/ui-internal/default_lint.stderr @@ -2,6 +2,7 @@ error: the lint `TEST_LINT_DEFAULT` has the default lint description --> tests/ui-internal/default_lint.rs:18:1 | LL | / declare_tool_lint! { +LL | | LL | | pub clippy::TEST_LINT_DEFAULT, LL | | Warn, LL | | "default lint description", diff --git a/tests/ui-internal/disallow_span_lint.rs b/tests/ui-internal/disallow_span_lint.rs index ca71dddcc24f..3fed38cab64d 100644 --- a/tests/ui-internal/disallow_span_lint.rs +++ b/tests/ui-internal/disallow_span_lint.rs @@ -12,12 +12,14 @@ use rustc_middle::ty::TyCtxt; pub fn a(cx: impl LintContext, lint: &'static Lint, span: impl Into, msg: impl Into) { cx.span_lint(lint, span, |lint| { + //~^ disallowed_methods lint.primary_message(msg); }); } pub fn b(tcx: TyCtxt<'_>, lint: &'static Lint, hir_id: HirId, span: impl Into, msg: impl Into) { tcx.node_span_lint(lint, hir_id, span, |lint| { + //~^ disallowed_methods lint.primary_message(msg); }); } diff --git a/tests/ui-internal/disallow_span_lint.stderr b/tests/ui-internal/disallow_span_lint.stderr index 16e1487f2bbc..9a7a7ecbbff9 100644 --- a/tests/ui-internal/disallow_span_lint.stderr +++ b/tests/ui-internal/disallow_span_lint.stderr @@ -9,7 +9,7 @@ LL | cx.span_lint(lint, span, |lint| { = help: to override `-D warnings` add `#[allow(clippy::disallowed_methods)]` error: use of a disallowed method `rustc_middle::ty::context::TyCtxt::node_span_lint` - --> tests/ui-internal/disallow_span_lint.rs:20:9 + --> tests/ui-internal/disallow_span_lint.rs:21:9 | LL | tcx.node_span_lint(lint, hir_id, span, |lint| { | ^^^^^^^^^^^^^^ diff --git a/tests/ui-internal/interning_defined_symbol.fixed b/tests/ui-internal/interning_defined_symbol.fixed index 3bcabb4ab2d3..92d3b1537e0c 100644 --- a/tests/ui-internal/interning_defined_symbol.fixed +++ b/tests/ui-internal/interning_defined_symbol.fixed @@ -15,15 +15,19 @@ macro_rules! sym { fn main() { // Direct use of Symbol::intern let _ = rustc_span::sym::f32; + //~^ interning_defined_symbol // Using a sym macro let _ = rustc_span::sym::f32; + //~^ interning_defined_symbol // Correct suggestion when symbol isn't stringified constant name let _ = rustc_span::sym::proc_dash_macro; + //~^ interning_defined_symbol // interning a keyword let _ = rustc_span::kw::SelfLower; + //~^ interning_defined_symbol // Interning a symbol that is not defined let _ = Symbol::intern("xyz123"); diff --git a/tests/ui-internal/interning_defined_symbol.rs b/tests/ui-internal/interning_defined_symbol.rs index 92e92d4fbc16..d1e6f9cb1c41 100644 --- a/tests/ui-internal/interning_defined_symbol.rs +++ b/tests/ui-internal/interning_defined_symbol.rs @@ -15,15 +15,19 @@ macro_rules! sym { fn main() { // Direct use of Symbol::intern let _ = Symbol::intern("f32"); + //~^ interning_defined_symbol // Using a sym macro let _ = sym!(f32); + //~^ interning_defined_symbol // Correct suggestion when symbol isn't stringified constant name let _ = Symbol::intern("proc-macro"); + //~^ interning_defined_symbol // interning a keyword let _ = Symbol::intern("self"); + //~^ interning_defined_symbol // Interning a symbol that is not defined let _ = Symbol::intern("xyz123"); diff --git a/tests/ui-internal/interning_defined_symbol.stderr b/tests/ui-internal/interning_defined_symbol.stderr index c4d0308979f6..c84a566436a8 100644 --- a/tests/ui-internal/interning_defined_symbol.stderr +++ b/tests/ui-internal/interning_defined_symbol.stderr @@ -12,19 +12,19 @@ LL | #![deny(clippy::internal)] = note: `#[deny(clippy::interning_defined_symbol)]` implied by `#[deny(clippy::internal)]` error: interning a defined symbol - --> tests/ui-internal/interning_defined_symbol.rs:20:13 + --> tests/ui-internal/interning_defined_symbol.rs:21:13 | LL | let _ = sym!(f32); | ^^^^^^^^^ help: try: `rustc_span::sym::f32` error: interning a defined symbol - --> tests/ui-internal/interning_defined_symbol.rs:23:13 + --> tests/ui-internal/interning_defined_symbol.rs:25:13 | LL | let _ = Symbol::intern("proc-macro"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::proc_dash_macro` error: interning a defined symbol - --> tests/ui-internal/interning_defined_symbol.rs:26:13 + --> tests/ui-internal/interning_defined_symbol.rs:29:13 | LL | let _ = Symbol::intern("self"); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::kw::SelfLower` diff --git a/tests/ui-internal/invalid_msrv_attr_impl.fixed b/tests/ui-internal/invalid_msrv_attr_impl.fixed index 7011ef518f20..6804e2bbae83 100644 --- a/tests/ui-internal/invalid_msrv_attr_impl.fixed +++ b/tests/ui-internal/invalid_msrv_attr_impl.fixed @@ -27,6 +27,7 @@ impl_lint_pass!(Pass => [TEST_LINT]); impl EarlyLintPass for Pass { extract_msrv_attr!(); + //~^ missing_msrv_attr_impl fn check_expr(&mut self, _: &EarlyContext<'_>, _: &rustc_ast::Expr) {} } diff --git a/tests/ui-internal/invalid_msrv_attr_impl.rs b/tests/ui-internal/invalid_msrv_attr_impl.rs index 323061decd23..c625a5d9a459 100644 --- a/tests/ui-internal/invalid_msrv_attr_impl.rs +++ b/tests/ui-internal/invalid_msrv_attr_impl.rs @@ -26,6 +26,7 @@ struct Pass { impl_lint_pass!(Pass => [TEST_LINT]); impl EarlyLintPass for Pass { + //~^ missing_msrv_attr_impl fn check_expr(&mut self, _: &EarlyContext<'_>, _: &rustc_ast::Expr) {} } diff --git a/tests/ui-internal/invalid_paths.rs b/tests/ui-internal/invalid_paths.rs index 9a9790a4bae5..abfb111f938e 100644 --- a/tests/ui-internal/invalid_paths.rs +++ b/tests/ui-internal/invalid_paths.rs @@ -13,12 +13,15 @@ mod paths { // Path with empty segment pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"]; + //~^ invalid_paths // Path with bad crate pub const BAD_CRATE_PATH: [&str; 2] = ["bad", "path"]; + //~^ invalid_paths // Path with bad module pub const BAD_MOD_PATH: [&str; 2] = ["std", "xxx"]; + //~^ invalid_paths // Path to method on an enum inherent impl pub const OPTION_IS_SOME: [&str; 4] = ["core", "option", "Option", "is_some"]; diff --git a/tests/ui-internal/invalid_paths.stderr b/tests/ui-internal/invalid_paths.stderr index fc530a2efa37..7bde37667be4 100644 --- a/tests/ui-internal/invalid_paths.stderr +++ b/tests/ui-internal/invalid_paths.stderr @@ -8,13 +8,13 @@ LL | pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute" = help: to override `-D warnings` add `#[allow(clippy::invalid_paths)]` error: invalid path - --> tests/ui-internal/invalid_paths.rs:18:5 + --> tests/ui-internal/invalid_paths.rs:19:5 | LL | pub const BAD_CRATE_PATH: [&str; 2] = ["bad", "path"]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: invalid path - --> tests/ui-internal/invalid_paths.rs:21:5 + --> tests/ui-internal/invalid_paths.rs:23:5 | LL | pub const BAD_MOD_PATH: [&str; 2] = ["std", "xxx"]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui-internal/lint_without_lint_pass.rs b/tests/ui-internal/lint_without_lint_pass.rs index d59e9cbbb617..69591523432c 100644 --- a/tests/ui-internal/lint_without_lint_pass.rs +++ b/tests/ui-internal/lint_without_lint_pass.rs @@ -10,6 +10,7 @@ extern crate rustc_lint; use rustc_lint::{LintPass, LintVec}; declare_tool_lint! { +//~^ lint_without_lint_pass pub clippy::TEST_LINT, Warn, "", diff --git a/tests/ui-internal/lint_without_lint_pass.stderr b/tests/ui-internal/lint_without_lint_pass.stderr index 187bba97fd41..9cca96ca1602 100644 --- a/tests/ui-internal/lint_without_lint_pass.stderr +++ b/tests/ui-internal/lint_without_lint_pass.stderr @@ -2,6 +2,7 @@ error: the lint `TEST_LINT` is not added to any `LintPass` --> tests/ui-internal/lint_without_lint_pass.rs:12:1 | LL | / declare_tool_lint! { +LL | | LL | | pub clippy::TEST_LINT, LL | | Warn, LL | | "", diff --git a/tests/ui-internal/outer_expn_data.fixed b/tests/ui-internal/outer_expn_data.fixed index cef16cf6ca5b..cb7680b8bb14 100644 --- a/tests/ui-internal/outer_expn_data.fixed +++ b/tests/ui-internal/outer_expn_data.fixed @@ -21,6 +21,7 @@ declare_lint_pass!(Pass => [TEST_LINT]); impl<'tcx> LateLintPass<'tcx> for Pass { fn check_expr(&mut self, _cx: &LateContext<'tcx>, expr: &'tcx Expr) { let _ = expr.span.ctxt().outer_expn_data(); + //~^ outer_expn_expn_data } } diff --git a/tests/ui-internal/outer_expn_data.rs b/tests/ui-internal/outer_expn_data.rs index fb453be661c8..41d735110b5a 100644 --- a/tests/ui-internal/outer_expn_data.rs +++ b/tests/ui-internal/outer_expn_data.rs @@ -21,6 +21,7 @@ declare_lint_pass!(Pass => [TEST_LINT]); impl<'tcx> LateLintPass<'tcx> for Pass { fn check_expr(&mut self, _cx: &LateContext<'tcx>, expr: &'tcx Expr) { let _ = expr.span.ctxt().outer_expn().expn_data(); + //~^ outer_expn_expn_data } } diff --git a/tests/ui-internal/unnecessary_def_path.fixed b/tests/ui-internal/unnecessary_def_path.fixed index d3fab60f9e3e..577fad9341b6 100644 --- a/tests/ui-internal/unnecessary_def_path.fixed +++ b/tests/ui-internal/unnecessary_def_path.fixed @@ -35,28 +35,43 @@ const RESULT: &[&str] = &["core", "result", "Result"]; fn _f<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, did: DefId, expr: &Expr<'_>) { let _ = is_type_diagnostic_item(cx, ty, sym::Option); + //~^ unnecessary_def_path let _ = is_type_diagnostic_item(cx, ty, sym::Result); + //~^ unnecessary_def_path let _ = is_type_diagnostic_item(cx, ty, sym::Result); + //~^ unnecessary_def_path #[allow(unused, clippy::unnecessary_def_path)] let rc_path = &["alloc", "rc", "Rc"]; let _ = is_type_diagnostic_item(cx, ty, sym::Rc); + //~^ unnecessary_def_path let _ = is_type_diagnostic_item(cx, ty, sym::Option); + //~^ unnecessary_def_path let _ = is_type_diagnostic_item(cx, ty, sym::Result); + //~^ unnecessary_def_path let _ = is_type_lang_item(cx, ty, LangItem::OwnedBox); + //~^ unnecessary_def_path let _ = is_type_diagnostic_item(cx, ty, sym::maybe_uninit_uninit); + //~^ unnecessary_def_path let _ = cx.tcx.lang_items().get(LangItem::OwnedBox) == Some(did); + //~^ unnecessary_def_path let _ = cx.tcx.is_diagnostic_item(sym::Option, did); + //~^ unnecessary_def_path let _ = cx.tcx.lang_items().get(LangItem::OptionSome) == Some(did); + //~^ unnecessary_def_path let _ = is_trait_method(cx, expr, sym::AsRef); + //~^ unnecessary_def_path let _ = is_path_diagnostic_item(cx, expr, sym::Option); + //~^ unnecessary_def_path let _ = path_res(cx, expr).opt_def_id().map_or(false, |id| cx.tcx.lang_items().get(LangItem::IteratorNext) == Some(id)); + //~^ unnecessary_def_path let _ = is_res_lang_ctor(cx, path_res(cx, expr), LangItem::OptionSome); + //~^ unnecessary_def_path } fn main() {} diff --git a/tests/ui-internal/unnecessary_def_path.rs b/tests/ui-internal/unnecessary_def_path.rs index 1b36f6b09e9c..d4deb3626d0b 100644 --- a/tests/ui-internal/unnecessary_def_path.rs +++ b/tests/ui-internal/unnecessary_def_path.rs @@ -35,28 +35,43 @@ const RESULT: &[&str] = &["core", "result", "Result"]; fn _f<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, did: DefId, expr: &Expr<'_>) { let _ = match_type(cx, ty, &OPTION); + //~^ unnecessary_def_path let _ = match_type(cx, ty, RESULT); + //~^ unnecessary_def_path let _ = match_type(cx, ty, &["core", "result", "Result"]); + //~^ unnecessary_def_path #[allow(unused, clippy::unnecessary_def_path)] let rc_path = &["alloc", "rc", "Rc"]; let _ = clippy_utils::ty::match_type(cx, ty, rc_path); + //~^ unnecessary_def_path let _ = match_type(cx, ty, &paths::OPTION); + //~^ unnecessary_def_path let _ = match_type(cx, ty, paths::RESULT); + //~^ unnecessary_def_path let _ = match_type(cx, ty, &["alloc", "boxed", "Box"]); + //~^ unnecessary_def_path let _ = match_type(cx, ty, &["core", "mem", "maybe_uninit", "MaybeUninit", "uninit"]); + //~^ unnecessary_def_path let _ = match_def_path(cx, did, &["alloc", "boxed", "Box"]); + //~^ unnecessary_def_path let _ = match_def_path(cx, did, &["core", "option", "Option"]); + //~^ unnecessary_def_path let _ = match_def_path(cx, did, &["core", "option", "Option", "Some"]); + //~^ unnecessary_def_path let _ = match_trait_method(cx, expr, &["core", "convert", "AsRef"]); + //~^ unnecessary_def_path let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option"]); + //~^ unnecessary_def_path let _ = is_expr_path_def_path(cx, expr, &["core", "iter", "traits", "Iterator", "next"]); + //~^ unnecessary_def_path let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option", "Some"]); + //~^ unnecessary_def_path } fn main() {} diff --git a/tests/ui-internal/unnecessary_def_path.stderr b/tests/ui-internal/unnecessary_def_path.stderr index 79521c5037a8..0053ba321bbe 100644 --- a/tests/ui-internal/unnecessary_def_path.stderr +++ b/tests/ui-internal/unnecessary_def_path.stderr @@ -12,61 +12,61 @@ LL | #![deny(clippy::internal)] = note: `#[deny(clippy::unnecessary_def_path)]` implied by `#[deny(clippy::internal)]` error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:38:13 + --> tests/ui-internal/unnecessary_def_path.rs:39:13 | LL | let _ = match_type(cx, ty, RESULT); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Result)` error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:39:13 + --> tests/ui-internal/unnecessary_def_path.rs:41:13 | LL | let _ = match_type(cx, ty, &["core", "result", "Result"]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Result)` error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:43:13 + --> tests/ui-internal/unnecessary_def_path.rs:46:13 | LL | let _ = clippy_utils::ty::match_type(cx, ty, rc_path); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Rc)` error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:45:13 + --> tests/ui-internal/unnecessary_def_path.rs:49:13 | LL | let _ = match_type(cx, ty, &paths::OPTION); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Option)` error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:46:13 + --> tests/ui-internal/unnecessary_def_path.rs:51:13 | LL | let _ = match_type(cx, ty, paths::RESULT); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Result)` error: use of a def path to a `LangItem` - --> tests/ui-internal/unnecessary_def_path.rs:48:13 + --> tests/ui-internal/unnecessary_def_path.rs:54:13 | LL | let _ = match_type(cx, ty, &["alloc", "boxed", "Box"]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_lang_item(cx, ty, LangItem::OwnedBox)` error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:49:13 + --> tests/ui-internal/unnecessary_def_path.rs:56:13 | LL | let _ = match_type(cx, ty, &["core", "mem", "maybe_uninit", "MaybeUninit", "uninit"]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::maybe_uninit_uninit)` error: use of a def path to a `LangItem` - --> tests/ui-internal/unnecessary_def_path.rs:51:13 + --> tests/ui-internal/unnecessary_def_path.rs:59:13 | LL | let _ = match_def_path(cx, did, &["alloc", "boxed", "Box"]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.lang_items().get(LangItem::OwnedBox) == Some(did)` error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:52:13 + --> tests/ui-internal/unnecessary_def_path.rs:61:13 | LL | let _ = match_def_path(cx, did, &["core", "option", "Option"]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.is_diagnostic_item(sym::Option, did)` error: use of a def path to a `LangItem` - --> tests/ui-internal/unnecessary_def_path.rs:53:13 + --> tests/ui-internal/unnecessary_def_path.rs:63:13 | LL | let _ = match_def_path(cx, did, &["core", "option", "Option", "Some"]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.lang_items().get(LangItem::OptionSome) == Some(did)` @@ -74,25 +74,25 @@ LL | let _ = match_def_path(cx, did, &["core", "option", "Option", "Some"]); = help: if this `DefId` came from a constructor expression or pattern then the parent `DefId` should be used instead error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:55:13 + --> tests/ui-internal/unnecessary_def_path.rs:66:13 | LL | let _ = match_trait_method(cx, expr, &["core", "convert", "AsRef"]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_trait_method(cx, expr, sym::AsRef)` error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:57:13 + --> tests/ui-internal/unnecessary_def_path.rs:69:13 | LL | let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option"]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_path_diagnostic_item(cx, expr, sym::Option)` error: use of a def path to a `LangItem` - --> tests/ui-internal/unnecessary_def_path.rs:58:13 + --> tests/ui-internal/unnecessary_def_path.rs:71:13 | LL | let _ = is_expr_path_def_path(cx, expr, &["core", "iter", "traits", "Iterator", "next"]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `path_res(cx, expr).opt_def_id().map_or(false, |id| cx.tcx.lang_items().get(LangItem::IteratorNext) == Some(id))` error: use of a def path to a `LangItem` - --> tests/ui-internal/unnecessary_def_path.rs:59:13 + --> tests/ui-internal/unnecessary_def_path.rs:73:13 | LL | let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option", "Some"]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_res_lang_ctor(cx, path_res(cx, expr), LangItem::OptionSome)` diff --git a/tests/ui-internal/unnecessary_def_path_hardcoded_path.rs b/tests/ui-internal/unnecessary_def_path_hardcoded_path.rs index f6abb3cc3d71..4801d76bd268 100644 --- a/tests/ui-internal/unnecessary_def_path_hardcoded_path.rs +++ b/tests/ui-internal/unnecessary_def_path_hardcoded_path.rs @@ -8,8 +8,11 @@ use rustc_hir::LangItem; fn main() { const DEREF_TRAIT: [&str; 4] = ["core", "ops", "deref", "Deref"]; + //~^ unnecessary_def_path const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"]; + //~^ unnecessary_def_path const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"]; + //~^ unnecessary_def_path // Don't lint, not a diagnostic or language item const OPS_MOD: [&str; 2] = ["core", "ops"]; diff --git a/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr b/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr index 9d7c00088fe8..b93839519323 100644 --- a/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr +++ b/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr @@ -9,7 +9,7 @@ LL | const DEREF_TRAIT: [&str; 4] = ["core", "ops", "deref", "Deref"]; = help: to override `-D warnings` add `#[allow(clippy::unnecessary_def_path)]` error: hardcoded path to a language item - --> tests/ui-internal/unnecessary_def_path_hardcoded_path.rs:11:40 + --> tests/ui-internal/unnecessary_def_path_hardcoded_path.rs:12:40 | LL | const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"] = help: convert all references to use `LangItem::DerefMut` error: hardcoded path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path_hardcoded_path.rs:12:43 + --> tests/ui-internal/unnecessary_def_path_hardcoded_path.rs:14:43 | LL | const OPS_MOD: [&str; 5] = ["core", "ops"]; | ^^^^^^^^^^^^^^^ diff --git a/tests/ui-internal/unnecessary_symbol_str.fixed b/tests/ui-internal/unnecessary_symbol_str.fixed index 3d9deb705ace..dc564daef829 100644 --- a/tests/ui-internal/unnecessary_symbol_str.fixed +++ b/tests/ui-internal/unnecessary_symbol_str.fixed @@ -14,8 +14,13 @@ use rustc_span::symbol::{Ident, Symbol}; fn main() { Symbol::intern("foo") == rustc_span::sym::clippy; + //~^ unnecessary_symbol_str Symbol::intern("foo") == rustc_span::kw::SelfLower; + //~^ unnecessary_symbol_str Symbol::intern("foo") != rustc_span::kw::SelfUpper; + //~^ unnecessary_symbol_str Ident::empty().name == rustc_span::sym::clippy; + //~^ unnecessary_symbol_str rustc_span::sym::clippy == Ident::empty().name; + //~^ unnecessary_symbol_str } diff --git a/tests/ui-internal/unnecessary_symbol_str.rs b/tests/ui-internal/unnecessary_symbol_str.rs index 9aeeb9aaf3aa..d74262d1294b 100644 --- a/tests/ui-internal/unnecessary_symbol_str.rs +++ b/tests/ui-internal/unnecessary_symbol_str.rs @@ -14,8 +14,13 @@ use rustc_span::symbol::{Ident, Symbol}; fn main() { Symbol::intern("foo").as_str() == "clippy"; + //~^ unnecessary_symbol_str Symbol::intern("foo").to_string() == "self"; + //~^ unnecessary_symbol_str Symbol::intern("foo").to_ident_string() != "Self"; + //~^ unnecessary_symbol_str &*Ident::empty().as_str() == "clippy"; + //~^ unnecessary_symbol_str "clippy" == Ident::empty().to_string(); + //~^ unnecessary_symbol_str } diff --git a/tests/ui-internal/unnecessary_symbol_str.stderr b/tests/ui-internal/unnecessary_symbol_str.stderr index 1742603eff6d..517a395e93f2 100644 --- a/tests/ui-internal/unnecessary_symbol_str.stderr +++ b/tests/ui-internal/unnecessary_symbol_str.stderr @@ -12,25 +12,25 @@ LL | #![deny(clippy::internal)] = note: `#[deny(clippy::unnecessary_symbol_str)]` implied by `#[deny(clippy::internal)]` error: unnecessary `Symbol` to string conversion - --> tests/ui-internal/unnecessary_symbol_str.rs:17:5 + --> tests/ui-internal/unnecessary_symbol_str.rs:18:5 | LL | Symbol::intern("foo").to_string() == "self"; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") == rustc_span::kw::SelfLower` error: unnecessary `Symbol` to string conversion - --> tests/ui-internal/unnecessary_symbol_str.rs:18:5 + --> tests/ui-internal/unnecessary_symbol_str.rs:20:5 | LL | Symbol::intern("foo").to_ident_string() != "Self"; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") != rustc_span::kw::SelfUpper` error: unnecessary `Symbol` to string conversion - --> tests/ui-internal/unnecessary_symbol_str.rs:19:5 + --> tests/ui-internal/unnecessary_symbol_str.rs:22:5 | LL | &*Ident::empty().as_str() == "clippy"; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Ident::empty().name == rustc_span::sym::clippy` error: unnecessary `Symbol` to string conversion - --> tests/ui-internal/unnecessary_symbol_str.rs:20:5 + --> tests/ui-internal/unnecessary_symbol_str.rs:24:5 | LL | "clippy" == Ident::empty().to_string(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::clippy == Ident::empty().name` diff --git a/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_4/clippy.toml b/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_4/clippy.toml new file mode 100644 index 000000000000..baf7041138f9 --- /dev/null +++ b/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_4/clippy.toml @@ -0,0 +1 @@ +module-items-ordered-within-groupings = true diff --git a/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_5/clippy.toml b/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_5/clippy.toml new file mode 100644 index 000000000000..1fa80c730126 --- /dev/null +++ b/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_5/clippy.toml @@ -0,0 +1 @@ +module-items-ordered-within-groupings = ["madules"] diff --git a/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_6/clippy.toml b/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_6/clippy.toml new file mode 100644 index 000000000000..cee857a155b9 --- /dev/null +++ b/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_6/clippy.toml @@ -0,0 +1 @@ +module-items-ordered-within-groupings = ["entirely garbled"] diff --git a/tests/ui-toml/arbitrary_source_item_ordering/default_exp/clippy.toml b/tests/ui-toml/arbitrary_source_item_ordering/default_exp/clippy.toml index ddca5cfa5779..af3aa1cc62a4 100644 --- a/tests/ui-toml/arbitrary_source_item_ordering/default_exp/clippy.toml +++ b/tests/ui-toml/arbitrary_source_item_ordering/default_exp/clippy.toml @@ -9,4 +9,4 @@ module-item-order-groupings = [ ["PascalCase", ["ty_alias", "enum", "struct", "union", "trait", "trait_alias", "impl"]], ["lower_snake_case", ["fn"]] ] - +module-items-ordered-within-groupings = "none" diff --git a/tests/ui-toml/arbitrary_source_item_ordering/ord_in_2/clippy.toml b/tests/ui-toml/arbitrary_source_item_ordering/ord_in_2/clippy.toml new file mode 100644 index 000000000000..fd961c82d6f2 --- /dev/null +++ b/tests/ui-toml/arbitrary_source_item_ordering/ord_in_2/clippy.toml @@ -0,0 +1 @@ +module-items-ordered-within-groupings = ["PascalCase"] diff --git a/tests/ui-toml/arbitrary_source_item_ordering/ord_in_3/clippy.toml b/tests/ui-toml/arbitrary_source_item_ordering/ord_in_3/clippy.toml new file mode 100644 index 000000000000..de2a7307eca6 --- /dev/null +++ b/tests/ui-toml/arbitrary_source_item_ordering/ord_in_3/clippy.toml @@ -0,0 +1,2 @@ +source-item-ordering = ["module"] +module-items-ordered-within-groupings = ["PascalCase"] diff --git a/tests/ui-toml/arbitrary_source_item_ordering/ord_within/clippy.toml b/tests/ui-toml/arbitrary_source_item_ordering/ord_within/clippy.toml new file mode 100644 index 000000000000..e7640efde10c --- /dev/null +++ b/tests/ui-toml/arbitrary_source_item_ordering/ord_within/clippy.toml @@ -0,0 +1 @@ +module-items-ordered-within-groupings = "all" diff --git a/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_4.stderr b/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_4.stderr new file mode 100644 index 000000000000..c38c54ffeb0d --- /dev/null +++ b/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_4.stderr @@ -0,0 +1,8 @@ +error: error reading Clippy's configuration file: data did not match any variant of untagged enum StringOrVecOfString The available options for configuring an ordering within module item groups are: "all", "none", or a list of module item group names (as configured with the `module-item-order-groupings` configuration option). + --> $DIR/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_4/clippy.toml:1:41 + | +LL | module-items-ordered-within-groupings = true + | ^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_5.stderr b/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_5.stderr new file mode 100644 index 000000000000..7b1dafb6d0d5 --- /dev/null +++ b/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_5.stderr @@ -0,0 +1,4 @@ +error: error reading Clippy's configuration file: unknown ordering group: `madules` was not specified in `module-items-ordered-within-groupings`, perhaps you meant `modules`? expected one of: `modules`, `use`, `macros`, `global_asm`, `UPPER_SNAKE_CASE`, `PascalCase`, `lower_snake_case` + +error: aborting due to 1 previous error + diff --git a/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_6.stderr b/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_6.stderr new file mode 100644 index 000000000000..996cabeeed4a --- /dev/null +++ b/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_6.stderr @@ -0,0 +1,4 @@ +error: error reading Clippy's configuration file: unknown ordering group: `entirely garbled` was not specified in `module-items-ordered-within-groupings`, expected one of: `modules`, `use`, `macros`, `global_asm`, `UPPER_SNAKE_CASE`, `PascalCase`, `lower_snake_case` + +error: aborting due to 1 previous error + diff --git a/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.rs b/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.rs index 05eb40506db4..b43791521cb5 100644 --- a/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.rs +++ b/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.rs @@ -1,15 +1,21 @@ //@aux-build:../../ui/auxiliary/proc_macros.rs -//@revisions: default default_exp bad_conf_1 bad_conf_2 bad_conf_3 +//@revisions: default default_exp bad_conf_1 bad_conf_2 bad_conf_3 bad_conf_4 bad_conf_5 bad_conf_6 //@[default] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/default //@[default_exp] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/default_exp //@[bad_conf_1] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/bad_conf_1 //@[bad_conf_2] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/bad_conf_2 //@[bad_conf_3] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/bad_conf_3 +//@[bad_conf_4] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/bad_conf_4 +//@[bad_conf_5] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/bad_conf_5 +//@[bad_conf_6] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/bad_conf_6 //@[default] check-pass //@[default_exp] check-pass //@[bad_conf_1] error-in-other-file: //@[bad_conf_2] error-in-other-file: //@[bad_conf_3] error-in-other-file: +//@[bad_conf_4] error-in-other-file: +//@[bad_conf_5] error-in-other-file: +//@[bad_conf_6] error-in-other-file: #![allow(dead_code)] #![warn(clippy::arbitrary_source_item_ordering)] diff --git a/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default.stderr b/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default.stderr index 3605952bddc9..50567e32b1bb 100644 --- a/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default.stderr +++ b/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default.stderr @@ -1,223 +1,160 @@ -error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:22:14 +error: incorrect ordering of items (module item groupings specify another order) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:26:14 | LL | use std::rc::Weak; | ^^^^ | note: should be placed before `SNAKE_CASE` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:20:7 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:23:7 | LL | const SNAKE_CASE: &str = "zzzzzzzz"; | ^^^^^^^^^^ = note: `-D clippy::arbitrary-source-item-ordering` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::arbitrary_source_item_ordering)]` -error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:67:1 - | -LL | / impl CloneSelf for StructOrdered { -LL | | -LL | | fn clone_self(&self) -> Self { -LL | | Self { -... | -LL | | } - | |_^ - | -note: should be placed before the following item - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:57:1 - | -LL | / impl Default for StructOrdered { -LL | | fn default() -> Self { -LL | | Self { -LL | | a: true, -... | -LL | | } - | |_^ - -error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:145:7 +error: incorrect ordering of items (module item groupings specify another order) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:149:7 | LL | const ZIS_SHOULD_BE_REALLY_EARLY: () = (); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: should be placed before `TraitUnorderedItemKinds` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:132:7 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:136:7 | LL | trait TraitUnorderedItemKinds { | ^^^^^^^^^^^^^^^^^^^^^^^ -error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:163:1 - | -LL | impl BasicEmptyTrait for StructOrdered {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: should be placed before the following item - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:148:1 - | -LL | / impl TraitUnordered for StructUnordered { -LL | | const A: bool = false; -LL | | const C: bool = false; -LL | | const B: bool = false; -... | -LL | | } - | |_^ - -error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:184:5 +error: incorrect ordering of items (module item groupings specify another order) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:188:5 | LL | mod this_is_in_the_wrong_position { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: should be placed before `main` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:179:4 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:183:4 | LL | fn main() { | ^^^^ -error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:194:7 +error: incorrect ordering of items (module item groupings specify another order) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:198:7 | LL | const ZIS_SHOULD_BE_EVEN_EARLIER: () = (); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: should be placed before `ZisShouldBeBeforeZeMainFn` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:192:8 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:196:8 | LL | struct ZisShouldBeBeforeZeMainFn; | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:12:11 - | -LL | const AFTER: i8 = 0; - | ^^^^^ - | -note: should be placed before `BEFORE` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:10:11 - | -LL | const BEFORE: i8 = 0; - | ^^^^^^ - -error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:40:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:44:5 | LL | B, | ^ | note: should be placed before `C` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:39:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:43:5 | LL | C, | ^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:92:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:96:5 | LL | b: bool, | ^ | note: should be placed before `c` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:91:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:95:5 | LL | c: bool, | ^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:101:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:105:5 | LL | b: bool, | ^ | note: should be placed before `c` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:100:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:104:5 | LL | c: bool, | ^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:121:11 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:125:11 | LL | const B: bool; | ^ | note: should be placed before `C` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:120:11 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:124:11 | LL | const C: bool; | ^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:128:8 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:132:8 | LL | fn b(); | ^ | note: should be placed before `c` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:127:8 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:131:8 | LL | fn c(); | ^ error: incorrect ordering of trait items (defined order: [Const, Type, Fn]) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:135:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:139:5 | LL | const A: bool; | ^^^^^^^^^^^^^^ | note: should be placed before `SomeType` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:133:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:137:5 | LL | type SomeType; | ^^^^^^^^^^^^^^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:151:11 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:155:11 | LL | const B: bool = false; | ^ | note: should be placed before `C` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:150:11 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:154:11 | LL | const C: bool = false; | ^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:158:8 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:162:8 | LL | fn b() {} | ^ | note: should be placed before `c` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:157:8 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:161:8 | LL | fn c() {} | ^ error: incorrect ordering of impl items (defined order: [Const, Type, Fn]) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:169:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:173:5 | LL | const A: bool = false; | ^^^^^^^^^^^^^^^^^^^^^^ | note: should be placed before `SomeType` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:167:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:171:5 | LL | type SomeType = (); | ^^^^^^^^^^^^^^^^^^^ -error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:187:11 - | -LL | const A: i8 = 1; - | ^ - | -note: should be placed before `C` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:186:11 - | -LL | const C: i8 = 0; - | ^ - -error: aborting due to 17 previous errors +error: aborting due to 13 previous errors diff --git a/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default_exp.stderr b/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default_exp.stderr new file mode 100644 index 000000000000..50567e32b1bb --- /dev/null +++ b/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default_exp.stderr @@ -0,0 +1,160 @@ +error: incorrect ordering of items (module item groupings specify another order) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:26:14 + | +LL | use std::rc::Weak; + | ^^^^ + | +note: should be placed before `SNAKE_CASE` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:23:7 + | +LL | const SNAKE_CASE: &str = "zzzzzzzz"; + | ^^^^^^^^^^ + = note: `-D clippy::arbitrary-source-item-ordering` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::arbitrary_source_item_ordering)]` + +error: incorrect ordering of items (module item groupings specify another order) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:149:7 + | +LL | const ZIS_SHOULD_BE_REALLY_EARLY: () = (); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: should be placed before `TraitUnorderedItemKinds` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:136:7 + | +LL | trait TraitUnorderedItemKinds { + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: incorrect ordering of items (module item groupings specify another order) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:188:5 + | +LL | mod this_is_in_the_wrong_position { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: should be placed before `main` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:183:4 + | +LL | fn main() { + | ^^^^ + +error: incorrect ordering of items (module item groupings specify another order) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:198:7 + | +LL | const ZIS_SHOULD_BE_EVEN_EARLIER: () = (); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: should be placed before `ZisShouldBeBeforeZeMainFn` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:196:8 + | +LL | struct ZisShouldBeBeforeZeMainFn; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:44:5 + | +LL | B, + | ^ + | +note: should be placed before `C` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:43:5 + | +LL | C, + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:96:5 + | +LL | b: bool, + | ^ + | +note: should be placed before `c` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:95:5 + | +LL | c: bool, + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:105:5 + | +LL | b: bool, + | ^ + | +note: should be placed before `c` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:104:5 + | +LL | c: bool, + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:125:11 + | +LL | const B: bool; + | ^ + | +note: should be placed before `C` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:124:11 + | +LL | const C: bool; + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:132:8 + | +LL | fn b(); + | ^ + | +note: should be placed before `c` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:131:8 + | +LL | fn c(); + | ^ + +error: incorrect ordering of trait items (defined order: [Const, Type, Fn]) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:139:5 + | +LL | const A: bool; + | ^^^^^^^^^^^^^^ + | +note: should be placed before `SomeType` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:137:5 + | +LL | type SomeType; + | ^^^^^^^^^^^^^^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:155:11 + | +LL | const B: bool = false; + | ^ + | +note: should be placed before `C` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:154:11 + | +LL | const C: bool = false; + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:162:8 + | +LL | fn b() {} + | ^ + | +note: should be placed before `c` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:161:8 + | +LL | fn c() {} + | ^ + +error: incorrect ordering of impl items (defined order: [Const, Type, Fn]) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:173:5 + | +LL | const A: bool = false; + | ^^^^^^^^^^^^^^^^^^^^^^ + | +note: should be placed before `SomeType` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:171:5 + | +LL | type SomeType = (); + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 13 previous errors + diff --git a/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.ord_within.stderr b/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.ord_within.stderr new file mode 100644 index 000000000000..ae5261dcc6df --- /dev/null +++ b/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.ord_within.stderr @@ -0,0 +1,235 @@ +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:23:7 + | +LL | const SNAKE_CASE: &str = "zzzzzzzz"; + | ^^^^^^^^^^ + | +note: should be placed before `ZNAKE_CASE` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:22:7 + | +LL | const ZNAKE_CASE: &str = "123"; + | ^^^^^^^^^^ + = note: `-D clippy::arbitrary-source-item-ordering` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::arbitrary_source_item_ordering)]` + +error: incorrect ordering of items (module item groupings specify another order) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:26:14 + | +LL | use std::rc::Weak; + | ^^^^ + | +note: should be placed before `SNAKE_CASE` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:23:7 + | +LL | const SNAKE_CASE: &str = "zzzzzzzz"; + | ^^^^^^^^^^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:71:1 + | +LL | / impl CloneSelf for StructOrdered { +LL | | +LL | | fn clone_self(&self) -> Self { +LL | | Self { +... | +LL | | } + | |_^ + | +note: should be placed before the following item + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:61:1 + | +LL | / impl Default for StructOrdered { +LL | | fn default() -> Self { +LL | | Self { +LL | | a: true, +... | +LL | | } + | |_^ + +error: incorrect ordering of items (module item groupings specify another order) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:149:7 + | +LL | const ZIS_SHOULD_BE_REALLY_EARLY: () = (); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: should be placed before `TraitUnorderedItemKinds` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:136:7 + | +LL | trait TraitUnorderedItemKinds { + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:167:1 + | +LL | impl BasicEmptyTrait for StructOrdered {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: should be placed before the following item + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:152:1 + | +LL | / impl TraitUnordered for StructUnordered { +LL | | const A: bool = false; +LL | | const C: bool = false; +LL | | const B: bool = false; +... | +LL | | } + | |_^ + +error: incorrect ordering of items (module item groupings specify another order) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:188:5 + | +LL | mod this_is_in_the_wrong_position { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: should be placed before `main` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:183:4 + | +LL | fn main() { + | ^^^^ + +error: incorrect ordering of items (module item groupings specify another order) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:198:7 + | +LL | const ZIS_SHOULD_BE_EVEN_EARLIER: () = (); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: should be placed before `ZisShouldBeBeforeZeMainFn` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:196:8 + | +LL | struct ZisShouldBeBeforeZeMainFn; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:14:11 + | +LL | const AFTER: i8 = 0; + | ^^^^^ + | +note: should be placed before `BEFORE` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:12:11 + | +LL | const BEFORE: i8 = 0; + | ^^^^^^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:44:5 + | +LL | B, + | ^ + | +note: should be placed before `C` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:43:5 + | +LL | C, + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:96:5 + | +LL | b: bool, + | ^ + | +note: should be placed before `c` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:95:5 + | +LL | c: bool, + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:105:5 + | +LL | b: bool, + | ^ + | +note: should be placed before `c` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:104:5 + | +LL | c: bool, + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:125:11 + | +LL | const B: bool; + | ^ + | +note: should be placed before `C` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:124:11 + | +LL | const C: bool; + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:132:8 + | +LL | fn b(); + | ^ + | +note: should be placed before `c` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:131:8 + | +LL | fn c(); + | ^ + +error: incorrect ordering of trait items (defined order: [Const, Type, Fn]) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:139:5 + | +LL | const A: bool; + | ^^^^^^^^^^^^^^ + | +note: should be placed before `SomeType` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:137:5 + | +LL | type SomeType; + | ^^^^^^^^^^^^^^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:155:11 + | +LL | const B: bool = false; + | ^ + | +note: should be placed before `C` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:154:11 + | +LL | const C: bool = false; + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:162:8 + | +LL | fn b() {} + | ^ + | +note: should be placed before `c` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:161:8 + | +LL | fn c() {} + | ^ + +error: incorrect ordering of impl items (defined order: [Const, Type, Fn]) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:173:5 + | +LL | const A: bool = false; + | ^^^^^^^^^^^^^^^^^^^^^^ + | +note: should be placed before `SomeType` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:171:5 + | +LL | type SomeType = (); + | ^^^^^^^^^^^^^^^^^^^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:191:11 + | +LL | const A: i8 = 1; + | ^ + | +note: should be placed before `C` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:190:11 + | +LL | const C: i8 = 0; + | ^ + +error: aborting due to 18 previous errors + diff --git a/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs b/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs index 9e65a9cca0da..90399470d4c0 100644 --- a/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs +++ b/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs @@ -1,6 +1,8 @@ //@aux-build:../../ui/auxiliary/proc_macros.rs -//@revisions: default +//@revisions: default default_exp ord_within //@[default] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/default +//@[default_exp] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/default_exp +//@[ord_within] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/ord_within #![allow(dead_code)] #![warn(clippy::arbitrary_source_item_ordering)] @@ -10,14 +12,16 @@ mod i_am_just_right { const BEFORE: i8 = 0; const AFTER: i8 = 0; - //~^ arbitrary_source_item_ordering + //~[ord_within]^ arbitrary_source_item_ordering } // Use statements should not be linted internally - this is normally auto-sorted using rustfmt. use std::rc::Rc; use std::sync::{Arc, Barrier, RwLock}; +const ZNAKE_CASE: &str = "123"; const SNAKE_CASE: &str = "zzzzzzzz"; +//~[ord_within]^ arbitrary_source_item_ordering use std::rc::Weak; //~^ arbitrary_source_item_ordering @@ -65,7 +69,7 @@ impl Default for StructOrdered { } impl CloneSelf for StructOrdered { - //~^ arbitrary_source_item_ordering + //~[ord_within]^ arbitrary_source_item_ordering fn clone_self(&self) -> Self { Self { a: true, @@ -161,7 +165,7 @@ impl TraitUnordered for StructUnordered { // Trait impls should be located just after the type they implement it for. impl BasicEmptyTrait for StructOrdered {} -//~^ arbitrary_source_item_ordering +//~[ord_within]^ arbitrary_source_item_ordering impl TraitUnorderedItemKinds for StructUnordered { type SomeType = (); @@ -185,7 +189,7 @@ mod this_is_in_the_wrong_position { //~^ arbitrary_source_item_ordering const C: i8 = 0; const A: i8 = 1; - //~^ arbitrary_source_item_ordering + //~[ord_within]^ arbitrary_source_item_ordering } #[derive(Default, std::clone::Clone)] diff --git a/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.default.stderr b/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.default.stderr new file mode 100644 index 000000000000..7fc216b30d50 --- /dev/null +++ b/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.default.stderr @@ -0,0 +1,19 @@ +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:35:5 + | +LL | a: bool, + | ^ + | +note: should be placed before `b` + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:34:5 + | +LL | b: bool, + | ^ +note: the lint level is defined here + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:32:8 + | +LL | #[deny(clippy::arbitrary_source_item_ordering)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.ord_in_2.stderr b/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.ord_in_2.stderr new file mode 100644 index 000000000000..1f75f5099ecc --- /dev/null +++ b/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.ord_in_2.stderr @@ -0,0 +1,36 @@ +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:24:8 + | +LL | struct OrderedChecked { + | ^^^^^^^^^^^^^^ + | +note: should be placed before `Unordered` + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:18:8 + | +LL | struct Unordered { + | ^^^^^^^^^ +note: the lint level is defined here + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:9:9 + | +LL | #![deny(clippy::arbitrary_source_item_ordering)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:35:5 + | +LL | a: bool, + | ^ + | +note: should be placed before `b` + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:34:5 + | +LL | b: bool, + | ^ +note: the lint level is defined here + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:32:8 + | +LL | #[deny(clippy::arbitrary_source_item_ordering)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.ord_in_3.stderr b/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.ord_in_3.stderr new file mode 100644 index 000000000000..8027f55add67 --- /dev/null +++ b/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.ord_in_3.stderr @@ -0,0 +1,19 @@ +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:24:8 + | +LL | struct OrderedChecked { + | ^^^^^^^^^^^^^^ + | +note: should be placed before `Unordered` + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:18:8 + | +LL | struct Unordered { + | ^^^^^^^^^ +note: the lint level is defined here + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:9:9 + | +LL | #![deny(clippy::arbitrary_source_item_ordering)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.ord_within.stderr b/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.ord_within.stderr new file mode 100644 index 000000000000..333a601f6a95 --- /dev/null +++ b/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.ord_within.stderr @@ -0,0 +1,48 @@ +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:24:8 + | +LL | struct OrderedChecked { + | ^^^^^^^^^^^^^^ + | +note: should be placed before `Unordered` + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:18:8 + | +LL | struct Unordered { + | ^^^^^^^^^ +note: the lint level is defined here + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:9:9 + | +LL | #![deny(clippy::arbitrary_source_item_ordering)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:45:4 + | +LL | fn before_main() {} + | ^^^^^^^^^^^ + | +note: should be placed before `main` + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:41:4 + | +LL | fn main() { + | ^^^^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:35:5 + | +LL | a: bool, + | ^ + | +note: should be placed before `b` + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:34:5 + | +LL | b: bool, + | ^ +note: the lint level is defined here + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:32:8 + | +LL | #[deny(clippy::arbitrary_source_item_ordering)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs b/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs new file mode 100644 index 000000000000..e32b921dd965 --- /dev/null +++ b/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs @@ -0,0 +1,46 @@ +//@aux-build:../../ui/auxiliary/proc_macros.rs +//@revisions: default ord_within ord_in_2 ord_in_3 +//@[default] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/default +//@[ord_within] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/ord_within +//@[ord_in_2] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/ord_in_2 +//@[ord_in_3] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/ord_in_3 + +#![allow(dead_code)] +#![deny(clippy::arbitrary_source_item_ordering)] + +#[allow(clippy::arbitrary_source_item_ordering)] +struct Ordered { + a: bool, + b: bool, +} + +#[allow(clippy::arbitrary_source_item_ordering)] +struct Unordered { + b: bool, + a: bool, +} + +#[deny(clippy::arbitrary_source_item_ordering)] +struct OrderedChecked { + //~[ord_within]^ arbitrary_source_item_ordering + //~[ord_in_2]| arbitrary_source_item_ordering + //~[ord_in_3]| arbitrary_source_item_ordering + a: bool, + b: bool, +} + +#[deny(clippy::arbitrary_source_item_ordering)] +struct UnorderedChecked { + b: bool, + a: bool, + //~[ord_within]^ arbitrary_source_item_ordering + //~[default]| arbitrary_source_item_ordering + //~[ord_in_2]| arbitrary_source_item_ordering +} + +fn main() { + // test code goes here +} + +fn before_main() {} +//~[ord_within]^ arbitrary_source_item_ordering diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index acfe739277cc..fee5b01b6898 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -60,6 +60,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect min-ident-chars-threshold missing-docs-in-crate-items module-item-order-groupings + module-items-ordered-within-groupings msrv pass-by-value-size-limit pub-underscore-fields-behavior @@ -152,6 +153,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect min-ident-chars-threshold missing-docs-in-crate-items module-item-order-groupings + module-items-ordered-within-groupings msrv pass-by-value-size-limit pub-underscore-fields-behavior @@ -244,6 +246,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni min-ident-chars-threshold missing-docs-in-crate-items module-item-order-groupings + module-items-ordered-within-groupings msrv pass-by-value-size-limit pub-underscore-fields-behavior diff --git a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.default.stderr b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.default.stderr index 32ed78151d23..8a2f201009a9 100644 --- a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.default.stderr +++ b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.default.stderr @@ -310,5 +310,49 @@ LL | let bar = unsafe {}; | = help: consider adding a safety comment on the preceding line -error: aborting due to 35 previous errors +error: unsafe block missing a safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:638:52 + | +LL | const NO_SAFETY_IN_TRAIT_BUT_IN_IMPL: u8 = unsafe { 0 }; + | ^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:647:41 + | +LL | const NO_SAFETY_IN_TRAIT: i32 = unsafe { 1 }; + | ^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:657:42 + | +LL | const HAS_SAFETY_IN_TRAIT: i32 = unsafe { 3 }; + | ^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:662:40 + | +LL | const NO_SAFETY_IN_IMPL: i32 = unsafe { 1 }; + | ^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: statement has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:719:5 + | +LL | _ = bar(); + | ^^^^^^^^^^ + | +help: consider removing the safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:718:5 + | +LL | // SAFETY: unnecessary_safety_comment triggers here + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 40 previous errors diff --git a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr index 83a6986addf2..e9c5e5f9f114 100644 --- a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr +++ b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr @@ -390,5 +390,65 @@ LL | unsafe {} | = help: consider adding a safety comment on the preceding line -error: aborting due to 45 previous errors +error: unsafe block missing a safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:638:52 + | +LL | const NO_SAFETY_IN_TRAIT_BUT_IN_IMPL: u8 = unsafe { 0 }; + | ^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:647:41 + | +LL | const NO_SAFETY_IN_TRAIT: i32 = unsafe { 1 }; + | ^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:657:42 + | +LL | const HAS_SAFETY_IN_TRAIT: i32 = unsafe { 3 }; + | ^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:662:40 + | +LL | const NO_SAFETY_IN_IMPL: i32 = unsafe { 1 }; + | ^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:673:9 + | +LL | unsafe { here_is_another_variable_with_long_name }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:702:9 + | +LL | unsafe { Date::__from_ordinal_date_unchecked(1970, 1) }.into_julian_day_just_make_this_line_longer(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: statement has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:719:5 + | +LL | _ = bar(); + | ^^^^^^^^^^ + | +help: consider removing the safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:718:5 + | +LL | // SAFETY: unnecessary_safety_comment triggers here + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 52 previous errors diff --git a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs index 6a3fda3df5c3..91a02bc3d7c6 100644 --- a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs +++ b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs @@ -632,4 +632,95 @@ mod issue_11246 { // Safety: Another safety comment const FOO: () = unsafe {}; +// trait items and impl items +mod issue_11709 { + trait MyTrait { + const NO_SAFETY_IN_TRAIT_BUT_IN_IMPL: u8 = unsafe { 0 }; + //~^ ERROR: unsafe block missing a safety comment + + // SAFETY: safe + const HAS_SAFETY_IN_TRAIT: i32 = unsafe { 1 }; + + // SAFETY: unrelated + unsafe fn unsafe_fn() {} + + const NO_SAFETY_IN_TRAIT: i32 = unsafe { 1 }; + //~^ ERROR: unsafe block missing a safety comment + } + + struct UnsafeStruct; + + impl MyTrait for UnsafeStruct { + // SAFETY: safe in this impl + const NO_SAFETY_IN_TRAIT_BUT_IN_IMPL: u8 = unsafe { 2 }; + + const HAS_SAFETY_IN_TRAIT: i32 = unsafe { 3 }; + //~^ ERROR: unsafe block missing a safety comment + } + + impl UnsafeStruct { + const NO_SAFETY_IN_IMPL: i32 = unsafe { 1 }; + //~^ ERROR: unsafe block missing a safety comment + } +} + +fn issue_13024() { + let mut just_a_simple_variable_with_a_very_long_name_that_has_seventy_eight_characters = 0; + let here_is_another_variable_with_long_name = 100; + + // SAFETY: fail ONLY if `accept-comment-above-statement = false` + just_a_simple_variable_with_a_very_long_name_that_has_seventy_eight_characters = + unsafe { here_is_another_variable_with_long_name }; + //~[disabled]^ undocumented_unsafe_blocks +} + +// https://docs.rs/time/0.3.36/src/time/offset_date_time.rs.html#35 +mod issue_11709_regression { + use std::num::NonZeroI32; + + struct Date { + value: NonZeroI32, + } + + impl Date { + const unsafe fn __from_ordinal_date_unchecked(year: i32, ordinal: u16) -> Self { + Self { + // Safety: The caller must guarantee that `ordinal` is not zero. + value: unsafe { NonZeroI32::new_unchecked((year << 9) | ordinal as i32) }, + } + } + + const fn into_julian_day_just_make_this_line_longer(self) -> i32 { + 42 + } + } + + /// The Julian day of the Unix epoch. + // SAFETY: fail ONLY if `accept-comment-above-attribute = false` + #[allow(unsafe_code)] + const UNIX_EPOCH_JULIAN_DAY: i32 = + unsafe { Date::__from_ordinal_date_unchecked(1970, 1) }.into_julian_day_just_make_this_line_longer(); + //~[disabled]^ undocumented_unsafe_blocks +} + +fn issue_13039() { + unsafe fn foo() -> usize { + 1234 + } + + fn bar() -> usize { + 1234 + } + + // SAFETY: unnecessary_safety_comment should not trigger here + _ = unsafe { foo() }; + + // SAFETY: unnecessary_safety_comment triggers here + _ = bar(); + //~^ unnecessary_safety_comment + + // SAFETY: unnecessary_safety_comment should not trigger here + _ = unsafe { foo() } +} + fn main() {} diff --git a/tests/ui/blocks_in_conditions.fixed b/tests/ui/blocks_in_conditions.fixed index df375e370573..cd307e803d0c 100644 --- a/tests/ui/blocks_in_conditions.fixed +++ b/tests/ui/blocks_in_conditions.fixed @@ -118,6 +118,13 @@ mod issue_12016 { } } +fn issue_9911() { + if { return } {} + + let a = 1; + if { if a == 1 { return } else { true } } {} +} + fn in_closure() { let v = vec![1, 2, 3]; if v.into_iter() diff --git a/tests/ui/blocks_in_conditions.rs b/tests/ui/blocks_in_conditions.rs index 1d9c9dd42460..6a211c8edfd4 100644 --- a/tests/ui/blocks_in_conditions.rs +++ b/tests/ui/blocks_in_conditions.rs @@ -118,6 +118,13 @@ mod issue_12016 { } } +fn issue_9911() { + if { return } {} + + let a = 1; + if { if a == 1 { return } else { true } } {} +} + fn in_closure() { let v = vec![1, 2, 3]; if v.into_iter() diff --git a/tests/ui/crashes/if_same_then_else.rs b/tests/ui/crashes/if_same_then_else.rs index 58ee751948b5..8a27b3c6d47d 100644 --- a/tests/ui/crashes/if_same_then_else.rs +++ b/tests/ui/crashes/if_same_then_else.rs @@ -1,6 +1,5 @@ //@ check-pass -#![allow(clippy::comparison_chain)] #![deny(clippy::if_same_then_else)] // Test for https://github.com/rust-lang/rust-clippy/issues/2426 diff --git a/tests/ui/crashes/needless_pass_by_value-w-late-bound.stderr b/tests/ui/crashes/needless_pass_by_value-w-late-bound.stderr index 284795700069..b4fb1222539a 100644 --- a/tests/ui/crashes/needless_pass_by_value-w-late-bound.stderr +++ b/tests/ui/crashes/needless_pass_by_value-w-late-bound.stderr @@ -2,7 +2,7 @@ error: this argument is passed by value, but not consumed in the function body --> tests/ui/crashes/needless_pass_by_value-w-late-bound.rs:7:12 | LL | fn test(x: Foo<'_>) {} - | ^^^^^^^ help: consider taking a reference instead: `&Foo<'_>` + | ^^^^^^^ | help: or consider marking this type as `Copy` --> tests/ui/crashes/needless_pass_by_value-w-late-bound.rs:5:1 @@ -11,6 +11,10 @@ LL | struct Foo<'a>(&'a [(); 100]); | ^^^^^^^^^^^^^^ = note: `-D clippy::needless-pass-by-value` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::needless_pass_by_value)]` +help: consider taking a reference instead + | +LL | fn test(x: &Foo<'_>) {} + | + error: aborting due to 1 previous error diff --git a/tests/ui/doc/doc_comment_double_space_linebreaks.fixed b/tests/ui/doc/doc_comment_double_space_linebreaks.fixed new file mode 100644 index 000000000000..6a616b2c7e18 --- /dev/null +++ b/tests/ui/doc/doc_comment_double_space_linebreaks.fixed @@ -0,0 +1,98 @@ +#![feature(custom_inner_attributes)] +#![rustfmt::skip] + +#![warn(clippy::doc_comment_double_space_linebreaks)] +#![allow(unused, clippy::empty_docs)] + +//~v doc_comment_double_space_linebreaks +//! Should warn on double space linebreaks\ +//! in file/module doc comment + +/// Should not warn on single-line doc comments +fn single_line() {} + +/// Should not warn on single-line doc comments +/// split across multiple lines +fn single_line_split() {} + +// Should not warn on normal comments + +// note: cargo fmt can remove double spaces from normal and block comments +// Should not warn on normal comments +// with double spaces at the end of a line + +#[doc = "This is a doc attribute, which should not be linted"] +fn normal_comment() { + /* + Should not warn on block comments + */ + + /* + Should not warn on block comments + with double space at the end of a line + */ +} + +//~v doc_comment_double_space_linebreaks +/// Should warn when doc comment uses double space\ +/// as a line-break, even when there are multiple\ +/// in a row +fn double_space_doc_comment() {} + +/// Should not warn when back-slash is used \ +/// as a line-break +fn back_slash_doc_comment() {} + +//~v doc_comment_double_space_linebreaks +/// 🌹 are 🟥\ +/// 🌷 are 🟦\ +/// 📎 is 😎\ +/// and so are 🫵\ +/// (hopefully no formatting weirdness linting this) +fn multi_byte_chars_tada() {} + +macro_rules! macro_that_makes_function { + () => { + /// Shouldn't lint on this! + /// (hopefully) + fn my_macro_created_function() {} + } +} + +macro_that_makes_function!(); + +// dont lint when its alone on a line +/// +fn alone() {} + +/// | First column | Second column | +/// | ------------ | ------------- | +/// | Not a line | break when | +/// | after a line | in a table | +fn table() {} + +/// ```text +/// It's also not a hard line break if +/// there's two spaces at the end of a +/// line in a block code. +/// ``` +fn codeblock() {} + +/// It's also not a hard line break `if +/// there's` two spaces in the middle of inline code. +fn inline() {} + +/// It's also not a hard line break [when]( +/// https://example.com) in a URL. +fn url() {} + +//~v doc_comment_double_space_linebreaks +/// here we mix\ +/// double spaces\ +/// and also\ +/// adding backslash\ +/// to some of them\ +/// to see how that looks +fn mixed() {} + +fn main() {} diff --git a/tests/ui/doc/doc_comment_double_space_linebreaks.rs b/tests/ui/doc/doc_comment_double_space_linebreaks.rs new file mode 100644 index 000000000000..e36cf7dea236 --- /dev/null +++ b/tests/ui/doc/doc_comment_double_space_linebreaks.rs @@ -0,0 +1,98 @@ +#![feature(custom_inner_attributes)] +#![rustfmt::skip] + +#![warn(clippy::doc_comment_double_space_linebreaks)] +#![allow(unused, clippy::empty_docs)] + +//~v doc_comment_double_space_linebreaks +//! Should warn on double space linebreaks +//! in file/module doc comment + +/// Should not warn on single-line doc comments +fn single_line() {} + +/// Should not warn on single-line doc comments +/// split across multiple lines +fn single_line_split() {} + +// Should not warn on normal comments + +// note: cargo fmt can remove double spaces from normal and block comments +// Should not warn on normal comments +// with double spaces at the end of a line + +#[doc = "This is a doc attribute, which should not be linted"] +fn normal_comment() { + /* + Should not warn on block comments + */ + + /* + Should not warn on block comments + with double space at the end of a line + */ +} + +//~v doc_comment_double_space_linebreaks +/// Should warn when doc comment uses double space +/// as a line-break, even when there are multiple +/// in a row +fn double_space_doc_comment() {} + +/// Should not warn when back-slash is used \ +/// as a line-break +fn back_slash_doc_comment() {} + +//~v doc_comment_double_space_linebreaks +/// 🌹 are 🟥 +/// 🌷 are 🟦 +/// 📎 is 😎 +/// and so are 🫵 +/// (hopefully no formatting weirdness linting this) +fn multi_byte_chars_tada() {} + +macro_rules! macro_that_makes_function { + () => { + /// Shouldn't lint on this! + /// (hopefully) + fn my_macro_created_function() {} + } +} + +macro_that_makes_function!(); + +// dont lint when its alone on a line +/// +fn alone() {} + +/// | First column | Second column | +/// | ------------ | ------------- | +/// | Not a line | break when | +/// | after a line | in a table | +fn table() {} + +/// ```text +/// It's also not a hard line break if +/// there's two spaces at the end of a +/// line in a block code. +/// ``` +fn codeblock() {} + +/// It's also not a hard line break `if +/// there's` two spaces in the middle of inline code. +fn inline() {} + +/// It's also not a hard line break [when]( +/// https://example.com) in a URL. +fn url() {} + +//~v doc_comment_double_space_linebreaks +/// here we mix +/// double spaces\ +/// and also +/// adding backslash\ +/// to some of them +/// to see how that looks +fn mixed() {} + +fn main() {} diff --git a/tests/ui/doc/doc_comment_double_space_linebreaks.stderr b/tests/ui/doc/doc_comment_double_space_linebreaks.stderr new file mode 100644 index 000000000000..08c6956c3d00 --- /dev/null +++ b/tests/ui/doc/doc_comment_double_space_linebreaks.stderr @@ -0,0 +1,50 @@ +error: doc comment uses two spaces for a hard line break + --> tests/ui/doc/doc_comment_double_space_linebreaks.rs:8:43 + | +LL | //! Should warn on double space linebreaks + | ^^ + | + = help: replace this double space with a backslash: `\` + = note: `-D clippy::doc-comment-double-space-linebreaks` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::doc_comment_double_space_linebreaks)]` + +error: doc comment uses two spaces for a hard line break + --> tests/ui/doc/doc_comment_double_space_linebreaks.rs:37:51 + | +LL | /// Should warn when doc comment uses double space + | ^^ +LL | /// as a line-break, even when there are multiple + | ^^ + | + = help: replace this double space with a backslash: `\` + +error: doc comment uses two spaces for a hard line break + --> tests/ui/doc/doc_comment_double_space_linebreaks.rs:47:12 + | +LL | /// 🌹 are 🟥 + | ^^ +LL | /// 🌷 are 🟦 + | ^^ +LL | /// 📎 is 😎 + | ^^ +LL | /// and so are 🫵 + | ^^ + | + = help: replace this double space with a backslash: `\` + +error: doc comment uses two spaces for a hard line break + --> tests/ui/doc/doc_comment_double_space_linebreaks.rs:90:16 + | +LL | /// here we mix + | ^^ +LL | /// double spaces\ +LL | /// and also + | ^^ +LL | /// adding backslash\ +LL | /// to some of them + | ^^ + | + = help: replace this double space with a backslash: `\` + +error: aborting due to 4 previous errors + diff --git a/tests/ui/entry_unfixable.rs b/tests/ui/entry_unfixable.rs new file mode 100644 index 000000000000..dbdacf950569 --- /dev/null +++ b/tests/ui/entry_unfixable.rs @@ -0,0 +1,94 @@ +#![allow(unused, clippy::needless_pass_by_value, clippy::collapsible_if)] +#![warn(clippy::map_entry)] +//@no-rustfix + +use std::collections::HashMap; +use std::hash::Hash; + +macro_rules! m { + ($e:expr) => {{ $e }}; +} + +macro_rules! insert { + ($map:expr, $key:expr, $val:expr) => { + $map.insert($key, $val) + }; +} + +mod issue13306 { + use std::collections::HashMap; + + struct Env { + enclosing: Option>, + values: HashMap, + } + + impl Env { + fn assign(&mut self, name: String, value: usize) -> bool { + if !self.values.contains_key(&name) { + //~^ map_entry + self.values.insert(name, value); + true + } else if let Some(enclosing) = &mut self.enclosing { + enclosing.assign(name, value) + } else { + false + } + } + } +} + +fn issue9925(mut hm: HashMap) { + let key = "hello".to_string(); + if hm.contains_key(&key) { + //~^ map_entry + let bval = hm.get_mut(&key).unwrap(); + *bval = false; + } else { + hm.insert(key, true); + } +} + +mod issue9470 { + use std::collections::HashMap; + use std::sync::Mutex; + + struct Interner(i32); + + impl Interner { + const fn new() -> Self { + Interner(0) + } + + fn resolve(&self, name: String) -> Option { + todo!() + } + } + + static INTERNER: Mutex = Mutex::new(Interner::new()); + + struct VM { + stack: Vec, + globals: HashMap, + } + + impl VM { + fn stack_top(&self) -> &i32 { + self.stack.last().unwrap() + } + + fn resolve(&mut self, name: String, value: i32) -> Result<(), String> { + if self.globals.contains_key(&name) { + //~^ map_entry + self.globals.insert(name, value); + } else { + let interner = INTERNER.lock().unwrap(); + return Err(interner.resolve(name).unwrap().to_owned()); + } + + Ok(()) + } + } +} + +fn main() {} diff --git a/tests/ui/entry_unfixable.stderr b/tests/ui/entry_unfixable.stderr new file mode 100644 index 000000000000..9f9956d351b2 --- /dev/null +++ b/tests/ui/entry_unfixable.stderr @@ -0,0 +1,41 @@ +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> tests/ui/entry_unfixable.rs:28:13 + | +LL | / if !self.values.contains_key(&name) { +LL | | +LL | | self.values.insert(name, value); +LL | | true +... | +LL | | false +LL | | } + | |_____________^ + | + = note: `-D clippy::map-entry` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::map_entry)]` + +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> tests/ui/entry_unfixable.rs:43:5 + | +LL | / if hm.contains_key(&key) { +LL | | +LL | | let bval = hm.get_mut(&key).unwrap(); +LL | | *bval = false; +LL | | } else { +LL | | hm.insert(key, true); +LL | | } + | |_____^ + +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> tests/ui/entry_unfixable.rs:81:13 + | +LL | / if self.globals.contains_key(&name) { +LL | | +LL | | self.globals.insert(name, value); +LL | | } else { +LL | | let interner = INTERNER.lock().unwrap(); +LL | | return Err(interner.resolve(name).unwrap().to_owned()); +LL | | } + | |_____________^ + +error: aborting due to 3 previous errors + diff --git a/tests/ui/from_over_into.fixed b/tests/ui/from_over_into.fixed index 7d6780a0e02c..7229e5a2d358 100644 --- a/tests/ui/from_over_into.fixed +++ b/tests/ui/from_over_into.fixed @@ -105,4 +105,15 @@ fn issue_12138() { } } +fn issue_112502() { + struct MyInt(i64); + + impl From for i64 { + //~^ from_over_into + fn from(val: MyInt) -> Self { + val.0 + } + } +} + fn main() {} diff --git a/tests/ui/from_over_into.rs b/tests/ui/from_over_into.rs index 387ddde359c1..9c75969c5c13 100644 --- a/tests/ui/from_over_into.rs +++ b/tests/ui/from_over_into.rs @@ -105,4 +105,15 @@ fn issue_12138() { } } +fn issue_112502() { + struct MyInt(i64); + + impl Into for MyInt { + //~^ from_over_into + fn into(self: MyInt) -> i64 { + self.0 + } + } +} + fn main() {} diff --git a/tests/ui/from_over_into.stderr b/tests/ui/from_over_into.stderr index a564bccbaf71..fe779544dd57 100644 --- a/tests/ui/from_over_into.stderr +++ b/tests/ui/from_over_into.stderr @@ -107,5 +107,21 @@ LL | LL ~ fn from(val: Hello) {} | -error: aborting due to 7 previous errors +error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true + --> tests/ui/from_over_into.rs:111:5 + | +LL | impl Into for MyInt { + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `impl From for Foreign` is allowed by the orphan rules, for more information see + https://doc.rust-lang.org/reference/items/implementations.html#trait-implementation-coherence +help: replace the `Into` implementation with `From` + | +LL ~ impl From for i64 { +LL | +LL ~ fn from(val: MyInt) -> Self { +LL ~ val.0 + | + +error: aborting due to 8 previous errors diff --git a/tests/ui/ifs_same_cond.rs b/tests/ui/ifs_same_cond.rs index 6ecb7cb1eba9..ebc3acb1b77f 100644 --- a/tests/ui/ifs_same_cond.rs +++ b/tests/ui/ifs_same_cond.rs @@ -1,10 +1,5 @@ #![warn(clippy::ifs_same_cond)] -#![allow( - clippy::if_same_then_else, - clippy::comparison_chain, - clippy::needless_if, - clippy::needless_else -)] // all empty blocks +#![allow(clippy::if_same_then_else, clippy::needless_if, clippy::needless_else)] // all empty blocks fn ifs_same_cond() { let a = 0; diff --git a/tests/ui/ifs_same_cond.stderr b/tests/ui/ifs_same_cond.stderr index 81fbb921e846..df21e6f1b826 100644 --- a/tests/ui/ifs_same_cond.stderr +++ b/tests/ui/ifs_same_cond.stderr @@ -1,11 +1,11 @@ error: this `if` has the same condition as a previous `if` - --> tests/ui/ifs_same_cond.rs:14:15 + --> tests/ui/ifs_same_cond.rs:9:15 | LL | } else if b { | ^ | note: same as this - --> tests/ui/ifs_same_cond.rs:13:8 + --> tests/ui/ifs_same_cond.rs:8:8 | LL | if b { | ^ @@ -13,37 +13,37 @@ LL | if b { = help: to override `-D warnings` add `#[allow(clippy::ifs_same_cond)]` error: this `if` has the same condition as a previous `if` - --> tests/ui/ifs_same_cond.rs:19:15 + --> tests/ui/ifs_same_cond.rs:14:15 | LL | } else if a == 1 { | ^^^^^^ | note: same as this - --> tests/ui/ifs_same_cond.rs:18:8 + --> tests/ui/ifs_same_cond.rs:13:8 | LL | if a == 1 { | ^^^^^^ error: this `if` has the same condition as a previous `if` - --> tests/ui/ifs_same_cond.rs:25:15 + --> tests/ui/ifs_same_cond.rs:20:15 | LL | } else if 2 * a == 1 { | ^^^^^^^^^^ | note: same as this - --> tests/ui/ifs_same_cond.rs:23:8 + --> tests/ui/ifs_same_cond.rs:18:8 | LL | if 2 * a == 1 { | ^^^^^^^^^^ error: this `if` has the same condition as a previous `if` - --> tests/ui/ifs_same_cond.rs:58:15 + --> tests/ui/ifs_same_cond.rs:53:15 | LL | } else if a.contains("ah") { | ^^^^^^^^^^^^^^^^ | note: same as this - --> tests/ui/ifs_same_cond.rs:57:8 + --> tests/ui/ifs_same_cond.rs:52:8 | LL | if a.contains("ah") { | ^^^^^^^^^^^^^^^^ diff --git a/tests/ui/implicit_saturating_sub.fixed b/tests/ui/implicit_saturating_sub.fixed index 136238f9ecac..1aab6c54407e 100644 --- a/tests/ui/implicit_saturating_sub.fixed +++ b/tests/ui/implicit_saturating_sub.fixed @@ -228,3 +228,27 @@ fn regression_13524(a: usize, b: usize, c: bool) -> usize { 123 } else { b.saturating_sub(a) } } + +fn with_side_effect(a: u64) -> u64 { + println!("a = {a}"); + a +} + +fn arbitrary_expression() { + let (a, b) = (15u64, 20u64); + + let _ = (a * 2).saturating_sub(b); + //~^ implicit_saturating_sub + + let _ = a.saturating_sub(b * 2); + //~^ implicit_saturating_sub + + let _ = a.saturating_sub(b * 2); + //~^ implicit_saturating_sub + + let _ = if with_side_effect(a) > a { + with_side_effect(a) - a + } else { + 0 + }; +} diff --git a/tests/ui/implicit_saturating_sub.rs b/tests/ui/implicit_saturating_sub.rs index 140cf0338602..7ca57a2902db 100644 --- a/tests/ui/implicit_saturating_sub.rs +++ b/tests/ui/implicit_saturating_sub.rs @@ -302,3 +302,27 @@ fn regression_13524(a: usize, b: usize, c: bool) -> usize { b - a } } + +fn with_side_effect(a: u64) -> u64 { + println!("a = {a}"); + a +} + +fn arbitrary_expression() { + let (a, b) = (15u64, 20u64); + + let _ = if a * 2 > b { a * 2 - b } else { 0 }; + //~^ implicit_saturating_sub + + let _ = if a > b * 2 { a - b * 2 } else { 0 }; + //~^ implicit_saturating_sub + + let _ = if a < b * 2 { 0 } else { a - b * 2 }; + //~^ implicit_saturating_sub + + let _ = if with_side_effect(a) > a { + with_side_effect(a) - a + } else { + 0 + }; +} diff --git a/tests/ui/implicit_saturating_sub.stderr b/tests/ui/implicit_saturating_sub.stderr index f9c94d3ad841..0c225856fd07 100644 --- a/tests/ui/implicit_saturating_sub.stderr +++ b/tests/ui/implicit_saturating_sub.stderr @@ -220,5 +220,23 @@ LL | | b - a LL | | } | |_____^ help: replace it with: `{ b.saturating_sub(a) }` -error: aborting due to 24 previous errors +error: manual arithmetic check found + --> tests/ui/implicit_saturating_sub.rs:314:13 + | +LL | let _ = if a * 2 > b { a * 2 - b } else { 0 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `(a * 2).saturating_sub(b)` + +error: manual arithmetic check found + --> tests/ui/implicit_saturating_sub.rs:317:13 + | +LL | let _ = if a > b * 2 { a - b * 2 } else { 0 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `a.saturating_sub(b * 2)` + +error: manual arithmetic check found + --> tests/ui/implicit_saturating_sub.rs:320:13 + | +LL | let _ = if a < b * 2 { 0 } else { a - b * 2 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `a.saturating_sub(b * 2)` + +error: aborting due to 27 previous errors diff --git a/tests/ui/incompatible_msrv.rs b/tests/ui/incompatible_msrv.rs index b4fea4cae5ed..99101b2bb8f2 100644 --- a/tests/ui/incompatible_msrv.rs +++ b/tests/ui/incompatible_msrv.rs @@ -1,5 +1,6 @@ #![warn(clippy::incompatible_msrv)] #![feature(custom_inner_attributes)] +#![feature(panic_internals)] #![clippy::msrv = "1.3.0"] use std::collections::HashMap; @@ -34,4 +35,42 @@ async fn issue12273(v: impl Future) { v.await; } +fn core_special_treatment(p: bool) { + // Do not lint code coming from `core` macros expanding into `core` function calls + if p { + panic!("foo"); // Do not lint + } + + // But still lint code calling `core` functions directly + if p { + core::panicking::panic("foo"); + //~^ ERROR: is `1.3.0` but this item is stable since `1.6.0` + } + + // Lint code calling `core` from non-`core` macros + macro_rules! my_panic { + ($msg:expr) => { + core::panicking::panic($msg) + }; //~^ ERROR: is `1.3.0` but this item is stable since `1.6.0` + } + my_panic!("foo"); + + // Lint even when the macro comes from `core` and calls `core` functions + assert!(core::panicking::panic("out of luck")); + //~^ ERROR: is `1.3.0` but this item is stable since `1.6.0` +} + +#[clippy::msrv = "1.26.0"] +fn lang_items() { + // Do not lint lang items. `..=` will expand into `RangeInclusive::new()`, which was introduced + // in Rust 1.27.0. + let _ = 1..=3; +} + +#[clippy::msrv = "1.80.0"] +fn issue14212() { + let _ = std::iter::repeat_n((), 5); + //~^ ERROR: is `1.80.0` but this item is stable since `1.82.0` +} + fn main() {} diff --git a/tests/ui/incompatible_msrv.stderr b/tests/ui/incompatible_msrv.stderr index 56c9eae5aafa..5ea2bb9cc58b 100644 --- a/tests/ui/incompatible_msrv.stderr +++ b/tests/ui/incompatible_msrv.stderr @@ -1,5 +1,5 @@ error: current MSRV (Minimum Supported Rust Version) is `1.3.0` but this item is stable since `1.10.0` - --> tests/ui/incompatible_msrv.rs:13:39 + --> tests/ui/incompatible_msrv.rs:14:39 | LL | assert_eq!(map.entry("poneyland").key(), &"poneyland"); | ^^^^^ @@ -8,16 +8,45 @@ LL | assert_eq!(map.entry("poneyland").key(), &"poneyland"); = help: to override `-D warnings` add `#[allow(clippy::incompatible_msrv)]` error: current MSRV (Minimum Supported Rust Version) is `1.3.0` but this item is stable since `1.12.0` - --> tests/ui/incompatible_msrv.rs:17:11 + --> tests/ui/incompatible_msrv.rs:18:11 | LL | v.into_key(); | ^^^^^^^^^^ error: current MSRV (Minimum Supported Rust Version) is `1.3.0` but this item is stable since `1.4.0` - --> tests/ui/incompatible_msrv.rs:21:5 + --> tests/ui/incompatible_msrv.rs:22:5 | LL | sleep(Duration::new(1, 0)); | ^^^^^ -error: aborting due to 3 previous errors +error: current MSRV (Minimum Supported Rust Version) is `1.3.0` but this item is stable since `1.6.0` + --> tests/ui/incompatible_msrv.rs:46:9 + | +LL | core::panicking::panic("foo"); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: current MSRV (Minimum Supported Rust Version) is `1.3.0` but this item is stable since `1.6.0` + --> tests/ui/incompatible_msrv.rs:53:13 + | +LL | core::panicking::panic($msg) + | ^^^^^^^^^^^^^^^^^^^^^^ +... +LL | my_panic!("foo"); + | ---------------- in this macro invocation + | + = note: this error originates in the macro `my_panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: current MSRV (Minimum Supported Rust Version) is `1.3.0` but this item is stable since `1.6.0` + --> tests/ui/incompatible_msrv.rs:59:13 + | +LL | assert!(core::panicking::panic("out of luck")); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: current MSRV (Minimum Supported Rust Version) is `1.80.0` but this item is stable since `1.82.0` + --> tests/ui/incompatible_msrv.rs:72:13 + | +LL | let _ = std::iter::repeat_n((), 5); + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 7 previous errors diff --git a/tests/ui/io_other_error.fixed b/tests/ui/io_other_error.fixed index 0054c56fb628..ce7e8ef281f7 100644 --- a/tests/ui/io_other_error.fixed +++ b/tests/ui/io_other_error.fixed @@ -53,3 +53,8 @@ mod paths { fn under_msrv() { let _err = std::io::Error::new(std::io::ErrorKind::Other, E); } + +pub fn issue14346(x: i32) -> std::io::Error { + std::io::Error::other(format!("{x}")) + //~^ ERROR: this can be `std::io::Error::other(_)` +} diff --git a/tests/ui/io_other_error.rs b/tests/ui/io_other_error.rs index 8529fb9a77f9..b66e7f88ab14 100644 --- a/tests/ui/io_other_error.rs +++ b/tests/ui/io_other_error.rs @@ -53,3 +53,8 @@ mod paths { fn under_msrv() { let _err = std::io::Error::new(std::io::ErrorKind::Other, E); } + +pub fn issue14346(x: i32) -> std::io::Error { + std::io::Error::new(std::io::ErrorKind::Other, format!("{x}")) + //~^ ERROR: this can be `std::io::Error::other(_)` +} diff --git a/tests/ui/io_other_error.stderr b/tests/ui/io_other_error.stderr index e79e05ecd406..b37e3d15064f 100644 --- a/tests/ui/io_other_error.stderr +++ b/tests/ui/io_other_error.stderr @@ -48,5 +48,17 @@ LL - let _err = io::Error::new(io::ErrorKind::Other, super::E); LL + let _err = io::Error::other(super::E); | -error: aborting due to 4 previous errors +error: this can be `std::io::Error::other(_)` + --> tests/ui/io_other_error.rs:58:5 + | +LL | std::io::Error::new(std::io::ErrorKind::Other, format!("{x}")) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `std::io::Error::other` + | +LL - std::io::Error::new(std::io::ErrorKind::Other, format!("{x}")) +LL + std::io::Error::other(format!("{x}")) + | + +error: aborting due to 5 previous errors diff --git a/tests/ui/literals.rs b/tests/ui/literals.rs index d21d49310a07..ba343a8f62f8 100644 --- a/tests/ui/literals.rs +++ b/tests/ui/literals.rs @@ -30,6 +30,10 @@ fn main() { //~^ separated_literal_suffix //~| mixed_case_hex_literals + let fail2 = 0xab_CD_isize; + //~^ separated_literal_suffix + //~| mixed_case_hex_literals + let fail_multi_zero = 000_123usize; //~^ unseparated_literal_suffix //~| zero_prefixed_literal diff --git a/tests/ui/literals.stderr b/tests/ui/literals.stderr index d65cd3c89ebb..6bfeb91625b3 100644 --- a/tests/ui/literals.stderr +++ b/tests/ui/literals.stderr @@ -25,6 +25,7 @@ error: inconsistent casing in hexadecimal literal LL | let fail1 = 0xabCD; | ^^^^^^ | + = help: consider using `0xabcd` or `0xABCD` = note: `-D clippy::mixed-case-hex-literals` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::mixed_case_hex_literals)]` @@ -39,6 +40,8 @@ error: inconsistent casing in hexadecimal literal | LL | let fail2 = 0xabCD_u32; | ^^^^^^^^^^ + | + = help: consider using `0xabcd_u32` or `0xABCD_u32` error: integer type suffix should not be separated by an underscore --> tests/ui/literals.rs:29:17 @@ -51,9 +54,25 @@ error: inconsistent casing in hexadecimal literal | LL | let fail2 = 0xabCD_isize; | ^^^^^^^^^^^^ + | + = help: consider using `0xabcd_isize` or `0xABCD_isize` + +error: integer type suffix should not be separated by an underscore + --> tests/ui/literals.rs:33:17 + | +LL | let fail2 = 0xab_CD_isize; + | ^^^^^^^^^^^^^ help: remove the underscore: `0xab_CDisize` + +error: inconsistent casing in hexadecimal literal + --> tests/ui/literals.rs:33:17 + | +LL | let fail2 = 0xab_CD_isize; + | ^^^^^^^^^^^^^ + | + = help: consider using `0xab_cd_isize` or `0xAB_CD_isize` error: integer type suffix should be separated by an underscore - --> tests/ui/literals.rs:33:27 + --> tests/ui/literals.rs:37:27 | LL | let fail_multi_zero = 000_123usize; | ^^^^^^^^^^^^ help: add an underscore: `000_123_usize` @@ -62,7 +81,7 @@ LL | let fail_multi_zero = 000_123usize; = help: to override `-D warnings` add `#[allow(clippy::unseparated_literal_suffix)]` error: this is a decimal constant - --> tests/ui/literals.rs:33:27 + --> tests/ui/literals.rs:37:27 | LL | let fail_multi_zero = 000_123usize; | ^^^^^^^^^^^^ @@ -81,13 +100,13 @@ LL + let fail_multi_zero = 0o123usize; | error: integer type suffix should not be separated by an underscore - --> tests/ui/literals.rs:38:16 + --> tests/ui/literals.rs:42:16 | LL | let ok10 = 0_i64; | ^^^^^ help: remove the underscore: `0i64` error: this is a decimal constant - --> tests/ui/literals.rs:41:17 + --> tests/ui/literals.rs:45:17 | LL | let fail8 = 0123; | ^^^^ @@ -103,13 +122,13 @@ LL | let fail8 = 0o123; | + error: integer type suffix should not be separated by an underscore - --> tests/ui/literals.rs:51:16 + --> tests/ui/literals.rs:55:16 | LL | let ok17 = 0x123_4567_8901_usize; | ^^^^^^^^^^^^^^^^^^^^^ help: remove the underscore: `0x123_4567_8901usize` error: digits grouped inconsistently by underscores - --> tests/ui/literals.rs:56:18 + --> tests/ui/literals.rs:60:18 | LL | let fail19 = 12_3456_21; | ^^^^^^^^^^ help: consider: `12_345_621` @@ -118,19 +137,19 @@ LL | let fail19 = 12_3456_21; = help: to override `-D warnings` add `#[allow(clippy::inconsistent_digit_grouping)]` error: digits grouped inconsistently by underscores - --> tests/ui/literals.rs:59:18 + --> tests/ui/literals.rs:63:18 | LL | let fail22 = 3__4___23; | ^^^^^^^^^ help: consider: `3_423` error: digits grouped inconsistently by underscores - --> tests/ui/literals.rs:62:18 + --> tests/ui/literals.rs:66:18 | LL | let fail23 = 3__16___23; | ^^^^^^^^^^ help: consider: `31_623` error: digits of hex, binary or octal literal not in groups of equal size - --> tests/ui/literals.rs:65:18 + --> tests/ui/literals.rs:69:18 | LL | let fail24 = 0xAB_ABC_AB; | ^^^^^^^^^^^ help: consider: `0x0ABA_BCAB` @@ -139,7 +158,7 @@ LL | let fail24 = 0xAB_ABC_AB; = help: to override `-D warnings` add `#[allow(clippy::unusual_byte_groupings)]` error: this is a decimal constant - --> tests/ui/literals.rs:75:13 + --> tests/ui/literals.rs:79:13 | LL | let _ = 08; | ^^ @@ -151,7 +170,7 @@ LL + let _ = 8; | error: this is a decimal constant - --> tests/ui/literals.rs:78:13 + --> tests/ui/literals.rs:82:13 | LL | let _ = 09; | ^^ @@ -163,7 +182,7 @@ LL + let _ = 9; | error: this is a decimal constant - --> tests/ui/literals.rs:81:13 + --> tests/ui/literals.rs:85:13 | LL | let _ = 089; | ^^^ @@ -174,5 +193,5 @@ LL - let _ = 089; LL + let _ = 89; | -error: aborting due to 20 previous errors +error: aborting due to 22 previous errors diff --git a/tests/ui/manual_arithmetic_check-2.rs b/tests/ui/manual_arithmetic_check-2.rs index 749d15f1cbd8..b094b2021b32 100644 --- a/tests/ui/manual_arithmetic_check-2.rs +++ b/tests/ui/manual_arithmetic_check-2.rs @@ -24,6 +24,15 @@ fn main() { let result = if b <= a { 0 } else { a - b }; //~^ inverted_saturating_sub + let result = if b * 2 <= a { 0 } else { a - b * 2 }; + //~^ inverted_saturating_sub + + let result = if b <= a * 2 { 0 } else { a * 2 - b }; + //~^ inverted_saturating_sub + + let result = if b + 3 <= a + 2 { 0 } else { (a + 2) - (b + 3) }; + //~^ inverted_saturating_sub + let af = 12f32; let bf = 13f32; // Should not lint! diff --git a/tests/ui/manual_arithmetic_check-2.stderr b/tests/ui/manual_arithmetic_check-2.stderr index 8841210befda..fb0f0da772c9 100644 --- a/tests/ui/manual_arithmetic_check-2.stderr +++ b/tests/ui/manual_arithmetic_check-2.stderr @@ -71,5 +71,41 @@ note: this subtraction underflows when `a < b` LL | let result = if b <= a { 0 } else { a - b }; | ^^^^^ -error: aborting due to 6 previous errors +error: inverted arithmetic check before subtraction + --> tests/ui/manual_arithmetic_check-2.rs:27:27 + | +LL | let result = if b * 2 <= a { 0 } else { a - b * 2 }; + | ^^ --------- help: try replacing it with: `b * 2 - a` + | +note: this subtraction underflows when `a < b * 2` + --> tests/ui/manual_arithmetic_check-2.rs:27:45 + | +LL | let result = if b * 2 <= a { 0 } else { a - b * 2 }; + | ^^^^^^^^^ + +error: inverted arithmetic check before subtraction + --> tests/ui/manual_arithmetic_check-2.rs:30:23 + | +LL | let result = if b <= a * 2 { 0 } else { a * 2 - b }; + | ^^ --------- help: try replacing it with: `b - a * 2` + | +note: this subtraction underflows when `a * 2 < b` + --> tests/ui/manual_arithmetic_check-2.rs:30:45 + | +LL | let result = if b <= a * 2 { 0 } else { a * 2 - b }; + | ^^^^^^^^^ + +error: inverted arithmetic check before subtraction + --> tests/ui/manual_arithmetic_check-2.rs:33:27 + | +LL | let result = if b + 3 <= a + 2 { 0 } else { (a + 2) - (b + 3) }; + | ^^ ----------------- help: try replacing it with: `b + 3 - (a + 2)` + | +note: this subtraction underflows when `a + 2 < b + 3` + --> tests/ui/manual_arithmetic_check-2.rs:33:49 + | +LL | let result = if b + 3 <= a + 2 { 0 } else { (a + 2) - (b + 3) }; + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 9 previous errors diff --git a/tests/ui/manual_bits.stderr b/tests/ui/manual_bits.stderr index a50975ed474e..44c4cf9239c8 100644 --- a/tests/ui/manual_bits.stderr +++ b/tests/ui/manual_bits.stderr @@ -1,4 +1,4 @@ -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:14:5 | LL | size_of::() * 8; @@ -7,169 +7,169 @@ LL | size_of::() * 8; = note: `-D clippy::manual-bits` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::manual_bits)]` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:16:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i16::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:18:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i32::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:20:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i64::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:22:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `i128::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:24:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `isize::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:27:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^ help: consider using: `u8::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:29:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u16::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:31:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u32::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:33:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u64::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:35:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:37:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `usize::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:40:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^ help: consider using: `i8::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:42:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i16::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:44:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i32::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:46:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i64::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:48:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `i128::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:50:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `isize::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:53:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^ help: consider using: `u8::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:55:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u16::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:57:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u32::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:59:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u64::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:61:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:63:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `usize::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:74:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `Word::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:79:18 | LL | let _: u32 = (size_of::() * 8) as u32; | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:81:18 | LL | let _: u32 = (size_of::() * 8).try_into().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:83:13 | LL | let _ = (size_of::() * 8).pow(5); | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(u128::BITS as usize)` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:85:14 | LL | let _ = &(size_of::() * 8); diff --git a/tests/ui/manual_let_else.rs b/tests/ui/manual_let_else.rs index d2350d97ed00..a753566b34cb 100644 --- a/tests/ui/manual_let_else.rs +++ b/tests/ui/manual_let_else.rs @@ -480,3 +480,37 @@ fn issue12337() { //~^ manual_let_else }; } + +mod issue13768 { + enum Foo { + Str(String), + None, + } + + fn foo(value: Foo) { + let signature = match value { + //~^ manual_let_else + Foo::Str(ref val) => val, + _ => { + println!("No signature found"); + return; + }, + }; + } + + enum Bar { + Str { inner: String }, + None, + } + + fn bar(mut value: Bar) { + let signature = match value { + //~^ manual_let_else + Bar::Str { ref mut inner } => inner, + _ => { + println!("No signature found"); + return; + }, + }; + } +} diff --git a/tests/ui/manual_let_else.stderr b/tests/ui/manual_let_else.stderr index 8f5cba64d545..ef0421921141 100644 --- a/tests/ui/manual_let_else.stderr +++ b/tests/ui/manual_let_else.stderr @@ -489,5 +489,45 @@ error: this could be rewritten as `let...else` LL | let v = if let Some(v_some) = g() { v_some } else { return }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { return };` -error: aborting due to 31 previous errors +error: this could be rewritten as `let...else` + --> tests/ui/manual_let_else.rs:491:9 + | +LL | / let signature = match value { +LL | | +LL | | Foo::Str(ref val) => val, +LL | | _ => { +... | +LL | | }, +LL | | }; + | |__________^ + | +help: consider writing + | +LL ~ let Foo::Str(ref signature) = value else { +LL + println!("No signature found"); +LL + return; +LL + }; + | + +error: this could be rewritten as `let...else` + --> tests/ui/manual_let_else.rs:507:9 + | +LL | / let signature = match value { +LL | | +LL | | Bar::Str { ref mut inner } => inner, +LL | | _ => { +... | +LL | | }, +LL | | }; + | |__________^ + | +help: consider writing + | +LL ~ let Bar::Str { inner: ref mut signature } = value else { +LL + println!("No signature found"); +LL + return; +LL + }; + | + +error: aborting due to 33 previous errors diff --git a/tests/ui/missing_const_for_fn/cant_be_const.rs b/tests/ui/missing_const_for_fn/cant_be_const.rs index bd6339b78700..40e7a5d745cc 100644 --- a/tests/ui/missing_const_for_fn/cant_be_const.rs +++ b/tests/ui/missing_const_for_fn/cant_be_const.rs @@ -218,3 +218,91 @@ mod with_ty_alias { fn mut_add(x: &mut i32) { *x += 1; } + +#[clippy::msrv = "1.87"] +mod issue14020 { + use std::ops::Add; + + fn f(a: T, b: T) -> ::Output { + a + b + } +} + +#[clippy::msrv = "1.87"] +mod issue14290 { + use std::ops::{Deref, DerefMut}; + + struct Wrapper { + t: T, + } + + impl Deref for Wrapper { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.t + } + } + impl DerefMut for Wrapper { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.t + } + } + + struct Example(bool); + + fn do_something(mut a: Wrapper) { + a.0 = !a.0; + } + + pub struct Stream(Vec); + + impl Stream { + pub fn bytes(&self) -> &[u8] { + &self.0 + } + } +} + +#[clippy::msrv = "1.87"] +mod issue14091 { + use std::mem::ManuallyDrop; + + struct BucketSlotGuard<'a> { + id: u32, + free_list: &'a mut Vec, + } + + impl BucketSlotGuard<'_> { + fn into_inner(self) -> u32 { + let this = ManuallyDrop::new(self); + this.id + } + } + + use std::ops::{Deref, DerefMut}; + + struct Wrap(T); + + impl Deref for Wrap { + type Target = T; + fn deref(&self) -> &T { + &self.0 + } + } + + impl DerefMut for Wrap { + fn deref_mut(&mut self) -> &mut T { + &mut self.0 + } + } + + fn smart_two_field(v: &mut Wrap<(i32, i32)>) { + let _a = &mut v.0; + let _b = &mut v.1; + } + + fn smart_destructure(v: &mut Wrap<(i32, i32)>) { + let (ref mut _head, ref mut _tail) = **v; + } +} diff --git a/tests/ui/needless_collect.fixed b/tests/ui/needless_collect.fixed index bad8c307586c..6551fa56b42c 100644 --- a/tests/ui/needless_collect.fixed +++ b/tests/ui/needless_collect.fixed @@ -98,6 +98,29 @@ fn main() { let mut out = vec![]; values.iter().cloned().map(|x| out.push(x)).collect::>(); let _y = values.iter().cloned().map(|x| out.push(x)).collect::>(); // this is fine + + // Don't write a warning if we call `clone()` on the iterator + // https://github.com/rust-lang/rust-clippy/issues/13430 + let my_collection: Vec<()> = vec![()].into_iter().map(|()| {}).collect(); + let _cloned = my_collection.into_iter().clone(); + let my_collection: Vec<()> = vec![()].into_iter().map(|()| {}).collect(); + let my_iter = my_collection.into_iter(); + let _cloned = my_iter.clone(); + // Same for `as_slice()`, for same reason. + let my_collection: Vec<()> = vec![()].into_iter().map(|()| {}).collect(); + let _sliced = my_collection.into_iter().as_slice(); + let my_collection: Vec<()> = vec![()].into_iter().map(|()| {}).collect(); + let my_iter = my_collection.into_iter(); + let _sliced = my_iter.as_slice(); + // Assignment outside of main scope + { + let x; + { + let xxx: Vec<()> = vec![()].into_iter().map(|()| {}).collect(); + x = xxx.into_iter(); + for i in x.as_slice() {} + } + } } fn foo(_: impl IntoIterator) {} diff --git a/tests/ui/needless_collect.rs b/tests/ui/needless_collect.rs index 3dfb5194f404..973c41c68754 100644 --- a/tests/ui/needless_collect.rs +++ b/tests/ui/needless_collect.rs @@ -98,6 +98,29 @@ fn main() { let mut out = vec![]; values.iter().cloned().map(|x| out.push(x)).collect::>(); let _y = values.iter().cloned().map(|x| out.push(x)).collect::>(); // this is fine + + // Don't write a warning if we call `clone()` on the iterator + // https://github.com/rust-lang/rust-clippy/issues/13430 + let my_collection: Vec<()> = vec![()].into_iter().map(|()| {}).collect(); + let _cloned = my_collection.into_iter().clone(); + let my_collection: Vec<()> = vec![()].into_iter().map(|()| {}).collect(); + let my_iter = my_collection.into_iter(); + let _cloned = my_iter.clone(); + // Same for `as_slice()`, for same reason. + let my_collection: Vec<()> = vec![()].into_iter().map(|()| {}).collect(); + let _sliced = my_collection.into_iter().as_slice(); + let my_collection: Vec<()> = vec![()].into_iter().map(|()| {}).collect(); + let my_iter = my_collection.into_iter(); + let _sliced = my_iter.as_slice(); + // Assignment outside of main scope + { + let x; + { + let xxx: Vec<()> = vec![()].into_iter().map(|()| {}).collect(); + x = xxx.into_iter(); + for i in x.as_slice() {} + } + } } fn foo(_: impl IntoIterator) {} diff --git a/tests/ui/needless_late_init.fixed b/tests/ui/needless_late_init.fixed index eb67e6cc8282..391d4bc3fcc7 100644 --- a/tests/ui/needless_late_init.fixed +++ b/tests/ui/needless_late_init.fixed @@ -297,3 +297,9 @@ fn issue13776() { let x; issue13776_mac!(x, 10); // should not lint } + +fn issue9895() { + + //~^ needless_late_init + let r = 5; +} diff --git a/tests/ui/needless_late_init.rs b/tests/ui/needless_late_init.rs index 7de2202dc323..6096e8300e1a 100644 --- a/tests/ui/needless_late_init.rs +++ b/tests/ui/needless_late_init.rs @@ -297,3 +297,9 @@ fn issue13776() { let x; issue13776_mac!(x, 10); // should not lint } + +fn issue9895() { + let r; + //~^ needless_late_init + (r = 5); +} diff --git a/tests/ui/needless_late_init.stderr b/tests/ui/needless_late_init.stderr index 02fd2811da58..e7c36136847b 100644 --- a/tests/ui/needless_late_init.stderr +++ b/tests/ui/needless_late_init.stderr @@ -275,5 +275,21 @@ LL | }, LL ~ }; | -error: aborting due to 16 previous errors +error: unneeded late initialization + --> tests/ui/needless_late_init.rs:302:5 + | +LL | let r; + | ^^^^^^ created here +LL | +LL | (r = 5); + | ^^^^^^^ initialised here + | +help: move the declaration `r` here + | +LL ~ +LL | +LL ~ let r = 5; + | + +error: aborting due to 17 previous errors diff --git a/tests/ui/needless_pass_by_value.rs b/tests/ui/needless_pass_by_value.rs index 885fb409417b..aef7fff28705 100644 --- a/tests/ui/needless_pass_by_value.rs +++ b/tests/ui/needless_pass_by_value.rs @@ -189,6 +189,42 @@ struct Obj(String); fn prefix_test(_unused_with_prefix: Obj) {} +// Regression test for . +// It's more idiomatic to write `Option<&T>` rather than `&Option`. +fn option_inner_ref(x: Option) { + //~^ ERROR: this argument is passed by value, but not consumed in the function body + assert!(x.is_some()); +} + +mod non_standard { + #[derive(Debug)] + pub struct Option(T); +} + +fn non_standard_option(x: non_standard::Option) { + //~^ needless_pass_by_value + dbg!(&x); +} + +fn option_by_name(x: Option>>>) { + //~^ needless_pass_by_value + dbg!(&x); +} + +type OptStr = Option; + +fn non_option(x: OptStr) { + //~^ needless_pass_by_value + dbg!(&x); +} + +type Opt = Option; + +fn non_option_either(x: Opt) { + //~^ needless_pass_by_value + dbg!(&x); +} + fn main() { // This should not cause an ICE either // https://github.com/rust-lang/rust-clippy/issues/3144 diff --git a/tests/ui/needless_pass_by_value.stderr b/tests/ui/needless_pass_by_value.stderr index 4ac4fdce972d..e4381d1db53a 100644 --- a/tests/ui/needless_pass_by_value.stderr +++ b/tests/ui/needless_pass_by_value.stderr @@ -17,43 +17,78 @@ error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:35:22 | LL | fn bar(x: String, y: Wrapper) { - | ^^^^^^^ help: consider taking a reference instead: `&Wrapper` + | ^^^^^^^ + | +help: consider taking a reference instead + | +LL | fn bar(x: String, y: &Wrapper) { + | + error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:44:71 | LL | fn test_borrow_trait, U: AsRef, V>(t: T, u: U, v: V) { - | ^ help: consider taking a reference instead: `&V` + | ^ + | +help: consider taking a reference instead + | +LL | fn test_borrow_trait, U: AsRef, V>(t: T, u: U, v: &V) { + | + error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:58:18 | LL | fn test_match(x: Option>, y: Option>) { - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider taking a reference instead: `&Option>` + | ^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider taking a reference instead + | +LL | fn test_match(x: Option>, y: Option>) { + | + error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:73:24 | LL | fn test_destructure(x: Wrapper, y: Wrapper, z: Wrapper) { - | ^^^^^^^ help: consider taking a reference instead: `&Wrapper` + | ^^^^^^^ + | +help: consider taking a reference instead + | +LL | fn test_destructure(x: &Wrapper, y: Wrapper, z: Wrapper) { + | + error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:73:36 | LL | fn test_destructure(x: Wrapper, y: Wrapper, z: Wrapper) { - | ^^^^^^^ help: consider taking a reference instead: `&Wrapper` + | ^^^^^^^ + | +help: consider taking a reference instead + | +LL | fn test_destructure(x: Wrapper, y: &Wrapper, z: Wrapper) { + | + error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:92:49 | LL | fn test_blanket_ref(vals: T, serializable: S) {} - | ^ help: consider taking a reference instead: `&T` + | ^ + | +help: consider taking a reference instead + | +LL | fn test_blanket_ref(vals: &T, serializable: S) {} + | + error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:95:18 | LL | fn issue_2114(s: String, t: String, u: Vec, v: Vec) { - | ^^^^^^ help: consider taking a reference instead: `&String` + | ^^^^^^ + | +help: consider taking a reference instead + | +LL | fn issue_2114(s: &String, t: String, u: Vec, v: Vec) { + | + error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:95:29 @@ -76,7 +111,12 @@ error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:95:40 | LL | fn issue_2114(s: String, t: String, u: Vec, v: Vec) { - | ^^^^^^^^ help: consider taking a reference instead: `&Vec` + | ^^^^^^^^ + | +help: consider taking a reference instead + | +LL | fn issue_2114(s: String, t: String, u: &Vec, v: Vec) { + | + error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:95:53 @@ -105,79 +145,175 @@ error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:115:12 | LL | t: String, - | ^^^^^^ help: consider taking a reference instead: `&String` + | ^^^^^^ + | +help: consider taking a reference instead + | +LL | t: &String, + | + error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:125:23 | LL | fn baz(&self, uu: U, ss: Self) {} - | ^ help: consider taking a reference instead: `&U` + | ^ + | +help: consider taking a reference instead + | +LL | fn baz(&self, uu: &U, ss: Self) {} + | + error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:125:30 | LL | fn baz(&self, uu: U, ss: Self) {} - | ^^^^ help: consider taking a reference instead: `&Self` + | ^^^^ + | +help: consider taking a reference instead + | +LL | fn baz(&self, uu: U, ss: &Self) {} + | + error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:149:24 | LL | fn bar_copy(x: u32, y: CopyWrapper) { - | ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper` + | ^^^^^^^^^^^ | help: or consider marking this type as `Copy` --> tests/ui/needless_pass_by_value.rs:147:1 | LL | struct CopyWrapper(u32); | ^^^^^^^^^^^^^^^^^^ +help: consider taking a reference instead + | +LL | fn bar_copy(x: u32, y: &CopyWrapper) { + | + error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:157:29 | LL | fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) { - | ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper` + | ^^^^^^^^^^^ | help: or consider marking this type as `Copy` --> tests/ui/needless_pass_by_value.rs:147:1 | LL | struct CopyWrapper(u32); | ^^^^^^^^^^^^^^^^^^ +help: consider taking a reference instead + | +LL | fn test_destructure_copy(x: &CopyWrapper, y: CopyWrapper, z: CopyWrapper) { + | + error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:157:45 | LL | fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) { - | ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper` + | ^^^^^^^^^^^ | help: or consider marking this type as `Copy` --> tests/ui/needless_pass_by_value.rs:147:1 | LL | struct CopyWrapper(u32); | ^^^^^^^^^^^^^^^^^^ +help: consider taking a reference instead + | +LL | fn test_destructure_copy(x: CopyWrapper, y: &CopyWrapper, z: CopyWrapper) { + | + error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:157:61 | LL | fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) { - | ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper` + | ^^^^^^^^^^^ | help: or consider marking this type as `Copy` --> tests/ui/needless_pass_by_value.rs:147:1 | LL | struct CopyWrapper(u32); | ^^^^^^^^^^^^^^^^^^ +help: consider taking a reference instead + | +LL | fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: &CopyWrapper) { + | + error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:173:40 | LL | fn some_fun<'b, S: Bar<'b, ()>>(items: S) {} - | ^ help: consider taking a reference instead: `&S` + | ^ + | +help: consider taking a reference instead + | +LL | fn some_fun<'b, S: Bar<'b, ()>>(items: &S) {} + | + error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:179:20 | LL | fn more_fun(items: impl Club<'static, i32>) {} - | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider taking a reference instead: `&impl Club<'static, i32>` + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider taking a reference instead + | +LL | fn more_fun(items: &impl Club<'static, i32>) {} + | + -error: aborting due to 22 previous errors +error: this argument is passed by value, but not consumed in the function body + --> tests/ui/needless_pass_by_value.rs:194:24 + | +LL | fn option_inner_ref(x: Option) { + | ^^^^^^^^^^^^^^ + | +help: consider taking a reference instead + | +LL | fn option_inner_ref(x: Option<&String>) { + | + + +error: this argument is passed by value, but not consumed in the function body + --> tests/ui/needless_pass_by_value.rs:204:27 + | +LL | fn non_standard_option(x: non_standard::Option) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider taking a reference instead + | +LL | fn non_standard_option(x: &non_standard::Option) { + | + + +error: this argument is passed by value, but not consumed in the function body + --> tests/ui/needless_pass_by_value.rs:209:22 + | +LL | fn option_by_name(x: Option>>>) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider taking a reference instead + | +LL | fn option_by_name(x: Option>>>) { + | + + +error: this argument is passed by value, but not consumed in the function body + --> tests/ui/needless_pass_by_value.rs:216:18 + | +LL | fn non_option(x: OptStr) { + | ^^^^^^ + | +help: consider taking a reference instead + | +LL | fn non_option(x: &OptStr) { + | + + +error: this argument is passed by value, but not consumed in the function body + --> tests/ui/needless_pass_by_value.rs:223:25 + | +LL | fn non_option_either(x: Opt) { + | ^^^^^^^^^^^ + | +help: consider taking a reference instead + | +LL | fn non_option_either(x: &Opt) { + | + + +error: aborting due to 27 previous errors diff --git a/tests/ui/needless_return.fixed b/tests/ui/needless_return.fixed index efc073ebe874..ad625ad6d507 100644 --- a/tests/ui/needless_return.fixed +++ b/tests/ui/needless_return.fixed @@ -6,7 +6,8 @@ clippy::single_match, clippy::needless_bool, clippy::equatable_if_let, - clippy::needless_else + clippy::needless_else, + clippy::missing_safety_doc )] #![warn(clippy::needless_return)] @@ -442,3 +443,12 @@ fn b(x: Option) -> Option { }, } } + +unsafe fn todo() -> *const u8 { + todo!() +} + +pub unsafe fn issue_12157() -> *const i32 { + (unsafe { todo() } as *const i32) + //~^ needless_return +} diff --git a/tests/ui/needless_return.rs b/tests/ui/needless_return.rs index 283d86f25fe5..41d7e5bdd506 100644 --- a/tests/ui/needless_return.rs +++ b/tests/ui/needless_return.rs @@ -6,7 +6,8 @@ clippy::single_match, clippy::needless_bool, clippy::equatable_if_let, - clippy::needless_else + clippy::needless_else, + clippy::missing_safety_doc )] #![warn(clippy::needless_return)] @@ -451,3 +452,12 @@ fn b(x: Option) -> Option { }, } } + +unsafe fn todo() -> *const u8 { + todo!() +} + +pub unsafe fn issue_12157() -> *const i32 { + return unsafe { todo() } as *const i32; + //~^ needless_return +} diff --git a/tests/ui/needless_return.stderr b/tests/ui/needless_return.stderr index 04c97a41d676..80863b9b62b2 100644 --- a/tests/ui/needless_return.stderr +++ b/tests/ui/needless_return.stderr @@ -1,5 +1,5 @@ error: unneeded `return` statement - --> tests/ui/needless_return.rs:29:5 + --> tests/ui/needless_return.rs:30:5 | LL | return true; | ^^^^^^^^^^^ @@ -13,7 +13,7 @@ LL + true | error: unneeded `return` statement - --> tests/ui/needless_return.rs:34:5 + --> tests/ui/needless_return.rs:35:5 | LL | return true; | ^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL + true | error: unneeded `return` statement - --> tests/ui/needless_return.rs:40:5 + --> tests/ui/needless_return.rs:41:5 | LL | return true;;; | ^^^^^^^^^^^ @@ -37,7 +37,7 @@ LL + true | error: unneeded `return` statement - --> tests/ui/needless_return.rs:46:5 + --> tests/ui/needless_return.rs:47:5 | LL | return true;; ; ; | ^^^^^^^^^^^ @@ -49,7 +49,7 @@ LL + true | error: unneeded `return` statement - --> tests/ui/needless_return.rs:52:9 + --> tests/ui/needless_return.rs:53:9 | LL | return true; | ^^^^^^^^^^^ @@ -61,7 +61,7 @@ LL + true | error: unneeded `return` statement - --> tests/ui/needless_return.rs:55:9 + --> tests/ui/needless_return.rs:56:9 | LL | return false; | ^^^^^^^^^^^^ @@ -73,7 +73,7 @@ LL + false | error: unneeded `return` statement - --> tests/ui/needless_return.rs:62:17 + --> tests/ui/needless_return.rs:63:17 | LL | true => return false, | ^^^^^^^^^^^^ @@ -85,7 +85,7 @@ LL + true => false, | error: unneeded `return` statement - --> tests/ui/needless_return.rs:65:13 + --> tests/ui/needless_return.rs:66:13 | LL | return true; | ^^^^^^^^^^^ @@ -97,7 +97,7 @@ LL + true | error: unneeded `return` statement - --> tests/ui/needless_return.rs:73:9 + --> tests/ui/needless_return.rs:74:9 | LL | return true; | ^^^^^^^^^^^ @@ -109,7 +109,7 @@ LL + true | error: unneeded `return` statement - --> tests/ui/needless_return.rs:76:16 + --> tests/ui/needless_return.rs:77:16 | LL | let _ = || return true; | ^^^^^^^^^^^ @@ -121,7 +121,7 @@ LL + let _ = || true; | error: unneeded `return` statement - --> tests/ui/needless_return.rs:81:5 + --> tests/ui/needless_return.rs:82:5 | LL | return the_answer!(); | ^^^^^^^^^^^^^^^^^^^^ @@ -133,7 +133,7 @@ LL + the_answer!() | error: unneeded `return` statement - --> tests/ui/needless_return.rs:85:21 + --> tests/ui/needless_return.rs:86:21 | LL | fn test_void_fun() { | _____________________^ @@ -148,7 +148,7 @@ LL + fn test_void_fun() { | error: unneeded `return` statement - --> tests/ui/needless_return.rs:91:11 + --> tests/ui/needless_return.rs:92:11 | LL | if b { | ___________^ @@ -163,7 +163,7 @@ LL + if b { | error: unneeded `return` statement - --> tests/ui/needless_return.rs:94:13 + --> tests/ui/needless_return.rs:95:13 | LL | } else { | _____________^ @@ -178,7 +178,7 @@ LL + } else { | error: unneeded `return` statement - --> tests/ui/needless_return.rs:103:14 + --> tests/ui/needless_return.rs:104:14 | LL | _ => return, | ^^^^^^ @@ -190,7 +190,7 @@ LL + _ => (), | error: unneeded `return` statement - --> tests/ui/needless_return.rs:112:24 + --> tests/ui/needless_return.rs:113:24 | LL | let _ = 42; | ________________________^ @@ -205,7 +205,7 @@ LL + let _ = 42; | error: unneeded `return` statement - --> tests/ui/needless_return.rs:116:14 + --> tests/ui/needless_return.rs:117:14 | LL | _ => return, | ^^^^^^ @@ -217,7 +217,7 @@ LL + _ => (), | error: unneeded `return` statement - --> tests/ui/needless_return.rs:130:9 + --> tests/ui/needless_return.rs:131:9 | LL | return String::from("test"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -229,7 +229,7 @@ LL + String::from("test") | error: unneeded `return` statement - --> tests/ui/needless_return.rs:133:9 + --> tests/ui/needless_return.rs:134:9 | LL | return String::new(); | ^^^^^^^^^^^^^^^^^^^^ @@ -241,7 +241,7 @@ LL + String::new() | error: unneeded `return` statement - --> tests/ui/needless_return.rs:156:32 + --> tests/ui/needless_return.rs:157:32 | LL | bar.unwrap_or_else(|_| return) | ^^^^^^ @@ -253,7 +253,7 @@ LL + bar.unwrap_or_else(|_| {}) | error: unneeded `return` statement - --> tests/ui/needless_return.rs:161:21 + --> tests/ui/needless_return.rs:162:21 | LL | let _ = || { | _____________________^ @@ -268,7 +268,7 @@ LL + let _ = || { | error: unneeded `return` statement - --> tests/ui/needless_return.rs:165:20 + --> tests/ui/needless_return.rs:166:20 | LL | let _ = || return; | ^^^^^^ @@ -280,7 +280,7 @@ LL + let _ = || {}; | error: unneeded `return` statement - --> tests/ui/needless_return.rs:172:32 + --> tests/ui/needless_return.rs:173:32 | LL | res.unwrap_or_else(|_| return Foo) | ^^^^^^^^^^ @@ -292,7 +292,7 @@ LL + res.unwrap_or_else(|_| Foo) | error: unneeded `return` statement - --> tests/ui/needless_return.rs:182:5 + --> tests/ui/needless_return.rs:183:5 | LL | return true; | ^^^^^^^^^^^ @@ -304,7 +304,7 @@ LL + true | error: unneeded `return` statement - --> tests/ui/needless_return.rs:187:5 + --> tests/ui/needless_return.rs:188:5 | LL | return true; | ^^^^^^^^^^^ @@ -316,7 +316,7 @@ LL + true | error: unneeded `return` statement - --> tests/ui/needless_return.rs:193:9 + --> tests/ui/needless_return.rs:194:9 | LL | return true; | ^^^^^^^^^^^ @@ -328,7 +328,7 @@ LL + true | error: unneeded `return` statement - --> tests/ui/needless_return.rs:196:9 + --> tests/ui/needless_return.rs:197:9 | LL | return false; | ^^^^^^^^^^^^ @@ -340,7 +340,7 @@ LL + false | error: unneeded `return` statement - --> tests/ui/needless_return.rs:203:17 + --> tests/ui/needless_return.rs:204:17 | LL | true => return false, | ^^^^^^^^^^^^ @@ -352,7 +352,7 @@ LL + true => false, | error: unneeded `return` statement - --> tests/ui/needless_return.rs:206:13 + --> tests/ui/needless_return.rs:207:13 | LL | return true; | ^^^^^^^^^^^ @@ -364,7 +364,7 @@ LL + true | error: unneeded `return` statement - --> tests/ui/needless_return.rs:214:9 + --> tests/ui/needless_return.rs:215:9 | LL | return true; | ^^^^^^^^^^^ @@ -376,7 +376,7 @@ LL + true | error: unneeded `return` statement - --> tests/ui/needless_return.rs:217:16 + --> tests/ui/needless_return.rs:218:16 | LL | let _ = || return true; | ^^^^^^^^^^^ @@ -388,7 +388,7 @@ LL + let _ = || true; | error: unneeded `return` statement - --> tests/ui/needless_return.rs:222:5 + --> tests/ui/needless_return.rs:223:5 | LL | return the_answer!(); | ^^^^^^^^^^^^^^^^^^^^ @@ -400,7 +400,7 @@ LL + the_answer!() | error: unneeded `return` statement - --> tests/ui/needless_return.rs:226:33 + --> tests/ui/needless_return.rs:227:33 | LL | async fn async_test_void_fun() { | _________________________________^ @@ -415,7 +415,7 @@ LL + async fn async_test_void_fun() { | error: unneeded `return` statement - --> tests/ui/needless_return.rs:232:11 + --> tests/ui/needless_return.rs:233:11 | LL | if b { | ___________^ @@ -430,7 +430,7 @@ LL + if b { | error: unneeded `return` statement - --> tests/ui/needless_return.rs:235:13 + --> tests/ui/needless_return.rs:236:13 | LL | } else { | _____________^ @@ -445,7 +445,7 @@ LL + } else { | error: unneeded `return` statement - --> tests/ui/needless_return.rs:244:14 + --> tests/ui/needless_return.rs:245:14 | LL | _ => return, | ^^^^^^ @@ -457,7 +457,7 @@ LL + _ => (), | error: unneeded `return` statement - --> tests/ui/needless_return.rs:258:9 + --> tests/ui/needless_return.rs:259:9 | LL | return String::from("test"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -469,7 +469,7 @@ LL + String::from("test") | error: unneeded `return` statement - --> tests/ui/needless_return.rs:261:9 + --> tests/ui/needless_return.rs:262:9 | LL | return String::new(); | ^^^^^^^^^^^^^^^^^^^^ @@ -481,7 +481,7 @@ LL + String::new() | error: unneeded `return` statement - --> tests/ui/needless_return.rs:278:5 + --> tests/ui/needless_return.rs:279:5 | LL | return format!("Hello {}", "world!"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -493,7 +493,7 @@ LL + format!("Hello {}", "world!") | error: unneeded `return` statement - --> tests/ui/needless_return.rs:320:9 + --> tests/ui/needless_return.rs:321:9 | LL | return true; | ^^^^^^^^^^^ @@ -508,7 +508,7 @@ LL ~ } | error: unneeded `return` statement - --> tests/ui/needless_return.rs:323:9 + --> tests/ui/needless_return.rs:324:9 | LL | return false; | ^^^^^^^^^^^^ @@ -521,7 +521,7 @@ LL ~ } | error: unneeded `return` statement - --> tests/ui/needless_return.rs:331:13 + --> tests/ui/needless_return.rs:332:13 | LL | return 10; | ^^^^^^^^^ @@ -536,7 +536,7 @@ LL ~ } | error: unneeded `return` statement - --> tests/ui/needless_return.rs:335:13 + --> tests/ui/needless_return.rs:336:13 | LL | return 100; | ^^^^^^^^^^ @@ -550,7 +550,7 @@ LL ~ } | error: unneeded `return` statement - --> tests/ui/needless_return.rs:344:9 + --> tests/ui/needless_return.rs:345:9 | LL | return 0; | ^^^^^^^^ @@ -563,7 +563,7 @@ LL ~ } | error: unneeded `return` statement - --> tests/ui/needless_return.rs:352:13 + --> tests/ui/needless_return.rs:353:13 | LL | return *(x as *const isize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -579,7 +579,7 @@ LL ~ } | error: unneeded `return` statement - --> tests/ui/needless_return.rs:355:13 + --> tests/ui/needless_return.rs:356:13 | LL | return !*(x as *const isize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -593,7 +593,7 @@ LL ~ } | error: unneeded `return` statement - --> tests/ui/needless_return.rs:363:20 + --> tests/ui/needless_return.rs:364:20 | LL | let _ = 42; | ____________________^ @@ -608,7 +608,7 @@ LL + let _ = 42; | error: unneeded `return` statement - --> tests/ui/needless_return.rs:370:20 + --> tests/ui/needless_return.rs:371:20 | LL | let _ = 42; return; | ^^^^^^^ @@ -620,7 +620,7 @@ LL + let _ = 42; | error: unneeded `return` statement - --> tests/ui/needless_return.rs:383:9 + --> tests/ui/needless_return.rs:384:9 | LL | return Ok(format!("ok!")); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -632,7 +632,7 @@ LL + Ok(format!("ok!")) | error: unneeded `return` statement - --> tests/ui/needless_return.rs:386:9 + --> tests/ui/needless_return.rs:387:9 | LL | return Err(format!("err!")); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -644,7 +644,7 @@ LL + Err(format!("err!")) | error: unneeded `return` statement - --> tests/ui/needless_return.rs:393:9 + --> tests/ui/needless_return.rs:394:9 | LL | return if true { 1 } else { 2 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -656,7 +656,7 @@ LL + if true { 1 } else { 2 } | error: unneeded `return` statement - --> tests/ui/needless_return.rs:398:9 + --> tests/ui/needless_return.rs:399:9 | LL | return if b1 { 0 } else { 1 } | if b2 { 2 } else { 3 } | if b3 { 4 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -668,7 +668,7 @@ LL + (if b1 { 0 } else { 1 } | if b2 { 2 } else { 3 } | if b3 { 4 } else | error: unneeded `return` statement - --> tests/ui/needless_return.rs:420:5 + --> tests/ui/needless_return.rs:421:5 | LL | return { "a".to_string() } + "b" + { "c" }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -680,7 +680,7 @@ LL + ({ "a".to_string() } + "b" + { "c" }) | error: unneeded `return` statement - --> tests/ui/needless_return.rs:425:5 + --> tests/ui/needless_return.rs:426:5 | LL | return "".split("").next().unwrap().to_string(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -691,5 +691,17 @@ LL - return "".split("").next().unwrap().to_string(); LL + "".split("").next().unwrap().to_string() | -error: aborting due to 54 previous errors +error: unneeded `return` statement + --> tests/ui/needless_return.rs:461:5 + | +LL | return unsafe { todo() } as *const i32; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove `return` and wrap the sequence with parentheses + | +LL - return unsafe { todo() } as *const i32; +LL + (unsafe { todo() } as *const i32) + | + +error: aborting due to 55 previous errors diff --git a/tests/ui/never_loop.rs b/tests/ui/never_loop.rs index 2d8e04c192e4..e0f54ef899b0 100644 --- a/tests/ui/never_loop.rs +++ b/tests/ui/never_loop.rs @@ -422,6 +422,34 @@ pub fn issue12205() -> Option<()> { } } +fn stmt_after_return() { + for v in 0..10 { + //~^ never_loop + break; + println!("{v}"); + } +} + +fn loop_label() { + 'outer: for v in 0..10 { + //~^ never_loop + loop { + //~^ never_loop + break 'outer; + } + return; + } + + for v in 0..10 { + //~^ never_loop + 'inner: loop { + //~^ never_loop + break 'inner; + } + return; + } +} + fn main() { test1(); test2(); diff --git a/tests/ui/never_loop.stderr b/tests/ui/never_loop.stderr index f6d6d109542b..bc9a7ec48b48 100644 --- a/tests/ui/never_loop.stderr +++ b/tests/ui/never_loop.stderr @@ -164,5 +164,73 @@ LL | | unimplemented!("not yet"); LL | | } | |_____^ -error: aborting due to 16 previous errors +error: this loop never actually loops + --> tests/ui/never_loop.rs:426:5 + | +LL | / for v in 0..10 { +LL | | +LL | | break; +LL | | println!("{v}"); +LL | | } + | |_____^ + | +help: if you need the first element of the iterator, try writing + | +LL - for v in 0..10 { +LL + if let Some(v) = (0..10).next() { + | + +error: this loop never actually loops + --> tests/ui/never_loop.rs:434:5 + | +LL | / 'outer: for v in 0..10 { +LL | | +LL | | loop { +... | +LL | | return; +LL | | } + | |_____^ + | +help: if you need the first element of the iterator, try writing + | +LL - 'outer: for v in 0..10 { +LL + if let Some(v) = (0..10).next() { + | + +error: this loop never actually loops + --> tests/ui/never_loop.rs:436:9 + | +LL | / loop { +LL | | +LL | | break 'outer; +LL | | } + | |_________^ + +error: this loop never actually loops + --> tests/ui/never_loop.rs:443:5 + | +LL | / for v in 0..10 { +LL | | +LL | | 'inner: loop { +... | +LL | | return; +LL | | } + | |_____^ + | +help: if you need the first element of the iterator, try writing + | +LL - for v in 0..10 { +LL + if let Some(v) = (0..10).next() { + | + +error: this loop never actually loops + --> tests/ui/never_loop.rs:445:9 + | +LL | / 'inner: loop { +LL | | +LL | | break 'inner; +LL | | } + | |_________^ + +error: aborting due to 21 previous errors diff --git a/tests/ui/never_loop_fixable.fixed b/tests/ui/never_loop_fixable.fixed new file mode 100644 index 000000000000..5bc9ff1bb4df --- /dev/null +++ b/tests/ui/never_loop_fixable.fixed @@ -0,0 +1,20 @@ +#![allow(clippy::iter_next_slice, clippy::needless_return)] + +fn no_break_or_continue_loop() { + if let Some(i) = [1, 2, 3].iter().next() { + //~^ never_loop + return; + } +} + +fn no_break_or_continue_loop_outer() { + if let Some(i) = [1, 2, 3].iter().next() { + //~^ never_loop + return; + loop { + if true { + continue; + } + } + } +} diff --git a/tests/ui/never_loop_fixable.rs b/tests/ui/never_loop_fixable.rs new file mode 100644 index 000000000000..9782bc107e9a --- /dev/null +++ b/tests/ui/never_loop_fixable.rs @@ -0,0 +1,20 @@ +#![allow(clippy::iter_next_slice, clippy::needless_return)] + +fn no_break_or_continue_loop() { + for i in [1, 2, 3].iter() { + //~^ never_loop + return; + } +} + +fn no_break_or_continue_loop_outer() { + for i in [1, 2, 3].iter() { + //~^ never_loop + return; + loop { + if true { + continue; + } + } + } +} diff --git a/tests/ui/never_loop_fixable.stderr b/tests/ui/never_loop_fixable.stderr new file mode 100644 index 000000000000..ee02d9a42976 --- /dev/null +++ b/tests/ui/never_loop_fixable.stderr @@ -0,0 +1,35 @@ +error: this loop never actually loops + --> tests/ui/never_loop_fixable.rs:4:5 + | +LL | / for i in [1, 2, 3].iter() { +LL | | +LL | | return; +LL | | } + | |_____^ + | + = note: `#[deny(clippy::never_loop)]` on by default +help: if you need the first element of the iterator, try writing + | +LL - for i in [1, 2, 3].iter() { +LL + if let Some(i) = [1, 2, 3].iter().next() { + | + +error: this loop never actually loops + --> tests/ui/never_loop_fixable.rs:11:5 + | +LL | / for i in [1, 2, 3].iter() { +LL | | +LL | | return; +LL | | loop { +... | +LL | | } + | |_____^ + | +help: if you need the first element of the iterator, try writing + | +LL - for i in [1, 2, 3].iter() { +LL + if let Some(i) = [1, 2, 3].iter().next() { + | + +error: aborting due to 2 previous errors + diff --git a/tests/ui/option_if_let_else.fixed b/tests/ui/option_if_let_else.fixed index f5a869cf2831..ee3098896017 100644 --- a/tests/ui/option_if_let_else.fixed +++ b/tests/ui/option_if_let_else.fixed @@ -268,3 +268,23 @@ fn issue11893() { panic!("Haven't thought about this condition."); } } + +mod issue13964 { + #[derive(Debug)] + struct A(Option); + + fn foo(a: A) { + let _ = match a.0 { + Some(x) => x, + None => panic!("{a:?} is invalid."), + }; + } + + fn bar(a: A) { + let _ = if let Some(x) = a.0 { + x + } else { + panic!("{a:?} is invalid.") + }; + } +} diff --git a/tests/ui/option_if_let_else.rs b/tests/ui/option_if_let_else.rs index d48272e618ac..525a5df4371c 100644 --- a/tests/ui/option_if_let_else.rs +++ b/tests/ui/option_if_let_else.rs @@ -331,3 +331,23 @@ fn issue11893() { panic!("Haven't thought about this condition."); } } + +mod issue13964 { + #[derive(Debug)] + struct A(Option); + + fn foo(a: A) { + let _ = match a.0 { + Some(x) => x, + None => panic!("{a:?} is invalid."), + }; + } + + fn bar(a: A) { + let _ = if let Some(x) = a.0 { + x + } else { + panic!("{a:?} is invalid.") + }; + } +} diff --git a/tests/ui/ptr_eq.fixed b/tests/ui/ptr_eq.fixed index 1ccd2c2237de..df6305ed497e 100644 --- a/tests/ui/ptr_eq.fixed +++ b/tests/ui/ptr_eq.fixed @@ -20,8 +20,10 @@ fn main() { //~^ ptr_eq let _ = std::ptr::eq(a, b); //~^ ptr_eq - let _ = a.as_ptr() == b as *const _; - let _ = a.as_ptr() == b.as_ptr(); + let _ = std::ptr::eq(a.as_ptr(), b as *const _); + //~^ ptr_eq + let _ = std::ptr::eq(a.as_ptr(), b.as_ptr()); + //~^ ptr_eq // Do not lint @@ -31,9 +33,22 @@ fn main() { let a = &mut [1, 2, 3]; let b = &mut [1, 2, 3]; - let _ = a.as_mut_ptr() == b as *mut [i32] as *mut _; - let _ = a.as_mut_ptr() == b.as_mut_ptr(); + let _ = std::ptr::eq(a.as_mut_ptr(), b as *mut [i32] as *mut _); + //~^ ptr_eq + let _ = std::ptr::eq(a.as_mut_ptr(), b.as_mut_ptr()); + //~^ ptr_eq let _ = a == b; let _ = core::ptr::eq(a, b); + + let (x, y) = (&0u32, &mut 1u32); + let _ = std::ptr::eq(x, y); + //~^ ptr_eq + + let _ = !std::ptr::eq(x, y); + //~^ ptr_eq + + #[allow(clippy::eq_op)] + let _issue14337 = std::ptr::eq(main as *const (), main as *const ()); + //~^ ptr_eq } diff --git a/tests/ui/ptr_eq.rs b/tests/ui/ptr_eq.rs index 0bc58a57fa53..0ed0ff0d1371 100644 --- a/tests/ui/ptr_eq.rs +++ b/tests/ui/ptr_eq.rs @@ -21,7 +21,9 @@ fn main() { let _ = a as *const _ == b as *const _; //~^ ptr_eq let _ = a.as_ptr() == b as *const _; + //~^ ptr_eq let _ = a.as_ptr() == b.as_ptr(); + //~^ ptr_eq // Do not lint @@ -32,8 +34,21 @@ fn main() { let b = &mut [1, 2, 3]; let _ = a.as_mut_ptr() == b as *mut [i32] as *mut _; + //~^ ptr_eq let _ = a.as_mut_ptr() == b.as_mut_ptr(); + //~^ ptr_eq let _ = a == b; let _ = core::ptr::eq(a, b); + + let (x, y) = (&0u32, &mut 1u32); + let _ = x as *const u32 == y as *mut u32 as *const u32; + //~^ ptr_eq + + let _ = x as *const u32 != y as *mut u32 as *const u32; + //~^ ptr_eq + + #[allow(clippy::eq_op)] + let _issue14337 = main as *const () == main as *const (); + //~^ ptr_eq } diff --git a/tests/ui/ptr_eq.stderr b/tests/ui/ptr_eq.stderr index 8e8b34f26ff7..33190df284a3 100644 --- a/tests/ui/ptr_eq.stderr +++ b/tests/ui/ptr_eq.stderr @@ -13,5 +13,47 @@ error: use `std::ptr::eq` when comparing raw pointers LL | let _ = a as *const _ == b as *const _; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(a, b)` -error: aborting due to 2 previous errors +error: use `std::ptr::eq` when comparing raw pointers + --> tests/ui/ptr_eq.rs:23:13 + | +LL | let _ = a.as_ptr() == b as *const _; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(a.as_ptr(), b as *const _)` + +error: use `std::ptr::eq` when comparing raw pointers + --> tests/ui/ptr_eq.rs:25:13 + | +LL | let _ = a.as_ptr() == b.as_ptr(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(a.as_ptr(), b.as_ptr())` + +error: use `std::ptr::eq` when comparing raw pointers + --> tests/ui/ptr_eq.rs:36:13 + | +LL | let _ = a.as_mut_ptr() == b as *mut [i32] as *mut _; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(a.as_mut_ptr(), b as *mut [i32] as *mut _)` + +error: use `std::ptr::eq` when comparing raw pointers + --> tests/ui/ptr_eq.rs:38:13 + | +LL | let _ = a.as_mut_ptr() == b.as_mut_ptr(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(a.as_mut_ptr(), b.as_mut_ptr())` + +error: use `std::ptr::eq` when comparing raw pointers + --> tests/ui/ptr_eq.rs:45:13 + | +LL | let _ = x as *const u32 == y as *mut u32 as *const u32; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(x, y)` + +error: use `std::ptr::eq` when comparing raw pointers + --> tests/ui/ptr_eq.rs:48:13 + | +LL | let _ = x as *const u32 != y as *mut u32 as *const u32; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `!std::ptr::eq(x, y)` + +error: use `std::ptr::eq` when comparing raw pointers + --> tests/ui/ptr_eq.rs:52:23 + | +LL | let _issue14337 = main as *const () == main as *const (); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(main as *const (), main as *const ())` + +error: aborting due to 9 previous errors diff --git a/tests/ui/ptr_eq_no_std.fixed b/tests/ui/ptr_eq_no_std.fixed index b3e82fae38f3..d8ee4ea88f84 100644 --- a/tests/ui/ptr_eq_no_std.fixed +++ b/tests/ui/ptr_eq_no_std.fixed @@ -32,8 +32,10 @@ fn main() { //~^ ptr_eq let _ = core::ptr::eq(a, b); //~^ ptr_eq - let _ = a.as_ptr() == b as *const _; - let _ = a.as_ptr() == b.as_ptr(); + let _ = core::ptr::eq(a.as_ptr(), b as *const _); + //~^ ptr_eq + let _ = core::ptr::eq(a.as_ptr(), b.as_ptr()); + //~^ ptr_eq // Do not lint @@ -43,8 +45,10 @@ fn main() { let a = &mut [1, 2, 3]; let b = &mut [1, 2, 3]; - let _ = a.as_mut_ptr() == b as *mut [i32] as *mut _; - let _ = a.as_mut_ptr() == b.as_mut_ptr(); + let _ = core::ptr::eq(a.as_mut_ptr(), b as *mut [i32] as *mut _); + //~^ ptr_eq + let _ = core::ptr::eq(a.as_mut_ptr(), b.as_mut_ptr()); + //~^ ptr_eq let _ = a == b; let _ = core::ptr::eq(a, b); diff --git a/tests/ui/ptr_eq_no_std.rs b/tests/ui/ptr_eq_no_std.rs index ba78f5ee5f84..a236314c29b7 100644 --- a/tests/ui/ptr_eq_no_std.rs +++ b/tests/ui/ptr_eq_no_std.rs @@ -33,7 +33,9 @@ fn main() { let _ = a as *const _ == b as *const _; //~^ ptr_eq let _ = a.as_ptr() == b as *const _; + //~^ ptr_eq let _ = a.as_ptr() == b.as_ptr(); + //~^ ptr_eq // Do not lint @@ -44,7 +46,9 @@ fn main() { let b = &mut [1, 2, 3]; let _ = a.as_mut_ptr() == b as *mut [i32] as *mut _; + //~^ ptr_eq let _ = a.as_mut_ptr() == b.as_mut_ptr(); + //~^ ptr_eq let _ = a == b; let _ = core::ptr::eq(a, b); diff --git a/tests/ui/ptr_eq_no_std.stderr b/tests/ui/ptr_eq_no_std.stderr index 8c7b1ff76661..5b8135dc8e8b 100644 --- a/tests/ui/ptr_eq_no_std.stderr +++ b/tests/ui/ptr_eq_no_std.stderr @@ -13,5 +13,29 @@ error: use `core::ptr::eq` when comparing raw pointers LL | let _ = a as *const _ == b as *const _; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `core::ptr::eq(a, b)` -error: aborting due to 2 previous errors +error: use `core::ptr::eq` when comparing raw pointers + --> tests/ui/ptr_eq_no_std.rs:35:13 + | +LL | let _ = a.as_ptr() == b as *const _; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `core::ptr::eq(a.as_ptr(), b as *const _)` + +error: use `core::ptr::eq` when comparing raw pointers + --> tests/ui/ptr_eq_no_std.rs:37:13 + | +LL | let _ = a.as_ptr() == b.as_ptr(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `core::ptr::eq(a.as_ptr(), b.as_ptr())` + +error: use `core::ptr::eq` when comparing raw pointers + --> tests/ui/ptr_eq_no_std.rs:48:13 + | +LL | let _ = a.as_mut_ptr() == b as *mut [i32] as *mut _; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `core::ptr::eq(a.as_mut_ptr(), b as *mut [i32] as *mut _)` + +error: use `core::ptr::eq` when comparing raw pointers + --> tests/ui/ptr_eq_no_std.rs:50:13 + | +LL | let _ = a.as_mut_ptr() == b.as_mut_ptr(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `core::ptr::eq(a.as_mut_ptr(), b.as_mut_ptr())` + +error: aborting due to 6 previous errors diff --git a/tests/ui/question_mark.fixed b/tests/ui/question_mark.fixed index 8dfef3202be9..fff41f578284 100644 --- a/tests/ui/question_mark.fixed +++ b/tests/ui/question_mark.fixed @@ -375,3 +375,58 @@ fn issue12412(foo: &Foo, bar: &Bar) -> Option<()> { //~^^^ question_mark Some(()) } + +struct StructWithOptionString { + opt_x: Option, +} + +struct WrapperStructWithString(String); + +#[allow(clippy::disallowed_names)] +fn issue_13417(foo: &mut StructWithOptionString) -> Option { + let x = foo.opt_x.as_ref()?; + //~^^^ question_mark + let opt_y = Some(x.clone()); + std::mem::replace(&mut foo.opt_x, opt_y) +} + +#[allow(clippy::disallowed_names)] +fn issue_13417_mut(foo: &mut StructWithOptionString) -> Option { + let x = foo.opt_x.as_mut()?; + //~^^^ question_mark + let opt_y = Some(x.clone()); + std::mem::replace(&mut foo.opt_x, opt_y) +} + +#[allow(clippy::disallowed_names)] +#[allow(unused)] +fn issue_13417_weirder(foo: &mut StructWithOptionString, mut bar: Option) -> Option<()> { + let x @ y = foo.opt_x.as_ref()?; + //~^^^ question_mark + let x @ &WrapperStructWithString(_) = bar.as_ref()?; + //~^^^ question_mark + let x @ &mut WrapperStructWithString(_) = bar.as_mut()?; + //~^^^ question_mark + Some(()) +} + +#[clippy::msrv = "1.12"] +fn msrv_1_12(arg: Option) -> Option { + if arg.is_none() { + return None; + } + let val = match arg { + Some(val) => val, + None => return None, + }; + println!("{}", val); + Some(val) +} + +#[clippy::msrv = "1.13"] +fn msrv_1_13(arg: Option) -> Option { + arg?; + let val = arg?; + println!("{}", val); + Some(val) +} diff --git a/tests/ui/question_mark.rs b/tests/ui/question_mark.rs index fffaa803f39c..c71c8ee984ed 100644 --- a/tests/ui/question_mark.rs +++ b/tests/ui/question_mark.rs @@ -452,3 +452,75 @@ fn issue12412(foo: &Foo, bar: &Bar) -> Option<()> { //~^^^ question_mark Some(()) } + +struct StructWithOptionString { + opt_x: Option, +} + +struct WrapperStructWithString(String); + +#[allow(clippy::disallowed_names)] +fn issue_13417(foo: &mut StructWithOptionString) -> Option { + let Some(ref x) = foo.opt_x else { + return None; + }; + //~^^^ question_mark + let opt_y = Some(x.clone()); + std::mem::replace(&mut foo.opt_x, opt_y) +} + +#[allow(clippy::disallowed_names)] +fn issue_13417_mut(foo: &mut StructWithOptionString) -> Option { + let Some(ref mut x) = foo.opt_x else { + return None; + }; + //~^^^ question_mark + let opt_y = Some(x.clone()); + std::mem::replace(&mut foo.opt_x, opt_y) +} + +#[allow(clippy::disallowed_names)] +#[allow(unused)] +fn issue_13417_weirder(foo: &mut StructWithOptionString, mut bar: Option) -> Option<()> { + let Some(ref x @ ref y) = foo.opt_x else { + return None; + }; + //~^^^ question_mark + let Some(ref x @ WrapperStructWithString(_)) = bar else { + return None; + }; + //~^^^ question_mark + let Some(ref mut x @ WrapperStructWithString(_)) = bar else { + return None; + }; + //~^^^ question_mark + Some(()) +} + +#[clippy::msrv = "1.12"] +fn msrv_1_12(arg: Option) -> Option { + if arg.is_none() { + return None; + } + let val = match arg { + Some(val) => val, + None => return None, + }; + println!("{}", val); + Some(val) +} + +#[clippy::msrv = "1.13"] +fn msrv_1_13(arg: Option) -> Option { + if arg.is_none() { + //~^ question_mark + return None; + } + let val = match arg { + //~^ question_mark + Some(val) => val, + None => return None, + }; + println!("{}", val); + Some(val) +} diff --git a/tests/ui/question_mark.stderr b/tests/ui/question_mark.stderr index c4db0fbc3022..183b8866a748 100644 --- a/tests/ui/question_mark.stderr +++ b/tests/ui/question_mark.stderr @@ -215,5 +215,65 @@ LL | | return None; LL | | }; | |______^ help: replace it with: `let v = bar.foo.owned.clone()?;` -error: aborting due to 22 previous errors +error: this `let...else` may be rewritten with the `?` operator + --> tests/ui/question_mark.rs:464:5 + | +LL | / let Some(ref x) = foo.opt_x else { +LL | | return None; +LL | | }; + | |______^ help: replace it with: `let x = foo.opt_x.as_ref()?;` + +error: this `let...else` may be rewritten with the `?` operator + --> tests/ui/question_mark.rs:474:5 + | +LL | / let Some(ref mut x) = foo.opt_x else { +LL | | return None; +LL | | }; + | |______^ help: replace it with: `let x = foo.opt_x.as_mut()?;` + +error: this `let...else` may be rewritten with the `?` operator + --> tests/ui/question_mark.rs:485:5 + | +LL | / let Some(ref x @ ref y) = foo.opt_x else { +LL | | return None; +LL | | }; + | |______^ help: replace it with: `let x @ y = foo.opt_x.as_ref()?;` + +error: this `let...else` may be rewritten with the `?` operator + --> tests/ui/question_mark.rs:489:5 + | +LL | / let Some(ref x @ WrapperStructWithString(_)) = bar else { +LL | | return None; +LL | | }; + | |______^ help: replace it with: `let x @ &WrapperStructWithString(_) = bar.as_ref()?;` + +error: this `let...else` may be rewritten with the `?` operator + --> tests/ui/question_mark.rs:493:5 + | +LL | / let Some(ref mut x @ WrapperStructWithString(_)) = bar else { +LL | | return None; +LL | | }; + | |______^ help: replace it with: `let x @ &mut WrapperStructWithString(_) = bar.as_mut()?;` + +error: this block may be rewritten with the `?` operator + --> tests/ui/question_mark.rs:515:5 + | +LL | / if arg.is_none() { +LL | | +LL | | return None; +LL | | } + | |_____^ help: replace it with: `arg?;` + +error: this `match` expression can be replaced with `?` + --> tests/ui/question_mark.rs:519:15 + | +LL | let val = match arg { + | _______________^ +LL | | +LL | | Some(val) => val, +LL | | None => return None, +LL | | }; + | |_____^ help: try instead: `arg?` + +error: aborting due to 29 previous errors diff --git a/tests/ui/same_functions_in_if_condition.rs b/tests/ui/same_functions_in_if_condition.rs index b7ed3aab0043..a98b73c9e1c8 100644 --- a/tests/ui/same_functions_in_if_condition.rs +++ b/tests/ui/same_functions_in_if_condition.rs @@ -3,12 +3,7 @@ // ifs_same_cond warning is different from `ifs_same_cond`. // clippy::if_same_then_else, clippy::comparison_chain -- all empty blocks #![allow(incomplete_features)] -#![allow( - clippy::comparison_chain, - clippy::if_same_then_else, - clippy::ifs_same_cond, - clippy::uninlined_format_args -)] +#![allow(clippy::if_same_then_else, clippy::ifs_same_cond, clippy::uninlined_format_args)] use std::marker::ConstParamTy; diff --git a/tests/ui/same_functions_in_if_condition.stderr b/tests/ui/same_functions_in_if_condition.stderr index 6cd4f96c13e3..35dcbadce595 100644 --- a/tests/ui/same_functions_in_if_condition.stderr +++ b/tests/ui/same_functions_in_if_condition.stderr @@ -1,11 +1,11 @@ error: this `if` has the same function call as a previous `if` - --> tests/ui/same_functions_in_if_condition.rs:39:15 + --> tests/ui/same_functions_in_if_condition.rs:34:15 | LL | } else if function() { | ^^^^^^^^^^ | note: same as this - --> tests/ui/same_functions_in_if_condition.rs:38:8 + --> tests/ui/same_functions_in_if_condition.rs:33:8 | LL | if function() { | ^^^^^^^^^^ @@ -16,61 +16,61 @@ LL | #![deny(clippy::same_functions_in_if_condition)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this `if` has the same function call as a previous `if` - --> tests/ui/same_functions_in_if_condition.rs:44:15 + --> tests/ui/same_functions_in_if_condition.rs:39:15 | LL | } else if fn_arg(a) { | ^^^^^^^^^ | note: same as this - --> tests/ui/same_functions_in_if_condition.rs:43:8 + --> tests/ui/same_functions_in_if_condition.rs:38:8 | LL | if fn_arg(a) { | ^^^^^^^^^ error: this `if` has the same function call as a previous `if` - --> tests/ui/same_functions_in_if_condition.rs:49:15 + --> tests/ui/same_functions_in_if_condition.rs:44:15 | LL | } else if obj.method() { | ^^^^^^^^^^^^ | note: same as this - --> tests/ui/same_functions_in_if_condition.rs:48:8 + --> tests/ui/same_functions_in_if_condition.rs:43:8 | LL | if obj.method() { | ^^^^^^^^^^^^ error: this `if` has the same function call as a previous `if` - --> tests/ui/same_functions_in_if_condition.rs:54:15 + --> tests/ui/same_functions_in_if_condition.rs:49:15 | LL | } else if obj.method_arg(a) { | ^^^^^^^^^^^^^^^^^ | note: same as this - --> tests/ui/same_functions_in_if_condition.rs:53:8 + --> tests/ui/same_functions_in_if_condition.rs:48:8 | LL | if obj.method_arg(a) { | ^^^^^^^^^^^^^^^^^ error: this `if` has the same function call as a previous `if` - --> tests/ui/same_functions_in_if_condition.rs:60:15 + --> tests/ui/same_functions_in_if_condition.rs:55:15 | LL | } else if v.pop().is_none() { | ^^^^^^^^^^^^^^^^^ | note: same as this - --> tests/ui/same_functions_in_if_condition.rs:59:8 + --> tests/ui/same_functions_in_if_condition.rs:54:8 | LL | if v.pop().is_none() { | ^^^^^^^^^^^^^^^^^ error: this `if` has the same function call as a previous `if` - --> tests/ui/same_functions_in_if_condition.rs:65:15 + --> tests/ui/same_functions_in_if_condition.rs:60:15 | LL | } else if v.len() == 42 { | ^^^^^^^^^^^^^ | note: same as this - --> tests/ui/same_functions_in_if_condition.rs:64:8 + --> tests/ui/same_functions_in_if_condition.rs:59:8 | LL | if v.len() == 42 { | ^^^^^^^^^^^^^ diff --git a/tests/ui/single_match.fixed b/tests/ui/single_match.fixed index c6ffe93eb7ab..0e198ec79344 100644 --- a/tests/ui/single_match.fixed +++ b/tests/ui/single_match.fixed @@ -1,3 +1,4 @@ +//@require-annotations-for-level: WARN #![warn(clippy::single_match)] #![allow( unused, @@ -18,13 +19,9 @@ fn single_match() { //~^^^^^^ single_match let x = Some(1u8); - match x { - // Note the missing block braces. - // We suggest `if let Some(y) = x { .. }` because the macro - // is expanded before we can do anything. - Some(y) => println!("{:?}", y), - _ => (), - } + if let Some(y) = x { println!("{:?}", y) } + //~^^^^^^^ single_match + //~| NOTE: you might want to preserve the comments from inside the `match` let z = (1u8, 1u8); if let (2..=3, 7..=9) = z { dummy() }; @@ -358,21 +355,14 @@ fn irrefutable_match() { let mut x = vec![1i8]; - // Should not lint. - match x.pop() { + if let Some(u) = x.pop() { println!("{u}") } + //~^^^^^^ single_match + //~| NOTE: you might want to preserve the comments from inside the `match` + + if let Some(u) = x.pop() { // bla - Some(u) => println!("{u}"), - // more comments! - None => {}, - } - // Should not lint. - match x.pop() { - // bla - Some(u) => { - // bla - println!("{u}"); - }, - // bla - None => {}, + println!("{u}"); } + //~^^^^^^^^^ single_match + //~| NOTE: you might want to preserve the comments from inside the `match` } diff --git a/tests/ui/single_match.rs b/tests/ui/single_match.rs index dc758fa4281c..fcac65f8aaf5 100644 --- a/tests/ui/single_match.rs +++ b/tests/ui/single_match.rs @@ -1,3 +1,4 @@ +//@require-annotations-for-level: WARN #![warn(clippy::single_match)] #![allow( unused, @@ -28,6 +29,8 @@ fn single_match() { Some(y) => println!("{:?}", y), _ => (), } + //~^^^^^^^ single_match + //~| NOTE: you might want to preserve the comments from inside the `match` let z = (1u8, 1u8); match z { @@ -437,14 +440,15 @@ fn irrefutable_match() { let mut x = vec![1i8]; - // Should not lint. match x.pop() { // bla Some(u) => println!("{u}"), // more comments! None => {}, } - // Should not lint. + //~^^^^^^ single_match + //~| NOTE: you might want to preserve the comments from inside the `match` + match x.pop() { // bla Some(u) => { @@ -454,4 +458,6 @@ fn irrefutable_match() { // bla None => {}, } + //~^^^^^^^^^ single_match + //~| NOTE: you might want to preserve the comments from inside the `match` } diff --git a/tests/ui/single_match.stderr b/tests/ui/single_match.stderr index c88296959489..2467423b9c17 100644 --- a/tests/ui/single_match.stderr +++ b/tests/ui/single_match.stderr @@ -1,5 +1,5 @@ error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:15:5 + --> tests/ui/single_match.rs:16:5 | LL | / match x { LL | | Some(y) => { @@ -19,7 +19,18 @@ LL ~ }; | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:33:5 + --> tests/ui/single_match.rs:25:5 + | +LL | / match x { +... | +LL | | _ => (), +LL | | } + | |_____^ help: try: `if let Some(y) = x { println!("{:?}", y) }` + | + = note: you might want to preserve the comments from inside the `match` + +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> tests/ui/single_match.rs:36:5 | LL | / match z { LL | | (2..=3, 7..=9) => dummy(), @@ -28,7 +39,7 @@ LL | | }; | |_____^ help: try: `if let (2..=3, 7..=9) = z { dummy() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:63:5 + --> tests/ui/single_match.rs:66:5 | LL | / match x { LL | | Some(y) => dummy(), @@ -37,7 +48,7 @@ LL | | }; | |_____^ help: try: `if let Some(y) = x { dummy() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:69:5 + --> tests/ui/single_match.rs:72:5 | LL | / match y { LL | | Ok(y) => dummy(), @@ -46,7 +57,7 @@ LL | | }; | |_____^ help: try: `if let Ok(y) = y { dummy() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:77:5 + --> tests/ui/single_match.rs:80:5 | LL | / match c { LL | | Cow::Borrowed(..) => dummy(), @@ -55,7 +66,7 @@ LL | | }; | |_____^ help: try: `if let Cow::Borrowed(..) = c { dummy() }` error: you seem to be trying to use `match` for an equality check. Consider using `if` - --> tests/ui/single_match.rs:99:5 + --> tests/ui/single_match.rs:102:5 | LL | / match x { LL | | "test" => println!(), @@ -64,7 +75,7 @@ LL | | } | |_____^ help: try: `if x == "test" { println!() }` error: you seem to be trying to use `match` for an equality check. Consider using `if` - --> tests/ui/single_match.rs:113:5 + --> tests/ui/single_match.rs:116:5 | LL | / match x { LL | | Foo::A => println!(), @@ -73,7 +84,7 @@ LL | | } | |_____^ help: try: `if x == Foo::A { println!() }` error: you seem to be trying to use `match` for an equality check. Consider using `if` - --> tests/ui/single_match.rs:120:5 + --> tests/ui/single_match.rs:123:5 | LL | / match x { LL | | FOO_C => println!(), @@ -82,7 +93,7 @@ LL | | } | |_____^ help: try: `if x == FOO_C { println!() }` error: you seem to be trying to use `match` for an equality check. Consider using `if` - --> tests/ui/single_match.rs:126:5 + --> tests/ui/single_match.rs:129:5 | LL | / match &&x { LL | | Foo::A => println!(), @@ -91,7 +102,7 @@ LL | | } | |_____^ help: try: `if x == Foo::A { println!() }` error: you seem to be trying to use `match` for an equality check. Consider using `if` - --> tests/ui/single_match.rs:133:5 + --> tests/ui/single_match.rs:136:5 | LL | / match &x { LL | | Foo::A => println!(), @@ -100,7 +111,7 @@ LL | | } | |_____^ help: try: `if x == &Foo::A { println!() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:151:5 + --> tests/ui/single_match.rs:154:5 | LL | / match x { LL | | Bar::A => println!(), @@ -109,7 +120,7 @@ LL | | } | |_____^ help: try: `if let Bar::A = x { println!() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:160:5 + --> tests/ui/single_match.rs:163:5 | LL | / match x { LL | | None => println!(), @@ -118,7 +129,7 @@ LL | | }; | |_____^ help: try: `if let None = x { println!() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:183:5 + --> tests/ui/single_match.rs:186:5 | LL | / match x { LL | | (Some(_), _) => {}, @@ -127,7 +138,7 @@ LL | | } | |_____^ help: try: `if let (Some(_), _) = x {}` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:190:5 + --> tests/ui/single_match.rs:193:5 | LL | / match x { LL | | (Some(E::V), _) => todo!(), @@ -136,7 +147,7 @@ LL | | } | |_____^ help: try: `if let (Some(E::V), _) = x { todo!() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:197:5 + --> tests/ui/single_match.rs:200:5 | LL | / match (Some(42), Some(E::V), Some(42)) { LL | | (.., Some(E::V), _) => {}, @@ -145,7 +156,7 @@ LL | | } | |_____^ help: try: `if let (.., Some(E::V), _) = (Some(42), Some(E::V), Some(42)) {}` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:270:5 + --> tests/ui/single_match.rs:273:5 | LL | / match bar { LL | | Some(v) => unsafe { @@ -165,7 +176,7 @@ LL + } } | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:279:5 + --> tests/ui/single_match.rs:282:5 | LL | / match bar { LL | | #[rustfmt::skip] @@ -187,7 +198,7 @@ LL + } | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:360:5 + --> tests/ui/single_match.rs:363:5 | LL | / match Ok::<_, u32>(Some(A)) { LL | | Ok(Some(A)) => println!(), @@ -196,7 +207,7 @@ LL | | } | |_____^ help: try: `if let Ok(Some(A)) = Ok::<_, u32>(Some(A)) { println!() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:376:5 + --> tests/ui/single_match.rs:379:5 | LL | / match &Some(A) { LL | | Some(A | B) => println!(), @@ -205,7 +216,7 @@ LL | | } | |_____^ help: try: `if let Some(A | B) = &Some(A) { println!() }` error: you seem to be trying to use `match` for an equality check. Consider using `if` - --> tests/ui/single_match.rs:384:5 + --> tests/ui/single_match.rs:387:5 | LL | / match &s[0..3] { LL | | b"foo" => println!(), @@ -214,7 +225,7 @@ LL | | } | |_____^ help: try: `if &s[0..3] == b"foo" { println!() }` error: this pattern is irrefutable, `match` is useless - --> tests/ui/single_match.rs:398:5 + --> tests/ui/single_match.rs:401:5 | LL | / match DATA { LL | | DATA => println!(), @@ -223,7 +234,7 @@ LL | | } | |_____^ help: try: `println!();` error: this pattern is irrefutable, `match` is useless - --> tests/ui/single_match.rs:404:5 + --> tests/ui/single_match.rs:407:5 | LL | / match CONST_I32 { LL | | CONST_I32 => println!(), @@ -232,7 +243,7 @@ LL | | } | |_____^ help: try: `println!();` error: this pattern is irrefutable, `match` is useless - --> tests/ui/single_match.rs:411:5 + --> tests/ui/single_match.rs:414:5 | LL | / match i { LL | | i => { @@ -252,7 +263,7 @@ LL + } | error: this pattern is irrefutable, `match` is useless - --> tests/ui/single_match.rs:420:5 + --> tests/ui/single_match.rs:423:5 | LL | / match i { LL | | i => {}, @@ -261,7 +272,7 @@ LL | | } | |_____^ help: `match` expression can be removed error: this pattern is irrefutable, `match` is useless - --> tests/ui/single_match.rs:426:5 + --> tests/ui/single_match.rs:429:5 | LL | / match i { LL | | i => (), @@ -270,7 +281,7 @@ LL | | } | |_____^ help: `match` expression can be removed error: this pattern is irrefutable, `match` is useless - --> tests/ui/single_match.rs:432:5 + --> tests/ui/single_match.rs:435:5 | LL | / match CONST_I32 { LL | | CONST_I32 => println!(), @@ -278,5 +289,37 @@ LL | | _ => {}, LL | | } | |_____^ help: try: `println!();` -error: aborting due to 26 previous errors +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> tests/ui/single_match.rs:443:5 + | +LL | / match x.pop() { +LL | | // bla +LL | | Some(u) => println!("{u}"), +... | +LL | | } + | |_____^ help: try: `if let Some(u) = x.pop() { println!("{u}") }` + | + = note: you might want to preserve the comments from inside the `match` + +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> tests/ui/single_match.rs:452:5 + | +LL | / match x.pop() { +LL | | // bla +LL | | Some(u) => { +... | +LL | | None => {}, +LL | | } + | |_____^ + | + = note: you might want to preserve the comments from inside the `match` +help: try + | +LL ~ if let Some(u) = x.pop() { +LL + // bla +LL + println!("{u}"); +LL + } + | + +error: aborting due to 29 previous errors diff --git a/tests/ui/single_match_else.fixed b/tests/ui/single_match_else.fixed index 64782bf62a78..fde13fb90dbb 100644 --- a/tests/ui/single_match_else.fixed +++ b/tests/ui/single_match_else.fixed @@ -1,4 +1,5 @@ //@aux-build: proc_macros.rs +//@require-annotations-for-level: WARN #![warn(clippy::single_match_else)] #![allow(unused, clippy::needless_return, clippy::no_effect, clippy::uninlined_format_args)] @@ -90,6 +91,13 @@ fn main() { } //~^^^^^^^ single_match_else + if let Some(a) = Some(1) { println!("${:?}", a) } else { + println!("else block"); + return; + } + //~^^^^^^^^ single_match_else + //~| NOTE: you might want to preserve the comments from inside the `match` + // lint here use std::convert::Infallible; if let Ok(a) = Result::::Ok(1) { println!("${:?}", a) } else { diff --git a/tests/ui/single_match_else.rs b/tests/ui/single_match_else.rs index 3f86f4d51803..ca282200067c 100644 --- a/tests/ui/single_match_else.rs +++ b/tests/ui/single_match_else.rs @@ -1,4 +1,5 @@ //@aux-build: proc_macros.rs +//@require-annotations-for-level: WARN #![warn(clippy::single_match_else)] #![allow(unused, clippy::needless_return, clippy::no_effect, clippy::uninlined_format_args)] @@ -99,6 +100,17 @@ fn main() { } //~^^^^^^^ single_match_else + match Some(1) { + Some(a) => println!("${:?}", a), + // This is an inner comment + None => { + println!("else block"); + return; + }, + } + //~^^^^^^^^ single_match_else + //~| NOTE: you might want to preserve the comments from inside the `match` + // lint here use std::convert::Infallible; match Result::::Ok(1) { diff --git a/tests/ui/single_match_else.stderr b/tests/ui/single_match_else.stderr index 7d4ba5fb75ef..570480f9a3f0 100644 --- a/tests/ui/single_match_else.stderr +++ b/tests/ui/single_match_else.stderr @@ -1,5 +1,5 @@ error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match_else.rs:17:13 + --> tests/ui/single_match_else.rs:18:13 | LL | let _ = match ExprNode::Butterflies { | _____________^ @@ -22,7 +22,7 @@ LL ~ }; | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match_else.rs:83:5 + --> tests/ui/single_match_else.rs:84:5 | LL | / match Some(1) { LL | | Some(a) => println!("${:?}", a), @@ -42,7 +42,7 @@ LL + } | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match_else.rs:93:5 + --> tests/ui/single_match_else.rs:94:5 | LL | / match Some(1) { LL | | Some(a) => println!("${:?}", a), @@ -62,7 +62,28 @@ LL + } | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match_else.rs:104:5 + --> tests/ui/single_match_else.rs:103:5 + | +LL | / match Some(1) { +LL | | Some(a) => println!("${:?}", a), +LL | | // This is an inner comment +LL | | None => { +... | +LL | | }, +LL | | } + | |_____^ + | + = note: you might want to preserve the comments from inside the `match` +help: try + | +LL ~ if let Some(a) = Some(1) { println!("${:?}", a) } else { +LL + println!("else block"); +LL + return; +LL + } + | + +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> tests/ui/single_match_else.rs:116:5 | LL | / match Result::::Ok(1) { LL | | Ok(a) => println!("${:?}", a), @@ -81,7 +102,7 @@ LL + } | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match_else.rs:114:5 + --> tests/ui/single_match_else.rs:126:5 | LL | / match Cow::from("moo") { LL | | Cow::Owned(a) => println!("${:?}", a), @@ -100,7 +121,7 @@ LL + } | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match_else.rs:125:5 + --> tests/ui/single_match_else.rs:137:5 | LL | / match bar { LL | | Some(v) => unsafe { @@ -123,7 +144,7 @@ LL + } | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match_else.rs:137:5 + --> tests/ui/single_match_else.rs:149:5 | LL | / match bar { LL | | Some(v) => { @@ -147,7 +168,7 @@ LL + } } | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match_else.rs:150:5 + --> tests/ui/single_match_else.rs:162:5 | LL | / match bar { LL | | Some(v) => unsafe { @@ -171,7 +192,7 @@ LL + } } | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match_else.rs:163:5 + --> tests/ui/single_match_else.rs:175:5 | LL | / match bar { LL | | #[rustfmt::skip] @@ -196,7 +217,7 @@ LL + } | error: this pattern is irrefutable, `match` is useless - --> tests/ui/single_match_else.rs:213:5 + --> tests/ui/single_match_else.rs:225:5 | LL | / match ExprNode::Butterflies { LL | | ExprNode::Butterflies => Some(&NODE), @@ -207,5 +228,5 @@ LL | | }, LL | | } | |_____^ help: try: `Some(&NODE)` -error: aborting due to 10 previous errors +error: aborting due to 11 previous errors diff --git a/tests/ui/size_of_ref.stderr b/tests/ui/size_of_ref.stderr index 6ac0b0dd2f06..46af9f55deaf 100644 --- a/tests/ui/size_of_ref.stderr +++ b/tests/ui/size_of_ref.stderr @@ -1,28 +1,28 @@ -error: argument to `std::mem::size_of_val()` is a reference to a reference +error: argument to `size_of_val()` is a reference to a reference --> tests/ui/size_of_ref.rs:13:5 | LL | size_of_val(&&x); | ^^^^^^^^^^^^^^^^ | - = help: dereference the argument to `std::mem::size_of_val()` to get the size of the value instead of the size of the reference-type + = help: dereference the argument to `size_of_val()` to get the size of the value instead of the size of the reference-type = note: `-D clippy::size-of-ref` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::size_of_ref)]` -error: argument to `std::mem::size_of_val()` is a reference to a reference +error: argument to `size_of_val()` is a reference to a reference --> tests/ui/size_of_ref.rs:16:5 | LL | size_of_val(&y); | ^^^^^^^^^^^^^^^ | - = help: dereference the argument to `std::mem::size_of_val()` to get the size of the value instead of the size of the reference-type + = help: dereference the argument to `size_of_val()` to get the size of the value instead of the size of the reference-type -error: argument to `std::mem::size_of_val()` is a reference to a reference +error: argument to `size_of_val()` is a reference to a reference --> tests/ui/size_of_ref.rs:28:9 | LL | std::mem::size_of_val(&self) + (std::mem::size_of::() * self.data.capacity()) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: dereference the argument to `std::mem::size_of_val()` to get the size of the value instead of the size of the reference-type + = help: dereference the argument to `size_of_val()` to get the size of the value instead of the size of the reference-type error: aborting due to 3 previous errors diff --git a/tests/ui/string_to_string.rs b/tests/ui/string_to_string.rs index 94174e1253b8..7c5bd8a897ba 100644 --- a/tests/ui/string_to_string.rs +++ b/tests/ui/string_to_string.rs @@ -1,8 +1,21 @@ #![warn(clippy::string_to_string)] -#![allow(clippy::redundant_clone)] +#![allow(clippy::redundant_clone, clippy::unnecessary_literal_unwrap)] fn main() { let mut message = String::from("Hello"); let mut v = message.to_string(); //~^ string_to_string + + let variable1 = String::new(); + let v = &variable1; + let variable2 = Some(v); + let _ = variable2.map(|x| { + println!(); + x.to_string() + }); + //~^^ string_to_string + + let x = Some(String::new()); + let _ = x.unwrap_or_else(|| v.to_string()); + //~^ string_to_string } diff --git a/tests/ui/string_to_string.stderr b/tests/ui/string_to_string.stderr index ae80597d1f84..99eea06f18eb 100644 --- a/tests/ui/string_to_string.stderr +++ b/tests/ui/string_to_string.stderr @@ -8,5 +8,21 @@ LL | let mut v = message.to_string(); = note: `-D clippy::string-to-string` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::string_to_string)]` -error: aborting due to 1 previous error +error: `to_string()` called on a `String` + --> tests/ui/string_to_string.rs:14:9 + | +LL | x.to_string() + | ^^^^^^^^^^^^^ + | + = help: consider using `.clone()` + +error: `to_string()` called on a `String` + --> tests/ui/string_to_string.rs:19:33 + | +LL | let _ = x.unwrap_or_else(|| v.to_string()); + | ^^^^^^^^^^^^^ + | + = help: consider using `.clone()` + +error: aborting due to 3 previous errors diff --git a/tests/ui/string_to_string_in_map.fixed b/tests/ui/string_to_string_in_map.fixed new file mode 100644 index 000000000000..efc085539f15 --- /dev/null +++ b/tests/ui/string_to_string_in_map.fixed @@ -0,0 +1,20 @@ +#![deny(clippy::string_to_string)] +#![allow(clippy::unnecessary_literal_unwrap, clippy::useless_vec, clippy::iter_cloned_collect)] + +fn main() { + let variable1 = String::new(); + let v = &variable1; + let variable2 = Some(v); + let _ = variable2.cloned(); + //~^ string_to_string + let _ = variable2.cloned(); + //~^ string_to_string + #[rustfmt::skip] + let _ = variable2.cloned(); + //~^ string_to_string + + let _ = vec![String::new()].iter().cloned().collect::>(); + //~^ string_to_string + let _ = vec![String::new()].iter().cloned().collect::>(); + //~^ string_to_string +} diff --git a/tests/ui/string_to_string_in_map.rs b/tests/ui/string_to_string_in_map.rs new file mode 100644 index 000000000000..5bf1d7ba5a2e --- /dev/null +++ b/tests/ui/string_to_string_in_map.rs @@ -0,0 +1,20 @@ +#![deny(clippy::string_to_string)] +#![allow(clippy::unnecessary_literal_unwrap, clippy::useless_vec, clippy::iter_cloned_collect)] + +fn main() { + let variable1 = String::new(); + let v = &variable1; + let variable2 = Some(v); + let _ = variable2.map(String::to_string); + //~^ string_to_string + let _ = variable2.map(|x| x.to_string()); + //~^ string_to_string + #[rustfmt::skip] + let _ = variable2.map(|x| { x.to_string() }); + //~^ string_to_string + + let _ = vec![String::new()].iter().map(String::to_string).collect::>(); + //~^ string_to_string + let _ = vec![String::new()].iter().map(|x| x.to_string()).collect::>(); + //~^ string_to_string +} diff --git a/tests/ui/string_to_string_in_map.stderr b/tests/ui/string_to_string_in_map.stderr new file mode 100644 index 000000000000..35aeed656eed --- /dev/null +++ b/tests/ui/string_to_string_in_map.stderr @@ -0,0 +1,38 @@ +error: `to_string()` called on a `String` + --> tests/ui/string_to_string_in_map.rs:8:23 + | +LL | let _ = variable2.map(String::to_string); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `cloned()` + | +note: the lint level is defined here + --> tests/ui/string_to_string_in_map.rs:1:9 + | +LL | #![deny(clippy::string_to_string)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `to_string()` called on a `String` + --> tests/ui/string_to_string_in_map.rs:10:23 + | +LL | let _ = variable2.map(|x| x.to_string()); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `cloned()` + +error: `to_string()` called on a `String` + --> tests/ui/string_to_string_in_map.rs:13:23 + | +LL | let _ = variable2.map(|x| { x.to_string() }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cloned()` + +error: `to_string()` called on a `String` + --> tests/ui/string_to_string_in_map.rs:16:40 + | +LL | let _ = vec![String::new()].iter().map(String::to_string).collect::>(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `cloned()` + +error: `to_string()` called on a `String` + --> tests/ui/string_to_string_in_map.rs:18:40 + | +LL | let _ = vec![String::new()].iter().map(|x| x.to_string()).collect::>(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `cloned()` + +error: aborting due to 5 previous errors + diff --git a/tests/ui/struct_fields.rs b/tests/ui/struct_fields.rs index 3dce530efffa..e7ff2e6573b2 100644 --- a/tests/ui/struct_fields.rs +++ b/tests/ui/struct_fields.rs @@ -344,4 +344,31 @@ struct Use { //~^ struct_field_names } +// should lint on private fields of public structs (renaming them is not breaking-exported-api) +pub struct PubStructFieldNamedAfterStruct { + pub_struct_field_named_after_struct: bool, + //~^ ERROR: field name starts with the struct's name + other1: bool, + other2: bool, +} +pub struct PubStructFieldPrefix { + //~^ ERROR: all fields have the same prefix: `field` + field_foo: u8, + field_bar: u8, + field_baz: u8, +} +// ...but should not lint on structs with public fields. +pub struct PubStructPubAndPrivateFields { + /// One could argue that this field should be linted, but currently, any public field stops all + /// checking. + pub_struct_pub_and_private_fields_1: bool, + pub pub_struct_pub_and_private_fields_2: bool, +} +// nor on common prefixes if one of the involved fields is public +pub struct PubStructPubAndPrivateFieldPrefix { + pub field_foo: u8, + field_bar: u8, + field_baz: u8, +} + fn main() {} diff --git a/tests/ui/struct_fields.stderr b/tests/ui/struct_fields.stderr index 79186cc1cfd8..a5ff1b125907 100644 --- a/tests/ui/struct_fields.stderr +++ b/tests/ui/struct_fields.stderr @@ -281,5 +281,24 @@ error: field name starts with the struct's name LL | use_baz: bool, | ^^^^^^^^^^^^^ -error: aborting due to 24 previous errors +error: field name starts with the struct's name + --> tests/ui/struct_fields.rs:349:5 + | +LL | pub_struct_field_named_after_struct: bool, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: all fields have the same prefix: `field` + --> tests/ui/struct_fields.rs:354:1 + | +LL | / pub struct PubStructFieldPrefix { +LL | | +LL | | field_foo: u8, +LL | | field_bar: u8, +LL | | field_baz: u8, +LL | | } + | |_^ + | + = help: remove the prefixes + +error: aborting due to 26 previous errors diff --git a/tests/ui/unnecessary_path_debug_formatting.rs b/tests/ui/unnecessary_path_debug_formatting.rs index 02adeece2809..f14f6085c9a1 100644 --- a/tests/ui/unnecessary_path_debug_formatting.rs +++ b/tests/ui/unnecessary_path_debug_formatting.rs @@ -41,3 +41,9 @@ fn main() { let deref_path = DerefPath { path }; println!("{:?}", &*deref_path); //~ unnecessary_debug_formatting } + +#[test] +fn issue_14345() { + let input = std::path::Path::new("/foo/bar"); + assert!(input.ends_with("baz"), "{input:?}"); +} diff --git a/tests/ui/unnecessary_to_owned.fixed b/tests/ui/unnecessary_to_owned.fixed index bf271aef763b..5410033dbd8f 100644 --- a/tests/ui/unnecessary_to_owned.fixed +++ b/tests/ui/unnecessary_to_owned.fixed @@ -195,19 +195,11 @@ fn main() { //~^ unnecessary_to_owned let _ = slice.iter().copied(); //~^ unnecessary_to_owned - let _ = [std::path::PathBuf::new()][..].iter().cloned(); - //~^ unnecessary_to_owned - let _ = [std::path::PathBuf::new()][..].iter().cloned(); - //~^ unnecessary_to_owned let _ = slice.iter().copied(); //~^ unnecessary_to_owned let _ = slice.iter().copied(); //~^ unnecessary_to_owned - let _ = [std::path::PathBuf::new()][..].iter().cloned(); - //~^ unnecessary_to_owned - let _ = [std::path::PathBuf::new()][..].iter().cloned(); - //~^ unnecessary_to_owned let _ = check_files(&[FileType::Account]); @@ -317,19 +309,6 @@ fn get_file_path(_file_type: &FileType) -> Result impl IntoIterator { cow.into_owned().into_iter() } + +mod issue_14242 { + use std::rc::Rc; + + #[derive(Copy, Clone)] + struct Foo; + + fn rc_slice_provider() -> Rc<[Foo]> { + Rc::from([Foo]) + } + + fn iterator_provider() -> impl Iterator { + rc_slice_provider().to_vec().into_iter() + } +} diff --git a/tests/ui/unnecessary_to_owned.rs b/tests/ui/unnecessary_to_owned.rs index 95b95ab6bd22..0619dd4ddec0 100644 --- a/tests/ui/unnecessary_to_owned.rs +++ b/tests/ui/unnecessary_to_owned.rs @@ -195,19 +195,11 @@ fn main() { //~^ unnecessary_to_owned let _ = slice.to_owned().into_iter(); //~^ unnecessary_to_owned - let _ = [std::path::PathBuf::new()][..].to_vec().into_iter(); - //~^ unnecessary_to_owned - let _ = [std::path::PathBuf::new()][..].to_owned().into_iter(); - //~^ unnecessary_to_owned let _ = IntoIterator::into_iter(slice.to_vec()); //~^ unnecessary_to_owned let _ = IntoIterator::into_iter(slice.to_owned()); //~^ unnecessary_to_owned - let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_vec()); - //~^ unnecessary_to_owned - let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_owned()); - //~^ unnecessary_to_owned let _ = check_files(&[FileType::Account]); @@ -317,19 +309,6 @@ fn get_file_path(_file_type: &FileType) -> Result impl IntoIterator { cow.into_owned().into_iter() } + +mod issue_14242 { + use std::rc::Rc; + + #[derive(Copy, Clone)] + struct Foo; + + fn rc_slice_provider() -> Rc<[Foo]> { + Rc::from([Foo]) + } + + fn iterator_provider() -> impl Iterator { + rc_slice_provider().to_vec().into_iter() + } +} diff --git a/tests/ui/unnecessary_to_owned.stderr b/tests/ui/unnecessary_to_owned.stderr index 4daa3876e60e..8926db34da8c 100644 --- a/tests/ui/unnecessary_to_owned.stderr +++ b/tests/ui/unnecessary_to_owned.stderr @@ -1,11 +1,11 @@ error: redundant clone - --> tests/ui/unnecessary_to_owned.rs:225:64 + --> tests/ui/unnecessary_to_owned.rs:217:64 | LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned()); | ^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/unnecessary_to_owned.rs:225:20 + --> tests/ui/unnecessary_to_owned.rs:217:20 | LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -13,49 +13,49 @@ LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned()) = help: to override `-D warnings` add `#[allow(clippy::redundant_clone)]` error: redundant clone - --> tests/ui/unnecessary_to_owned.rs:227:40 + --> tests/ui/unnecessary_to_owned.rs:219:40 | LL | require_os_str(&OsString::from("x").to_os_string()); | ^^^^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/unnecessary_to_owned.rs:227:21 + --> tests/ui/unnecessary_to_owned.rs:219:21 | LL | require_os_str(&OsString::from("x").to_os_string()); | ^^^^^^^^^^^^^^^^^^^ error: redundant clone - --> tests/ui/unnecessary_to_owned.rs:229:48 + --> tests/ui/unnecessary_to_owned.rs:221:48 | LL | require_path(&std::path::PathBuf::from("x").to_path_buf()); | ^^^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/unnecessary_to_owned.rs:229:19 + --> tests/ui/unnecessary_to_owned.rs:221:19 | LL | require_path(&std::path::PathBuf::from("x").to_path_buf()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: redundant clone - --> tests/ui/unnecessary_to_owned.rs:231:35 + --> tests/ui/unnecessary_to_owned.rs:223:35 | LL | require_str(&String::from("x").to_string()); | ^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/unnecessary_to_owned.rs:231:18 + --> tests/ui/unnecessary_to_owned.rs:223:18 | LL | require_str(&String::from("x").to_string()); | ^^^^^^^^^^^^^^^^^ error: redundant clone - --> tests/ui/unnecessary_to_owned.rs:233:39 + --> tests/ui/unnecessary_to_owned.rs:225:39 | LL | require_slice(&[String::from("x")].to_owned()); | ^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/unnecessary_to_owned.rs:233:20 + --> tests/ui/unnecessary_to_owned.rs:225:20 | LL | require_slice(&[String::from("x")].to_owned()); | ^^^^^^^^^^^^^^^^^^^ @@ -442,43 +442,19 @@ LL | let _ = slice.to_owned().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:198:13 - | -LL | let _ = [std::path::PathBuf::new()][..].to_vec().into_iter(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` - -error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:200:13 - | -LL | let _ = [std::path::PathBuf::new()][..].to_owned().into_iter(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` - -error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:203:13 + --> tests/ui/unnecessary_to_owned.rs:199:13 | LL | let _ = IntoIterator::into_iter(slice.to_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:205:13 + --> tests/ui/unnecessary_to_owned.rs:201:13 | LL | let _ = IntoIterator::into_iter(slice.to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` -error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:207:13 - | -LL | let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_vec()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` - -error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:209:13 - | -LL | let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_owned()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` - error: allocating a new `String` only to create a temporary `&str` from it - --> tests/ui/unnecessary_to_owned.rs:237:26 + --> tests/ui/unnecessary_to_owned.rs:229:26 | LL | let _ref_str: &str = &String::from_utf8(slice.to_vec()).expect("not UTF-8"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -490,7 +466,7 @@ LL + let _ref_str: &str = core::str::from_utf8(&slice).expect("not UTF-8"); | error: allocating a new `String` only to create a temporary `&str` from it - --> tests/ui/unnecessary_to_owned.rs:239:26 + --> tests/ui/unnecessary_to_owned.rs:231:26 | LL | let _ref_str: &str = &String::from_utf8(b"foo".to_vec()).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -502,7 +478,7 @@ LL + let _ref_str: &str = core::str::from_utf8(b"foo").unwrap(); | error: allocating a new `String` only to create a temporary `&str` from it - --> tests/ui/unnecessary_to_owned.rs:241:26 + --> tests/ui/unnecessary_to_owned.rs:233:26 | LL | let _ref_str: &str = &String::from_utf8(b"foo".as_slice().to_owned()).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -514,7 +490,7 @@ LL + let _ref_str: &str = core::str::from_utf8(b"foo".as_slice()).unwrap(); | error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:299:14 + --> tests/ui/unnecessary_to_owned.rs:291:14 | LL | for t in file_types.to_vec() { | ^^^^^^^^^^^^^^^^^^^ @@ -526,65 +502,53 @@ LL | LL ~ let path = match get_file_path(t) { | -error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:323:14 - | -LL | let _ = &["x"][..].to_vec().into_iter(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().cloned()` - -error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:329:14 - | -LL | let _ = &["x"][..].to_vec().into_iter(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().copied()` - error: unnecessary use of `to_string` - --> tests/ui/unnecessary_to_owned.rs:378:24 + --> tests/ui/unnecessary_to_owned.rs:357:24 | LL | Box::new(build(y.to_string())) | ^^^^^^^^^^^^^ help: use: `y` error: unnecessary use of `to_string` - --> tests/ui/unnecessary_to_owned.rs:488:12 + --> tests/ui/unnecessary_to_owned.rs:467:12 | LL | id("abc".to_string()) | ^^^^^^^^^^^^^^^^^ help: use: `"abc"` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:632:37 + --> tests/ui/unnecessary_to_owned.rs:611:37 | LL | IntoFuture::into_future(foo([].to_vec(), &0)); | ^^^^^^^^^^^ help: use: `[]` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:643:18 + --> tests/ui/unnecessary_to_owned.rs:622:18 | LL | s.remove(&a.to_vec()); | ^^^^^^^^^^^ help: replace it with: `a` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:648:14 + --> tests/ui/unnecessary_to_owned.rs:627:14 | LL | s.remove(&"b".to_owned()); | ^^^^^^^^^^^^^^^ help: replace it with: `"b"` error: unnecessary use of `to_string` - --> tests/ui/unnecessary_to_owned.rs:650:14 + --> tests/ui/unnecessary_to_owned.rs:629:14 | LL | s.remove(&"b".to_string()); | ^^^^^^^^^^^^^^^^ help: replace it with: `"b"` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:656:14 + --> tests/ui/unnecessary_to_owned.rs:635:14 | LL | s.remove(&["b"].to_vec()); | ^^^^^^^^^^^^^^^ help: replace it with: `["b"].as_slice()` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:658:14 + --> tests/ui/unnecessary_to_owned.rs:637:14 | LL | s.remove(&(&["b"]).to_vec()); | ^^^^^^^^^^^^^^^^^^ help: replace it with: `(&["b"]).as_slice()` -error: aborting due to 88 previous errors +error: aborting due to 82 previous errors diff --git a/triagebot.toml b/triagebot.toml index 3d35116ebc17..33d3b0728f3d 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -34,5 +34,4 @@ users_on_vacation = [ "@Jarcho", "@blyxyas", "@y21", - "@Centri3", ] diff --git a/util/gh-pages/index_template.html b/util/gh-pages/index_template.html index a9b646280030..19dc1ec0b0cc 100644 --- a/util/gh-pages/index_template.html +++ b/util/gh-pages/index_template.html @@ -19,10 +19,10 @@ Otherwise, have a great day =^.^= {# #} - {# #} - {# #} - {# #} - {# #} + {# #} + {# #} + {# #} + {# #} {# #} {# #} {# #} From 6c8400e342609de052ec287fecaf67cef04da1d2 Mon Sep 17 00:00:00 2001 From: lcnr Date: Fri, 14 Mar 2025 12:47:08 +0100 Subject: [PATCH 08/19] remove `feature(inline_const_pat)` --- clippy_lints/src/matches/redundant_guards.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/clippy_lints/src/matches/redundant_guards.rs b/clippy_lints/src/matches/redundant_guards.rs index ab53ad98572e..9bbef8da0a46 100644 --- a/clippy_lints/src/matches/redundant_guards.rs +++ b/clippy_lints/src/matches/redundant_guards.rs @@ -246,7 +246,6 @@ fn emit_redundant_guards<'tcx>( fn expr_can_be_pat(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { for_each_expr_without_closures(expr, |expr| { if match expr.kind { - ExprKind::ConstBlock(..) => cx.tcx.features().inline_const_pat(), ExprKind::Call(c, ..) if let ExprKind::Path(qpath) = c.kind => { // Allow ctors matches!(cx.qpath_res(&qpath, c.hir_id), Res::Def(DefKind::Ctor(..), ..)) From aba76d0e78aeb031f941c7a3466eb4d9d13d7a85 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 24 Mar 2025 23:19:18 +0000 Subject: [PATCH 09/19] Allow defining opaques in statics and consts --- clippy_utils/src/ast_utils/mod.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/clippy_utils/src/ast_utils/mod.rs b/clippy_utils/src/ast_utils/mod.rs index 54261079fcad..6023ae9cc7b1 100644 --- a/clippy_utils/src/ast_utils/mod.rs +++ b/clippy_utils/src/ast_utils/mod.rs @@ -336,12 +336,14 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { mutability: lm, expr: le, safety: ls, + define_opaque: _, }), Static(box StaticItem { ty: rt, mutability: rm, expr: re, safety: rs, + define_opaque: _, }), ) => lm == rm && ls == rs && eq_ty(lt, rt) && eq_expr_opt(le.as_ref(), re.as_ref()), ( @@ -350,12 +352,14 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { generics: lg, ty: lt, expr: le, + define_opaque: _, }), Const(box ConstItem { defaultness: rd, generics: rg, ty: rt, expr: re, + define_opaque: _, }), ) => eq_defaultness(*ld, *rd) && eq_generics(lg, rg) && eq_ty(lt, rt) && eq_expr_opt(le.as_ref(), re.as_ref()), ( @@ -490,12 +494,14 @@ pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool { mutability: lm, expr: le, safety: ls, + define_opaque: _, }), Static(box StaticItem { ty: rt, mutability: rm, expr: re, safety: rs, + define_opaque: _, }), ) => lm == rm && eq_ty(lt, rt) && eq_expr_opt(le.as_ref(), re.as_ref()) && ls == rs, ( @@ -557,12 +563,14 @@ pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool { generics: lg, ty: lt, expr: le, + define_opaque: _, }), Const(box ConstItem { defaultness: rd, generics: rg, ty: rt, expr: re, + define_opaque: _, }), ) => eq_defaultness(*ld, *rd) && eq_generics(lg, rg) && eq_ty(lt, rt) && eq_expr_opt(le.as_ref(), re.as_ref()), ( From 1b92712f3f11a7a5c0f1b50343d533844c5fb134 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sat, 22 Mar 2025 21:42:34 +0300 Subject: [PATCH 10/19] expand: Leave traces when expanding `cfg` attributes --- clippy_lints/src/attrs/duplicated_attributes.rs | 3 +-- clippy_lints/src/cfg_not_test.rs | 2 +- clippy_lints/src/methods/is_empty.rs | 2 +- clippy_utils/src/lib.rs | 8 ++++---- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/attrs/duplicated_attributes.rs b/clippy_lints/src/attrs/duplicated_attributes.rs index 5c486eb90cc2..4c84e61b1f26 100644 --- a/clippy_lints/src/attrs/duplicated_attributes.rs +++ b/clippy_lints/src/attrs/duplicated_attributes.rs @@ -37,7 +37,6 @@ fn check_duplicated_attr( let Some(ident) = attr.ident() else { return }; let name = ident.name; if name == sym::doc - || name == sym::cfg_attr || name == sym::cfg_attr_trace || name == sym::rustc_on_unimplemented || name == sym::reason { @@ -47,7 +46,7 @@ fn check_duplicated_attr( return; } if let Some(direct_parent) = parent.last() - && ["cfg", "cfg_attr"].contains(&direct_parent.as_str()) + && direct_parent == sym::cfg_trace.as_str() && [sym::all, sym::not, sym::any].contains(&name) { // FIXME: We don't correctly check `cfg`s for now, so if it's more complex than just a one diff --git a/clippy_lints/src/cfg_not_test.rs b/clippy_lints/src/cfg_not_test.rs index 84136a2e6c28..7590fe96fd21 100644 --- a/clippy_lints/src/cfg_not_test.rs +++ b/clippy_lints/src/cfg_not_test.rs @@ -32,7 +32,7 @@ declare_lint_pass!(CfgNotTest => [CFG_NOT_TEST]); impl EarlyLintPass for CfgNotTest { fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &rustc_ast::Attribute) { - if attr.has_name(rustc_span::sym::cfg) && contains_not_test(attr.meta_item_list().as_deref(), false) { + if attr.has_name(rustc_span::sym::cfg_trace) && contains_not_test(attr.meta_item_list().as_deref(), false) { span_lint_and_then( cx, CFG_NOT_TEST, diff --git a/clippy_lints/src/methods/is_empty.rs b/clippy_lints/src/methods/is_empty.rs index 7c190e123b72..4c81b22861b4 100644 --- a/clippy_lints/src/methods/is_empty.rs +++ b/clippy_lints/src/methods/is_empty.rs @@ -41,7 +41,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &'_ Expr<'_>, receiver: &Expr<'_ fn is_under_cfg(cx: &LateContext<'_>, id: HirId) -> bool { cx.tcx .hir_parent_id_iter(id) - .any(|id| cx.tcx.hir_attrs(id).iter().any(|attr| attr.has_name(sym::cfg))) + .any(|id| cx.tcx.hir_attrs(id).iter().any(|attr| attr.has_name(sym::cfg_trace))) } /// Similar to [`clippy_utils::expr_or_init`], but does not go up the chain if the initialization diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 1307ff79bc5d..668b0cb69e20 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -2629,7 +2629,7 @@ pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir> pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool { if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind { if let Res::Def(_, def_id) = path.res { - return cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr); + return cx.tcx.has_attr(def_id, sym::cfg_trace) || cx.tcx.has_attr(def_id, sym::cfg_attr); } } false @@ -2699,7 +2699,7 @@ pub fn is_in_test_function(tcx: TyCtxt<'_>, id: HirId) -> bool { /// use [`is_in_cfg_test`] pub fn is_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool { tcx.hir_attrs(id).iter().any(|attr| { - if attr.has_name(sym::cfg) + if attr.has_name(sym::cfg_trace) && let Some(items) = attr.meta_item_list() && let [item] = &*items && item.has_name(sym::test) @@ -2723,11 +2723,11 @@ pub fn is_in_test(tcx: TyCtxt<'_>, hir_id: HirId) -> bool { /// Checks if the item of any of its parents has `#[cfg(...)]` attribute applied. pub fn inherits_cfg(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { - tcx.has_attr(def_id, sym::cfg) + tcx.has_attr(def_id, sym::cfg_trace) || tcx .hir_parent_iter(tcx.local_def_id_to_hir_id(def_id)) .flat_map(|(parent_id, _)| tcx.hir_attrs(parent_id)) - .any(|attr| attr.has_name(sym::cfg)) + .any(|attr| attr.has_name(sym::cfg_trace)) } /// Walks up the HIR tree from the given expression in an attempt to find where the value is From 5f26d0e9703544c751952ba5e13bb55a64f55abf Mon Sep 17 00:00:00 2001 From: Urgau Date: Sun, 9 Mar 2025 14:55:46 +0100 Subject: [PATCH 11/19] Drop `clippy::invalid_null_ptr_usage` --- clippy_lints/src/declared_lints.rs | 1 - clippy_lints/src/deprecated_lints.rs | 2 + clippy_lints/src/ptr.rs | 73 +--------- tests/ui/crashes/ice-1782.rs | 2 +- tests/ui/invalid_null_ptr_usage.fixed | 66 --------- tests/ui/invalid_null_ptr_usage.rs | 66 --------- tests/ui/invalid_null_ptr_usage.stderr | 136 ------------------ tests/ui/invalid_null_ptr_usage_no_std.fixed | 79 ---------- tests/ui/invalid_null_ptr_usage_no_std.rs | 79 ---------- tests/ui/invalid_null_ptr_usage_no_std.stderr | 136 ------------------ tests/ui/rename.fixed | 1 + tests/ui/rename.rs | 1 + tests/ui/rename.stderr | 30 ++-- 13 files changed, 24 insertions(+), 648 deletions(-) delete mode 100644 tests/ui/invalid_null_ptr_usage.fixed delete mode 100644 tests/ui/invalid_null_ptr_usage.rs delete mode 100644 tests/ui/invalid_null_ptr_usage.stderr delete mode 100644 tests/ui/invalid_null_ptr_usage_no_std.fixed delete mode 100644 tests/ui/invalid_null_ptr_usage_no_std.rs delete mode 100644 tests/ui/invalid_null_ptr_usage_no_std.stderr diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 7fa23dad6981..39e451637070 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -638,7 +638,6 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::precedence::PRECEDENCE_INFO, crate::precedence::PRECEDENCE_BITS_INFO, crate::ptr::CMP_NULL_INFO, - crate::ptr::INVALID_NULL_PTR_USAGE_INFO, crate::ptr::MUT_FROM_REF_INFO, crate::ptr::PTR_ARG_INFO, crate::ptr::PTR_EQ_INFO, diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index 0031da406f17..de66ead4f420 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -131,6 +131,8 @@ declare_with_version! { RENAMED(RENAMED_VERSION): &[(&str, &str)] = &[ ("clippy::clone_double_ref", "suspicious_double_ref_op"), #[clippy::version = ""] ("clippy::cmp_nan", "invalid_nan_comparisons"), + #[clippy::version = "CURRENT_RUSTC_VERSION"] + ("clippy::invalid_null_ptr_usage", "invalid_null_arguments"), #[clippy::version = "1.86.0"] ("clippy::double_neg", "double_negations"), #[clippy::version = ""] diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 55f1ece05593..50ef56db167c 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -125,29 +125,6 @@ declare_clippy_lint! { "fns that create mutable refs from immutable ref args" } -declare_clippy_lint! { - /// ### What it does - /// This lint checks for invalid usages of `ptr::null`. - /// - /// ### Why is this bad? - /// This causes undefined behavior. - /// - /// ### Example - /// ```ignore - /// // Undefined behavior - /// unsafe { std::slice::from_raw_parts(ptr::null(), 0); } - /// ``` - /// - /// Use instead: - /// ```ignore - /// unsafe { std::slice::from_raw_parts(NonNull::dangling().as_ptr(), 0); } - /// ``` - #[clippy::version = "1.53.0"] - pub INVALID_NULL_PTR_USAGE, - correctness, - "invalid usage of a null pointer, suggesting `NonNull::dangling()` instead" -} - declare_clippy_lint! { /// ### What it does /// Use `std::ptr::eq` when applicable @@ -177,7 +154,7 @@ declare_clippy_lint! { "use `std::ptr::eq` when comparing raw pointers" } -declare_lint_pass!(Ptr => [PTR_ARG, CMP_NULL, MUT_FROM_REF, INVALID_NULL_PTR_USAGE, PTR_EQ]); +declare_lint_pass!(Ptr => [PTR_ARG, CMP_NULL, MUT_FROM_REF, PTR_EQ]); impl<'tcx> LateLintPass<'tcx> for Ptr { fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { @@ -301,54 +278,6 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { format!("{non_null_path_snippet}.is_null()"), Applicability::MachineApplicable, ); - } else { - check_invalid_ptr_usage(cx, expr); - } - } -} - -fn check_invalid_ptr_usage<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Call(fun, args) = expr.kind - && let ExprKind::Path(ref qpath) = fun.kind - && 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_write - | sym::ptr_write_bytes - | sym::ptr_write_unaligned - | sym::ptr_write_volatile - | sym::slice_from_raw_parts - | sym::slice_from_raw_parts_mut => &[0], - sym::ptr_copy | sym::ptr_copy_nonoverlapping | sym::ptr_swap | sym::ptr_swap_nonoverlapping => &[0, 1], - _ => return, - }; - - for &arg_idx in arg_indices { - if let Some(arg) = args.get(arg_idx).filter(|arg| is_null_path(cx, arg)) - && let Some(std_or_core) = std_or_core(cx) - { - span_lint_and_sugg( - cx, - INVALID_NULL_PTR_USAGE, - arg.span, - "pointer must be non-null", - "change this to", - format!("{std_or_core}::ptr::NonNull::dangling().as_ptr()"), - Applicability::MachineApplicable, - ); - } } } } diff --git a/tests/ui/crashes/ice-1782.rs b/tests/ui/crashes/ice-1782.rs index fefdc405cce2..4a1886c08af6 100644 --- a/tests/ui/crashes/ice-1782.rs +++ b/tests/ui/crashes/ice-1782.rs @@ -1,6 +1,6 @@ //@ check-pass -#![allow(dead_code, unused_variables)] +#![allow(dead_code, unused_variables, invalid_null_arguments)] #![allow(clippy::unnecessary_cast, clippy::missing_transmute_annotations)] /// Should not trigger an ICE in `SpanlessEq` / `consts::constant` diff --git a/tests/ui/invalid_null_ptr_usage.fixed b/tests/ui/invalid_null_ptr_usage.fixed deleted file mode 100644 index ce78e89ee829..000000000000 --- a/tests/ui/invalid_null_ptr_usage.fixed +++ /dev/null @@ -1,66 +0,0 @@ -fn main() { - unsafe { - let _slice: &[usize] = std::slice::from_raw_parts(std::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - let _slice: &[usize] = std::slice::from_raw_parts(std::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - - let _slice: &[usize] = std::slice::from_raw_parts_mut(std::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - - std::ptr::copy::(std::ptr::NonNull::dangling().as_ptr(), std::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - std::ptr::copy::(std::ptr::NonNull::dangling().as_ptr(), std::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - - std::ptr::copy_nonoverlapping::(std::ptr::NonNull::dangling().as_ptr(), std::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - std::ptr::copy_nonoverlapping::(std::ptr::NonNull::dangling().as_ptr(), std::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - - struct A; // zero sized struct - assert_eq!(std::mem::size_of::(), 0); - - let _a: A = std::ptr::read(std::ptr::NonNull::dangling().as_ptr()); - //~^ invalid_null_ptr_usage - let _a: A = std::ptr::read(std::ptr::NonNull::dangling().as_ptr()); - //~^ invalid_null_ptr_usage - - let _a: A = std::ptr::read_unaligned(std::ptr::NonNull::dangling().as_ptr()); - //~^ invalid_null_ptr_usage - let _a: A = std::ptr::read_unaligned(std::ptr::NonNull::dangling().as_ptr()); - //~^ invalid_null_ptr_usage - - let _a: A = std::ptr::read_volatile(std::ptr::NonNull::dangling().as_ptr()); - //~^ invalid_null_ptr_usage - let _a: A = std::ptr::read_volatile(std::ptr::NonNull::dangling().as_ptr()); - //~^ invalid_null_ptr_usage - - let _a: A = std::ptr::replace(std::ptr::NonNull::dangling().as_ptr(), A); - //~^ invalid_null_ptr_usage - let _slice: *const [usize] = std::ptr::slice_from_raw_parts(std::ptr::null_mut(), 0); // shouldn't lint - let _slice: *const [usize] = std::ptr::slice_from_raw_parts_mut(std::ptr::null_mut(), 0); - - std::ptr::swap::(std::ptr::NonNull::dangling().as_ptr(), &mut A); - //~^ invalid_null_ptr_usage - std::ptr::swap::(&mut A, std::ptr::NonNull::dangling().as_ptr()); - //~^ invalid_null_ptr_usage - - std::ptr::swap_nonoverlapping::(std::ptr::NonNull::dangling().as_ptr(), &mut A, 0); - //~^ invalid_null_ptr_usage - std::ptr::swap_nonoverlapping::(&mut A, std::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - - std::ptr::write(std::ptr::NonNull::dangling().as_ptr(), A); - //~^ invalid_null_ptr_usage - - std::ptr::write_unaligned(std::ptr::NonNull::dangling().as_ptr(), A); - //~^ invalid_null_ptr_usage - - std::ptr::write_volatile(std::ptr::NonNull::dangling().as_ptr(), A); - //~^ invalid_null_ptr_usage - - std::ptr::write_bytes::(std::ptr::NonNull::dangling().as_ptr(), 42, 0); - //~^ invalid_null_ptr_usage - } -} diff --git a/tests/ui/invalid_null_ptr_usage.rs b/tests/ui/invalid_null_ptr_usage.rs deleted file mode 100644 index 361865fbd960..000000000000 --- a/tests/ui/invalid_null_ptr_usage.rs +++ /dev/null @@ -1,66 +0,0 @@ -fn main() { - unsafe { - let _slice: &[usize] = std::slice::from_raw_parts(std::ptr::null(), 0); - //~^ invalid_null_ptr_usage - let _slice: &[usize] = std::slice::from_raw_parts(std::ptr::null_mut(), 0); - //~^ invalid_null_ptr_usage - - let _slice: &[usize] = std::slice::from_raw_parts_mut(std::ptr::null_mut(), 0); - //~^ invalid_null_ptr_usage - - std::ptr::copy::(std::ptr::null(), std::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - std::ptr::copy::(std::ptr::NonNull::dangling().as_ptr(), std::ptr::null_mut(), 0); - //~^ invalid_null_ptr_usage - - std::ptr::copy_nonoverlapping::(std::ptr::null(), std::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - std::ptr::copy_nonoverlapping::(std::ptr::NonNull::dangling().as_ptr(), std::ptr::null_mut(), 0); - //~^ invalid_null_ptr_usage - - struct A; // zero sized struct - assert_eq!(std::mem::size_of::(), 0); - - let _a: A = std::ptr::read(std::ptr::null()); - //~^ invalid_null_ptr_usage - let _a: A = std::ptr::read(std::ptr::null_mut()); - //~^ invalid_null_ptr_usage - - let _a: A = std::ptr::read_unaligned(std::ptr::null()); - //~^ invalid_null_ptr_usage - let _a: A = std::ptr::read_unaligned(std::ptr::null_mut()); - //~^ invalid_null_ptr_usage - - let _a: A = std::ptr::read_volatile(std::ptr::null()); - //~^ invalid_null_ptr_usage - let _a: A = std::ptr::read_volatile(std::ptr::null_mut()); - //~^ invalid_null_ptr_usage - - let _a: A = std::ptr::replace(std::ptr::null_mut(), A); - //~^ invalid_null_ptr_usage - let _slice: *const [usize] = std::ptr::slice_from_raw_parts(std::ptr::null_mut(), 0); // shouldn't lint - let _slice: *const [usize] = std::ptr::slice_from_raw_parts_mut(std::ptr::null_mut(), 0); - - std::ptr::swap::(std::ptr::null_mut(), &mut A); - //~^ invalid_null_ptr_usage - std::ptr::swap::(&mut A, std::ptr::null_mut()); - //~^ invalid_null_ptr_usage - - std::ptr::swap_nonoverlapping::(std::ptr::null_mut(), &mut A, 0); - //~^ invalid_null_ptr_usage - std::ptr::swap_nonoverlapping::(&mut A, std::ptr::null_mut(), 0); - //~^ invalid_null_ptr_usage - - std::ptr::write(std::ptr::null_mut(), A); - //~^ invalid_null_ptr_usage - - std::ptr::write_unaligned(std::ptr::null_mut(), A); - //~^ invalid_null_ptr_usage - - std::ptr::write_volatile(std::ptr::null_mut(), A); - //~^ invalid_null_ptr_usage - - std::ptr::write_bytes::(std::ptr::null_mut(), 42, 0); - //~^ invalid_null_ptr_usage - } -} diff --git a/tests/ui/invalid_null_ptr_usage.stderr b/tests/ui/invalid_null_ptr_usage.stderr deleted file mode 100644 index 3f9d15b90401..000000000000 --- a/tests/ui/invalid_null_ptr_usage.stderr +++ /dev/null @@ -1,136 +0,0 @@ -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:3:59 - | -LL | let _slice: &[usize] = std::slice::from_raw_parts(std::ptr::null(), 0); - | ^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - | - = note: `#[deny(clippy::invalid_null_ptr_usage)]` on by default - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:5:59 - | -LL | let _slice: &[usize] = std::slice::from_raw_parts(std::ptr::null_mut(), 0); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:8:63 - | -LL | let _slice: &[usize] = std::slice::from_raw_parts_mut(std::ptr::null_mut(), 0); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:11:33 - | -LL | std::ptr::copy::(std::ptr::null(), std::ptr::NonNull::dangling().as_ptr(), 0); - | ^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:13:73 - | -LL | std::ptr::copy::(std::ptr::NonNull::dangling().as_ptr(), std::ptr::null_mut(), 0); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:16:48 - | -LL | std::ptr::copy_nonoverlapping::(std::ptr::null(), std::ptr::NonNull::dangling().as_ptr(), 0); - | ^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:18:88 - | -LL | std::ptr::copy_nonoverlapping::(std::ptr::NonNull::dangling().as_ptr(), std::ptr::null_mut(), 0); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:24:36 - | -LL | let _a: A = std::ptr::read(std::ptr::null()); - | ^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:26:36 - | -LL | let _a: A = std::ptr::read(std::ptr::null_mut()); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:29:46 - | -LL | let _a: A = std::ptr::read_unaligned(std::ptr::null()); - | ^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:31:46 - | -LL | let _a: A = std::ptr::read_unaligned(std::ptr::null_mut()); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:34:45 - | -LL | let _a: A = std::ptr::read_volatile(std::ptr::null()); - | ^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:36:45 - | -LL | let _a: A = std::ptr::read_volatile(std::ptr::null_mut()); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:39:39 - | -LL | let _a: A = std::ptr::replace(std::ptr::null_mut(), A); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:44:29 - | -LL | std::ptr::swap::(std::ptr::null_mut(), &mut A); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:46:37 - | -LL | std::ptr::swap::(&mut A, std::ptr::null_mut()); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:49:44 - | -LL | std::ptr::swap_nonoverlapping::(std::ptr::null_mut(), &mut A, 0); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:51:52 - | -LL | std::ptr::swap_nonoverlapping::(&mut A, std::ptr::null_mut(), 0); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:54:25 - | -LL | std::ptr::write(std::ptr::null_mut(), A); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:57:35 - | -LL | std::ptr::write_unaligned(std::ptr::null_mut(), A); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:60:34 - | -LL | std::ptr::write_volatile(std::ptr::null_mut(), A); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:63:40 - | -LL | std::ptr::write_bytes::(std::ptr::null_mut(), 42, 0); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: aborting due to 22 previous errors - diff --git a/tests/ui/invalid_null_ptr_usage_no_std.fixed b/tests/ui/invalid_null_ptr_usage_no_std.fixed deleted file mode 100644 index df7ab166187d..000000000000 --- a/tests/ui/invalid_null_ptr_usage_no_std.fixed +++ /dev/null @@ -1,79 +0,0 @@ -#![no_std] -#![feature(lang_items)] - -use core::panic::PanicInfo; - -#[lang = "eh_personality"] -extern "C" fn eh_personality() {} - -#[panic_handler] -fn panic(info: &PanicInfo) -> ! { - loop {} -} - -fn main() { - unsafe { - let _slice: &[usize] = core::slice::from_raw_parts(core::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - let _slice: &[usize] = core::slice::from_raw_parts(core::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - - let _slice: &[usize] = core::slice::from_raw_parts_mut(core::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - - core::ptr::copy::(core::ptr::NonNull::dangling().as_ptr(), core::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - core::ptr::copy::(core::ptr::NonNull::dangling().as_ptr(), core::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - - core::ptr::copy_nonoverlapping::(core::ptr::NonNull::dangling().as_ptr(), core::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - core::ptr::copy_nonoverlapping::(core::ptr::NonNull::dangling().as_ptr(), core::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - - struct A; // zero sized struct - assert_eq!(core::mem::size_of::(), 0); - - let _a: A = core::ptr::read(core::ptr::NonNull::dangling().as_ptr()); - //~^ invalid_null_ptr_usage - let _a: A = core::ptr::read(core::ptr::NonNull::dangling().as_ptr()); - //~^ invalid_null_ptr_usage - - let _a: A = core::ptr::read_unaligned(core::ptr::NonNull::dangling().as_ptr()); - //~^ invalid_null_ptr_usage - let _a: A = core::ptr::read_unaligned(core::ptr::NonNull::dangling().as_ptr()); - //~^ invalid_null_ptr_usage - - let _a: A = core::ptr::read_volatile(core::ptr::NonNull::dangling().as_ptr()); - //~^ invalid_null_ptr_usage - let _a: A = core::ptr::read_volatile(core::ptr::NonNull::dangling().as_ptr()); - //~^ invalid_null_ptr_usage - - let _a: A = core::ptr::replace(core::ptr::NonNull::dangling().as_ptr(), A); - //~^ invalid_null_ptr_usage - let _slice: *const [usize] = core::ptr::slice_from_raw_parts(core::ptr::null_mut(), 0); // shouldn't lint - let _slice: *const [usize] = core::ptr::slice_from_raw_parts_mut(core::ptr::null_mut(), 0); - - core::ptr::swap::(core::ptr::NonNull::dangling().as_ptr(), &mut A); - //~^ invalid_null_ptr_usage - core::ptr::swap::(&mut A, core::ptr::NonNull::dangling().as_ptr()); - //~^ invalid_null_ptr_usage - - core::ptr::swap_nonoverlapping::(core::ptr::NonNull::dangling().as_ptr(), &mut A, 0); - //~^ invalid_null_ptr_usage - core::ptr::swap_nonoverlapping::(&mut A, core::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - - core::ptr::write(core::ptr::NonNull::dangling().as_ptr(), A); - //~^ invalid_null_ptr_usage - - core::ptr::write_unaligned(core::ptr::NonNull::dangling().as_ptr(), A); - //~^ invalid_null_ptr_usage - - core::ptr::write_volatile(core::ptr::NonNull::dangling().as_ptr(), A); - //~^ invalid_null_ptr_usage - - core::ptr::write_bytes::(core::ptr::NonNull::dangling().as_ptr(), 42, 0); - //~^ invalid_null_ptr_usage - } -} diff --git a/tests/ui/invalid_null_ptr_usage_no_std.rs b/tests/ui/invalid_null_ptr_usage_no_std.rs deleted file mode 100644 index 38ddfff05535..000000000000 --- a/tests/ui/invalid_null_ptr_usage_no_std.rs +++ /dev/null @@ -1,79 +0,0 @@ -#![no_std] -#![feature(lang_items)] - -use core::panic::PanicInfo; - -#[lang = "eh_personality"] -extern "C" fn eh_personality() {} - -#[panic_handler] -fn panic(info: &PanicInfo) -> ! { - loop {} -} - -fn main() { - unsafe { - let _slice: &[usize] = core::slice::from_raw_parts(core::ptr::null(), 0); - //~^ invalid_null_ptr_usage - let _slice: &[usize] = core::slice::from_raw_parts(core::ptr::null_mut(), 0); - //~^ invalid_null_ptr_usage - - let _slice: &[usize] = core::slice::from_raw_parts_mut(core::ptr::null_mut(), 0); - //~^ invalid_null_ptr_usage - - core::ptr::copy::(core::ptr::null(), core::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - core::ptr::copy::(core::ptr::NonNull::dangling().as_ptr(), core::ptr::null_mut(), 0); - //~^ invalid_null_ptr_usage - - core::ptr::copy_nonoverlapping::(core::ptr::null(), core::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - core::ptr::copy_nonoverlapping::(core::ptr::NonNull::dangling().as_ptr(), core::ptr::null_mut(), 0); - //~^ invalid_null_ptr_usage - - struct A; // zero sized struct - assert_eq!(core::mem::size_of::(), 0); - - let _a: A = core::ptr::read(core::ptr::null()); - //~^ invalid_null_ptr_usage - let _a: A = core::ptr::read(core::ptr::null_mut()); - //~^ invalid_null_ptr_usage - - let _a: A = core::ptr::read_unaligned(core::ptr::null()); - //~^ invalid_null_ptr_usage - let _a: A = core::ptr::read_unaligned(core::ptr::null_mut()); - //~^ invalid_null_ptr_usage - - let _a: A = core::ptr::read_volatile(core::ptr::null()); - //~^ invalid_null_ptr_usage - let _a: A = core::ptr::read_volatile(core::ptr::null_mut()); - //~^ invalid_null_ptr_usage - - let _a: A = core::ptr::replace(core::ptr::null_mut(), A); - //~^ invalid_null_ptr_usage - let _slice: *const [usize] = core::ptr::slice_from_raw_parts(core::ptr::null_mut(), 0); // shouldn't lint - let _slice: *const [usize] = core::ptr::slice_from_raw_parts_mut(core::ptr::null_mut(), 0); - - core::ptr::swap::(core::ptr::null_mut(), &mut A); - //~^ invalid_null_ptr_usage - core::ptr::swap::(&mut A, core::ptr::null_mut()); - //~^ invalid_null_ptr_usage - - core::ptr::swap_nonoverlapping::(core::ptr::null_mut(), &mut A, 0); - //~^ invalid_null_ptr_usage - core::ptr::swap_nonoverlapping::(&mut A, core::ptr::null_mut(), 0); - //~^ invalid_null_ptr_usage - - core::ptr::write(core::ptr::null_mut(), A); - //~^ invalid_null_ptr_usage - - core::ptr::write_unaligned(core::ptr::null_mut(), A); - //~^ invalid_null_ptr_usage - - core::ptr::write_volatile(core::ptr::null_mut(), A); - //~^ invalid_null_ptr_usage - - core::ptr::write_bytes::(core::ptr::null_mut(), 42, 0); - //~^ invalid_null_ptr_usage - } -} diff --git a/tests/ui/invalid_null_ptr_usage_no_std.stderr b/tests/ui/invalid_null_ptr_usage_no_std.stderr deleted file mode 100644 index b5dd21ce6248..000000000000 --- a/tests/ui/invalid_null_ptr_usage_no_std.stderr +++ /dev/null @@ -1,136 +0,0 @@ -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:16:60 - | -LL | let _slice: &[usize] = core::slice::from_raw_parts(core::ptr::null(), 0); - | ^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - | - = note: `#[deny(clippy::invalid_null_ptr_usage)]` on by default - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:18:60 - | -LL | let _slice: &[usize] = core::slice::from_raw_parts(core::ptr::null_mut(), 0); - | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:21:64 - | -LL | let _slice: &[usize] = core::slice::from_raw_parts_mut(core::ptr::null_mut(), 0); - | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:24:34 - | -LL | core::ptr::copy::(core::ptr::null(), core::ptr::NonNull::dangling().as_ptr(), 0); - | ^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:26:75 - | -LL | core::ptr::copy::(core::ptr::NonNull::dangling().as_ptr(), core::ptr::null_mut(), 0); - | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:29:49 - | -LL | core::ptr::copy_nonoverlapping::(core::ptr::null(), core::ptr::NonNull::dangling().as_ptr(), 0); - | ^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:31:90 - | -LL | core::ptr::copy_nonoverlapping::(core::ptr::NonNull::dangling().as_ptr(), core::ptr::null_mut(), 0); - | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:37:37 - | -LL | let _a: A = core::ptr::read(core::ptr::null()); - | ^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:39:37 - | -LL | let _a: A = core::ptr::read(core::ptr::null_mut()); - | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:42:47 - | -LL | let _a: A = core::ptr::read_unaligned(core::ptr::null()); - | ^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:44:47 - | -LL | let _a: A = core::ptr::read_unaligned(core::ptr::null_mut()); - | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:47:46 - | -LL | let _a: A = core::ptr::read_volatile(core::ptr::null()); - | ^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:49:46 - | -LL | let _a: A = core::ptr::read_volatile(core::ptr::null_mut()); - | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:52:40 - | -LL | let _a: A = core::ptr::replace(core::ptr::null_mut(), A); - | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:57:30 - | -LL | core::ptr::swap::(core::ptr::null_mut(), &mut A); - | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:59:38 - | -LL | core::ptr::swap::(&mut A, core::ptr::null_mut()); - | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:62:45 - | -LL | core::ptr::swap_nonoverlapping::(core::ptr::null_mut(), &mut A, 0); - | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:64:53 - | -LL | core::ptr::swap_nonoverlapping::(&mut A, core::ptr::null_mut(), 0); - | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:67:26 - | -LL | core::ptr::write(core::ptr::null_mut(), A); - | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:70:36 - | -LL | core::ptr::write_unaligned(core::ptr::null_mut(), A); - | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:73:35 - | -LL | core::ptr::write_volatile(core::ptr::null_mut(), A); - | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:76:41 - | -LL | core::ptr::write_bytes::(core::ptr::null_mut(), 42, 0); - | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: aborting due to 22 previous errors - diff --git a/tests/ui/rename.fixed b/tests/ui/rename.fixed index 501811fa491b..796404706968 100644 --- a/tests/ui/rename.fixed +++ b/tests/ui/rename.fixed @@ -119,6 +119,7 @@ #![warn(invalid_atomic_ordering)] //~ ERROR: lint `clippy::invalid_atomic_ordering` #![warn(invalid_value)] //~ ERROR: lint `clippy::invalid_ref` #![warn(invalid_from_utf8_unchecked)] //~ ERROR: lint `clippy::invalid_utf8_in_unchecked` +#![warn(invalid_null_arguments)] //~ ERROR: lint `clippy::invalid_null_ptr_usage` #![warn(let_underscore_drop)] //~ ERROR: lint `clippy::let_underscore_drop` #![warn(unexpected_cfgs)] //~ ERROR: lint `clippy::maybe_misused_cfg` #![warn(enum_intrinsics_non_enums)] //~ ERROR: lint `clippy::mem_discriminant_non_enum` diff --git a/tests/ui/rename.rs b/tests/ui/rename.rs index 7f4b8062e1b4..aa7b905b4b81 100644 --- a/tests/ui/rename.rs +++ b/tests/ui/rename.rs @@ -119,6 +119,7 @@ #![warn(clippy::invalid_atomic_ordering)] //~ ERROR: lint `clippy::invalid_atomic_ordering` #![warn(clippy::invalid_ref)] //~ ERROR: lint `clippy::invalid_ref` #![warn(clippy::invalid_utf8_in_unchecked)] //~ ERROR: lint `clippy::invalid_utf8_in_unchecked` +#![warn(clippy::invalid_null_ptr_usage)] //~ ERROR: lint `clippy::invalid_null_ptr_usage` #![warn(clippy::let_underscore_drop)] //~ ERROR: lint `clippy::let_underscore_drop` #![warn(clippy::maybe_misused_cfg)] //~ ERROR: lint `clippy::maybe_misused_cfg` #![warn(clippy::mem_discriminant_non_enum)] //~ ERROR: lint `clippy::mem_discriminant_non_enum` diff --git a/tests/ui/rename.stderr b/tests/ui/rename.stderr index f24eaec3917a..b3c88167c111 100644 --- a/tests/ui/rename.stderr +++ b/tests/ui/rename.stderr @@ -343,71 +343,77 @@ error: lint `clippy::invalid_utf8_in_unchecked` has been renamed to `invalid_fro LL | #![warn(clippy::invalid_utf8_in_unchecked)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_from_utf8_unchecked` -error: lint `clippy::let_underscore_drop` has been renamed to `let_underscore_drop` +error: lint `clippy::invalid_null_ptr_usage` has been renamed to `invalid_null_arguments` --> tests/ui/rename.rs:122:9 | +LL | #![warn(clippy::invalid_null_ptr_usage)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_null_arguments` + +error: lint `clippy::let_underscore_drop` has been renamed to `let_underscore_drop` + --> tests/ui/rename.rs:123:9 + | LL | #![warn(clippy::let_underscore_drop)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `let_underscore_drop` error: lint `clippy::maybe_misused_cfg` has been renamed to `unexpected_cfgs` - --> tests/ui/rename.rs:123:9 + --> tests/ui/rename.rs:124:9 | LL | #![warn(clippy::maybe_misused_cfg)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unexpected_cfgs` error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` - --> tests/ui/rename.rs:124:9 + --> tests/ui/rename.rs:125:9 | LL | #![warn(clippy::mem_discriminant_non_enum)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` error: lint `clippy::mismatched_target_os` has been renamed to `unexpected_cfgs` - --> tests/ui/rename.rs:125:9 + --> tests/ui/rename.rs:126:9 | LL | #![warn(clippy::mismatched_target_os)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unexpected_cfgs` error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` - --> tests/ui/rename.rs:126:9 + --> tests/ui/rename.rs:127:9 | LL | #![warn(clippy::panic_params)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics` error: lint `clippy::positional_named_format_parameters` has been renamed to `named_arguments_used_positionally` - --> tests/ui/rename.rs:127:9 + --> tests/ui/rename.rs:128:9 | LL | #![warn(clippy::positional_named_format_parameters)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally` error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `dangling_pointers_from_temporaries` - --> tests/ui/rename.rs:128:9 + --> tests/ui/rename.rs:129:9 | LL | #![warn(clippy::temporary_cstring_as_ptr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `dangling_pointers_from_temporaries` error: lint `clippy::undropped_manually_drops` has been renamed to `undropped_manually_drops` - --> tests/ui/rename.rs:129:9 + --> tests/ui/rename.rs:130:9 | LL | #![warn(clippy::undropped_manually_drops)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `undropped_manually_drops` error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints` - --> tests/ui/rename.rs:130:9 + --> tests/ui/rename.rs:131:9 | LL | #![warn(clippy::unknown_clippy_lints)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints` error: lint `clippy::unused_label` has been renamed to `unused_labels` - --> tests/ui/rename.rs:131:9 + --> tests/ui/rename.rs:132:9 | LL | #![warn(clippy::unused_label)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` error: lint `clippy::vtable_address_comparisons` has been renamed to `ambiguous_wide_pointer_comparisons` - --> tests/ui/rename.rs:132:9 + --> tests/ui/rename.rs:133:9 | LL | #![warn(clippy::vtable_address_comparisons)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `ambiguous_wide_pointer_comparisons` -error: aborting due to 68 previous errors +error: aborting due to 69 previous errors From a50fb2248a02368888e18ae6e13410c8617313e2 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 27 Mar 2025 22:56:13 +1100 Subject: [PATCH 12/19] Avoid `kw::Empty` use for `AuxParamsAttr`. By changing two of the fields to use `Option` instead of `Ident`. As a result, `None` now means "no identifier", which is much clearer than using an empty identifier. --- .../src/significant_drop_tightening.rs | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/clippy_lints/src/significant_drop_tightening.rs b/clippy_lints/src/significant_drop_tightening.rs index e9db7c9d031a..76874cc34206 100644 --- a/clippy_lints/src/significant_drop_tightening.rs +++ b/clippy_lints/src/significant_drop_tightening.rs @@ -79,10 +79,11 @@ impl<'tcx> LateLintPass<'tcx> for SignificantDropTightening<'tcx> { if apa.counter <= 1 || !apa.has_expensive_expr_after_last_attr { continue; } + let first_bind_ident = apa.first_bind_ident.unwrap(); span_lint_and_then( cx, SIGNIFICANT_DROP_TIGHTENING, - apa.first_bind_ident.span, + first_bind_ident.span, "temporary with significant `Drop` can be early dropped", |diag| { match apa.counter { @@ -91,13 +92,13 @@ impl<'tcx> LateLintPass<'tcx> for SignificantDropTightening<'tcx> { let indent = " ".repeat(indent_of(cx, apa.last_stmt_span).unwrap_or(0)); let init_method = snippet(cx, apa.first_method_span, ".."); let usage_method = snippet(cx, apa.last_method_span, ".."); - let stmt = if apa.last_bind_ident == Ident::empty() { - format!("\n{indent}{init_method}.{usage_method};") - } else { + let stmt = if let Some(last_bind_ident) = apa.last_bind_ident { format!( "\n{indent}let {} = {init_method}.{usage_method};", - snippet(cx, apa.last_bind_ident.span, ".."), + snippet(cx, last_bind_ident.span, ".."), ) + } else { + format!("\n{indent}{init_method}.{usage_method};") }; diag.multipart_suggestion_verbose( @@ -113,7 +114,7 @@ impl<'tcx> LateLintPass<'tcx> for SignificantDropTightening<'tcx> { format!( "\n{}drop({});", " ".repeat(indent_of(cx, apa.last_stmt_span).unwrap_or(0)), - apa.first_bind_ident + first_bind_ident ), Applicability::MaybeIncorrect, ); @@ -124,7 +125,7 @@ impl<'tcx> LateLintPass<'tcx> for SignificantDropTightening<'tcx> { apa.first_block_span, format!( "temporary `{}` is currently being dropped at the end of its contained scope", - apa.first_bind_ident + first_bind_ident ), ); }, @@ -283,7 +284,7 @@ impl<'tcx> Visitor<'tcx> for StmtsChecker<'_, '_, '_, '_, 'tcx> { let mut apa = AuxParamsAttr { first_block_hir_id: self.ap.curr_block_hir_id, first_block_span: self.ap.curr_block_span, - first_bind_ident: ident, + first_bind_ident: Some(ident), first_method_span: { let expr_or_init = expr_or_init(self.cx, expr); if let hir::ExprKind::MethodCall(_, local_expr, _, span) = expr_or_init.kind { @@ -307,7 +308,7 @@ impl<'tcx> Visitor<'tcx> for StmtsChecker<'_, '_, '_, '_, 'tcx> { match self.ap.curr_stmt.kind { hir::StmtKind::Let(local) => { if let hir::PatKind::Binding(_, _, ident, _) = local.pat.kind { - apa.last_bind_ident = ident; + apa.last_bind_ident = Some(ident); } if let Some(local_init) = local.init && let hir::ExprKind::MethodCall(_, _, _, span) = local_init.kind @@ -373,7 +374,7 @@ struct AuxParamsAttr { first_block_span: Span, /// The binding or variable that references the initial construction of the type marked with /// `#[has_significant_drop]`. - first_bind_ident: Ident, + first_bind_ident: Option, /// Similar to `init_bind_ident` but encompasses the right-hand method call. first_method_span: Span, /// Similar to `init_bind_ident` but encompasses the whole contained statement. @@ -381,7 +382,7 @@ struct AuxParamsAttr { /// The last visited binding or variable span within a block that had any referenced inner type /// marked with `#[has_significant_drop]`. - last_bind_ident: Ident, + last_bind_ident: Option, /// Similar to `last_bind_span` but encompasses the right-hand method call. last_method_span: Span, /// Similar to `last_bind_span` but encompasses the whole contained statement. @@ -395,10 +396,10 @@ impl Default for AuxParamsAttr { has_expensive_expr_after_last_attr: false, first_block_hir_id: HirId::INVALID, first_block_span: DUMMY_SP, - first_bind_ident: Ident::empty(), + first_bind_ident: None, first_method_span: DUMMY_SP, first_stmt_span: DUMMY_SP, - last_bind_ident: Ident::empty(), + last_bind_ident: None, last_method_span: DUMMY_SP, last_stmt_span: DUMMY_SP, } @@ -413,7 +414,7 @@ fn dummy_stmt_expr<'any>(expr: &'any hir::Expr<'any>) -> hir::Stmt<'any> { } } -fn has_drop(expr: &hir::Expr<'_>, first_bind_ident: &Ident, lcx: &LateContext<'_>) -> bool { +fn has_drop(expr: &hir::Expr<'_>, first_bind_ident: &Option, lcx: &LateContext<'_>) -> bool { if let hir::ExprKind::Call(fun, [first_arg]) = expr.kind && let hir::ExprKind::Path(hir::QPath::Resolved(_, fun_path)) = &fun.kind && let Res::Def(DefKind::Fn, did) = fun_path.res @@ -422,6 +423,7 @@ fn has_drop(expr: &hir::Expr<'_>, first_bind_ident: &Ident, lcx: &LateContext<'_ let has_ident = |local_expr: &hir::Expr<'_>| { if let hir::ExprKind::Path(hir::QPath::Resolved(_, arg_path)) = &local_expr.kind && let [first_arg_ps, ..] = arg_path.segments + && let Some(first_bind_ident) = first_bind_ident && &first_arg_ps.ident == first_bind_ident { true From 5101c8e87f6e307c618576f76281e011372a65a4 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 21 Mar 2025 09:47:43 +1100 Subject: [PATCH 13/19] Move `ast::Item::ident` into `ast::ItemKind`. `ast::Item` has an `ident` field. - It's always non-empty for these item kinds: `ExternCrate`, `Static`, `Const`, `Fn`, `Mod`, `TyAlias`, `Enum`, `Struct`, `Union`, `Trait`, `TraitAlias`, `MacroDef`, `Delegation`. - It's always empty for these item kinds: `Use`, `ForeignMod`, `GlobalAsm`, `Impl`, `MacCall`, `DelegationMac`. There is a similar story for `AssocItemKind` and `ForeignItemKind`. Some sites that handle items check for an empty ident, some don't. This is a very C-like way of doing things, but this is Rust, we have sum types, we can do this properly and never forget to check for the exceptional case and never YOLO possibly empty identifiers (or possibly dummy spans) around and hope that things will work out. The commit is large but it's mostly obvious plumbing work. Some notable things. - `ast::Item` got 8 bytes bigger. This could be avoided by boxing the fields within some of the `ast::ItemKind` variants (specifically: `Struct`, `Union`, `Enum`). I might do that in a follow-up; this commit is big enough already. - For the visitors: `FnKind` no longer needs an `ident` field because the `Fn` within how has one. - In the parser, the `ItemInfo` typedef is no longer needed. It was used in various places to return an `Ident` alongside an `ItemKind`, but now the `Ident` (if present) is within the `ItemKind`. - In a few places I renamed identifier variables called `name` (or `foo_name`) as `ident` (or `foo_ident`), to better match the type, and because `name` is normally used for `Symbol`s. It's confusing to see something like `foo_name.name`. --- clippy_lints/src/crate_in_macro_def.rs | 2 +- clippy_lints/src/doc/needless_doctest_main.rs | 16 ++-- clippy_lints/src/duplicate_mod.rs | 2 +- clippy_lints/src/empty_line_after.rs | 16 ++-- clippy_lints/src/empty_with_brackets.rs | 5 +- .../src/field_scoped_visibility_modifiers.rs | 2 +- clippy_lints/src/multiple_bound_locations.rs | 2 +- clippy_lints/src/partial_pub_fields.rs | 2 +- .../src/single_component_path_imports.rs | 6 +- clippy_utils/src/ast_utils/mod.rs | 79 +++++++++++++++---- 10 files changed, 89 insertions(+), 43 deletions(-) diff --git a/clippy_lints/src/crate_in_macro_def.rs b/clippy_lints/src/crate_in_macro_def.rs index 7d86bd3e540a..c2aac7ca090b 100644 --- a/clippy_lints/src/crate_in_macro_def.rs +++ b/clippy_lints/src/crate_in_macro_def.rs @@ -53,7 +53,7 @@ declare_lint_pass!(CrateInMacroDef => [CRATE_IN_MACRO_DEF]); impl EarlyLintPass for CrateInMacroDef { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - if let ItemKind::MacroDef(macro_def) = &item.kind + if let ItemKind::MacroDef(_, macro_def) = &item.kind && item.attrs.iter().any(is_macro_export) && let Some(span) = contains_unhygienic_crate_reference(¯o_def.body.tokens) { diff --git a/clippy_lints/src/doc/needless_doctest_main.rs b/clippy_lints/src/doc/needless_doctest_main.rs index 3008082c2329..f6c10da1596b 100644 --- a/clippy_lints/src/doc/needless_doctest_main.rs +++ b/clippy_lints/src/doc/needless_doctest_main.rs @@ -13,16 +13,16 @@ use rustc_parse::parser::ForceCollect; use rustc_session::parse::ParseSess; use rustc_span::edition::Edition; use rustc_span::source_map::{FilePathMapping, SourceMap}; -use rustc_span::{FileName, Pos, sym}; +use rustc_span::{FileName, Ident, Pos, sym}; use super::Fragments; -fn get_test_spans(item: &Item, test_attr_spans: &mut Vec>) { +fn get_test_spans(item: &Item, ident: Ident, test_attr_spans: &mut Vec>) { test_attr_spans.extend( item.attrs .iter() .find(|attr| attr.has_name(sym::test)) - .map(|attr| attr.span.lo().to_usize()..item.ident.span.hi().to_usize()), + .map(|attr| attr.span.lo().to_usize()..ident.span.hi().to_usize()), ); } @@ -64,10 +64,10 @@ pub fn check( match parser.parse_item(ForceCollect::No) { Ok(Some(item)) => match &item.kind { ItemKind::Fn(box Fn { - sig, body: Some(block), .. - }) if item.ident.name == sym::main => { + ident, sig, body: Some(block), .. + }) if ident.name == sym::main => { if !ignore { - get_test_spans(&item, &mut test_attr_spans); + get_test_spans(&item, *ident, &mut test_attr_spans); } let is_async = matches!(sig.header.coroutine_kind, Some(CoroutineKind::Async { .. })); let returns_nothing = match &sig.decl.output { @@ -85,10 +85,10 @@ pub fn check( } }, // Another function was found; this case is ignored for needless_doctest_main - ItemKind::Fn(box Fn { .. }) => { + ItemKind::Fn(fn_) => { eligible = false; if !ignore { - get_test_spans(&item, &mut test_attr_spans); + get_test_spans(&item, fn_.ident, &mut test_attr_spans); } }, // Tests with one of these items are ignored diff --git a/clippy_lints/src/duplicate_mod.rs b/clippy_lints/src/duplicate_mod.rs index 1dac7b971f95..243c99a19ce1 100644 --- a/clippy_lints/src/duplicate_mod.rs +++ b/clippy_lints/src/duplicate_mod.rs @@ -63,7 +63,7 @@ impl_lint_pass!(DuplicateMod => [DUPLICATE_MOD]); impl EarlyLintPass for DuplicateMod { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - if let ItemKind::Mod(_, ModKind::Loaded(_, Inline::No, mod_spans, _)) = &item.kind + if let ItemKind::Mod(_, _, ModKind::Loaded(_, Inline::No, mod_spans, _)) = &item.kind && let FileName::Real(real) = cx.sess().source_map().span_to_filename(mod_spans.inner_span) && let Some(local_path) = real.into_local_path() && let Ok(absolute_path) = local_path.canonicalize() diff --git a/clippy_lints/src/empty_line_after.rs b/clippy_lints/src/empty_line_after.rs index 80c2b03c41cf..899b5c595261 100644 --- a/clippy_lints/src/empty_line_after.rs +++ b/clippy_lints/src/empty_line_after.rs @@ -9,7 +9,7 @@ use rustc_lexer::TokenKind; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_session::impl_lint_pass; use rustc_span::symbol::kw; -use rustc_span::{BytePos, ExpnKind, Ident, InnerSpan, Span, SpanData, Symbol}; +use rustc_span::{BytePos, ExpnKind, Ident, InnerSpan, Span, SpanData, Symbol, sym}; declare_clippy_lint! { /// ### What it does @@ -375,21 +375,21 @@ impl EmptyLineAfter { &mut self, cx: &EarlyContext<'_>, kind: &ItemKind, - ident: &Ident, + ident: &Option, span: Span, attrs: &[Attribute], id: NodeId, ) { self.items.push(ItemInfo { kind: kind.descr(), - name: ident.name, - span: if span.contains(ident.span) { + name: if let Some(ident) = ident { ident.name } else { sym::dummy }, + span: if let Some(ident) = ident { span.with_hi(ident.span.hi()) } else { span.with_hi(span.lo()) }, mod_items: match kind { - ItemKind::Mod(_, ModKind::Loaded(items, _, _, _)) => items + ItemKind::Mod(_, _, ModKind::Loaded(items, _, _, _)) => items .iter() .filter(|i| !matches!(i.span.ctxt().outer_expn_data().kind, ExpnKind::AstPass(_))) .map(|i| i.id) @@ -471,7 +471,7 @@ impl EarlyLintPass for EmptyLineAfter { self.check_item_kind( cx, &item.kind.clone().into(), - &item.ident, + &item.kind.ident(), item.span, &item.attrs, item.id, @@ -482,7 +482,7 @@ impl EarlyLintPass for EmptyLineAfter { self.check_item_kind( cx, &item.kind.clone().into(), - &item.ident, + &item.kind.ident(), item.span, &item.attrs, item.id, @@ -490,6 +490,6 @@ impl EarlyLintPass for EmptyLineAfter { } fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - self.check_item_kind(cx, &item.kind, &item.ident, item.span, &item.attrs, item.id); + self.check_item_kind(cx, &item.kind, &item.kind.ident(), item.span, &item.attrs, item.id); } } diff --git a/clippy_lints/src/empty_with_brackets.rs b/clippy_lints/src/empty_with_brackets.rs index 743ec5b9ea7f..7d87f04fef9d 100644 --- a/clippy_lints/src/empty_with_brackets.rs +++ b/clippy_lints/src/empty_with_brackets.rs @@ -74,10 +74,9 @@ declare_lint_pass!(EmptyWithBrackets => [EMPTY_STRUCTS_WITH_BRACKETS, EMPTY_ENUM impl EarlyLintPass for EmptyWithBrackets { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - let span_after_ident = item.span.with_lo(item.ident.span.hi()); - - if let ItemKind::Struct(var_data, _) = &item.kind + if let ItemKind::Struct(ident, var_data, _) = &item.kind && has_brackets(var_data) + && let span_after_ident = item.span.with_lo(ident.span.hi()) && has_no_fields(cx, var_data, span_after_ident) { span_lint_and_then( diff --git a/clippy_lints/src/field_scoped_visibility_modifiers.rs b/clippy_lints/src/field_scoped_visibility_modifiers.rs index ba2b37fbf11a..aae8291905d3 100644 --- a/clippy_lints/src/field_scoped_visibility_modifiers.rs +++ b/clippy_lints/src/field_scoped_visibility_modifiers.rs @@ -51,7 +51,7 @@ declare_lint_pass!(FieldScopedVisibilityModifiers => [FIELD_SCOPED_VISIBILITY_MO impl EarlyLintPass for FieldScopedVisibilityModifiers { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - let ItemKind::Struct(ref st, _) = item.kind else { + let ItemKind::Struct(_, ref st, _) = item.kind else { return; }; for field in st.fields() { diff --git a/clippy_lints/src/multiple_bound_locations.rs b/clippy_lints/src/multiple_bound_locations.rs index 0e1980a6acb6..4b32ba83b325 100644 --- a/clippy_lints/src/multiple_bound_locations.rs +++ b/clippy_lints/src/multiple_bound_locations.rs @@ -39,7 +39,7 @@ declare_lint_pass!(MultipleBoundLocations => [MULTIPLE_BOUND_LOCATIONS]); impl EarlyLintPass for MultipleBoundLocations { fn check_fn(&mut self, cx: &EarlyContext<'_>, kind: FnKind<'_>, _: Span, _: NodeId) { - if let FnKind::Fn(_, _, _, Fn { generics, .. }) = kind + if let FnKind::Fn(_, _, Fn { generics, .. }) = kind && !generics.params.is_empty() && !generics.where_clause.predicates.is_empty() { diff --git a/clippy_lints/src/partial_pub_fields.rs b/clippy_lints/src/partial_pub_fields.rs index 267e2067e101..cda752d003fa 100644 --- a/clippy_lints/src/partial_pub_fields.rs +++ b/clippy_lints/src/partial_pub_fields.rs @@ -41,7 +41,7 @@ declare_lint_pass!(PartialPubFields => [PARTIAL_PUB_FIELDS]); impl EarlyLintPass for PartialPubFields { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - let ItemKind::Struct(ref st, _) = item.kind else { + let ItemKind::Struct(_, ref st, _) = item.kind else { return; }; diff --git a/clippy_lints/src/single_component_path_imports.rs b/clippy_lints/src/single_component_path_imports.rs index fa0824535042..35f80b2acda6 100644 --- a/clippy_lints/src/single_component_path_imports.rs +++ b/clippy_lints/src/single_component_path_imports.rs @@ -174,11 +174,11 @@ impl SingleComponentPathImports { } match &item.kind { - ItemKind::Mod(_, ModKind::Loaded(items, ..)) => { + ItemKind::Mod(_, _, ModKind::Loaded(items, ..)) => { self.check_mod(items); }, - ItemKind::MacroDef(MacroDef { macro_rules: true, .. }) => { - macros.push(item.ident.name); + ItemKind::MacroDef(ident, MacroDef { macro_rules: true, .. }) => { + macros.push(ident.name); }, ItemKind::Use(use_tree) => { let segments = &use_tree.prefix.segments; diff --git a/clippy_utils/src/ast_utils/mod.rs b/clippy_utils/src/ast_utils/mod.rs index 6023ae9cc7b1..ff63eb505a5c 100644 --- a/clippy_utils/src/ast_utils/mod.rs +++ b/clippy_utils/src/ast_utils/mod.rs @@ -321,17 +321,18 @@ pub fn eq_local_kind(l: &LocalKind, r: &LocalKind) -> bool { } pub fn eq_item(l: &Item, r: &Item, mut eq_kind: impl FnMut(&K, &K) -> bool) -> bool { - eq_id(l.ident, r.ident) && over(&l.attrs, &r.attrs, eq_attr) && eq_vis(&l.vis, &r.vis) && eq_kind(&l.kind, &r.kind) + over(&l.attrs, &r.attrs, eq_attr) && eq_vis(&l.vis, &r.vis) && eq_kind(&l.kind, &r.kind) } #[expect(clippy::similar_names, clippy::too_many_lines)] // Just a big match statement pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { use ItemKind::*; match (l, r) { - (ExternCrate(l), ExternCrate(r)) => l == r, + (ExternCrate(ls, li), ExternCrate(rs, ri)) => ls == rs && eq_id(*li, *ri), (Use(l), Use(r)) => eq_use_tree(l, r), ( Static(box StaticItem { + ident: li, ty: lt, mutability: lm, expr: le, @@ -339,16 +340,22 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { define_opaque: _, }), Static(box StaticItem { + ident: ri, ty: rt, mutability: rm, expr: re, safety: rs, define_opaque: _, }), - ) => lm == rm && ls == rs && eq_ty(lt, rt) && eq_expr_opt(le.as_ref(), re.as_ref()), + ) => eq_id(*li, *ri) + && lm == rm + && ls == rs + && eq_ty(lt, rt) + && eq_expr_opt(le.as_ref(), re.as_ref()), ( Const(box ConstItem { defaultness: ld, + ident: li, generics: lg, ty: lt, expr: le, @@ -356,16 +363,22 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { }), Const(box ConstItem { defaultness: rd, + ident: ri, generics: rg, ty: rt, expr: re, define_opaque: _, }), - ) => eq_defaultness(*ld, *rd) && eq_generics(lg, rg) && eq_ty(lt, rt) && eq_expr_opt(le.as_ref(), re.as_ref()), + ) => eq_defaultness(*ld, *rd) + && eq_id(*li, *ri) + && eq_generics(lg, rg) + && eq_ty(lt, rt) + && eq_expr_opt(le.as_ref(), re.as_ref()), ( Fn(box ast::Fn { defaultness: ld, sig: lf, + ident: li, generics: lg, contract: lc, body: lb, @@ -374,6 +387,7 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { Fn(box ast::Fn { defaultness: rd, sig: rf, + ident: ri, generics: rg, contract: rc, body: rb, @@ -382,12 +396,14 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { ) => { eq_defaultness(*ld, *rd) && eq_fn_sig(lf, rf) + && eq_id(*li, *ri) && eq_generics(lg, rg) && eq_opt_fn_contract(lc, rc) && both(lb.as_ref(), rb.as_ref(), |l, r| eq_block(l, r)) }, - (Mod(lu, lmk), Mod(ru, rmk)) => { - lu == ru + (Mod(ls, li, lmk), Mod(rs, ri, rmk)) => { + ls == rs + && eq_id(*li, *ri) && match (lmk, rmk) { (ModKind::Loaded(litems, linline, _, _), ModKind::Loaded(ritems, rinline, _, _)) => { linline == rinline && over(litems, ritems, |l, r| eq_item(l, r, eq_item_kind)) @@ -421,33 +437,40 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { && over(lb, rb, eq_generic_bound) && both(lt.as_ref(), rt.as_ref(), |l, r| eq_ty(l, r)) }, - (Enum(le, lg), Enum(re, rg)) => over(&le.variants, &re.variants, eq_variant) && eq_generics(lg, rg), - (Struct(lv, lg), Struct(rv, rg)) | (Union(lv, lg), Union(rv, rg)) => { - eq_variant_data(lv, rv) && eq_generics(lg, rg) + (Enum(li, le, lg), Enum(ri, re, rg)) => { + eq_id(*li, *ri) && over(&le.variants, &re.variants, eq_variant) && eq_generics(lg, rg) + } + (Struct(li, lv, lg), Struct(ri, rv, rg)) | (Union(li, lv, lg), Union(ri, rv, rg)) => { + eq_id(*li, *ri) && eq_variant_data(lv, rv) && eq_generics(lg, rg) }, ( Trait(box ast::Trait { is_auto: la, safety: lu, + ident: li, generics: lg, bounds: lb, - items: li, + items: lis, }), Trait(box ast::Trait { is_auto: ra, safety: ru, + ident: ri, generics: rg, bounds: rb, - items: ri, + items: ris, }), ) => { la == ra && matches!(lu, Safety::Default) == matches!(ru, Safety::Default) + && eq_id(*li, *ri) && eq_generics(lg, rg) && over(lb, rb, eq_generic_bound) - && over(li, ri, |l, r| eq_item(l, r, eq_assoc_item_kind)) + && over(lis, ris, |l, r| eq_item(l, r, eq_assoc_item_kind)) }, - (TraitAlias(lg, lb), TraitAlias(rg, rb)) => eq_generics(lg, rg) && over(lb, rb, eq_generic_bound), + (TraitAlias(li, lg, lb), TraitAlias(ri, rg, rb)) => { + eq_id(*li, *ri) && eq_generics(lg, rg) && over(lb, rb, eq_generic_bound) + } ( Impl(box ast::Impl { safety: lu, @@ -480,7 +503,9 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { && over(li, ri, |l, r| eq_item(l, r, eq_assoc_item_kind)) }, (MacCall(l), MacCall(r)) => eq_mac_call(l, r), - (MacroDef(l), MacroDef(r)) => l.macro_rules == r.macro_rules && eq_delim_args(&l.body, &r.body), + (MacroDef(li, ld), MacroDef(ri, rd)) => { + eq_id(*li, *ri) && ld.macro_rules == rd.macro_rules && eq_delim_args(&ld.body, &rd.body) + } _ => false, } } @@ -490,6 +515,7 @@ pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool { match (l, r) { ( Static(box StaticItem { + ident: li, ty: lt, mutability: lm, expr: le, @@ -497,17 +523,25 @@ pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool { define_opaque: _, }), Static(box StaticItem { + ident: ri, ty: rt, mutability: rm, expr: re, safety: rs, define_opaque: _, }), - ) => lm == rm && eq_ty(lt, rt) && eq_expr_opt(le.as_ref(), re.as_ref()) && ls == rs, + ) => { + eq_id(*li, *ri) + && eq_ty(lt, rt) + && lm == rm + && eq_expr_opt(le.as_ref(), re.as_ref()) + && ls == rs + } ( Fn(box ast::Fn { defaultness: ld, sig: lf, + ident: li, generics: lg, contract: lc, body: lb, @@ -516,6 +550,7 @@ pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool { Fn(box ast::Fn { defaultness: rd, sig: rf, + ident: ri, generics: rg, contract: rc, body: rb, @@ -524,6 +559,7 @@ pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool { ) => { eq_defaultness(*ld, *rd) && eq_fn_sig(lf, rf) + && eq_id(*li, *ri) && eq_generics(lg, rg) && eq_opt_fn_contract(lc, rc) && both(lb.as_ref(), rb.as_ref(), |l, r| eq_block(l, r)) @@ -560,6 +596,7 @@ pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool { ( Const(box ConstItem { defaultness: ld, + ident: li, generics: lg, ty: lt, expr: le, @@ -567,16 +604,24 @@ pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool { }), Const(box ConstItem { defaultness: rd, + ident: ri, generics: rg, ty: rt, expr: re, define_opaque: _, }), - ) => eq_defaultness(*ld, *rd) && eq_generics(lg, rg) && eq_ty(lt, rt) && eq_expr_opt(le.as_ref(), re.as_ref()), + ) => { + eq_defaultness(*ld, *rd) + && eq_id(*li, *ri) + && eq_generics(lg, rg) + && eq_ty(lt, rt) + && eq_expr_opt(le.as_ref(), re.as_ref()) + } ( Fn(box ast::Fn { defaultness: ld, sig: lf, + ident: li, generics: lg, contract: lc, body: lb, @@ -585,6 +630,7 @@ pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool { Fn(box ast::Fn { defaultness: rd, sig: rf, + ident: ri, generics: rg, contract: rc, body: rb, @@ -593,6 +639,7 @@ pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool { ) => { eq_defaultness(*ld, *rd) && eq_fn_sig(lf, rf) + && eq_id(*li, *ri) && eq_generics(lg, rg) && eq_opt_fn_contract(lc, rc) && both(lb.as_ref(), rb.as_ref(), |l, r| eq_block(l, r)) From 3f752b45eb232a2126500e0146d13eb6761dcc74 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 1 Apr 2025 14:49:58 +1100 Subject: [PATCH 14/19] Address review comments. --- clippy_lints/src/empty_line_after.rs | 15 ++++++++------- clippy_utils/src/ast_utils/mod.rs | 14 ++++++++++---- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/clippy_lints/src/empty_line_after.rs b/clippy_lints/src/empty_line_after.rs index 899b5c595261..c67dcd3c82b5 100644 --- a/clippy_lints/src/empty_line_after.rs +++ b/clippy_lints/src/empty_line_after.rs @@ -8,8 +8,7 @@ use rustc_errors::{Applicability, Diag, SuggestionStyle}; use rustc_lexer::TokenKind; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_session::impl_lint_pass; -use rustc_span::symbol::kw; -use rustc_span::{BytePos, ExpnKind, Ident, InnerSpan, Span, SpanData, Symbol, sym}; +use rustc_span::{BytePos, ExpnKind, Ident, InnerSpan, Span, SpanData, Symbol, kw}; declare_clippy_lint! { /// ### What it does @@ -375,14 +374,16 @@ impl EmptyLineAfter { &mut self, cx: &EarlyContext<'_>, kind: &ItemKind, - ident: &Option, + ident: Option, span: Span, attrs: &[Attribute], id: NodeId, ) { self.items.push(ItemInfo { kind: kind.descr(), - name: if let Some(ident) = ident { ident.name } else { sym::dummy }, + // FIXME: this `sym::empty` can be leaked, see + // https://github.com/rust-lang/rust/pull/138740#discussion_r2021979899 + name: if let Some(ident) = ident { ident.name } else { kw::Empty }, span: if let Some(ident) = ident { span.with_hi(ident.span.hi()) } else { @@ -471,7 +472,7 @@ impl EarlyLintPass for EmptyLineAfter { self.check_item_kind( cx, &item.kind.clone().into(), - &item.kind.ident(), + item.kind.ident(), item.span, &item.attrs, item.id, @@ -482,7 +483,7 @@ impl EarlyLintPass for EmptyLineAfter { self.check_item_kind( cx, &item.kind.clone().into(), - &item.kind.ident(), + item.kind.ident(), item.span, &item.attrs, item.id, @@ -490,6 +491,6 @@ impl EarlyLintPass for EmptyLineAfter { } fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - self.check_item_kind(cx, &item.kind, &item.kind.ident(), item.span, &item.attrs, item.id); + self.check_item_kind(cx, &item.kind, item.kind.ident(), item.span, &item.attrs, item.id); } } diff --git a/clippy_utils/src/ast_utils/mod.rs b/clippy_utils/src/ast_utils/mod.rs index ff63eb505a5c..eba576392ebc 100644 --- a/clippy_utils/src/ast_utils/mod.rs +++ b/clippy_utils/src/ast_utils/mod.rs @@ -567,20 +567,23 @@ pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool { ( TyAlias(box ast::TyAlias { defaultness: ld, + ident: li, generics: lg, + where_clauses: _, bounds: lb, ty: lt, - .. }), TyAlias(box ast::TyAlias { defaultness: rd, + ident: ri, generics: rg, + where_clauses: _, bounds: rb, ty: rt, - .. }), ) => { eq_defaultness(*ld, *rd) + && eq_id(*li, *ri) && eq_generics(lg, rg) && over(lb, rb, eq_generic_bound) && both(lt.as_ref(), rt.as_ref(), |l, r| eq_ty(l, r)) @@ -647,20 +650,23 @@ pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool { ( Type(box TyAlias { defaultness: ld, + ident: li, generics: lg, + where_clauses: _, bounds: lb, ty: lt, - .. }), Type(box TyAlias { defaultness: rd, + ident: ri, generics: rg, + where_clauses: _, bounds: rb, ty: rt, - .. }), ) => { eq_defaultness(*ld, *rd) + && eq_id(*li, *ri) && eq_generics(lg, rg) && over(lb, rb, eq_generic_bound) && both(lt.as_ref(), rt.as_ref(), |l, r| eq_ty(l, r)) From 86e6cb5608457f59c90fd1dc3914fa2a6c4adbf0 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 27 Mar 2025 11:48:06 +0000 Subject: [PATCH 15/19] Decouple trait impls of different traits wrt incremental --- clippy_lints/src/derive.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 2ae35b400557..fae01026487a 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -324,11 +324,9 @@ fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &h // there's a Copy impl for any instance of the adt. if !is_copy(cx, ty) { if ty_subs.non_erasable_generics().next().is_some() { - let has_copy_impl = cx.tcx.all_local_trait_impls(()).get(©_id).is_some_and(|impls| { - impls.iter().any(|&id| { - matches!(cx.tcx.type_of(id).instantiate_identity().kind(), ty::Adt(adt, _) + let has_copy_impl = cx.tcx.local_trait_impls(copy_id).iter().any(|&id| { + matches!(cx.tcx.type_of(id).instantiate_identity().kind(), ty::Adt(adt, _) if ty_adt.did() == adt.did()) - }) }); if !has_copy_impl { return; From 130af3fc3ab0d9cbfd249b533aa21e73492076fe Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 2 Apr 2025 07:04:24 +1100 Subject: [PATCH 16/19] Move methods from `Map` to `TyCtxt`, part 5. This eliminates all methods on `Map`. Actually removing `Map` will occur in a follow-up PR. --- clippy_lints/src/casts/unnecessary_cast.rs | 2 +- clippy_lints/src/derive.rs | 4 ++-- clippy_lints/src/escape.rs | 2 +- clippy_lints/src/index_refutable_slice.rs | 2 +- clippy_lints/src/loops/mut_range_bound.rs | 8 ++++---- clippy_lints/src/macro_metavars_in_unsafe.rs | 2 +- clippy_lints/src/misc.rs | 2 +- clippy_lints/src/needless_pass_by_ref_mut.rs | 2 +- clippy_lints/src/needless_pass_by_value.rs | 2 +- clippy_lints/src/operators/arithmetic_side_effects.rs | 4 ++-- clippy_lints/src/operators/numeric_arithmetic.rs | 4 ++-- clippy_lints/src/shadow.rs | 2 +- clippy_lints/src/single_call_fn.rs | 2 +- clippy_utils/src/lib.rs | 2 +- clippy_utils/src/macros.rs | 3 +-- clippy_utils/src/sugg.rs | 3 +-- 16 files changed, 22 insertions(+), 24 deletions(-) diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs index 7885f171461d..72953818a708 100644 --- a/clippy_lints/src/casts/unnecessary_cast.rs +++ b/clippy_lints/src/casts/unnecessary_cast.rs @@ -143,7 +143,7 @@ pub(super) fn check<'tcx>( if cast_from.kind() == cast_to.kind() && !expr.span.in_external_macro(cx.sess().source_map()) { if let Some(id) = path_to_local(cast_expr) - && !cx.tcx.hir().span(id).eq_ctxt(cast_expr.span) + && !cx.tcx.hir_span(id).eq_ctxt(cast_expr.span) { // Binding context is different than the identifiers context. // Weird macro wizardry could be involved here. diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 2ae35b400557..7480b409100c 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -254,7 +254,7 @@ fn check_hash_peq<'tcx>( |diag| { if let Some(local_def_id) = impl_id.as_local() { let hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id); - diag.span_note(cx.tcx.hir().span(hir_id), "`PartialEq` implemented here"); + diag.span_note(cx.tcx.hir_span(hir_id), "`PartialEq` implemented here"); } }, ); @@ -298,7 +298,7 @@ fn check_ord_partial_ord<'tcx>( span_lint_and_then(cx, DERIVE_ORD_XOR_PARTIAL_ORD, span, mess, |diag| { if let Some(local_def_id) = impl_id.as_local() { let hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id); - diag.span_note(cx.tcx.hir().span(hir_id), "`PartialOrd` implemented here"); + diag.span_note(cx.tcx.hir_span(hir_id), "`PartialOrd` implemented here"); } }); } diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index de0fc2b1bf4b..e248f08789b2 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -120,7 +120,7 @@ impl<'tcx> LateLintPass<'tcx> for BoxedLocal { cx, BOXED_LOCAL, node, - cx.tcx.hir().span(node), + cx.tcx.hir_span(node), "local variable doesn't need to be boxed here", ); } diff --git a/clippy_lints/src/index_refutable_slice.rs b/clippy_lints/src/index_refutable_slice.rs index d53e139de014..989997d69f7c 100644 --- a/clippy_lints/src/index_refutable_slice.rs +++ b/clippy_lints/src/index_refutable_slice.rs @@ -248,7 +248,7 @@ impl<'tcx> Visitor<'tcx> for SliceIndexLintingVisitor<'_, 'tcx> { { use_info .index_use - .push((index_value, cx.tcx.hir().span(parent_expr.hir_id))); + .push((index_value, cx.tcx.hir_span(parent_expr.hir_id))); return; } diff --git a/clippy_lints/src/loops/mut_range_bound.rs b/clippy_lints/src/loops/mut_range_bound.rs index 5afcf51167d4..a24dd44790e1 100644 --- a/clippy_lints/src/loops/mut_range_bound.rs +++ b/clippy_lints/src/loops/mut_range_bound.rs @@ -85,10 +85,10 @@ impl<'tcx> Delegate<'tcx> for MutatePairDelegate<'_, 'tcx> { if bk == ty::BorrowKind::Mutable { if let PlaceBase::Local(id) = cmt.place.base { if Some(id) == self.hir_id_low && !BreakAfterExprVisitor::is_found(self.cx, diag_expr_id) { - self.span_low = Some(self.cx.tcx.hir().span(diag_expr_id)); + self.span_low = Some(self.cx.tcx.hir_span(diag_expr_id)); } if Some(id) == self.hir_id_high && !BreakAfterExprVisitor::is_found(self.cx, diag_expr_id) { - self.span_high = Some(self.cx.tcx.hir().span(diag_expr_id)); + self.span_high = Some(self.cx.tcx.hir_span(diag_expr_id)); } } } @@ -97,10 +97,10 @@ impl<'tcx> Delegate<'tcx> for MutatePairDelegate<'_, 'tcx> { fn mutate(&mut self, cmt: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) { if let PlaceBase::Local(id) = cmt.place.base { if Some(id) == self.hir_id_low && !BreakAfterExprVisitor::is_found(self.cx, diag_expr_id) { - self.span_low = Some(self.cx.tcx.hir().span(diag_expr_id)); + self.span_low = Some(self.cx.tcx.hir_span(diag_expr_id)); } if Some(id) == self.hir_id_high && !BreakAfterExprVisitor::is_found(self.cx, diag_expr_id) { - self.span_high = Some(self.cx.tcx.hir().span(diag_expr_id)); + self.span_high = Some(self.cx.tcx.hir_span(diag_expr_id)); } } } diff --git a/clippy_lints/src/macro_metavars_in_unsafe.rs b/clippy_lints/src/macro_metavars_in_unsafe.rs index 006addb987f5..df6e85611fb2 100644 --- a/clippy_lints/src/macro_metavars_in_unsafe.rs +++ b/clippy_lints/src/macro_metavars_in_unsafe.rs @@ -253,7 +253,7 @@ impl<'tcx> LateLintPass<'tcx> for ExprMetavarsInUnsafe { // Remove the syntax context to hide "in this macro invocation" in the diagnostic. // The invocation doesn't matter. Also we want to dedupe by the unsafe block and not by anything // related to the callsite. - let span = cx.tcx.hir().span(id); + let span = cx.tcx.hir_span(id); (id, Span::new(span.lo(), span.hi(), SyntaxContext::root(), None)) }) diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index 693d1a8dd764..09ee6f7037c6 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -329,7 +329,7 @@ fn used_underscore_binding<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { let name = ident.name.as_str(); if name.starts_with('_') && !name.starts_with("__") - && let definition_span = cx.tcx.hir().span(definition_hir_id) + && let definition_span = cx.tcx.hir_span(definition_hir_id) && !definition_span.from_expansion() && !fulfill_or_allowed(cx, USED_UNDERSCORE_BINDING, [expr.hir_id, definition_hir_id]) { diff --git a/clippy_lints/src/needless_pass_by_ref_mut.rs b/clippy_lints/src/needless_pass_by_ref_mut.rs index 576bb27b254c..b61061d076b7 100644 --- a/clippy_lints/src/needless_pass_by_ref_mut.rs +++ b/clippy_lints/src/needless_pass_by_ref_mut.rs @@ -280,7 +280,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> { diag.span_suggestion( sp, "consider changing to".to_string(), - format!("&{}", snippet(cx, cx.tcx.hir().span(inner_ty.ty.hir_id), "_"),), + format!("&{}", snippet(cx, cx.tcx.hir_span(inner_ty.ty.hir_id), "_"),), Applicability::Unspecified, ); if cx.effective_visibilities.is_exported(*fn_def_id) { diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 55ca875edcee..978fde33d517 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -198,7 +198,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { // Dereference suggestion let sugg = |diag: &mut Diag<'_, ()>| { if let ty::Adt(def, ..) = ty.kind() { - if let Some(span) = cx.tcx.hir().span_if_local(def.did()) { + if let Some(span) = cx.tcx.hir_span_if_local(def.did()) { if type_allowed_to_implement_copy( cx.tcx, cx.param_env, diff --git a/clippy_lints/src/operators/arithmetic_side_effects.rs b/clippy_lints/src/operators/arithmetic_side_effects.rs index 594101427f5a..08fcd17e5555 100644 --- a/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -354,7 +354,7 @@ impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects { let body_owner_kind = cx.tcx.hir_body_owner_kind(body_owner_def_id); if let hir::BodyOwnerKind::Const { .. } | hir::BodyOwnerKind::Static(_) = body_owner_kind { - let body_span = cx.tcx.hir().span_with_body(body_owner); + let body_span = cx.tcx.hir_span_with_body(body_owner); if let Some(span) = self.const_span && span.contains(body_span) { @@ -366,7 +366,7 @@ impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects { fn check_body_post(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>) { let body_owner = cx.tcx.hir_body_owner(body.id()); - let body_span = cx.tcx.hir().span(body_owner); + let body_span = cx.tcx.hir_span(body_owner); if let Some(span) = self.const_span && span.contains(body_span) { diff --git a/clippy_lints/src/operators/numeric_arithmetic.rs b/clippy_lints/src/operators/numeric_arithmetic.rs index c261fd9bd9cb..96c9775e292e 100644 --- a/clippy_lints/src/operators/numeric_arithmetic.rs +++ b/clippy_lints/src/operators/numeric_arithmetic.rs @@ -73,7 +73,7 @@ impl Context { match cx.tcx.hir_body_owner_kind(body_owner_def_id) { hir::BodyOwnerKind::Static(_) | hir::BodyOwnerKind::Const { .. } => { - let body_span = cx.tcx.hir().span_with_body(body_owner); + let body_span = cx.tcx.hir_span_with_body(body_owner); if let Some(span) = self.const_span { if span.contains(body_span) { @@ -88,7 +88,7 @@ impl Context { pub fn body_post(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>) { let body_owner = cx.tcx.hir_body_owner(body.id()); - let body_span = cx.tcx.hir().span_with_body(body_owner); + let body_span = cx.tcx.hir_span_with_body(body_owner); if let Some(span) = self.const_span { if span.contains(body_span) { diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index ee282ee1dfb7..b82ddedd56c0 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -218,7 +218,7 @@ fn lint_shadow(cx: &LateContext<'_>, pat: &Pat<'_>, shadowed: HirId, span: Span) }, }; span_lint_and_then(cx, lint, span, msg, |diag| { - diag.span_note(cx.tcx.hir().span(shadowed), "previous binding is here"); + diag.span_note(cx.tcx.hir_span(shadowed), "previous binding is here"); }); } diff --git a/clippy_lints/src/single_call_fn.rs b/clippy_lints/src/single_call_fn.rs index 1a2fb77acc15..64891743dc63 100644 --- a/clippy_lints/src/single_call_fn.rs +++ b/clippy_lints/src/single_call_fn.rs @@ -137,7 +137,7 @@ impl<'tcx> LateLintPass<'tcx> for SingleCallFn { for (&def_id, usage) in &self.def_id_to_usage { if let CallState::Once { call_site } = *usage && let fn_hir_id = cx.tcx.local_def_id_to_hir_id(def_id) - && let fn_span = cx.tcx.hir().span_with_body(fn_hir_id) + && let fn_span = cx.tcx.hir_span_with_body(fn_hir_id) && !self.is_function_allowed(cx, def_id, fn_hir_id, fn_span) { span_lint_hir_and_then( diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 668b0cb69e20..8dc28fa3077e 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -2990,7 +2990,7 @@ pub fn expr_use_ctxt<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> ExprU { adjustments = cx.typeck_results().expr_adjustments(e); } - same_ctxt &= cx.tcx.hir().span(parent_id).ctxt() == ctxt; + same_ctxt &= cx.tcx.hir_span(parent_id).ctxt() == ctxt; if let Node::Expr(e) = parent { match e.kind { ExprKind::If(e, _, _) | ExprKind::Match(e, _, _) if e.hir_id != child_id => { diff --git a/clippy_utils/src/macros.rs b/clippy_utils/src/macros.rs index 1a457bc7f214..dfb30b9c2186 100644 --- a/clippy_utils/src/macros.rs +++ b/clippy_utils/src/macros.rs @@ -178,7 +178,6 @@ pub fn first_node_in_macro(cx: &LateContext<'_>, node: &impl HirNode) -> Option< // get the parent node, possibly skipping over a statement // if the parent is not found, it is sensible to return `Some(root)` - let hir = cx.tcx.hir(); let mut parent_iter = cx.tcx.hir_parent_iter(node.hir_id()); let (parent_id, _) = match parent_iter.next() { None => return Some(ExpnId::root()), @@ -190,7 +189,7 @@ pub fn first_node_in_macro(cx: &LateContext<'_>, node: &impl HirNode) -> Option< }; // get the macro expansion of the parent node - let parent_span = hir.span(parent_id); + let parent_span = cx.tcx.hir_span(parent_id); let Some(parent_macro_call) = macro_backtrace(parent_span).next() else { // the parent node is not in a macro return Some(ExpnId::root()); diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index 68a1de96a351..065d48119a24 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -839,8 +839,7 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId, _: ty::BorrowKind) { if let PlaceBase::Local(id) = cmt.place.base { - let map = self.cx.tcx.hir(); - let span = map.span(cmt.hir_id); + let span = self.cx.tcx.hir_span(cmt.hir_id); let start_span = Span::new(self.next_pos, span.lo(), span.ctxt(), None); let mut start_snip = snippet_with_applicability(self.cx, start_span, "..", &mut self.applicability); From 777c7cdce0713762c5b6c278dae017ef0aefd8c8 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 1 Apr 2025 10:03:55 +0000 Subject: [PATCH 17/19] Remove a function that has no necessary callers --- clippy_lints/src/new_without_default.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/new_without_default.rs b/clippy_lints/src/new_without_default.rs index f0ee613791fb..1e469c3b63a4 100644 --- a/clippy_lints/src/new_without_default.rs +++ b/clippy_lints/src/new_without_default.rs @@ -97,14 +97,14 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { { if self.impling_types.is_none() { let mut impls = HirIdSet::default(); - cx.tcx.for_each_impl(default_trait_id, |d| { + for &d in cx.tcx.local_trait_impls(default_trait_id) { let ty = cx.tcx.type_of(d).instantiate_identity(); if let Some(ty_def) = ty.ty_adt_def() { if let Some(local_def_id) = ty_def.did().as_local() { impls.insert(cx.tcx.local_def_id_to_hir_id(local_def_id)); } } - }); + } self.impling_types = Some(impls); } From c44191af484d3dc2c74557723afa04aa668b90fd Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 3 Apr 2025 21:31:56 +0200 Subject: [PATCH 18/19] Bump nightly version -> 2025-04-03 --- clippy_utils/README.md | 2 +- rust-toolchain.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_utils/README.md b/clippy_utils/README.md index 7c665b424977..bbf9d7935566 100644 --- a/clippy_utils/README.md +++ b/clippy_utils/README.md @@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain: ``` -nightly-2025-03-20 +nightly-2025-04-03 ``` diff --git a/rust-toolchain.toml b/rust-toolchain.toml index fcaeedc9a66b..eb246030d01f 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2025-03-20" +channel = "nightly-2025-04-03" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" From c97bd7463cd830e4e47a9366da86c0d760a2156a Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 3 Apr 2025 21:32:37 +0200 Subject: [PATCH 19/19] Bump Clippy version -> 0.1.88 --- Cargo.toml | 2 +- clippy_config/Cargo.toml | 2 +- clippy_lints/Cargo.toml | 2 +- clippy_utils/Cargo.toml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1ae5b7d6af07..5a6904d63be9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clippy" # begin autogenerated version -version = "0.1.87" +version = "0.1.88" # end autogenerated version description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" diff --git a/clippy_config/Cargo.toml b/clippy_config/Cargo.toml index 934725fccb8e..93fd2e35d1ba 100644 --- a/clippy_config/Cargo.toml +++ b/clippy_config/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clippy_config" # begin autogenerated version -version = "0.1.87" +version = "0.1.88" # end autogenerated version edition = "2024" publish = false diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 54347043a13d..d36b6ae9c953 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clippy_lints" # begin autogenerated version -version = "0.1.87" +version = "0.1.88" # end autogenerated version description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index ba4bb1d177c5..b98e99017503 100644 --- a/clippy_utils/Cargo.toml +++ b/clippy_utils/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clippy_utils" # begin autogenerated version -version = "0.1.87" +version = "0.1.88" # end autogenerated version edition = "2024" description = "Helpful tools for writing lints, provided as they are used in Clippy"