Skip to content

Commit bf5d08f

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

10 files changed

Lines changed: 173 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/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, 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,63 @@ 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+
&& item.ident.name == sym::assert_receiver_is_total_eq
3240+
&& cx.tcx.is_diagnostic_item(sym::assert_receiver_is_total_eq, trait_item_def_id)
3241+
{
3242+
cx.emit_span_lint(
3243+
INTERNAL_EQ_TRAIT_METHOD_IMPLS,
3244+
item.span,
3245+
EqInternalMethodImplemented,
3246+
);
3247+
}
3248+
}
3249+
}

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: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1285,6 +1285,7 @@ symbols! {
12851285
integer_: "integer", // underscore to avoid clashing with the function `sym::integer` below
12861286
integral,
12871287
internal,
1288+
internal_eq_trait_method_impls,
12881289
internal_features,
12891290
into_async_iter_into_iter,
12901291
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.95.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
@@ -65,6 +65,7 @@ impl ::core::cmp::Eq for Empty {
6565
#[inline]
6666
#[doc(hidden)]
6767
#[coverage(off)]
68+
#[allow(internal_eq_trait_method_impls)]
6869
fn assert_receiver_is_total_eq(&self) {}
6970
}
7071
#[automatically_derived]
@@ -142,6 +143,7 @@ impl ::core::cmp::Eq for Point {
142143
#[inline]
143144
#[doc(hidden)]
144145
#[coverage(off)]
146+
#[allow(internal_eq_trait_method_impls)]
145147
fn assert_receiver_is_total_eq(&self) {
146148
let _: ::core::cmp::AssertParamIsEq<u32>;
147149
}
@@ -230,6 +232,7 @@ impl ::core::cmp::Eq for PackedPoint {
230232
#[inline]
231233
#[doc(hidden)]
232234
#[coverage(off)]
235+
#[allow(internal_eq_trait_method_impls)]
233236
fn assert_receiver_is_total_eq(&self) {
234237
let _: ::core::cmp::AssertParamIsEq<u32>;
235238
}
@@ -313,6 +316,7 @@ impl ::core::cmp::Eq for TupleSingleField {
313316
#[inline]
314317
#[doc(hidden)]
315318
#[coverage(off)]
319+
#[allow(internal_eq_trait_method_impls)]
316320
fn assert_receiver_is_total_eq(&self) {
317321
let _: ::core::cmp::AssertParamIsEq<u32>;
318322
}
@@ -388,6 +392,7 @@ impl ::core::cmp::Eq for SingleField {
388392
#[inline]
389393
#[doc(hidden)]
390394
#[coverage(off)]
395+
#[allow(internal_eq_trait_method_impls)]
391396
fn assert_receiver_is_total_eq(&self) {
392397
let _: ::core::cmp::AssertParamIsEq<bool>;
393398
}
@@ -493,6 +498,7 @@ impl ::core::cmp::Eq for Big {
493498
#[inline]
494499
#[doc(hidden)]
495500
#[coverage(off)]
501+
#[allow(internal_eq_trait_method_impls)]
496502
fn assert_receiver_is_total_eq(&self) {
497503
let _: ::core::cmp::AssertParamIsEq<u32>;
498504
}
@@ -757,6 +763,7 @@ impl ::core::cmp::Eq for Unsized {
757763
#[inline]
758764
#[doc(hidden)]
759765
#[coverage(off)]
766+
#[allow(internal_eq_trait_method_impls)]
760767
fn assert_receiver_is_total_eq(&self) {
761768
let _: ::core::cmp::AssertParamIsEq<[u32]>;
762769
}
@@ -852,6 +859,7 @@ impl<T: ::core::cmp::Eq + Trait, U: ::core::cmp::Eq> ::core::cmp::Eq for
852859
#[inline]
853860
#[doc(hidden)]
854861
#[coverage(off)]
862+
#[allow(internal_eq_trait_method_impls)]
855863
fn assert_receiver_is_total_eq(&self) {
856864
let _: ::core::cmp::AssertParamIsEq<T>;
857865
let _: ::core::cmp::AssertParamIsEq<T::A>;
@@ -974,6 +982,7 @@ impl<T: ::core::cmp::Eq + ::core::marker::Copy + Trait, U: ::core::cmp::Eq +
974982
#[inline]
975983
#[doc(hidden)]
976984
#[coverage(off)]
985+
#[allow(internal_eq_trait_method_impls)]
977986
fn assert_receiver_is_total_eq(&self) {
978987
let _: ::core::cmp::AssertParamIsEq<T>;
979988
let _: ::core::cmp::AssertParamIsEq<T::A>;
@@ -1059,6 +1068,7 @@ impl ::core::cmp::Eq for Enum0 {
10591068
#[inline]
10601069
#[doc(hidden)]
10611070
#[coverage(off)]
1071+
#[allow(internal_eq_trait_method_impls)]
10621072
fn assert_receiver_is_total_eq(&self) {}
10631073
}
10641074
#[automatically_derived]
@@ -1129,6 +1139,7 @@ impl ::core::cmp::Eq for Enum1 {
11291139
#[inline]
11301140
#[doc(hidden)]
11311141
#[coverage(off)]
1142+
#[allow(internal_eq_trait_method_impls)]
11321143
fn assert_receiver_is_total_eq(&self) {
11331144
let _: ::core::cmp::AssertParamIsEq<u32>;
11341145
}
@@ -1195,6 +1206,7 @@ impl ::core::cmp::Eq for Fieldless1 {
11951206
#[inline]
11961207
#[doc(hidden)]
11971208
#[coverage(off)]
1209+
#[allow(internal_eq_trait_method_impls)]
11981210
fn assert_receiver_is_total_eq(&self) {}
11991211
}
12001212
#[automatically_derived]
@@ -1272,6 +1284,7 @@ impl ::core::cmp::Eq for Fieldless {
12721284
#[inline]
12731285
#[doc(hidden)]
12741286
#[coverage(off)]
1287+
#[allow(internal_eq_trait_method_impls)]
12751288
fn assert_receiver_is_total_eq(&self) {}
12761289
}
12771290
#[automatically_derived]
@@ -1382,6 +1395,7 @@ impl ::core::cmp::Eq for Mixed {
13821395
#[inline]
13831396
#[doc(hidden)]
13841397
#[coverage(off)]
1398+
#[allow(internal_eq_trait_method_impls)]
13851399
fn assert_receiver_is_total_eq(&self) {
13861400
let _: ::core::cmp::AssertParamIsEq<u32>;
13871401
let _: ::core::cmp::AssertParamIsEq<Option<u32>>;
@@ -1580,6 +1594,7 @@ impl ::core::cmp::Eq for Fielded {
15801594
#[inline]
15811595
#[doc(hidden)]
15821596
#[coverage(off)]
1597+
#[allow(internal_eq_trait_method_impls)]
15831598
fn assert_receiver_is_total_eq(&self) {
15841599
let _: ::core::cmp::AssertParamIsEq<u32>;
15851600
let _: ::core::cmp::AssertParamIsEq<bool>;
@@ -1702,6 +1717,7 @@ impl<T: ::core::cmp::Eq, U: ::core::cmp::Eq> ::core::cmp::Eq for
17021717
#[inline]
17031718
#[doc(hidden)]
17041719
#[coverage(off)]
1720+
#[allow(internal_eq_trait_method_impls)]
17051721
fn assert_receiver_is_total_eq(&self) {
17061722
let _: ::core::cmp::AssertParamIsEq<T>;
17071723
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 #152336 <https://github.com/rust-lang/rust/issues/152336>
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+

tests/ui/stats/macro-stats.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ macro-stats #[derive(Hash)] 2 17 8.5
88
macro-stats q! 1 26 26.0 519 519.0
99
macro-stats #[derive(Ord)] 1 15 15.0 503 503.0
1010
macro-stats #[derive(Default)] 2 16 8.0 403 201.5
11-
macro-stats #[derive(Eq)] 1 11 11.0 319 319.0
11+
macro-stats #[derive(Eq)] 1 12 12.0 364 364.0
1212
macro-stats #[derive(Debug)] 1 8 8.0 277 277.0
1313
macro-stats #[derive(PartialEq)] 1 9 9.0 267 267.0
1414
macro-stats #[derive(Copy)] 1 2 2.0 61 61.0

0 commit comments

Comments
 (0)