From 7d3d1360374a824a7cea003b194af30cd51100e8 Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Wed, 4 Feb 2026 22:14:41 +0100 Subject: [PATCH 1/6] WIP: implement lint & fix it in std --- compiler/rustc_lint/src/lib.rs | 4 + compiler/rustc_lint/src/receiver.rs | 145 ++++++++++++++++++ library/alloc/src/borrow.rs | 2 + .../alloc/src/collections/binary_heap/mod.rs | 3 + library/core/src/pin.rs | 6 + 5 files changed, 160 insertions(+) create mode 100644 compiler/rustc_lint/src/receiver.rs diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 80b32645a8953..1d108b4dc99e6 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -69,6 +69,7 @@ mod pass_by_value; mod passes; mod precedence; mod ptr_nulls; +mod receiver; mod redundant_semicolon; mod reference_casting; mod shadowed_into_iter; @@ -139,6 +140,8 @@ pub use rustc_errors::BufferedEarlyLint; pub use rustc_session::lint::Level::{self, *}; pub use rustc_session::lint::{FutureIncompatibleInfo, Lint, LintId, LintPass, LintVec}; +use crate::receiver::InherentMethodOnReceiver; + rustc_fluent_macro::fluent_messages! { "../messages.ftl" } pub fn provide(providers: &mut Providers) { @@ -204,6 +207,7 @@ late_lint_methods!( LetUnderscore: LetUnderscore, InvalidReferenceCasting: InvalidReferenceCasting, ImplicitAutorefs: ImplicitAutorefs, + InherentMethodOnReceiver: InherentMethodOnReceiver, // Depends on referenced function signatures in expressions UnusedResults: UnusedResults, UnitBindings: UnitBindings, diff --git a/compiler/rustc_lint/src/receiver.rs b/compiler/rustc_lint/src/receiver.rs new file mode 100644 index 0000000000000..f2dcbe2b66acc --- /dev/null +++ b/compiler/rustc_lint/src/receiver.rs @@ -0,0 +1,145 @@ +use rustc_hir::def_id::DefId; +use rustc_hir::{ImplItemImplKind, ImplItemKind, LangItem, PatKind}; +use rustc_infer::infer::InferCtxt; +use rustc_middle::ty::{self, AliasTy, AliasTyKind, ParamEnv, Ty, TyCtxt, TypingEnv, TypingMode}; +use rustc_session::{declare_lint, declare_lint_pass}; +use rustc_span::{DUMMY_SP, kw, sym}; +use rustc_trait_selection::infer::{InferCtxtExt, TyCtxtInferExt}; + +use crate::{LateLintPass, LintContext}; + +declare_lint! { + INHERENT_METHOD_ON_RECEIVER, + Warn, + "inherent methods on types that implement `Deref` or `Receiver` shadow methods of their target", +} + +declare_lint_pass!(InherentMethodOnReceiver => [INHERENT_METHOD_ON_RECEIVER]); + +// FIXME: +// - make this lint nightly-only + +impl<'tcx> LateLintPass<'tcx> for InherentMethodOnReceiver { + fn check_impl_item( + &mut self, + cx: &crate::LateContext<'tcx>, + item: &'tcx rustc_hir::ImplItem<'tcx>, + ) { + let ImplItemImplKind::Inherent { .. } = item.impl_kind else { + return; + }; + if !cx.tcx.effective_visibilities(()).is_exported(item.owner_id.def_id) { + return; + } + let Some(impl_id) = cx.tcx.inherent_impl_of_assoc(item.owner_id.def_id.to_def_id()) else { + return; + }; + let ImplItemKind::Fn(signature, body) = item.kind else { + return; + }; + + let Some(first_param) = cx.tcx.hir_body(body).params.first() else { + return; + }; + + if !signature.decl.implicit_self.has_implicit_self() { + let PatKind::Binding(_, _, ident, None) = first_param.pat.kind else { + return; + }; + if ident.name != kw::SelfLower { + return; + } + } + let self_ty: ty::EarlyBinder<'tcx, Ty<'tcx>> = cx.tcx.type_of(impl_id); + let self_ty = self_ty.instantiate_identity(); + let infcx: InferCtxt<'tcx> = cx.tcx.infer_ctxt().build(TypingMode::non_body_analysis()); + if let Some(CheckResult { impl_plus_target }) = + check(cx.tcx, &infcx, cx.typing_env(), cx.param_env, self_ty) + { + cx.span_lint(INHERENT_METHOD_ON_RECEIVER, first_param.span, |lint| { + for (impl_id, target_id) in impl_plus_target { + lint.span_label(cx.tcx.def_span(impl_id), "trait implemented here"); + lint.span_label(cx.tcx.def_span(target_id), "with `Target` set here"); + } + lint.primary_message( + "inherent methods on types that implement `Deref` or `Receiver` shadow \ + methods of their target", + ); + }); + } + } +} + +struct CheckResult { + impl_plus_target: Vec<(DefId, DefId)>, +} + +fn check<'tcx>( + tcx: TyCtxt<'tcx>, + infcx: &InferCtxt<'tcx>, + typing_env: TypingEnv<'tcx>, + param_env: ParamEnv<'tcx>, + ty: Ty<'tcx>, +) -> Option { + let deref_trait = tcx.require_lang_item(LangItem::Deref, DUMMY_SP); + let deref_target = tcx.require_lang_item(LangItem::DerefTarget, DUMMY_SP); + let receiver_trait = tcx.require_lang_item(LangItem::Receiver, DUMMY_SP); + let receiver_target = tcx.require_lang_item(LangItem::ReceiverTarget, DUMMY_SP); + + if infcx.type_implements_trait(deref_trait, [ty], param_env).must_apply_modulo_regions() { + let target_ty = tcx.normalize_erasing_regions( + typing_env, + Ty::new_alias(tcx, AliasTyKind::Projection, AliasTy::new(tcx, deref_target, [ty])), + ); + if let ty::Param(_) = target_ty.kind() { + let (impl_id, target_id) = find_impl_and_target_id(tcx, deref_trait, ty); + return Some(CheckResult { impl_plus_target: vec![(impl_id, target_id)] }); + } + if let Some(mut result) = check(tcx, infcx, typing_env, param_env, target_ty) { + let (impl_id, target_id) = find_impl_and_target_id(tcx, deref_trait, ty); + result.impl_plus_target.push((impl_id, target_id)); + return Some(result); + } + } + if tcx.features().arbitrary_self_types() + && infcx.type_implements_trait(receiver_trait, [ty], param_env).must_apply_modulo_regions() + { + let target_ty = tcx.normalize_erasing_regions( + typing_env, + Ty::new_alias(tcx, AliasTyKind::Projection, AliasTy::new(tcx, receiver_target, [ty])), + ); + if let ty::Param(_) = target_ty.kind() { + let (impl_id, target_id) = find_impl_and_target_id(tcx, receiver_trait, ty); + return Some(CheckResult { impl_plus_target: vec![(impl_id, target_id)] }); + } + if let Some(mut result) = check(tcx, infcx, typing_env, param_env, target_ty) { + let (impl_id, target_id) = find_impl_and_target_id(tcx, receiver_trait, ty); + result.impl_plus_target.push((impl_id, target_id)); + return Some(result); + } + } + None +} + +fn find_impl_and_target_id<'tcx>( + tcx: TyCtxt<'tcx>, + trait_id: DefId, + ty: Ty<'tcx>, +) -> (DefId, DefId) { + let mut impl_id: Option = None; + tcx.for_each_relevant_impl(trait_id, ty, |did| { + if let Some(_impl_id) = impl_id.take() { + // let spans = vec![tcx.def_span(impl_id)]; + // println!("{impl_id:?} {did:?}"); + // span_bug!(spans, "found two impl blocks for the trait"); + } + impl_id = Some(did); + }); + let impl_id = impl_id.expect("trait not implemented?"); + + let targets: Vec<_> = + tcx.associated_items(impl_id).filter_by_name_unhygienic(sym::Target).collect(); + //assert!(targets.len() == 1, "did not find exactly one target"); + let target_id = targets[0].def_id; + (impl_id, target_id) +} diff --git a/library/alloc/src/borrow.rs b/library/alloc/src/borrow.rs index d1c7cd47da0f0..5157d89ab034f 100644 --- a/library/alloc/src/borrow.rs +++ b/library/alloc/src/borrow.rs @@ -280,6 +280,7 @@ impl Cow<'_, B> { /// ); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[expect(inherent_method_on_receiver, reason = "stable API cannot be changed")] pub fn to_mut(&mut self) -> &mut ::Owned { match *self { Borrowed(borrowed) => { @@ -328,6 +329,7 @@ impl Cow<'_, B> { /// ); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[expect(inherent_method_on_receiver, reason = "stable API cannot be changed")] pub fn into_owned(self) -> ::Owned { match self { Borrowed(borrowed) => borrowed.to_owned(), diff --git a/library/alloc/src/collections/binary_heap/mod.rs b/library/alloc/src/collections/binary_heap/mod.rs index 97aafbc7b6994..013385528f20d 100644 --- a/library/alloc/src/collections/binary_heap/mod.rs +++ b/library/alloc/src/collections/binary_heap/mod.rs @@ -408,6 +408,9 @@ impl<'a, T: Ord, A: Allocator> PeekMut<'a, T, A> { /// ``` #[unstable(feature = "binary_heap_peek_mut_refresh", issue = "138355")] #[must_use = "is equivalent to dropping and getting a new PeekMut except for return information"] + // FIXME(inherent_method_on_receiver): t-libs-api needs to weigh in on whether this should be an + // associated functions instead + #[allow(inherent_method_on_receiver)] pub fn refresh(&mut self) -> bool { // The length of the underlying heap is unchanged by sifting down. The value stored for leak // amplification thus remains accurate. We erase the leak amplification firstly because the diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index c4981facc04c3..e4e377a87c110 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -1531,6 +1531,7 @@ impl<'a, T: ?Sized> Pin<&'a T> { /// /// [`pin` module]: self#projections-and-structural-pinning #[stable(feature = "pin", since = "1.33.0")] + #[expect(inherent_method_on_receiver, reason = "stable API cannot be changed")] pub unsafe fn map_unchecked(self, func: F) -> Pin<&'a U> where U: ?Sized, @@ -1565,6 +1566,7 @@ impl<'a, T: ?Sized> Pin<&'a T> { #[must_use] #[rustc_const_stable(feature = "const_pin", since = "1.84.0")] #[stable(feature = "pin", since = "1.33.0")] + #[expect(inherent_method_on_receiver, reason = "stable API cannot be changed")] pub const fn get_ref(self) -> &'a T { self.pointer } @@ -1576,6 +1578,7 @@ impl<'a, T: ?Sized> Pin<&'a mut T> { #[must_use = "`self` will be dropped if the result is not used"] #[rustc_const_stable(feature = "const_pin", since = "1.84.0")] #[stable(feature = "pin", since = "1.33.0")] + #[expect(inherent_method_on_receiver, reason = "stable API cannot be changed")] pub const fn into_ref(self) -> Pin<&'a T> { Pin { pointer: self.pointer } } @@ -1593,6 +1596,7 @@ impl<'a, T: ?Sized> Pin<&'a mut T> { #[must_use = "`self` will be dropped if the result is not used"] #[stable(feature = "pin", since = "1.33.0")] #[rustc_const_stable(feature = "const_pin", since = "1.84.0")] + #[expect(inherent_method_on_receiver, reason = "stable API cannot be changed")] pub const fn get_mut(self) -> &'a mut T where T: Unpin, @@ -1614,6 +1618,7 @@ impl<'a, T: ?Sized> Pin<&'a mut T> { #[must_use = "`self` will be dropped if the result is not used"] #[stable(feature = "pin", since = "1.33.0")] #[rustc_const_stable(feature = "const_pin", since = "1.84.0")] + #[expect(inherent_method_on_receiver, reason = "stable API cannot be changed")] pub const unsafe fn get_unchecked_mut(self) -> &'a mut T { self.pointer } @@ -1635,6 +1640,7 @@ impl<'a, T: ?Sized> Pin<&'a mut T> { /// [`pin` module]: self#projections-and-structural-pinning #[must_use = "`self` will be dropped if the result is not used"] #[stable(feature = "pin", since = "1.33.0")] + #[expect(inherent_method_on_receiver, reason = "stable API cannot be changed")] pub unsafe fn map_unchecked_mut(self, func: F) -> Pin<&'a mut U> where U: ?Sized, From c5b9d1915f240d547d8407f13524fc0f1537ca6d Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Fri, 6 Feb 2026 12:00:04 +0100 Subject: [PATCH 2/6] add test for inherent_method_on_receiver --- .../lint/lint-inherent-method-on-receiver.rs | 99 +++++++++++++++++++ .../lint-inherent-method-on-receiver.stderr | 57 +++++++++++ 2 files changed, 156 insertions(+) create mode 100644 tests/ui/lint/lint-inherent-method-on-receiver.rs create mode 100644 tests/ui/lint/lint-inherent-method-on-receiver.stderr diff --git a/tests/ui/lint/lint-inherent-method-on-receiver.rs b/tests/ui/lint/lint-inherent-method-on-receiver.rs new file mode 100644 index 0000000000000..95799272c519e --- /dev/null +++ b/tests/ui/lint/lint-inherent-method-on-receiver.rs @@ -0,0 +1,99 @@ +#![feature(arbitrary_self_types)] +#![deny(inherent_method_on_receiver)] +#![allow(dead_code)] + +use std::ops::{Deref, Receiver}; + +// Base case to trigger the lint on `Deref` + +pub struct Thing(T); + +impl Deref for Thing { + type Target = T; + + fn deref(&self) -> &T { + &self.0 + } +} + +impl Thing { + pub fn method(&self) {} + //~^ ERROR: inherent methods on types that implement `Deref` or `Receiver` shadow methods of their target [inherent_method_on_receiver] +} + +// Base case to trigger the lint on `Receiver` + +pub struct Thing2(T); + +impl Receiver for Thing2 { + type Target = T; +} + +impl Thing2 { + pub fn method(&self) {} + //~^ ERROR: inherent methods on types that implement `Deref` or `Receiver` shadow methods of their target [inherent_method_on_receiver] +} + +// Also work with the `self: X` syntax + +pub struct Thing3(T); + +impl Receiver for Thing3 { + type Target = T; +} + +impl Thing3 { + pub fn method(self: Box) {} + //~^ ERROR: inherent methods on types that implement `Deref` or `Receiver` shadow methods of their target [inherent_method_on_receiver] +} + +// Not publicily accessible, no lint + +pub struct Thing4(T); + +impl Receiver for Thing4 { + type Target = T; +} + +impl Thing4 { + fn method(&self) {} +} + +// Not publicily accessible, no lint 2 + +pub(crate) struct Thing5(T); + +impl Receiver for Thing5 { + type Target = T; +} + +impl Thing5 { + fn method(&self) {} +} + +// Only true generics are triggering the lint + +pub(crate) struct Thing6(T); + +impl Receiver for Thing6 { + type Target = [T]; +} + +impl Thing6 { + pub fn method(&self) {} +} + +// Receiving to something concrete, but that then derefs to a generic still triggers the lint + +pub struct Thing7(T); + +impl Receiver for Thing7 { + type Target = Thing; +} + +impl Thing7 { + pub fn method(&self) {} + //~^ ERROR: inherent methods on types that implement `Deref` or `Receiver` shadow methods of their target [inherent_method_on_receiver] +} + +fn main() {} diff --git a/tests/ui/lint/lint-inherent-method-on-receiver.stderr b/tests/ui/lint/lint-inherent-method-on-receiver.stderr new file mode 100644 index 0000000000000..d0a1753845da1 --- /dev/null +++ b/tests/ui/lint/lint-inherent-method-on-receiver.stderr @@ -0,0 +1,57 @@ +error: inherent methods on types that implement `Deref` or `Receiver` shadow methods of their target + --> $DIR/lint-inherent-method-on-receiver.rs:20:19 + | +LL | impl Deref for Thing { + | -------------------------- trait implemented here +LL | type Target = T; + | ----------- with `Target` set here +... +LL | pub fn method(&self) {} + | ^^^^^ + | +note: the lint level is defined here + --> $DIR/lint-inherent-method-on-receiver.rs:2:9 + | +LL | #![deny(inherent_method_on_receiver)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: inherent methods on types that implement `Deref` or `Receiver` shadow methods of their target + --> $DIR/lint-inherent-method-on-receiver.rs:33:19 + | +LL | impl Receiver for Thing2 { + | ------------------------------ trait implemented here +LL | type Target = T; + | ----------- with `Target` set here +... +LL | pub fn method(&self) {} + | ^^^^^ + +error: inherent methods on types that implement `Deref` or `Receiver` shadow methods of their target + --> $DIR/lint-inherent-method-on-receiver.rs:46:19 + | +LL | impl Receiver for Thing3 { + | ------------------------------ trait implemented here +LL | type Target = T; + | ----------- with `Target` set here +... +LL | pub fn method(self: Box) {} + | ^^^^ + +error: inherent methods on types that implement `Deref` or `Receiver` shadow methods of their target + --> $DIR/lint-inherent-method-on-receiver.rs:95:19 + | +LL | impl Deref for Thing { + | -------------------------- trait implemented here +LL | type Target = T; + | ----------- with `Target` set here +... +LL | impl Receiver for Thing7 { + | ------------------------------ trait implemented here +LL | type Target = Thing; + | ----------- with `Target` set here +... +LL | pub fn method(&self) {} + | ^^^^^ + +error: aborting due to 4 previous errors + From 564dfee14d9494f8ee90c0134b0ec434cb6bb8fa Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Fri, 6 Feb 2026 12:30:22 +0100 Subject: [PATCH 3/6] move `LangItem::Receiver` into the feature gate check --- compiler/rustc_lint/src/receiver.rs | 39 ++++++++++++++++------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_lint/src/receiver.rs b/compiler/rustc_lint/src/receiver.rs index f2dcbe2b66acc..523eac8d446d0 100644 --- a/compiler/rustc_lint/src/receiver.rs +++ b/compiler/rustc_lint/src/receiver.rs @@ -83,8 +83,6 @@ fn check<'tcx>( ) -> Option { let deref_trait = tcx.require_lang_item(LangItem::Deref, DUMMY_SP); let deref_target = tcx.require_lang_item(LangItem::DerefTarget, DUMMY_SP); - let receiver_trait = tcx.require_lang_item(LangItem::Receiver, DUMMY_SP); - let receiver_target = tcx.require_lang_item(LangItem::ReceiverTarget, DUMMY_SP); if infcx.type_implements_trait(deref_trait, [ty], param_env).must_apply_modulo_regions() { let target_ty = tcx.normalize_erasing_regions( @@ -101,21 +99,28 @@ fn check<'tcx>( return Some(result); } } - if tcx.features().arbitrary_self_types() - && infcx.type_implements_trait(receiver_trait, [ty], param_env).must_apply_modulo_regions() - { - let target_ty = tcx.normalize_erasing_regions( - typing_env, - Ty::new_alias(tcx, AliasTyKind::Projection, AliasTy::new(tcx, receiver_target, [ty])), - ); - if let ty::Param(_) = target_ty.kind() { - let (impl_id, target_id) = find_impl_and_target_id(tcx, receiver_trait, ty); - return Some(CheckResult { impl_plus_target: vec![(impl_id, target_id)] }); - } - if let Some(mut result) = check(tcx, infcx, typing_env, param_env, target_ty) { - let (impl_id, target_id) = find_impl_and_target_id(tcx, receiver_trait, ty); - result.impl_plus_target.push((impl_id, target_id)); - return Some(result); + if tcx.features().arbitrary_self_types() { + let receiver_trait = tcx.require_lang_item(LangItem::Receiver, DUMMY_SP); + let receiver_target = tcx.require_lang_item(LangItem::ReceiverTarget, DUMMY_SP); + if infcx.type_implements_trait(receiver_trait, [ty], param_env).must_apply_modulo_regions() + { + let target_ty = tcx.normalize_erasing_regions( + typing_env, + Ty::new_alias( + tcx, + AliasTyKind::Projection, + AliasTy::new(tcx, receiver_target, [ty]), + ), + ); + if let ty::Param(_) = target_ty.kind() { + let (impl_id, target_id) = find_impl_and_target_id(tcx, receiver_trait, ty); + return Some(CheckResult { impl_plus_target: vec![(impl_id, target_id)] }); + } + if let Some(mut result) = check(tcx, infcx, typing_env, param_env, target_ty) { + let (impl_id, target_id) = find_impl_and_target_id(tcx, receiver_trait, ty); + result.impl_plus_target.push((impl_id, target_id)); + return Some(result); + } } } None From e41b9c32ecc58443d102b405bb730874462fa0f0 Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Fri, 6 Feb 2026 12:36:21 +0100 Subject: [PATCH 4/6] remove param_env and fix typing mode --- compiler/rustc_lint/src/receiver.rs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_lint/src/receiver.rs b/compiler/rustc_lint/src/receiver.rs index 523eac8d446d0..9a3e595630f8b 100644 --- a/compiler/rustc_lint/src/receiver.rs +++ b/compiler/rustc_lint/src/receiver.rs @@ -1,7 +1,7 @@ use rustc_hir::def_id::DefId; use rustc_hir::{ImplItemImplKind, ImplItemKind, LangItem, PatKind}; use rustc_infer::infer::InferCtxt; -use rustc_middle::ty::{self, AliasTy, AliasTyKind, ParamEnv, Ty, TyCtxt, TypingEnv, TypingMode}; +use rustc_middle::ty::{self, AliasTy, AliasTyKind, Ty, TyCtxt, TypingEnv}; use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::{DUMMY_SP, kw, sym}; use rustc_trait_selection::infer::{InferCtxtExt, TyCtxtInferExt}; @@ -52,9 +52,9 @@ impl<'tcx> LateLintPass<'tcx> for InherentMethodOnReceiver { } let self_ty: ty::EarlyBinder<'tcx, Ty<'tcx>> = cx.tcx.type_of(impl_id); let self_ty = self_ty.instantiate_identity(); - let infcx: InferCtxt<'tcx> = cx.tcx.infer_ctxt().build(TypingMode::non_body_analysis()); + let infcx: InferCtxt<'tcx> = cx.tcx.infer_ctxt().build(cx.typing_mode()); if let Some(CheckResult { impl_plus_target }) = - check(cx.tcx, &infcx, cx.typing_env(), cx.param_env, self_ty) + check(cx.tcx, &infcx, cx.typing_env(), self_ty) { cx.span_lint(INHERENT_METHOD_ON_RECEIVER, first_param.span, |lint| { for (impl_id, target_id) in impl_plus_target { @@ -78,13 +78,15 @@ fn check<'tcx>( tcx: TyCtxt<'tcx>, infcx: &InferCtxt<'tcx>, typing_env: TypingEnv<'tcx>, - param_env: ParamEnv<'tcx>, ty: Ty<'tcx>, ) -> Option { let deref_trait = tcx.require_lang_item(LangItem::Deref, DUMMY_SP); let deref_target = tcx.require_lang_item(LangItem::DerefTarget, DUMMY_SP); - if infcx.type_implements_trait(deref_trait, [ty], param_env).must_apply_modulo_regions() { + if infcx + .type_implements_trait(deref_trait, [ty], typing_env.param_env) + .must_apply_modulo_regions() + { let target_ty = tcx.normalize_erasing_regions( typing_env, Ty::new_alias(tcx, AliasTyKind::Projection, AliasTy::new(tcx, deref_target, [ty])), @@ -93,7 +95,7 @@ fn check<'tcx>( let (impl_id, target_id) = find_impl_and_target_id(tcx, deref_trait, ty); return Some(CheckResult { impl_plus_target: vec![(impl_id, target_id)] }); } - if let Some(mut result) = check(tcx, infcx, typing_env, param_env, target_ty) { + if let Some(mut result) = check(tcx, infcx, typing_env, target_ty) { let (impl_id, target_id) = find_impl_and_target_id(tcx, deref_trait, ty); result.impl_plus_target.push((impl_id, target_id)); return Some(result); @@ -102,7 +104,9 @@ fn check<'tcx>( if tcx.features().arbitrary_self_types() { let receiver_trait = tcx.require_lang_item(LangItem::Receiver, DUMMY_SP); let receiver_target = tcx.require_lang_item(LangItem::ReceiverTarget, DUMMY_SP); - if infcx.type_implements_trait(receiver_trait, [ty], param_env).must_apply_modulo_regions() + if infcx + .type_implements_trait(receiver_trait, [ty], typing_env.param_env) + .must_apply_modulo_regions() { let target_ty = tcx.normalize_erasing_regions( typing_env, @@ -116,7 +120,7 @@ fn check<'tcx>( let (impl_id, target_id) = find_impl_and_target_id(tcx, receiver_trait, ty); return Some(CheckResult { impl_plus_target: vec![(impl_id, target_id)] }); } - if let Some(mut result) = check(tcx, infcx, typing_env, param_env, target_ty) { + if let Some(mut result) = check(tcx, infcx, typing_env, target_ty) { let (impl_id, target_id) = find_impl_and_target_id(tcx, receiver_trait, ty); result.impl_plus_target.push((impl_id, target_id)); return Some(result); From 244f34ca29025ffc7dfd05e7ebd45cbfc74e2477 Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Fri, 6 Feb 2026 13:06:12 +0100 Subject: [PATCH 5/6] expect lint in compiler's crates --- compiler/rustc_data_structures/src/atomic_ref.rs | 1 + compiler/rustc_data_structures/src/marker.rs | 2 ++ compiler/rustc_data_structures/src/sync/freeze.rs | 1 + compiler/rustc_data_structures/src/sync/worker_local.rs | 1 + compiler/rustc_data_structures/src/tagged_ptr.rs | 3 +++ compiler/rustc_thread_pool/src/worker_local.rs | 1 + 6 files changed, 9 insertions(+) diff --git a/compiler/rustc_data_structures/src/atomic_ref.rs b/compiler/rustc_data_structures/src/atomic_ref.rs index eeb1b309257d4..8ce98f7d3ad57 100644 --- a/compiler/rustc_data_structures/src/atomic_ref.rs +++ b/compiler/rustc_data_structures/src/atomic_ref.rs @@ -9,6 +9,7 @@ impl AtomicRef { AtomicRef(AtomicPtr::new(initial as *const T as *mut T), PhantomData) } + #[cfg_attr(not(bootstrap), expect(inherent_method_on_receiver, reason = "compiler internals"))] pub fn swap(&self, new: &'static T) -> &'static T { // We never allow storing anything but a `'static` reference so it's safe to // return it for the same. diff --git a/compiler/rustc_data_structures/src/marker.rs b/compiler/rustc_data_structures/src/marker.rs index 72d5f004194a8..ae5e6126bd542 100644 --- a/compiler/rustc_data_structures/src/marker.rs +++ b/compiler/rustc_data_structures/src/marker.rs @@ -197,12 +197,14 @@ impl FromDyn { } #[inline(always)] + #[cfg_attr(not(bootstrap), expect(inherent_method_on_receiver, reason = "compiler internals"))] pub fn derive(&self, val: O) -> FromDyn { // We already did the check for `sync::is_dyn_thread_safe()` when creating `Self` FromDyn(val) } #[inline(always)] + #[cfg_attr(not(bootstrap), expect(inherent_method_on_receiver, reason = "compiler internals"))] pub fn into_inner(self) -> T { self.0 } diff --git a/compiler/rustc_data_structures/src/sync/freeze.rs b/compiler/rustc_data_structures/src/sync/freeze.rs index 6338afb92c34b..2a8ae3eda8969 100644 --- a/compiler/rustc_data_structures/src/sync/freeze.rs +++ b/compiler/rustc_data_structures/src/sync/freeze.rs @@ -155,6 +155,7 @@ pub struct FreezeWriteGuard<'a, T: ?Sized> { } impl<'a, T> FreezeWriteGuard<'a, T> { + #[cfg_attr(not(bootstrap), expect(inherent_method_on_receiver, reason = "compiler internals"))] pub fn freeze(self) -> &'a T { self.frozen.store(true, Ordering::Release); diff --git a/compiler/rustc_data_structures/src/sync/worker_local.rs b/compiler/rustc_data_structures/src/sync/worker_local.rs index d75af00985047..61cd2633bf8ec 100644 --- a/compiler/rustc_data_structures/src/sync/worker_local.rs +++ b/compiler/rustc_data_structures/src/sync/worker_local.rs @@ -126,6 +126,7 @@ impl WorkerLocal { /// Returns the worker-local values for each thread #[inline] + #[cfg_attr(not(bootstrap), expect(inherent_method_on_receiver, reason = "compiler internals"))] pub fn into_inner(self) -> impl Iterator { self.locals.into_vec().into_iter().map(|local| local.0) } diff --git a/compiler/rustc_data_structures/src/tagged_ptr.rs b/compiler/rustc_data_structures/src/tagged_ptr.rs index 94db421f77e89..d2693fe431c93 100644 --- a/compiler/rustc_data_structures/src/tagged_ptr.rs +++ b/compiler/rustc_data_structures/src/tagged_ptr.rs @@ -136,6 +136,7 @@ where /// Retrieves the pointer. #[inline] + #[cfg_attr(not(bootstrap), expect(inherent_method_on_receiver, reason = "compiler internals"))] pub fn pointer(self) -> &'a P { // SAFETY: pointer_raw returns the original pointer unsafe { self.pointer_raw().as_ref() } @@ -143,6 +144,7 @@ where /// Retrieves the tag. #[inline] + #[cfg_attr(not(bootstrap), expect(inherent_method_on_receiver, reason = "compiler internals"))] pub fn tag(&self) -> T { // Unpack the tag, according to the `self.packed` encoding scheme let tag = self.packed.addr().get() >> Self::TAG_BIT_SHIFT; @@ -155,6 +157,7 @@ where /// Sets the tag to a new value. #[inline] + #[cfg_attr(not(bootstrap), expect(inherent_method_on_receiver, reason = "compiler internals"))] pub fn set_tag(&mut self, tag: T) { self.packed = Self::pack(self.pointer_raw(), tag); } diff --git a/compiler/rustc_thread_pool/src/worker_local.rs b/compiler/rustc_thread_pool/src/worker_local.rs index 912001233bfea..2294ee0ab6341 100644 --- a/compiler/rustc_thread_pool/src/worker_local.rs +++ b/compiler/rustc_thread_pool/src/worker_local.rs @@ -35,6 +35,7 @@ impl WorkerLocal { /// Returns the worker-local value for each thread #[inline] + #[cfg_attr(not(bootstrap), expect(inherent_method_on_receiver, reason = "compiler internals"))] pub fn into_inner(self) -> Vec { self.locals.into_iter().map(|c| c.0).collect() } From 71b953890a642ab1b0abcdf0b6546529f0126067 Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Fri, 6 Feb 2026 13:50:25 +0100 Subject: [PATCH 6/6] fix ui tests --- tests/ui/self/arbitrary_self_types_shadowing_val_constptr.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/ui/self/arbitrary_self_types_shadowing_val_constptr.rs b/tests/ui/self/arbitrary_self_types_shadowing_val_constptr.rs index 2b718cb0454ce..5072ebd669cb6 100644 --- a/tests/ui/self/arbitrary_self_types_shadowing_val_constptr.rs +++ b/tests/ui/self/arbitrary_self_types_shadowing_val_constptr.rs @@ -6,7 +6,9 @@ pub struct A; impl A { - pub fn f(self: *const MyNonNull) -> i32 { 1 } + pub fn f(self: *const MyNonNull) -> i32 { + 1 + } } pub struct MyNonNull(T); @@ -18,6 +20,7 @@ impl core::ops::Receiver for MyNonNull { impl MyNonNull { // Imagine this a NEW method in B shadowing an EXISTING // method in A. + #[expect(inherent_method_on_receiver, reason = "we're testing this behavior")] pub fn f(self: *mut Self) -> i32 { 2 }