diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs index d9b82e97cb462..a3dd447dd2791 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs @@ -34,7 +34,8 @@ pub(crate) fn expand_deriving_eq( attributes: thin_vec![ cx.attr_word(sym::inline, span), cx.attr_nested_word(sym::doc, sym::hidden, span), - cx.attr_nested_word(sym::coverage, sym::off, span) + cx.attr_nested_word(sym::coverage, sym::off, span), + cx.attr_nested_word(sym::allow, sym::internal_eq_trait_method_impls, span) ], fieldless_variants_strategy: FieldlessVariantsStrategy::Unify, combine_substructure: combine_substructure(Box::new(|a, b, c| { diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index b49b090272d45..a36553a8122bf 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -320,6 +320,9 @@ lint_enum_intrinsics_mem_variant = the return value of `mem::variant_count` is unspecified when called with a non-enum type .note = the type parameter of `variant_count` should be an enum, but it was instantiated with the type `{$ty_param}`, which is not an enum +lint_eq_internal_method = `Eq::assert_receiver_is_total_eq` should never be implemented by hand + .note = this method is only used to add checks to the `Eq` derive macro + lint_expectation = this lint expectation is unfulfilled .note = the `unfulfilled_lint_expectations` lint can't be expected and will always produce this message .rationale = {$rationale} diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index e2a061cab680a..6d189b3bd48db 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -24,12 +24,11 @@ use rustc_ast_pretty::pprust::expr_to_string; use rustc_attr_parsing::AttributeParser; use rustc_errors::{Applicability, LintDiagnostic}; use rustc_feature::GateIssue; -use rustc_hir as hir; use rustc_hir::attrs::{AttributeKind, DocAttribute}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId}; use rustc_hir::intravisit::FnKind as HirFnKind; -use rustc_hir::{Body, FnDecl, ImplItemImplKind, PatKind, PredicateOrigin, find_attr}; +use rustc_hir::{self as hir, Body, FnDecl, ImplItemImplKind, PatKind, PredicateOrigin, find_attr}; use rustc_middle::bug; use rustc_middle::lint::LevelAndSource; use rustc_middle::ty::layout::LayoutOf; @@ -59,7 +58,7 @@ use crate::lints::{ BuiltinSpecialModuleNameUsed, BuiltinTrivialBounds, BuiltinTypeAliasBounds, BuiltinUngatedAsyncFnTrackCaller, BuiltinUnpermittedTypeInit, BuiltinUnpermittedTypeInitSub, BuiltinUnreachablePub, BuiltinUnsafe, BuiltinUnstableFeatures, BuiltinUnusedDocComment, - BuiltinUnusedDocCommentSub, BuiltinWhileTrue, InvalidAsmLabel, + BuiltinUnusedDocCommentSub, BuiltinWhileTrue, EqInternalMethodImplemented, InvalidAsmLabel, }; use crate::{ EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level, LintContext, @@ -3187,3 +3186,63 @@ impl EarlyLintPass for SpecialModuleName { } } } + +declare_lint! { + /// The `internal_eq_trait_method_impls` lint detects manual + /// implementations of `Eq::assert_receiver_is_total_eq`. + /// + /// ### Example + /// + /// ```rust + /// #[derive(PartialEq)] + /// pub struct Foo; + /// + /// impl Eq for Foo { + /// fn assert_receiver_is_total_eq(&self) {} + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// This method exists so that `#[derive(Eq)]` can check that all + /// fields of a type implement `Eq`. Other users were never supposed + /// to implement it and it was hidden from documentation. + /// + /// Unfortunately, it was not explicitly marked as unstable and some + /// people have now mistakenly assumed they had to implement this method. + /// + /// As the method is never called by the standard library, you can safely + /// remove any implementations of the method and just write `impl Eq for Foo {}`. + /// + /// This is a [future-incompatible] lint to transition this to a hard + /// error in the future. See [issue #150000] for more details. + /// + /// [issue #150000]: https://github.com/rust-lang/rust/issues/150000 + pub INTERNAL_EQ_TRAIT_METHOD_IMPLS, + Warn, + "manual implementation of the internal `Eq::assert_receiver_is_total_eq` method", + @future_incompatible = FutureIncompatibleInfo { + reason: fcw!(FutureReleaseError #150000), + report_in_deps: false, + }; +} + +declare_lint_pass!(InternalEqTraitMethodImpls => [INTERNAL_EQ_TRAIT_METHOD_IMPLS]); + +impl<'tcx> LateLintPass<'tcx> for InternalEqTraitMethodImpls { + fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx rustc_hir::ImplItem<'tcx>) { + if let ImplItemImplKind::Trait { defaultness: _, trait_item_def_id: Ok(trait_item_def_id) } = + item.impl_kind + && item.ident.name == sym::assert_receiver_is_total_eq + && cx.tcx.is_diagnostic_item(sym::assert_receiver_is_total_eq, trait_item_def_id) + { + cx.emit_span_lint( + INTERNAL_EQ_TRAIT_METHOD_IMPLS, + item.span, + EqInternalMethodImplemented, + ); + } + } +} diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index ec7ce1ade9290..20024d5954f46 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -252,6 +252,7 @@ late_lint_methods!( FunctionCastsAsInteger: FunctionCastsAsInteger, CheckTransmutes: CheckTransmutes, LifetimeSyntax: LifetimeSyntax, + InternalEqTraitMethodImpls: InternalEqTraitMethodImpls, ] ] ); diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 841b11c996872..cc786617310ed 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -3312,3 +3312,8 @@ pub(crate) struct DocTestUnknown { #[derive(LintDiagnostic)] #[diag(lint_doc_test_literal)] pub(crate) struct DocTestLiteral; + +#[derive(LintDiagnostic)] +#[diag(lint_eq_internal_method)] +#[note] +pub(crate) struct EqInternalMethodImplemented; diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index c7ff28ccaffb7..da52e9a588056 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1267,6 +1267,7 @@ symbols! { integer_: "integer", // underscore to avoid clashing with the function `sym::integer` below integral, internal, + internal_eq_trait_method_impls, internal_features, into_async_iter_into_iter, into_future, diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index feb9c43196044..868483bb4aa1c 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -345,6 +345,8 @@ pub const trait Eq: [const] PartialEq + PointeeSized { #[coverage(off)] #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_diagnostic_item = "assert_receiver_is_total_eq"] + #[deprecated(since = "1.94.0", note = "implementation detail of `#[derive(Eq)]`")] fn assert_receiver_is_total_eq(&self) {} } diff --git a/tests/ui/deriving/deriving-all-codegen.stdout b/tests/ui/deriving/deriving-all-codegen.stdout index a40dece22a261..f62d875071b95 100644 --- a/tests/ui/deriving/deriving-all-codegen.stdout +++ b/tests/ui/deriving/deriving-all-codegen.stdout @@ -66,6 +66,7 @@ impl ::core::cmp::Eq for Empty { #[inline] #[doc(hidden)] #[coverage(off)] + #[allow(internal_eq_trait_method_impls)] fn assert_receiver_is_total_eq(&self) -> () {} } #[automatically_derived] @@ -143,6 +144,7 @@ impl ::core::cmp::Eq for Point { #[inline] #[doc(hidden)] #[coverage(off)] + #[allow(internal_eq_trait_method_impls)] fn assert_receiver_is_total_eq(&self) -> () { let _: ::core::cmp::AssertParamIsEq; } @@ -231,6 +233,7 @@ impl ::core::cmp::Eq for PackedPoint { #[inline] #[doc(hidden)] #[coverage(off)] + #[allow(internal_eq_trait_method_impls)] fn assert_receiver_is_total_eq(&self) -> () { let _: ::core::cmp::AssertParamIsEq; } @@ -314,6 +317,7 @@ impl ::core::cmp::Eq for TupleSingleField { #[inline] #[doc(hidden)] #[coverage(off)] + #[allow(internal_eq_trait_method_impls)] fn assert_receiver_is_total_eq(&self) -> () { let _: ::core::cmp::AssertParamIsEq; } @@ -389,6 +393,7 @@ impl ::core::cmp::Eq for SingleField { #[inline] #[doc(hidden)] #[coverage(off)] + #[allow(internal_eq_trait_method_impls)] fn assert_receiver_is_total_eq(&self) -> () { let _: ::core::cmp::AssertParamIsEq; } @@ -494,6 +499,7 @@ impl ::core::cmp::Eq for Big { #[inline] #[doc(hidden)] #[coverage(off)] + #[allow(internal_eq_trait_method_impls)] fn assert_receiver_is_total_eq(&self) -> () { let _: ::core::cmp::AssertParamIsEq; } @@ -758,6 +764,7 @@ impl ::core::cmp::Eq for Unsized { #[inline] #[doc(hidden)] #[coverage(off)] + #[allow(internal_eq_trait_method_impls)] fn assert_receiver_is_total_eq(&self) -> () { let _: ::core::cmp::AssertParamIsEq<[u32]>; } @@ -853,6 +860,7 @@ impl ::core::cmp::Eq for #[inline] #[doc(hidden)] #[coverage(off)] + #[allow(internal_eq_trait_method_impls)] fn assert_receiver_is_total_eq(&self) -> () { let _: ::core::cmp::AssertParamIsEq; let _: ::core::cmp::AssertParamIsEq; @@ -975,6 +983,7 @@ impl () { let _: ::core::cmp::AssertParamIsEq; let _: ::core::cmp::AssertParamIsEq; @@ -1060,6 +1069,7 @@ impl ::core::cmp::Eq for Enum0 { #[inline] #[doc(hidden)] #[coverage(off)] + #[allow(internal_eq_trait_method_impls)] fn assert_receiver_is_total_eq(&self) -> () {} } #[automatically_derived] @@ -1130,6 +1140,7 @@ impl ::core::cmp::Eq for Enum1 { #[inline] #[doc(hidden)] #[coverage(off)] + #[allow(internal_eq_trait_method_impls)] fn assert_receiver_is_total_eq(&self) -> () { let _: ::core::cmp::AssertParamIsEq; } @@ -1196,6 +1207,7 @@ impl ::core::cmp::Eq for Fieldless1 { #[inline] #[doc(hidden)] #[coverage(off)] + #[allow(internal_eq_trait_method_impls)] fn assert_receiver_is_total_eq(&self) -> () {} } #[automatically_derived] @@ -1273,6 +1285,7 @@ impl ::core::cmp::Eq for Fieldless { #[inline] #[doc(hidden)] #[coverage(off)] + #[allow(internal_eq_trait_method_impls)] fn assert_receiver_is_total_eq(&self) -> () {} } #[automatically_derived] @@ -1383,6 +1396,7 @@ impl ::core::cmp::Eq for Mixed { #[inline] #[doc(hidden)] #[coverage(off)] + #[allow(internal_eq_trait_method_impls)] fn assert_receiver_is_total_eq(&self) -> () { let _: ::core::cmp::AssertParamIsEq; let _: ::core::cmp::AssertParamIsEq>; @@ -1581,6 +1595,7 @@ impl ::core::cmp::Eq for Fielded { #[inline] #[doc(hidden)] #[coverage(off)] + #[allow(internal_eq_trait_method_impls)] fn assert_receiver_is_total_eq(&self) -> () { let _: ::core::cmp::AssertParamIsEq; let _: ::core::cmp::AssertParamIsEq; @@ -1703,6 +1718,7 @@ impl ::core::cmp::Eq for #[inline] #[doc(hidden)] #[coverage(off)] + #[allow(internal_eq_trait_method_impls)] fn assert_receiver_is_total_eq(&self) -> () { let _: ::core::cmp::AssertParamIsEq; let _: ::core::cmp::AssertParamIsEq; diff --git a/tests/ui/deriving/internal_eq_trait_method_impls.rs b/tests/ui/deriving/internal_eq_trait_method_impls.rs new file mode 100644 index 0000000000000..5f79ffd92f368 --- /dev/null +++ b/tests/ui/deriving/internal_eq_trait_method_impls.rs @@ -0,0 +1,42 @@ +#![deny(deprecated, internal_eq_trait_method_impls)] +pub struct Bad; + +impl PartialEq for Bad { + fn eq(&self, _: &Self) -> bool { + true + } +} + +impl Eq for Bad { + fn assert_receiver_is_total_eq(&self) {} + //~^ ERROR: `Eq::assert_receiver_is_total_eq` should never be implemented by hand [internal_eq_trait_method_impls] + //~| WARN: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +} + +#[derive(PartialEq, Eq)] +pub struct Good; + +#[derive(PartialEq)] +pub struct Good2; + +impl Eq for Good2 {} + +pub struct Foo; + +pub trait SameName { + fn assert_receiver_is_total_eq(&self) {} +} + +impl SameName for Foo { + fn assert_receiver_is_total_eq(&self) {} +} + +pub fn main() { + Foo.assert_receiver_is_total_eq(); + Good2.assert_receiver_is_total_eq(); + //~^ ERROR: use of deprecated method `std::cmp::Eq::assert_receiver_is_total_eq`: implementation detail of `#[derive(Eq)]` [deprecated] + Good.assert_receiver_is_total_eq(); + //~^ ERROR: use of deprecated method `std::cmp::Eq::assert_receiver_is_total_eq`: implementation detail of `#[derive(Eq)]` [deprecated] + Bad.assert_receiver_is_total_eq(); + //~^ ERROR: use of deprecated method `std::cmp::Eq::assert_receiver_is_total_eq`: implementation detail of `#[derive(Eq)]` [deprecated] +} diff --git a/tests/ui/deriving/internal_eq_trait_method_impls.stderr b/tests/ui/deriving/internal_eq_trait_method_impls.stderr new file mode 100644 index 0000000000000..7e59cd2e9d27b --- /dev/null +++ b/tests/ui/deriving/internal_eq_trait_method_impls.stderr @@ -0,0 +1,41 @@ +error: use of deprecated method `std::cmp::Eq::assert_receiver_is_total_eq`: implementation detail of `#[derive(Eq)]` + --> $DIR/internal_eq_trait_method_impls.rs:36:11 + | +LL | Good2.assert_receiver_is_total_eq(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/internal_eq_trait_method_impls.rs:1:9 + | +LL | #![deny(deprecated, internal_eq_trait_method_impls)] + | ^^^^^^^^^^ + +error: use of deprecated method `std::cmp::Eq::assert_receiver_is_total_eq`: implementation detail of `#[derive(Eq)]` + --> $DIR/internal_eq_trait_method_impls.rs:38:10 + | +LL | Good.assert_receiver_is_total_eq(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: use of deprecated method `std::cmp::Eq::assert_receiver_is_total_eq`: implementation detail of `#[derive(Eq)]` + --> $DIR/internal_eq_trait_method_impls.rs:40:9 + | +LL | Bad.assert_receiver_is_total_eq(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `Eq::assert_receiver_is_total_eq` should never be implemented by hand + --> $DIR/internal_eq_trait_method_impls.rs:11:5 + | +LL | fn assert_receiver_is_total_eq(&self) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #150000 + = note: this method is only used to add checks to the `Eq` derive macro +note: the lint level is defined here + --> $DIR/internal_eq_trait_method_impls.rs:1:21 + | +LL | #![deny(deprecated, internal_eq_trait_method_impls)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + diff --git a/tests/ui/stats/macro-stats.stderr b/tests/ui/stats/macro-stats.stderr index 11a6bfdfcd292..c6e7c0d15dadd 100644 --- a/tests/ui/stats/macro-stats.stderr +++ b/tests/ui/stats/macro-stats.stderr @@ -8,7 +8,7 @@ macro-stats #[derive(Hash)] 2 17 8.5 macro-stats q! 1 26 26.0 519 519.0 macro-stats #[derive(Ord)] 1 15 15.0 503 503.0 macro-stats #[derive(Default)] 2 16 8.0 403 201.5 -macro-stats #[derive(Eq)] 1 11 11.0 325 325.0 +macro-stats #[derive(Eq)] 1 12 12.0 370 370.0 macro-stats #[derive(Debug)] 1 8 8.0 277 277.0 macro-stats #[derive(PartialEq)] 1 9 9.0 267 267.0 macro-stats #[derive(Copy)] 1 2 2.0 61 61.0