Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions compiler/rustc_lint/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,9 @@ lint_enum_intrinsics_mem_variant =
the return value of `mem::variant_count` is unspecified when called with a non-enum type
.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

lint_eq_internal_method = `Eq::assert_receiver_is_total_eq` should never be implemented by hand
.note = this method is only used to add checks to the `Eq` derive macro

lint_expectation = this lint expectation is unfulfilled
.note = the `unfulfilled_lint_expectations` lint can't be expected and will always produce this message
.rationale = {$rationale}
Expand Down
66 changes: 63 additions & 3 deletions compiler/rustc_lint/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,11 @@ use rustc_ast_pretty::pprust::expr_to_string;
use rustc_attr_parsing::AttributeParser;
use rustc_errors::{Applicability, LintDiagnostic};
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;
Expand Down Expand Up @@ -59,7 +58,7 @@ use crate::lints::{
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,
Expand Down Expand Up @@ -3191,3 +3190,64 @@ impl EarlyLintPass for SpecialModuleName {
}
}
}

declare_lint! {
/// The `eq_assert_receiver_is_total_eq_impl` 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 exists so that `#[derive(Eq)]` can 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 #150000] for more details.
///
/// [issue #150000]: https://github.com/rust-lang/rust/issues/150000
pub EQ_ASSERT_RECEIVER_IS_TOTAL_EQ_IMPL,
Warn,
"manual implementation of the internal `Eq::assert_receiver_is_total_eq` method",
@future_incompatible = FutureIncompatibleInfo {
reason: fcw!(FutureReleaseError #150000),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: create a real tracking issue once there is consensus on this change

report_in_deps: false,
};
}

declare_lint_pass!(EqAssertReceiverIsTotalEq => [EQ_ASSERT_RECEIVER_IS_TOTAL_EQ_IMPL]);

impl<'tcx> LateLintPass<'tcx> for EqAssertReceiverIsTotalEq {
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
&& item.ident.name == sym::assert_receiver_is_total_eq
&& cx.tcx.is_diagnostic_item(sym::assert_receiver_is_total_eq, trait_item_def_id)
&& !item.span.from_expansion()
{
cx.emit_span_lint(
EQ_ASSERT_RECEIVER_IS_TOTAL_EQ_IMPL,
item.span,
EqInternalMethodImplemented,
);
}
}
}
1 change: 1 addition & 0 deletions compiler/rustc_lint/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@ late_lint_methods!(
FunctionCastsAsInteger: FunctionCastsAsInteger,
CheckTransmutes: CheckTransmutes,
LifetimeSyntax: LifetimeSyntax,
EqAssertReceiverIsTotalEq: EqAssertReceiverIsTotalEq,
]
]
);
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_lint/src/lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3298,3 +3298,8 @@ pub(crate) struct DocTestUnknown {
#[derive(LintDiagnostic)]
#[diag(lint_doc_test_literal)]
pub(crate) struct DocTestLiteral;

#[derive(LintDiagnostic)]
#[diag(lint_eq_internal_method)]
#[note]
pub(crate) struct EqInternalMethodImplemented;
2 changes: 2 additions & 0 deletions library/core/src/cmp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,8 @@ pub const trait Eq: [const] PartialEq<Self> + PointeeSized {
#[coverage(off)]
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_diagnostic_item = "assert_receiver_is_total_eq"]
#[deprecated(since = "1.94.0", note = "implementation detail of `#[derive(Eq)]`")]
fn assert_receiver_is_total_eq(&self) {}
}

Expand Down
42 changes: 42 additions & 0 deletions tests/ui/deriving/eq_assert_receiver_is_total_eq_impl.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#![deny(deprecated, eq_assert_receiver_is_total_eq_impl)]
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 [eq_assert_receiver_is_total_eq_impl]
//~| 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]
}
41 changes: 41 additions & 0 deletions tests/ui/deriving/eq_assert_receiver_is_total_eq_impl.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
error: use of deprecated method `std::cmp::Eq::assert_receiver_is_total_eq`: implementation detail of `#[derive(Eq)]`
--> $DIR/eq_assert_receiver_is_total_eq_impl.rs:36:11
|
LL | Good2.assert_receiver_is_total_eq();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: the lint level is defined here
--> $DIR/eq_assert_receiver_is_total_eq_impl.rs:1:9
|
LL | #![deny(deprecated, eq_assert_receiver_is_total_eq_impl)]
| ^^^^^^^^^^

error: use of deprecated method `std::cmp::Eq::assert_receiver_is_total_eq`: implementation detail of `#[derive(Eq)]`
--> $DIR/eq_assert_receiver_is_total_eq_impl.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/eq_assert_receiver_is_total_eq_impl.rs:40:9
|
LL | Bad.assert_receiver_is_total_eq();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: `Eq::assert_receiver_is_total_eq` should never be implemented by hand
--> $DIR/eq_assert_receiver_is_total_eq_impl.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 #150000 <https://github.com/rust-lang/rust/issues/150000>
= note: this method is only used to add checks to the `Eq` derive macro
note: the lint level is defined here
--> $DIR/eq_assert_receiver_is_total_eq_impl.rs:1:21
|
LL | #![deny(deprecated, eq_assert_receiver_is_total_eq_impl)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to 4 previous errors

Loading