Skip to content

Commit 4ee1588

Browse files
committed
deprecate Eq::assert_receiver_is_total_eq and emit a FCW on manual impls
1 parent fc546b9 commit 4ee1588

11 files changed

Lines changed: 176 additions & 5 deletions

File tree

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ pub(crate) fn expand_deriving_eq(
3434
attributes: thin_vec![
3535
cx.attr_word(sym::inline, span),
3636
cx.attr_nested_word(sym::doc, sym::hidden, span),
37-
cx.attr_nested_word(sym::coverage, sym::off, span)
37+
cx.attr_nested_word(sym::coverage, sym::off, span),
38+
cx.attr_nested_word(sym::allow, sym::internal_eq_trait_method_impls, span)
3839
],
3940
fieldless_variants_strategy: FieldlessVariantsStrategy::Unify,
4041
combine_substructure: combine_substructure(Box::new(|a, b, c| {

compiler/rustc_lint/messages.ftl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,9 @@ lint_enum_intrinsics_mem_variant =
320320
the return value of `mem::variant_count` is unspecified when called with a non-enum type
321321
.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
322322
323+
lint_eq_internal_method = `Eq::assert_receiver_is_total_eq` should never be implemented by hand
324+
.note = this method is only used to add checks to the `Eq` derive macro
325+
323326
lint_expectation = this lint expectation is unfulfilled
324327
.note = the `unfulfilled_lint_expectations` lint can't be expected and will always produce this message
325328
.rationale = {$rationale}

compiler/rustc_lint/src/builtin.rs

Lines changed: 62 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};
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::{
6564
EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level, LintContext,
@@ -3187,3 +3186,63 @@ impl EarlyLintPass for SpecialModuleName {
31873186
}
31883187
}
31893188
}
3189+
3190+
declare_lint! {
3191+
/// The `internal_eq_trait_method_impls` lint detects manual
3192+
/// implementations of `Eq::assert_receiver_is_total_eq`.
3193+
///
3194+
/// ### Example
3195+
///
3196+
/// ```rust
3197+
/// #[derive(PartialEq)]
3198+
/// pub struct Foo;
3199+
///
3200+
/// impl Eq for Foo {
3201+
/// fn assert_receiver_is_total_eq(&self) {}
3202+
/// }
3203+
/// ```
3204+
///
3205+
/// {{produces}}
3206+
///
3207+
/// ### Explanation
3208+
///
3209+
/// This method exists so that `#[derive(Eq)]` can check that all
3210+
/// fields of a type implement `Eq`. Other users were never supposed
3211+
/// to implement it and it was hidden from documentation.
3212+
///
3213+
/// Unfortunately, it was not explicitly marked as unstable and some
3214+
/// people have now mistakenly assumed they had to implement this method.
3215+
///
3216+
/// As the method is never called by the standard library, you can safely
3217+
/// remove any implementations of the method and just write `impl Eq for Foo {}`.
3218+
///
3219+
/// This is a [future-incompatible] lint to transition this to a hard
3220+
/// error in the future. See [issue #150000] for more details.
3221+
///
3222+
/// [issue #150000]: https://github.com/rust-lang/rust/issues/150000
3223+
pub INTERNAL_EQ_TRAIT_METHOD_IMPLS,
3224+
Warn,
3225+
"manual implementation of the internal `Eq::assert_receiver_is_total_eq` method",
3226+
@future_incompatible = FutureIncompatibleInfo {
3227+
reason: fcw!(FutureReleaseError #150000),
3228+
report_in_deps: false,
3229+
};
3230+
}
3231+
3232+
declare_lint_pass!(InternalEqTraitMethodImpls => [INTERNAL_EQ_TRAIT_METHOD_IMPLS]);
3233+
3234+
impl<'tcx> LateLintPass<'tcx> for InternalEqTraitMethodImpls {
3235+
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx rustc_hir::ImplItem<'tcx>) {
3236+
if let ImplItemImplKind::Trait { defaultness: _, trait_item_def_id: Ok(trait_item_def_id) } =
3237+
item.impl_kind
3238+
&& item.ident.name == sym::assert_receiver_is_total_eq
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
@@ -252,6 +252,7 @@ late_lint_methods!(
252252
FunctionCastsAsInteger: FunctionCastsAsInteger,
253253
CheckTransmutes: CheckTransmutes,
254254
LifetimeSyntax: LifetimeSyntax,
255+
InternalEqTraitMethodImpls: InternalEqTraitMethodImpls,
255256
]
256257
]
257258
);

compiler/rustc_lint/src/lints.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3312,3 +3312,8 @@ pub(crate) struct DocTestUnknown {
33123312
#[derive(LintDiagnostic)]
33133313
#[diag(lint_doc_test_literal)]
33143314
pub(crate) struct DocTestLiteral;
3315+
3316+
#[derive(LintDiagnostic)]
3317+
#[diag(lint_eq_internal_method)]
3318+
#[note]
3319+
pub(crate) struct EqInternalMethodImplemented;

compiler/rustc_span/src/symbol.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1267,6 +1267,7 @@ symbols! {
12671267
integer_: "integer", // underscore to avoid clashing with the function `sym::integer` below
12681268
integral,
12691269
internal,
1270+
internal_eq_trait_method_impls,
12701271
internal_features,
12711272
into_async_iter_into_iter,
12721273
into_future,

library/core/src/cmp.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,8 @@ 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.94.0", note = "implementation detail of `#[derive(Eq)]`")]
348350
fn assert_receiver_is_total_eq(&self) {}
349351
}
350352

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ impl ::core::cmp::Eq for Empty {
6666
#[inline]
6767
#[doc(hidden)]
6868
#[coverage(off)]
69+
#[allow(internal_eq_trait_method_impls)]
6970
fn assert_receiver_is_total_eq(&self) -> () {}
7071
}
7172
#[automatically_derived]
@@ -143,6 +144,7 @@ impl ::core::cmp::Eq for Point {
143144
#[inline]
144145
#[doc(hidden)]
145146
#[coverage(off)]
147+
#[allow(internal_eq_trait_method_impls)]
146148
fn assert_receiver_is_total_eq(&self) -> () {
147149
let _: ::core::cmp::AssertParamIsEq<u32>;
148150
}
@@ -231,6 +233,7 @@ impl ::core::cmp::Eq for PackedPoint {
231233
#[inline]
232234
#[doc(hidden)]
233235
#[coverage(off)]
236+
#[allow(internal_eq_trait_method_impls)]
234237
fn assert_receiver_is_total_eq(&self) -> () {
235238
let _: ::core::cmp::AssertParamIsEq<u32>;
236239
}
@@ -314,6 +317,7 @@ impl ::core::cmp::Eq for TupleSingleField {
314317
#[inline]
315318
#[doc(hidden)]
316319
#[coverage(off)]
320+
#[allow(internal_eq_trait_method_impls)]
317321
fn assert_receiver_is_total_eq(&self) -> () {
318322
let _: ::core::cmp::AssertParamIsEq<u32>;
319323
}
@@ -389,6 +393,7 @@ impl ::core::cmp::Eq for SingleField {
389393
#[inline]
390394
#[doc(hidden)]
391395
#[coverage(off)]
396+
#[allow(internal_eq_trait_method_impls)]
392397
fn assert_receiver_is_total_eq(&self) -> () {
393398
let _: ::core::cmp::AssertParamIsEq<bool>;
394399
}
@@ -494,6 +499,7 @@ impl ::core::cmp::Eq for Big {
494499
#[inline]
495500
#[doc(hidden)]
496501
#[coverage(off)]
502+
#[allow(internal_eq_trait_method_impls)]
497503
fn assert_receiver_is_total_eq(&self) -> () {
498504
let _: ::core::cmp::AssertParamIsEq<u32>;
499505
}
@@ -758,6 +764,7 @@ impl ::core::cmp::Eq for Unsized {
758764
#[inline]
759765
#[doc(hidden)]
760766
#[coverage(off)]
767+
#[allow(internal_eq_trait_method_impls)]
761768
fn assert_receiver_is_total_eq(&self) -> () {
762769
let _: ::core::cmp::AssertParamIsEq<[u32]>;
763770
}
@@ -853,6 +860,7 @@ impl<T: ::core::cmp::Eq + Trait, U: ::core::cmp::Eq> ::core::cmp::Eq for
853860
#[inline]
854861
#[doc(hidden)]
855862
#[coverage(off)]
863+
#[allow(internal_eq_trait_method_impls)]
856864
fn assert_receiver_is_total_eq(&self) -> () {
857865
let _: ::core::cmp::AssertParamIsEq<T>;
858866
let _: ::core::cmp::AssertParamIsEq<T::A>;
@@ -975,6 +983,7 @@ impl<T: ::core::cmp::Eq + ::core::marker::Copy + Trait, U: ::core::cmp::Eq +
975983
#[inline]
976984
#[doc(hidden)]
977985
#[coverage(off)]
986+
#[allow(internal_eq_trait_method_impls)]
978987
fn assert_receiver_is_total_eq(&self) -> () {
979988
let _: ::core::cmp::AssertParamIsEq<T>;
980989
let _: ::core::cmp::AssertParamIsEq<T::A>;
@@ -1060,6 +1069,7 @@ impl ::core::cmp::Eq for Enum0 {
10601069
#[inline]
10611070
#[doc(hidden)]
10621071
#[coverage(off)]
1072+
#[allow(internal_eq_trait_method_impls)]
10631073
fn assert_receiver_is_total_eq(&self) -> () {}
10641074
}
10651075
#[automatically_derived]
@@ -1130,6 +1140,7 @@ impl ::core::cmp::Eq for Enum1 {
11301140
#[inline]
11311141
#[doc(hidden)]
11321142
#[coverage(off)]
1143+
#[allow(internal_eq_trait_method_impls)]
11331144
fn assert_receiver_is_total_eq(&self) -> () {
11341145
let _: ::core::cmp::AssertParamIsEq<u32>;
11351146
}
@@ -1196,6 +1207,7 @@ impl ::core::cmp::Eq for Fieldless1 {
11961207
#[inline]
11971208
#[doc(hidden)]
11981209
#[coverage(off)]
1210+
#[allow(internal_eq_trait_method_impls)]
11991211
fn assert_receiver_is_total_eq(&self) -> () {}
12001212
}
12011213
#[automatically_derived]
@@ -1273,6 +1285,7 @@ impl ::core::cmp::Eq for Fieldless {
12731285
#[inline]
12741286
#[doc(hidden)]
12751287
#[coverage(off)]
1288+
#[allow(internal_eq_trait_method_impls)]
12761289
fn assert_receiver_is_total_eq(&self) -> () {}
12771290
}
12781291
#[automatically_derived]
@@ -1383,6 +1396,7 @@ impl ::core::cmp::Eq for Mixed {
13831396
#[inline]
13841397
#[doc(hidden)]
13851398
#[coverage(off)]
1399+
#[allow(internal_eq_trait_method_impls)]
13861400
fn assert_receiver_is_total_eq(&self) -> () {
13871401
let _: ::core::cmp::AssertParamIsEq<u32>;
13881402
let _: ::core::cmp::AssertParamIsEq<Option<u32>>;
@@ -1581,6 +1595,7 @@ impl ::core::cmp::Eq for Fielded {
15811595
#[inline]
15821596
#[doc(hidden)]
15831597
#[coverage(off)]
1598+
#[allow(internal_eq_trait_method_impls)]
15841599
fn assert_receiver_is_total_eq(&self) -> () {
15851600
let _: ::core::cmp::AssertParamIsEq<u32>;
15861601
let _: ::core::cmp::AssertParamIsEq<bool>;
@@ -1703,6 +1718,7 @@ impl<T: ::core::cmp::Eq, U: ::core::cmp::Eq> ::core::cmp::Eq for
17031718
#[inline]
17041719
#[doc(hidden)]
17051720
#[coverage(off)]
1721+
#[allow(internal_eq_trait_method_impls)]
17061722
fn assert_receiver_is_total_eq(&self) -> () {
17071723
let _: ::core::cmp::AssertParamIsEq<T>;
17081724
let _: ::core::cmp::AssertParamIsEq<U>;
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#![deny(deprecated, internal_eq_trait_method_impls)]
2+
pub struct Bad;
3+
4+
impl PartialEq for Bad {
5+
fn eq(&self, _: &Self) -> bool {
6+
true
7+
}
8+
}
9+
10+
impl Eq for Bad {
11+
fn assert_receiver_is_total_eq(&self) {}
12+
//~^ ERROR: `Eq::assert_receiver_is_total_eq` should never be implemented by hand [internal_eq_trait_method_impls]
13+
//~| WARN: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
14+
}
15+
16+
#[derive(PartialEq, Eq)]
17+
pub struct Good;
18+
19+
#[derive(PartialEq)]
20+
pub struct Good2;
21+
22+
impl Eq for Good2 {}
23+
24+
pub struct Foo;
25+
26+
pub trait SameName {
27+
fn assert_receiver_is_total_eq(&self) {}
28+
}
29+
30+
impl SameName for Foo {
31+
fn assert_receiver_is_total_eq(&self) {}
32+
}
33+
34+
pub fn main() {
35+
Foo.assert_receiver_is_total_eq();
36+
Good2.assert_receiver_is_total_eq();
37+
//~^ ERROR: use of deprecated method `std::cmp::Eq::assert_receiver_is_total_eq`: implementation detail of `#[derive(Eq)]` [deprecated]
38+
Good.assert_receiver_is_total_eq();
39+
//~^ ERROR: use of deprecated method `std::cmp::Eq::assert_receiver_is_total_eq`: implementation detail of `#[derive(Eq)]` [deprecated]
40+
Bad.assert_receiver_is_total_eq();
41+
//~^ ERROR: use of deprecated method `std::cmp::Eq::assert_receiver_is_total_eq`: implementation detail of `#[derive(Eq)]` [deprecated]
42+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
error: use of deprecated method `std::cmp::Eq::assert_receiver_is_total_eq`: implementation detail of `#[derive(Eq)]`
2+
--> $DIR/internal_eq_trait_method_impls.rs:36:11
3+
|
4+
LL | Good2.assert_receiver_is_total_eq();
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
note: the lint level is defined here
8+
--> $DIR/internal_eq_trait_method_impls.rs:1:9
9+
|
10+
LL | #![deny(deprecated, internal_eq_trait_method_impls)]
11+
| ^^^^^^^^^^
12+
13+
error: use of deprecated method `std::cmp::Eq::assert_receiver_is_total_eq`: implementation detail of `#[derive(Eq)]`
14+
--> $DIR/internal_eq_trait_method_impls.rs:38:10
15+
|
16+
LL | Good.assert_receiver_is_total_eq();
17+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
18+
19+
error: use of deprecated method `std::cmp::Eq::assert_receiver_is_total_eq`: implementation detail of `#[derive(Eq)]`
20+
--> $DIR/internal_eq_trait_method_impls.rs:40:9
21+
|
22+
LL | Bad.assert_receiver_is_total_eq();
23+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
24+
25+
error: `Eq::assert_receiver_is_total_eq` should never be implemented by hand
26+
--> $DIR/internal_eq_trait_method_impls.rs:11:5
27+
|
28+
LL | fn assert_receiver_is_total_eq(&self) {}
29+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
30+
|
31+
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
32+
= note: for more information, see issue #150000 <https://github.com/rust-lang/rust/issues/150000>
33+
= note: this method is only used to add checks to the `Eq` derive macro
34+
note: the lint level is defined here
35+
--> $DIR/internal_eq_trait_method_impls.rs:1:21
36+
|
37+
LL | #![deny(deprecated, internal_eq_trait_method_impls)]
38+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
39+
40+
error: aborting due to 4 previous errors
41+

0 commit comments

Comments
 (0)