Skip to content

Commit 81dc07d

Browse files
committed
deprecate Eq::assert_receiver_is_total_eq and emit a FCW on manual implementations
1 parent 3391c01 commit 81dc07d

File tree

7 files changed

+157
-3
lines changed

7 files changed

+157
-3
lines changed

compiler/rustc_lint/messages.ftl

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

compiler/rustc_lint/src/builtin.rs

Lines changed: 63 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,
@@ -3191,3 +3190,64 @@ impl EarlyLintPass for SpecialModuleName {
31913190
}
31923191
}
31933192
}
3193+
3194+
declare_lint! {
3195+
/// The `eq_assert_receiver_is_total_eq_impl` lint detects manual
3196+
/// implementations of `Eq::assert_receiver_is_total_eq`.
3197+
///
3198+
/// ### Example
3199+
///
3200+
/// ```rust
3201+
/// #[derive(PartialEq)]
3202+
/// pub struct Foo;
3203+
///
3204+
/// impl Eq for Foo {
3205+
/// fn assert_receiver_is_total_eq(&self) {}
3206+
/// }
3207+
/// ```
3208+
///
3209+
/// {{produces}}
3210+
///
3211+
/// ### Explanation
3212+
///
3213+
/// This method exists so that `#[derive(Eq)]` can check that all
3214+
/// fields of a type implement `Eq`. Other users were never supposed
3215+
/// to implement it and it was hidden from documentation.
3216+
///
3217+
/// Unfortunately, it was not explicitly marked as unstable and some
3218+
/// people have now mistakenly assumed they had to implement this method.
3219+
///
3220+
/// As the method is never called by the standard library, you can safely
3221+
/// remove any implementations of the method and just write `impl Eq for Foo {}`.
3222+
///
3223+
/// This is a [future-incompatible] lint to transition this to a hard
3224+
/// error in the future. See [issue #150000] for more details.
3225+
///
3226+
/// [issue #150000]: https://github.com/rust-lang/rust/issues/150000
3227+
pub EQ_ASSERT_RECEIVER_IS_TOTAL_EQ_IMPL,
3228+
Warn,
3229+
"manual implementation of the internal `Eq::assert_receiver_is_total_eq` method",
3230+
@future_incompatible = FutureIncompatibleInfo {
3231+
reason: fcw!(FutureReleaseError #150000),
3232+
report_in_deps: false,
3233+
};
3234+
}
3235+
3236+
declare_lint_pass!(EqAssertReceiverIsTotalEq => [EQ_ASSERT_RECEIVER_IS_TOTAL_EQ_IMPL]);
3237+
3238+
impl<'tcx> LateLintPass<'tcx> for EqAssertReceiverIsTotalEq {
3239+
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx rustc_hir::ImplItem<'tcx>) {
3240+
if let ImplItemImplKind::Trait { defaultness: _, trait_item_def_id: Ok(trait_item_def_id) } =
3241+
item.impl_kind
3242+
&& item.ident.name == sym::assert_receiver_is_total_eq
3243+
&& cx.tcx.is_diagnostic_item(sym::assert_receiver_is_total_eq, trait_item_def_id)
3244+
&& !item.span.from_expansion()
3245+
{
3246+
cx.emit_span_lint(
3247+
EQ_ASSERT_RECEIVER_IS_TOTAL_EQ_IMPL,
3248+
item.span,
3249+
EqInternalMethodImplemented,
3250+
);
3251+
}
3252+
}
3253+
}

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+
EqAssertReceiverIsTotalEq: EqAssertReceiverIsTotalEq,
252253
]
253254
]
254255
);

compiler/rustc_lint/src/lints.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3298,3 +3298,8 @@ pub(crate) struct DocTestUnknown {
32983298
#[derive(LintDiagnostic)]
32993299
#[diag(lint_doc_test_literal)]
33003300
pub(crate) struct DocTestLiteral;
3301+
3302+
#[derive(LintDiagnostic)]
3303+
#[diag(lint_eq_internal_method)]
3304+
#[note]
3305+
pub(crate) struct EqInternalMethodImplemented;

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

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#![deny(deprecated, eq_assert_receiver_is_total_eq_impl)]
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 [eq_assert_receiver_is_total_eq_impl]
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/eq_assert_receiver_is_total_eq_impl.rs:36:11
3+
|
4+
LL | Good2.assert_receiver_is_total_eq();
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
note: the lint level is defined here
8+
--> $DIR/eq_assert_receiver_is_total_eq_impl.rs:1:9
9+
|
10+
LL | #![deny(deprecated, eq_assert_receiver_is_total_eq_impl)]
11+
| ^^^^^^^^^^
12+
13+
error: use of deprecated method `std::cmp::Eq::assert_receiver_is_total_eq`: implementation detail of `#[derive(Eq)]`
14+
--> $DIR/eq_assert_receiver_is_total_eq_impl.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/eq_assert_receiver_is_total_eq_impl.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/eq_assert_receiver_is_total_eq_impl.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/eq_assert_receiver_is_total_eq_impl.rs:1:21
36+
|
37+
LL | #![deny(deprecated, eq_assert_receiver_is_total_eq_impl)]
38+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
39+
40+
error: aborting due to 4 previous errors
41+

0 commit comments

Comments
 (0)