Skip to content

Commit 3a8632c

Browse files
committed
deprecate Eq::assert_receiver_is_total_eq and emit a FCW on manual impls
1 parent 13c3873 commit 3a8632c

10 files changed

Lines changed: 185 additions & 40 deletions

File tree

compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,14 @@ pub(crate) fn expand_deriving_eq(
2626
additional_bounds: Vec::new(),
2727
supports_unions: true,
2828
methods: vec![MethodDef {
29-
name: sym::assert_receiver_is_total_eq,
29+
name: sym::assert_fields_are_eq,
3030
generics: Bounds::empty(),
3131
explicit_self: true,
3232
nonself_args: vec![],
3333
ret_ty: Unit,
3434
attributes: thin_vec![
35-
cx.attr_word(sym::inline, span),
3635
cx.attr_nested_word(sym::doc, sym::hidden, span),
37-
cx.attr_nested_word(sym::coverage, sym::off, span)
36+
cx.attr_nested_word(sym::coverage, sym::off, span),
3837
],
3938
fieldless_variants_strategy: FieldlessVariantsStrategy::Unify,
4039
combine_substructure: combine_substructure(Box::new(|a, b, c| {

compiler/rustc_lint/src/builtin.rs

Lines changed: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,11 @@ use rustc_ast_pretty::pprust::expr_to_string;
2424
use rustc_attr_parsing::AttributeParser;
2525
use rustc_errors::{Applicability, LintDiagnostic, inline_fluent};
2626
use rustc_feature::GateIssue;
27-
use rustc_hir as hir;
2827
use rustc_hir::attrs::{AttributeKind, DocAttribute};
2928
use rustc_hir::def::{DefKind, Res};
3029
use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId};
3130
use rustc_hir::intravisit::FnKind as HirFnKind;
32-
use rustc_hir::{Body, FnDecl, ImplItemImplKind, PatKind, PredicateOrigin, find_attr};
31+
use rustc_hir::{self as hir, Body, FnDecl, ImplItemImplKind, PatKind, PredicateOrigin, find_attr};
3332
use rustc_middle::bug;
3433
use rustc_middle::lint::LevelAndSource;
3534
use rustc_middle::ty::layout::LayoutOf;
@@ -59,7 +58,7 @@ use crate::lints::{
5958
BuiltinSpecialModuleNameUsed, BuiltinTrivialBounds, BuiltinTypeAliasBounds,
6059
BuiltinUngatedAsyncFnTrackCaller, BuiltinUnpermittedTypeInit, BuiltinUnpermittedTypeInitSub,
6160
BuiltinUnreachablePub, BuiltinUnsafe, BuiltinUnstableFeatures, BuiltinUnusedDocComment,
62-
BuiltinUnusedDocCommentSub, BuiltinWhileTrue, InvalidAsmLabel,
61+
BuiltinUnusedDocCommentSub, BuiltinWhileTrue, EqInternalMethodImplemented, InvalidAsmLabel,
6362
};
6463
use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level, LintContext};
6564
declare_lint! {
@@ -3188,3 +3187,62 @@ impl EarlyLintPass for SpecialModuleName {
31883187
}
31893188
}
31903189
}
3190+
3191+
declare_lint! {
3192+
/// The `internal_eq_trait_method_impls` lint detects manual
3193+
/// implementations of `Eq::assert_receiver_is_total_eq`.
3194+
///
3195+
/// ### Example
3196+
///
3197+
/// ```rust
3198+
/// #[derive(PartialEq)]
3199+
/// pub struct Foo;
3200+
///
3201+
/// impl Eq for Foo {
3202+
/// fn assert_receiver_is_total_eq(&self) {}
3203+
/// }
3204+
/// ```
3205+
///
3206+
/// {{produces}}
3207+
///
3208+
/// ### Explanation
3209+
///
3210+
/// This method exists so that `#[derive(Eq)]` can check that all
3211+
/// fields of a type implement `Eq`. Other users were never supposed
3212+
/// to implement it and it was hidden from documentation.
3213+
///
3214+
/// Unfortunately, it was not explicitly marked as unstable and some
3215+
/// people have now mistakenly assumed they had to implement this method.
3216+
///
3217+
/// As the method is never called by the standard library, you can safely
3218+
/// remove any implementations of the method and just write `impl Eq for Foo {}`.
3219+
///
3220+
/// This is a [future-incompatible] lint to transition this to a hard
3221+
/// error in the future. See [issue #152336] for more details.
3222+
///
3223+
/// [issue #152336]: https://github.com/rust-lang/rust/issues/152336
3224+
pub INTERNAL_EQ_TRAIT_METHOD_IMPLS,
3225+
Warn,
3226+
"manual implementation of the internal `Eq::assert_receiver_is_total_eq` method",
3227+
@future_incompatible = FutureIncompatibleInfo {
3228+
reason: fcw!(FutureReleaseError #152336),
3229+
report_in_deps: false,
3230+
};
3231+
}
3232+
3233+
declare_lint_pass!(InternalEqTraitMethodImpls => [INTERNAL_EQ_TRAIT_METHOD_IMPLS]);
3234+
3235+
impl<'tcx> LateLintPass<'tcx> for InternalEqTraitMethodImpls {
3236+
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx rustc_hir::ImplItem<'tcx>) {
3237+
if let ImplItemImplKind::Trait { defaultness: _, trait_item_def_id: Ok(trait_item_def_id) } =
3238+
item.impl_kind
3239+
&& cx.tcx.is_diagnostic_item(sym::assert_receiver_is_total_eq, trait_item_def_id)
3240+
{
3241+
cx.emit_span_lint(
3242+
INTERNAL_EQ_TRAIT_METHOD_IMPLS,
3243+
item.span,
3244+
EqInternalMethodImplemented,
3245+
);
3246+
}
3247+
}
3248+
}

compiler/rustc_lint/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,7 @@ late_lint_methods!(
249249
FunctionCastsAsInteger: FunctionCastsAsInteger,
250250
CheckTransmutes: CheckTransmutes,
251251
LifetimeSyntax: LifetimeSyntax,
252+
InternalEqTraitMethodImpls: InternalEqTraitMethodImpls,
252253
]
253254
]
254255
);

compiler/rustc_lint/src/lints.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3883,3 +3883,8 @@ pub(crate) struct UnreachableCfgSelectPredicateWildcard {
38833883
#[label("always matches")]
38843884
pub wildcard_span: Span,
38853885
}
3886+
3887+
#[derive(LintDiagnostic)]
3888+
#[diag("`Eq::assert_receiver_is_total_eq` should never be implemented by hand")]
3889+
#[note("this method is only used to add checks to the `Eq` derive macro")]
3890+
pub(crate) struct EqInternalMethodImplemented;

compiler/rustc_span/src/symbol.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,7 @@ symbols! {
505505
assert,
506506
assert_eq,
507507
assert_eq_macro,
508+
assert_fields_are_eq,
508509
assert_inhabited,
509510
assert_macro,
510511
assert_mem_uninitialized_valid,
@@ -1285,6 +1286,7 @@ symbols! {
12851286
integer_: "integer", // underscore to avoid clashing with the function `sym::integer` below
12861287
integral,
12871288
internal,
1289+
internal_eq_trait_method_impls,
12881290
internal_features,
12891291
into_async_iter_into_iter,
12901292
into_future,

library/core/src/cmp.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,7 @@ pub macro PartialEq($item:item) {
336336
#[rustc_diagnostic_item = "Eq"]
337337
#[rustc_const_unstable(feature = "const_cmp", issue = "143800")]
338338
pub const trait Eq: [const] PartialEq<Self> + PointeeSized {
339-
// this method is used solely by `impl Eq or #[derive(Eq)]` to assert that every component of a
339+
// this method is used solely by `#[derive(Eq)]` to assert that every component of a
340340
// type implements `Eq` itself. The current deriving infrastructure means doing this assertion
341341
// without using a method on this trait is nearly impossible.
342342
//
@@ -345,7 +345,14 @@ pub const trait Eq: [const] PartialEq<Self> + PointeeSized {
345345
#[coverage(off)]
346346
#[inline]
347347
#[stable(feature = "rust1", since = "1.0.0")]
348+
#[rustc_diagnostic_item = "assert_receiver_is_total_eq"]
349+
#[deprecated(since = "1.95.0", note = "implementation detail of `#[derive(Eq)]`")]
348350
fn assert_receiver_is_total_eq(&self) {}
351+
352+
#[doc(hidden)]
353+
#[coverage(off)]
354+
#[unstable(feature = "derive_eq_internals", issue = "none")]
355+
fn assert_fields_are_eq(&self) {}
349356
}
350357

351358
/// Derive macro generating an impl of the trait [`Eq`].

tests/ui/deriving/deriving-all-codegen.stdout

Lines changed: 16 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,9 @@ impl ::core::cmp::PartialEq for Empty {
6262
}
6363
#[automatically_derived]
6464
impl ::core::cmp::Eq for Empty {
65-
#[inline]
6665
#[doc(hidden)]
6766
#[coverage(off)]
68-
fn assert_receiver_is_total_eq(&self) {}
67+
fn assert_fields_are_eq(&self) {}
6968
}
7069
#[automatically_derived]
7170
impl ::core::cmp::PartialOrd for Empty {
@@ -139,10 +138,9 @@ impl ::core::cmp::PartialEq for Point {
139138
}
140139
#[automatically_derived]
141140
impl ::core::cmp::Eq for Point {
142-
#[inline]
143141
#[doc(hidden)]
144142
#[coverage(off)]
145-
fn assert_receiver_is_total_eq(&self) {
143+
fn assert_fields_are_eq(&self) {
146144
let _: ::core::cmp::AssertParamIsEq<u32>;
147145
}
148146
}
@@ -227,10 +225,9 @@ impl ::core::cmp::PartialEq for PackedPoint {
227225
}
228226
#[automatically_derived]
229227
impl ::core::cmp::Eq for PackedPoint {
230-
#[inline]
231228
#[doc(hidden)]
232229
#[coverage(off)]
233-
fn assert_receiver_is_total_eq(&self) {
230+
fn assert_fields_are_eq(&self) {
234231
let _: ::core::cmp::AssertParamIsEq<u32>;
235232
}
236233
}
@@ -310,10 +307,9 @@ impl ::core::cmp::PartialEq for TupleSingleField {
310307
}
311308
#[automatically_derived]
312309
impl ::core::cmp::Eq for TupleSingleField {
313-
#[inline]
314310
#[doc(hidden)]
315311
#[coverage(off)]
316-
fn assert_receiver_is_total_eq(&self) {
312+
fn assert_fields_are_eq(&self) {
317313
let _: ::core::cmp::AssertParamIsEq<u32>;
318314
}
319315
}
@@ -385,10 +381,9 @@ impl ::core::cmp::PartialEq for SingleField {
385381
}
386382
#[automatically_derived]
387383
impl ::core::cmp::Eq for SingleField {
388-
#[inline]
389384
#[doc(hidden)]
390385
#[coverage(off)]
391-
fn assert_receiver_is_total_eq(&self) {
386+
fn assert_fields_are_eq(&self) {
392387
let _: ::core::cmp::AssertParamIsEq<bool>;
393388
}
394389
}
@@ -490,10 +485,9 @@ impl ::core::cmp::PartialEq for Big {
490485
}
491486
#[automatically_derived]
492487
impl ::core::cmp::Eq for Big {
493-
#[inline]
494488
#[doc(hidden)]
495489
#[coverage(off)]
496-
fn assert_receiver_is_total_eq(&self) {
490+
fn assert_fields_are_eq(&self) {
497491
let _: ::core::cmp::AssertParamIsEq<u32>;
498492
}
499493
}
@@ -754,10 +748,9 @@ impl ::core::cmp::PartialEq for Unsized {
754748
}
755749
#[automatically_derived]
756750
impl ::core::cmp::Eq for Unsized {
757-
#[inline]
758751
#[doc(hidden)]
759752
#[coverage(off)]
760-
fn assert_receiver_is_total_eq(&self) {
753+
fn assert_fields_are_eq(&self) {
761754
let _: ::core::cmp::AssertParamIsEq<[u32]>;
762755
}
763756
}
@@ -849,10 +842,9 @@ impl<T: ::core::cmp::PartialEq + Trait, U: ::core::cmp::PartialEq>
849842
#[automatically_derived]
850843
impl<T: ::core::cmp::Eq + Trait, U: ::core::cmp::Eq> ::core::cmp::Eq for
851844
Generic<T, U> where T::A: ::core::cmp::Eq {
852-
#[inline]
853845
#[doc(hidden)]
854846
#[coverage(off)]
855-
fn assert_receiver_is_total_eq(&self) {
847+
fn assert_fields_are_eq(&self) {
856848
let _: ::core::cmp::AssertParamIsEq<T>;
857849
let _: ::core::cmp::AssertParamIsEq<T::A>;
858850
let _: ::core::cmp::AssertParamIsEq<U>;
@@ -971,10 +963,9 @@ impl<T: ::core::cmp::PartialEq + ::core::marker::Copy + Trait,
971963
impl<T: ::core::cmp::Eq + ::core::marker::Copy + Trait, U: ::core::cmp::Eq +
972964
::core::marker::Copy> ::core::cmp::Eq for PackedGeneric<T, U> where
973965
T::A: ::core::cmp::Eq + ::core::marker::Copy {
974-
#[inline]
975966
#[doc(hidden)]
976967
#[coverage(off)]
977-
fn assert_receiver_is_total_eq(&self) {
968+
fn assert_fields_are_eq(&self) {
978969
let _: ::core::cmp::AssertParamIsEq<T>;
979970
let _: ::core::cmp::AssertParamIsEq<T::A>;
980971
let _: ::core::cmp::AssertParamIsEq<U>;
@@ -1056,10 +1047,9 @@ impl ::core::cmp::PartialEq for Enum0 {
10561047
}
10571048
#[automatically_derived]
10581049
impl ::core::cmp::Eq for Enum0 {
1059-
#[inline]
10601050
#[doc(hidden)]
10611051
#[coverage(off)]
1062-
fn assert_receiver_is_total_eq(&self) {}
1052+
fn assert_fields_are_eq(&self) {}
10631053
}
10641054
#[automatically_derived]
10651055
impl ::core::cmp::PartialOrd for Enum0 {
@@ -1126,10 +1116,9 @@ impl ::core::cmp::PartialEq for Enum1 {
11261116
}
11271117
#[automatically_derived]
11281118
impl ::core::cmp::Eq for Enum1 {
1129-
#[inline]
11301119
#[doc(hidden)]
11311120
#[coverage(off)]
1132-
fn assert_receiver_is_total_eq(&self) {
1121+
fn assert_fields_are_eq(&self) {
11331122
let _: ::core::cmp::AssertParamIsEq<u32>;
11341123
}
11351124
}
@@ -1192,10 +1181,9 @@ impl ::core::cmp::PartialEq for Fieldless1 {
11921181
}
11931182
#[automatically_derived]
11941183
impl ::core::cmp::Eq for Fieldless1 {
1195-
#[inline]
11961184
#[doc(hidden)]
11971185
#[coverage(off)]
1198-
fn assert_receiver_is_total_eq(&self) {}
1186+
fn assert_fields_are_eq(&self) {}
11991187
}
12001188
#[automatically_derived]
12011189
impl ::core::cmp::PartialOrd for Fieldless1 {
@@ -1269,10 +1257,9 @@ impl ::core::cmp::PartialEq for Fieldless {
12691257
}
12701258
#[automatically_derived]
12711259
impl ::core::cmp::Eq for Fieldless {
1272-
#[inline]
12731260
#[doc(hidden)]
12741261
#[coverage(off)]
1275-
fn assert_receiver_is_total_eq(&self) {}
1262+
fn assert_fields_are_eq(&self) {}
12761263
}
12771264
#[automatically_derived]
12781265
impl ::core::cmp::PartialOrd for Fieldless {
@@ -1379,10 +1366,9 @@ impl ::core::cmp::PartialEq for Mixed {
13791366
}
13801367
#[automatically_derived]
13811368
impl ::core::cmp::Eq for Mixed {
1382-
#[inline]
13831369
#[doc(hidden)]
13841370
#[coverage(off)]
1385-
fn assert_receiver_is_total_eq(&self) {
1371+
fn assert_fields_are_eq(&self) {
13861372
let _: ::core::cmp::AssertParamIsEq<u32>;
13871373
let _: ::core::cmp::AssertParamIsEq<Option<u32>>;
13881374
let _: ::core::cmp::AssertParamIsEq<Option<i32>>;
@@ -1577,10 +1563,9 @@ impl ::core::cmp::PartialEq for Fielded {
15771563
}
15781564
#[automatically_derived]
15791565
impl ::core::cmp::Eq for Fielded {
1580-
#[inline]
15811566
#[doc(hidden)]
15821567
#[coverage(off)]
1583-
fn assert_receiver_is_total_eq(&self) {
1568+
fn assert_fields_are_eq(&self) {
15841569
let _: ::core::cmp::AssertParamIsEq<u32>;
15851570
let _: ::core::cmp::AssertParamIsEq<bool>;
15861571
let _: ::core::cmp::AssertParamIsEq<Option<i32>>;
@@ -1699,10 +1684,9 @@ impl<T: ::core::cmp::PartialEq, U: ::core::cmp::PartialEq>
16991684
#[automatically_derived]
17001685
impl<T: ::core::cmp::Eq, U: ::core::cmp::Eq> ::core::cmp::Eq for
17011686
EnumGeneric<T, U> {
1702-
#[inline]
17031687
#[doc(hidden)]
17041688
#[coverage(off)]
1705-
fn assert_receiver_is_total_eq(&self) {
1689+
fn assert_fields_are_eq(&self) {
17061690
let _: ::core::cmp::AssertParamIsEq<T>;
17071691
let _: ::core::cmp::AssertParamIsEq<U>;
17081692
}

0 commit comments

Comments
 (0)