diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 56a0a3ceebf5a..ffa7db010b592 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -318,6 +318,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 87b208923ddb9..90dff6bbb97a5 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, @@ -3191,3 +3190,64 @@ impl EarlyLintPass for SpecialModuleName { } } } + +declare_lint! { + /// The `eq_assert_receiver_is_total_eq_impl` 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 EQ_ASSERT_RECEIVER_IS_TOTAL_EQ_IMPL, + 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!(EqAssertReceiverIsTotalEq => [EQ_ASSERT_RECEIVER_IS_TOTAL_EQ_IMPL]); + +impl<'tcx> LateLintPass<'tcx> for EqAssertReceiverIsTotalEq { + 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) + && !item.span.from_expansion() + { + cx.emit_span_lint( + EQ_ASSERT_RECEIVER_IS_TOTAL_EQ_IMPL, + item.span, + EqInternalMethodImplemented, + ); + } + } +} diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 4e7a3e4051767..b24ad3cc9c56a 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -249,6 +249,7 @@ late_lint_methods!( FunctionCastsAsInteger: FunctionCastsAsInteger, CheckTransmutes: CheckTransmutes, LifetimeSyntax: LifetimeSyntax, + EqAssertReceiverIsTotalEq: EqAssertReceiverIsTotalEq, ] ] ); diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 5017ce7caa525..0f4c180885345 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -3298,3 +3298,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/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/eq_assert_receiver_is_total_eq_impl.rs b/tests/ui/deriving/eq_assert_receiver_is_total_eq_impl.rs new file mode 100644 index 0000000000000..7b1c9b110d966 --- /dev/null +++ b/tests/ui/deriving/eq_assert_receiver_is_total_eq_impl.rs @@ -0,0 +1,42 @@ +#![deny(deprecated, eq_assert_receiver_is_total_eq_impl)] +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 [eq_assert_receiver_is_total_eq_impl] + //~| 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/eq_assert_receiver_is_total_eq_impl.stderr b/tests/ui/deriving/eq_assert_receiver_is_total_eq_impl.stderr new file mode 100644 index 0000000000000..835d61d8ba5d5 --- /dev/null +++ b/tests/ui/deriving/eq_assert_receiver_is_total_eq_impl.stderr @@ -0,0 +1,41 @@ +error: use of deprecated method `std::cmp::Eq::assert_receiver_is_total_eq`: implementation detail of `#[derive(Eq)]` + --> $DIR/eq_assert_receiver_is_total_eq_impl.rs:36:11 + | +LL | Good2.assert_receiver_is_total_eq(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/eq_assert_receiver_is_total_eq_impl.rs:1:9 + | +LL | #![deny(deprecated, eq_assert_receiver_is_total_eq_impl)] + | ^^^^^^^^^^ + +error: use of deprecated method `std::cmp::Eq::assert_receiver_is_total_eq`: implementation detail of `#[derive(Eq)]` + --> $DIR/eq_assert_receiver_is_total_eq_impl.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/eq_assert_receiver_is_total_eq_impl.rs:40:9 + | +LL | Bad.assert_receiver_is_total_eq(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `Eq::assert_receiver_is_total_eq` should never be implemented by hand + --> $DIR/eq_assert_receiver_is_total_eq_impl.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/eq_assert_receiver_is_total_eq_impl.rs:1:21 + | +LL | #![deny(deprecated, eq_assert_receiver_is_total_eq_impl)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors +