Skip to content

Commit 3cb0272

Browse files
committedFeb 13, 2025·
Auto merge of rust-lang#125782 - compiler-errors:supertrait-item-shadowing, r=BoxyUwU
Implement RFC 3624 `supertrait_item_shadowing` (v2) Implements RFC 3624 and the associated lint in the RFC. Implements: * Shadowing algorithm * Lint for call-site shadowing (allow by default, gated) * Lint for definition-site shadowing (allow by default, gated) Tracking: - rust-lang#89151 cc `@Amanieu` and rust-lang/rfcs#3624 and rust-lang#89151
2 parents cfe9ffc + 72b4df3 commit 3cb0272

36 files changed

+1071
-3
lines changed
 

‎compiler/rustc_feature/src/unstable.rs

+2
Original file line numberDiff line numberDiff line change
@@ -633,6 +633,8 @@ declare_features! (
633633
(unstable, strict_provenance_lints, "1.61.0", Some(130351)),
634634
/// Allows string patterns to dereference values to match them.
635635
(unstable, string_deref_patterns, "1.67.0", Some(87121)),
636+
/// Allows subtrait items to shadow supertrait items.
637+
(unstable, supertrait_item_shadowing, "CURRENT_RUSTC_VERSION", Some(89151)),
636638
/// Allows using `#[thread_local]` on `static` items.
637639
(unstable, thread_local, "1.0.0", Some(29594)),
638640
/// Allows defining `trait X = A + B;` alias items.

‎compiler/rustc_hir_analysis/messages.ftl

+6
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,12 @@ hir_analysis_specialization_trait = implementing `rustc_specialization_trait` tr
506506
507507
hir_analysis_static_specialize = cannot specialize on `'static` lifetime
508508
509+
hir_analysis_supertrait_item_multiple_shadowee = items from several supertraits are shadowed: {$traits}
510+
511+
hir_analysis_supertrait_item_shadowee = item from `{$supertrait}` is shadowed by a subtrait item
512+
513+
hir_analysis_supertrait_item_shadowing = trait item `{$item}` from `{$subtrait}` shadows identically named item from supertrait
514+
509515
hir_analysis_tait_forward_compat = item constrains opaque type that is not in its signature
510516
.note = this item must mention the opaque type in its signature in order to be able to register hidden types
511517

‎compiler/rustc_hir_analysis/src/check/wfcheck.rs

+45
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use rustc_hir::lang_items::LangItem;
1212
use rustc_hir::{AmbigArg, ItemKind};
1313
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
1414
use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt};
15+
use rustc_lint_defs::builtin::SUPERTRAIT_ITEM_SHADOWING_DEFINITION;
1516
use rustc_macros::LintDiagnostic;
1617
use rustc_middle::mir::interpret::ErrorHandled;
1718
use rustc_middle::query::Providers;
@@ -388,7 +389,12 @@ fn check_trait_item<'tcx>(
388389
hir::TraitItemKind::Type(_bounds, Some(ty)) => (None, ty.span),
389390
_ => (None, trait_item.span),
390391
};
392+
391393
check_dyn_incompatible_self_trait_by_name(tcx, trait_item);
394+
395+
// Check that an item definition in a subtrait is shadowing a supertrait item.
396+
lint_item_shadowing_supertrait_item(tcx, def_id);
397+
392398
let mut res = check_associated_item(tcx, def_id, span, method_sig);
393399

394400
if matches!(trait_item.kind, hir::TraitItemKind::Fn(..)) {
@@ -898,6 +904,45 @@ fn check_dyn_incompatible_self_trait_by_name(tcx: TyCtxt<'_>, item: &hir::TraitI
898904
}
899905
}
900906

