Skip to content

Commit 74d03a9

Browse files
authored
Unrolled build for #149978
Rollup merge of #149978 - cyrgani:no-eq-assert-receiver-method, r=madsmtm deprecate `Eq::assert_receiver_is_total_eq` and emit FCW on manual impls The `Eq::assert_receiver_is_total_eq` method is purely meant as an implementation detail by `#[derive(Eq)]` to add checks that all fields of the type the derive is applied to also implement `Eq`. The method is already `#[doc(hidden)]` and has a comment saying `// This should never be implemented by hand.`. Unfortunately, it has been stable since 1.0 and there are some cases on GitHub (https://github.com/search?q=assert_receiver_is_total_eq&type=code) where people have implemented this method manually, sometimes even with actual code in the method body (example: https://github.com/Shresht7/codecrafters-redis-rust/blob/31f0ec453c504b4ab053a7b1c3ff548ff36a9db5/src/parser/resp/types.rs#L255). To prevent further confusion from this, this PR is deprecating the method and adds a FCW when it is manually implemented (this is necessary as the deprecation warning is not emitted when the method is implemented, only when it is called). This is similar to what was previously done with the `soft_unstable` lint (#64266). See also rust-lang/libs-team#704.
2 parents 1ed4882 + f1ec10e commit 74d03a9

10 files changed

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

compiler/rustc_lint/src/lib.rs

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

compiler/rustc_lint/src/lints.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3932,3 +3932,8 @@ pub(crate) struct MalformedOnConstAttrLint {
39323932
#[label("invalid option found here")]
39333933
pub span: Span,
39343934
}
3935+
3936+
#[derive(LintDiagnostic)]
3937+
#[diag("`Eq::assert_receiver_is_total_eq` should never be implemented by hand")]
3938+
#[note("this method was used to add checks to the `Eq` derive macro")]
3939+
pub(crate) struct EqInternalMethodImplemented;

compiler/rustc_span/src/symbol.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,7 @@ symbols! {
432432
assert,
433433
assert_eq,
434434
assert_eq_macro,
435+
assert_fields_are_eq,
435436
assert_inhabited,
436437
assert_macro,
437438
assert_mem_uninitialized_valid,
@@ -1089,6 +1090,7 @@ symbols! {
10891090
integer_: "integer", // underscore to avoid clashing with the function `sym::integer` below
10901091
integral,
10911092
internal,
1093+
internal_eq_trait_method_impls,
10921094
internal_features,
10931095
into_async_iter_into_iter,
10941096
into_future,

library/core/src/cmp.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -336,16 +336,24 @@ 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
340-
// type implements `Eq` itself. The current deriving infrastructure means doing this assertion
341-
// without using a method on this trait is nearly impossible.
339+
// This method was used solely by `#[derive(Eq)]` to assert that every component of a
340+
// type implements `Eq` itself.
342341
//
343342
// This should never be implemented by hand.
344343
#[doc(hidden)]
345344
#[coverage(off)]
346345
#[inline]
347346
#[stable(feature = "rust1", since = "1.0.0")]
347+
#[rustc_diagnostic_item = "assert_receiver_is_total_eq"]
348+
#[deprecated(since = "1.95.0", note = "implementation detail of `#[derive(Eq)]`")]
348349
fn assert_receiver_is_total_eq(&self) {}
350+
351+
// FIXME (#152504): this method is used solely by `#[derive(Eq)]` to assert that
352+
// every component of a type implements `Eq` itself. It will be removed again soon.
353+
#[doc(hidden)]
354+
#[coverage(off)]
355+
#[unstable(feature = "derive_eq_internals", issue = "none")]
356+
fn assert_fields_are_eq(&self) {}
349357
}
350358

351359
/// 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)