deprecate Eq::assert_receiver_is_total_eq and emit a FCW on manual impls

This commit is contained in:
cyrgani
2026-01-08 12:17:55 +00:00
parent 859951e3c7
commit f1ec10ecbd
10 changed files with 188 additions and 42 deletions
@@ -26,15 +26,14 @@ pub(crate) fn expand_deriving_eq(
additional_bounds: Vec::new(),
supports_unions: true,
methods: vec![MethodDef {
name: sym::assert_receiver_is_total_eq,
name: sym::assert_fields_are_eq,
generics: Bounds::empty(),
explicit_self: true,
nonself_args: vec![],
ret_ty: Unit,
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),
],
fieldless_variants_strategy: FieldlessVariantsStrategy::Unify,
combine_substructure: combine_substructure(Box::new(|a, b, c| {
+61 -3
View File
@@ -24,12 +24,11 @@
use rustc_attr_parsing::AttributeParser;
use rustc_errors::{Applicability, LintDiagnostic, msg};
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 @@
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};
declare_lint! {
@@ -3184,3 +3183,62 @@ fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &ast::Crate) {
}
}
}
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 existed so that `#[derive(Eq)]` could 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 #152336] for more details.
///
/// [issue #152336]: https://github.com/rust-lang/rust/issues/152336
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 #152336),
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
&& 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,
);
}
}
}
+1
View File
@@ -250,6 +250,7 @@ fn lint_mod(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) {
FunctionCastsAsInteger: FunctionCastsAsInteger,
CheckTransmutes: CheckTransmutes,
LifetimeSyntax: LifetimeSyntax,
InternalEqTraitMethodImpls: InternalEqTraitMethodImpls,
]
]
);
+5
View File
@@ -3926,3 +3926,8 @@ pub(crate) struct MalformedOnConstAttrLint {
#[label("invalid option found here")]
pub span: Span,
}
#[derive(LintDiagnostic)]
#[diag("`Eq::assert_receiver_is_total_eq` should never be implemented by hand")]
#[note("this method was used to add checks to the `Eq` derive macro")]
pub(crate) struct EqInternalMethodImplemented;
+2
View File
@@ -432,6 +432,7 @@
assert,
assert_eq,
assert_eq_macro,
assert_fields_are_eq,
assert_inhabited,
assert_macro,
assert_mem_uninitialized_valid,
@@ -1089,6 +1090,7 @@
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,
+11 -3
View File
@@ -336,16 +336,24 @@ fn ne(&self, other: &Rhs) -> bool {
#[rustc_diagnostic_item = "Eq"]
#[rustc_const_unstable(feature = "const_cmp", issue = "143800")]
pub const trait Eq: [const] PartialEq<Self> + PointeeSized {
// this method is used solely by `impl Eq or #[derive(Eq)]` to assert that every component of a
// type implements `Eq` itself. The current deriving infrastructure means doing this assertion
// without using a method on this trait is nearly impossible.
// This method was used solely by `#[derive(Eq)]` to assert that every component of a
// type implements `Eq` itself.
//
// This should never be implemented by hand.
#[doc(hidden)]
#[coverage(off)]
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_diagnostic_item = "assert_receiver_is_total_eq"]
#[deprecated(since = "1.95.0", note = "implementation detail of `#[derive(Eq)]`")]
fn assert_receiver_is_total_eq(&self) {}
// FIXME (#152504): this method is used solely by `#[derive(Eq)]` to assert that
// every component of a type implements `Eq` itself. It will be removed again soon.
#[doc(hidden)]
#[coverage(off)]
#[unstable(feature = "derive_eq_internals", issue = "none")]
fn assert_fields_are_eq(&self) {}
}
/// Derive macro generating an impl of the trait [`Eq`].
+16 -32
View File
@@ -62,10 +62,9 @@ impl ::core::cmp::PartialEq for Empty {
}
#[automatically_derived]
impl ::core::cmp::Eq for Empty {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_receiver_is_total_eq(&self) {}
fn assert_fields_are_eq(&self) {}
}
#[automatically_derived]
impl ::core::cmp::PartialOrd for Empty {
@@ -139,10 +138,9 @@ impl ::core::cmp::PartialEq for Point {
}
#[automatically_derived]
impl ::core::cmp::Eq for Point {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_receiver_is_total_eq(&self) {
fn assert_fields_are_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<u32>;
}
}
@@ -227,10 +225,9 @@ impl ::core::cmp::PartialEq for PackedPoint {
}
#[automatically_derived]
impl ::core::cmp::Eq for PackedPoint {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_receiver_is_total_eq(&self) {
fn assert_fields_are_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<u32>;
}
}
@@ -310,10 +307,9 @@ impl ::core::cmp::PartialEq for TupleSingleField {
}
#[automatically_derived]
impl ::core::cmp::Eq for TupleSingleField {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_receiver_is_total_eq(&self) {
fn assert_fields_are_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<u32>;
}
}
@@ -385,10 +381,9 @@ impl ::core::cmp::PartialEq for SingleField {
}
#[automatically_derived]
impl ::core::cmp::Eq for SingleField {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_receiver_is_total_eq(&self) {
fn assert_fields_are_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<bool>;
}
}
@@ -490,10 +485,9 @@ impl ::core::cmp::PartialEq for Big {
}
#[automatically_derived]
impl ::core::cmp::Eq for Big {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_receiver_is_total_eq(&self) {
fn assert_fields_are_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<u32>;
}
}
@@ -754,10 +748,9 @@ impl ::core::cmp::PartialEq for Unsized {
}
#[automatically_derived]
impl ::core::cmp::Eq for Unsized {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_receiver_is_total_eq(&self) {
fn assert_fields_are_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<[u32]>;
}
}
@@ -849,10 +842,9 @@ impl<T: ::core::cmp::PartialEq + Trait, U: ::core::cmp::PartialEq>
#[automatically_derived]
impl<T: ::core::cmp::Eq + Trait, U: ::core::cmp::Eq> ::core::cmp::Eq for
Generic<T, U> where T::A: ::core::cmp::Eq {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_receiver_is_total_eq(&self) {
fn assert_fields_are_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<T>;
let _: ::core::cmp::AssertParamIsEq<T::A>;
let _: ::core::cmp::AssertParamIsEq<U>;
@@ -971,10 +963,9 @@ impl<T: ::core::cmp::PartialEq + ::core::marker::Copy + Trait,
impl<T: ::core::cmp::Eq + ::core::marker::Copy + Trait, U: ::core::cmp::Eq +
::core::marker::Copy> ::core::cmp::Eq for PackedGeneric<T, U> where
T::A: ::core::cmp::Eq + ::core::marker::Copy {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_receiver_is_total_eq(&self) {
fn assert_fields_are_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<T>;
let _: ::core::cmp::AssertParamIsEq<T::A>;
let _: ::core::cmp::AssertParamIsEq<U>;
@@ -1056,10 +1047,9 @@ impl ::core::cmp::PartialEq for Enum0 {
}
#[automatically_derived]
impl ::core::cmp::Eq for Enum0 {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_receiver_is_total_eq(&self) {}
fn assert_fields_are_eq(&self) {}
}
#[automatically_derived]
impl ::core::cmp::PartialOrd for Enum0 {
@@ -1126,10 +1116,9 @@ impl ::core::cmp::PartialEq for Enum1 {
}
#[automatically_derived]
impl ::core::cmp::Eq for Enum1 {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_receiver_is_total_eq(&self) {
fn assert_fields_are_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<u32>;
}
}
@@ -1192,10 +1181,9 @@ impl ::core::cmp::PartialEq for Fieldless1 {
}
#[automatically_derived]
impl ::core::cmp::Eq for Fieldless1 {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_receiver_is_total_eq(&self) {}
fn assert_fields_are_eq(&self) {}
}
#[automatically_derived]
impl ::core::cmp::PartialOrd for Fieldless1 {
@@ -1269,10 +1257,9 @@ impl ::core::cmp::PartialEq for Fieldless {
}
#[automatically_derived]
impl ::core::cmp::Eq for Fieldless {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_receiver_is_total_eq(&self) {}
fn assert_fields_are_eq(&self) {}
}
#[automatically_derived]
impl ::core::cmp::PartialOrd for Fieldless {
@@ -1379,10 +1366,9 @@ impl ::core::cmp::PartialEq for Mixed {
}
#[automatically_derived]
impl ::core::cmp::Eq for Mixed {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_receiver_is_total_eq(&self) {
fn assert_fields_are_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<u32>;
let _: ::core::cmp::AssertParamIsEq<Option<u32>>;
let _: ::core::cmp::AssertParamIsEq<Option<i32>>;
@@ -1577,10 +1563,9 @@ impl ::core::cmp::PartialEq for Fielded {
}
#[automatically_derived]
impl ::core::cmp::Eq for Fielded {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_receiver_is_total_eq(&self) {
fn assert_fields_are_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<u32>;
let _: ::core::cmp::AssertParamIsEq<bool>;
let _: ::core::cmp::AssertParamIsEq<Option<i32>>;
@@ -1699,10 +1684,9 @@ impl<T: ::core::cmp::PartialEq, U: ::core::cmp::PartialEq>
#[automatically_derived]
impl<T: ::core::cmp::Eq, U: ::core::cmp::Eq> ::core::cmp::Eq for
EnumGeneric<T, U> {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_receiver_is_total_eq(&self) {
fn assert_fields_are_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<T>;
let _: ::core::cmp::AssertParamIsEq<U>;
}
@@ -0,0 +1,48 @@
#![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]
}
#[forbid(internal_eq_trait_method_impls)]
mod forbid {
#[derive(PartialEq, Eq)]
pub struct Foo;
}
@@ -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 #152336 <https://github.com/rust-lang/rust/issues/152336>
= note: this method was 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
+1 -1
View File
@@ -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 319 319.0
macro-stats #[derive(Eq)] 1 10 10.0 298 298.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