907+
fn lint_item_shadowing_supertrait_item<'tcx>(tcx: TyCtxt<'tcx>, trait_item_def_id: LocalDefId) {
908+
let item_name = tcx.item_name(trait_item_def_id.to_def_id());
909+
let trait_def_id = tcx.local_parent(trait_item_def_id);
910+
911+
let shadowed: Vec<_> = traits::supertrait_def_ids(tcx, trait_def_id.to_def_id())
912+
.skip(1)
913+
.flat_map(|supertrait_def_id| {
914+
tcx.associated_items(supertrait_def_id).filter_by_name_unhygienic(item_name)
915+
})
916+
.collect();
917+
if !shadowed.is_empty() {
918+
let shadowee = if let [shadowed] = shadowed[..] {
919+
errors::SupertraitItemShadowee::Labeled {
920+
span: tcx.def_span(shadowed.def_id),
921+
supertrait: tcx.item_name(shadowed.trait_container(tcx).unwrap()),
922+
}
923+
} else {
924+
let (traits, spans): (Vec<_>, Vec<_>) = shadowed
925+
.iter()
926+
.map(|item| {
927+
(tcx.item_name(item.trait_container(tcx).unwrap()), tcx.def_span(item.def_id))
928+
})
929+
.unzip();
930+
errors::SupertraitItemShadowee::Several { traits: traits.into(), spans: spans.into() }
931+
};
932+
933+
tcx.emit_node_span_lint(
934+
SUPERTRAIT_ITEM_SHADOWING_DEFINITION,
935+
tcx.local_def_id_to_hir_id(trait_item_def_id),
936+
tcx.def_span(trait_item_def_id),
937+
errors::SupertraitItemShadowing {
938+
item: item_name,
939+
subtrait: tcx.item_name(trait_def_id.to_def_id()),
940+
shadowee,
941+
},
942+
);
943+
}
944+
}
945+
901946
fn check_impl_item<'tcx>(
902947
tcx: TyCtxt<'tcx>,
903948
impl_item: &'tcx hir::ImplItem<'tcx>,

‎compiler/rustc_hir_analysis/src/errors.rs

+27-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
use rustc_abi::ExternAbi;
44
use rustc_errors::codes::*;
55
use rustc_errors::{
6-
Applicability, Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, MultiSpan,
6+
Applicability, Diag, DiagCtxtHandle, DiagSymbolList, Diagnostic, EmissionGuarantee, Level,
7+
MultiSpan,
78
};
89
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
910
use rustc_middle::ty::Ty;
@@ -1733,3 +1734,28 @@ pub(crate) struct RegisterTypeUnstable<'a> {
17331734
pub span: Span,
17341735
pub ty: Ty<'a>,
17351736
}
1737+
1738+
#[derive(LintDiagnostic)]
1739+
#[diag(hir_analysis_supertrait_item_shadowing)]
1740+
pub(crate) struct SupertraitItemShadowing {
1741+
pub item: Symbol,
1742+
pub subtrait: Symbol,
1743+
#[subdiagnostic]
1744+
pub shadowee: SupertraitItemShadowee,
1745+
}
1746+
1747+
#[derive(Subdiagnostic)]
1748+
pub(crate) enum SupertraitItemShadowee {
1749+
#[note(hir_analysis_supertrait_item_shadowee)]
1750+
Labeled {
1751+
#[primary_span]
1752+
span: Span,
1753+
supertrait: Symbol,
1754+
},
1755+
#[note(hir_analysis_supertrait_item_multiple_shadowee)]
1756+
Several {
1757+
#[primary_span]
1758+
spans: MultiSpan,
1759+
traits: DiagSymbolList,
1760+
},
1761+
}

‎compiler/rustc_hir_typeck/messages.ftl

+8
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,14 @@ hir_typeck_suggest_boxing_when_appropriate = store this in the heap by calling `
192192
193193
hir_typeck_suggest_ptr_null_mut = consider using `core::ptr::null_mut` instead
194194
195+
hir_typeck_supertrait_item_multiple_shadowee = items from several supertraits are shadowed: {$traits}
196+
197+
hir_typeck_supertrait_item_shadowee = item from `{$supertrait}` is shadowed by a subtrait item
198+
199+
hir_typeck_supertrait_item_shadower = item from `{$subtrait}` shadows a supertrait item
200+
201+
hir_typeck_supertrait_item_shadowing = trait item `{$item}` from `{$subtrait}` shadows identically named item from supertrait
202+
195203
hir_typeck_trivial_cast = trivial {$numeric ->
196204
[true] numeric cast
197205
*[false] cast

‎compiler/rustc_hir_typeck/src/errors.rs

+35
Original file line numberDiff line numberDiff line change
@@ -868,3 +868,38 @@ pub(crate) struct ReplaceCommaWithSemicolon {
868868
pub comma_span: Span,
869869
pub descr: &'static str,
870870
}
871+
872+
#[derive(LintDiagnostic)]
873+
#[diag(hir_typeck_supertrait_item_shadowing)]
874+
pub(crate) struct SupertraitItemShadowing {
875+
pub item: Symbol,
876+
pub subtrait: Symbol,
877+
#[subdiagnostic]
878+
pub shadower: SupertraitItemShadower,
879+
#[subdiagnostic]
880+
pub shadowee: SupertraitItemShadowee,
881+
}
882+
883+
#[derive(Subdiagnostic)]
884+
#[note(hir_typeck_supertrait_item_shadower)]
885+
pub(crate) struct SupertraitItemShadower {
886+
pub subtrait: Symbol,
887+
#[primary_span]
888+
pub span: Span,
889+
}
890+
891+
#[derive(Subdiagnostic)]
892+
pub(crate) enum SupertraitItemShadowee {
893+
#[note(hir_typeck_supertrait_item_shadowee)]
894+
Labeled {
895+
#[primary_span]
896+
span: Span,
897+
supertrait: Symbol,
898+
},
899+
#[note(hir_typeck_supertrait_item_multiple_shadowee)]
900+
Several {
901+
#[primary_span]
902+
spans: MultiSpan,
903+
traits: DiagSymbolList,
904+
},
905+
}

‎compiler/rustc_hir_typeck/src/method/confirm.rs

+46-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use rustc_hir_analysis::hir_ty_lowering::{
1010
FeedConstTy, GenericArgsLowerer, HirTyLowerer, IsMethodCall, RegionInferReason,
1111
};
1212
use rustc_infer::infer::{self, DefineOpaqueTypes, InferOk};
13+
use rustc_lint::builtin::SUPERTRAIT_ITEM_SHADOWING_USAGE;
1314
use rustc_middle::traits::{ObligationCauseCode, UnifyReceiverContext};
1415
use rustc_middle::ty::adjustment::{
1516
Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCoercion,
@@ -24,6 +25,7 @@ use rustc_trait_selection::traits;
2425
use tracing::debug;
2526

2627
use super::{MethodCallee, probe};
28+
use crate::errors::{SupertraitItemShadowee, SupertraitItemShadower, SupertraitItemShadowing};
2729
use crate::{FnCtxt, callee};
2830

2931
struct ConfirmContext<'a, 'tcx> {
@@ -141,7 +143,10 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
141143
let method_sig = ty::Binder::dummy(method_sig);
142144

143145
// Make sure nobody calls `drop()` explicitly.
144-
self.enforce_illegal_method_limitations(pick);
146+
self.check_for_illegal_method_calls(pick);
147+
148+
// Lint when an item is shadowing a supertrait item.
149+
self.lint_shadowed_supertrait_items(pick, segment);
145150

146151
// Add any trait/regions obligations specified on the method's type parameters.
147152
// We won't add these if we encountered an illegal sized bound, so that we can use
@@ -656,7 +661,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
656661
})
657662
}
658663

659-
fn enforce_illegal_method_limitations(&self, pick: &probe::Pick<'_>) {
664+
fn check_for_illegal_method_calls(&self, pick: &probe::Pick<'_>) {
660665
// Disallow calls to the method `drop` defined in the `Drop` trait.
661666
if let Some(trait_def_id) = pick.item.trait_container(self.tcx) {
662667
if let Err(e) = callee::check_legal_trait_for_method_call(
@@ -672,6 +677,45 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
672677
}
673678
}
674679

680+
fn lint_shadowed_supertrait_items(
681+
&self,
682+
pick: &probe::Pick<'_>,
683+
segment: &hir::PathSegment<'tcx>,
684+
) {
685+
if pick.shadowed_candidates.is_empty() {
686+
return;
687+
}
688+
689+
let shadower_span = self.tcx.def_span(pick.item.def_id);
690+
let subtrait = self.tcx.item_name(pick.item.trait_container(self.tcx).unwrap());
691+
let shadower = SupertraitItemShadower { span: shadower_span, subtrait };
692+
693+
let shadowee = if let [shadowee] = &pick.shadowed_candidates[..] {
694+
let shadowee_span = self.tcx.def_span(shadowee.def_id);
695+
let supertrait = self.tcx.item_name(shadowee.trait_container(self.tcx).unwrap());
696+
SupertraitItemShadowee::Labeled { span: shadowee_span, supertrait }
697+
} else {
698+
let (traits, spans): (Vec<_>, Vec<_>) = pick
699+
.shadowed_candidates
700+
.iter()
701+
.map(|item| {
702+
(
703+
self.tcx.item_name(item.trait_container(self.tcx).unwrap()),
704+
self.tcx.def_span(item.def_id),
705+
)
706+
})
707+
.unzip();
708+
SupertraitItemShadowee::Several { traits: traits.into(), spans: spans.into() }
709+
};
710+
711+
self.tcx.emit_node_span_lint(
712+
SUPERTRAIT_ITEM_SHADOWING_USAGE,
713+
segment.hir_id,
714+
segment.ident.span,
715+
SupertraitItemShadowing { shadower, shadowee, item: segment.ident.name, subtrait },
716+
);
717+
}
718+
675719
fn upcast(
676720
&mut self,
677721
source_trait_ref: ty::PolyTraitRef<'tcx>,

0 commit comments

Comments
 (0)
Please sign in to comment.