Skip to content

Commit

Permalink
Auto merge of #125782 - compiler-errors:supertrait-item-shadowing, r=…
Browse files Browse the repository at this point in the history
…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:
- #89151

cc `@Amanieu` and rust-lang/rfcs#3624 and #89151
  • Loading branch information
bors committed Feb 13, 2025
2 parents cfe9ffc + 72b4df3 commit 3cb0272
Show file tree
Hide file tree
Showing 36 changed files with 1,071 additions and 3 deletions.
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -633,6 +633,8 @@ declare_features! (
(unstable, strict_provenance_lints, "1.61.0", Some(130351)),
/// Allows string patterns to dereference values to match them.
(unstable, string_deref_patterns, "1.67.0", Some(87121)),
/// Allows subtrait items to shadow supertrait items.
(unstable, supertrait_item_shadowing, "CURRENT_RUSTC_VERSION", Some(89151)),
/// Allows using `#[thread_local]` on `static` items.
(unstable, thread_local, "1.0.0", Some(29594)),
/// Allows defining `trait X = A + B;` alias items.
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_hir_analysis/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,12 @@ hir_analysis_specialization_trait = implementing `rustc_specialization_trait` tr
hir_analysis_static_specialize = cannot specialize on `'static` lifetime
hir_analysis_supertrait_item_multiple_shadowee = items from several supertraits are shadowed: {$traits}
hir_analysis_supertrait_item_shadowee = item from `{$supertrait}` is shadowed by a subtrait item
hir_analysis_supertrait_item_shadowing = trait item `{$item}` from `{$subtrait}` shadows identically named item from supertrait
hir_analysis_tait_forward_compat = item constrains opaque type that is not in its signature
.note = this item must mention the opaque type in its signature in order to be able to register hidden types
Expand Down
45 changes: 45 additions & 0 deletions compiler/rustc_hir_analysis/src/check/wfcheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use rustc_hir::lang_items::LangItem;
use rustc_hir::{AmbigArg, ItemKind};
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt};
use rustc_lint_defs::builtin::SUPERTRAIT_ITEM_SHADOWING_DEFINITION;
use rustc_macros::LintDiagnostic;
use rustc_middle::mir::interpret::ErrorHandled;
use rustc_middle::query::Providers;
Expand Down Expand Up @@ -388,7 +389,12 @@ fn check_trait_item<'tcx>(
hir::TraitItemKind::Type(_bounds, Some(ty)) => (None, ty.span),
_ => (None, trait_item.span),
};

check_dyn_incompatible_self_trait_by_name(tcx, trait_item);

// Check that an item definition in a subtrait is shadowing a supertrait item.
lint_item_shadowing_supertrait_item(tcx, def_id);

let mut res = check_associated_item(tcx, def_id, span, method_sig);

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

fn lint_item_shadowing_supertrait_item<'tcx>(tcx: TyCtxt<'tcx>, trait_item_def_id: LocalDefId) {
let item_name = tcx.item_name(trait_item_def_id.to_def_id());
let trait_def_id = tcx.local_parent(trait_item_def_id);

let shadowed: Vec<_> = traits::supertrait_def_ids(tcx, trait_def_id.to_def_id())
.skip(1)
.flat_map(|supertrait_def_id| {
tcx.associated_items(supertrait_def_id).filter_by_name_unhygienic(item_name)
})
.collect();
if !shadowed.is_empty() {
let shadowee = if let [shadowed] = shadowed[..] {
errors::SupertraitItemShadowee::Labeled {
span: tcx.def_span(shadowed.def_id),
supertrait: tcx.item_name(shadowed.trait_container(tcx).unwrap()),
}
} else {
let (traits, spans): (Vec<_>, Vec<_>) = shadowed
.iter()
.map(|item| {
(tcx.item_name(item.trait_container(tcx).unwrap()), tcx.def_span(item.def_id))
})
.unzip();
errors::SupertraitItemShadowee::Several { traits: traits.into(), spans: spans.into() }
};

tcx.emit_node_span_lint(
SUPERTRAIT_ITEM_SHADOWING_DEFINITION,
tcx.local_def_id_to_hir_id(trait_item_def_id),
tcx.def_span(trait_item_def_id),
errors::SupertraitItemShadowing {
item: item_name,
subtrait: tcx.item_name(trait_def_id.to_def_id()),
shadowee,
},
);
}
}

fn check_impl_item<'tcx>(
tcx: TyCtxt<'tcx>,
impl_item: &'tcx hir::ImplItem<'tcx>,
Expand Down
28 changes: 27 additions & 1 deletion compiler/rustc_hir_analysis/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
use rustc_abi::ExternAbi;
use rustc_errors::codes::*;
use rustc_errors::{
Applicability, Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, MultiSpan,
Applicability, Diag, DiagCtxtHandle, DiagSymbolList, Diagnostic, EmissionGuarantee, Level,
MultiSpan,
};
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
use rustc_middle::ty::Ty;
Expand Down Expand Up @@ -1733,3 +1734,28 @@ pub(crate) struct RegisterTypeUnstable<'a> {
pub span: Span,
pub ty: Ty<'a>,
}

#[derive(LintDiagnostic)]
#[diag(hir_analysis_supertrait_item_shadowing)]
pub(crate) struct SupertraitItemShadowing {
pub item: Symbol,
pub subtrait: Symbol,
#[subdiagnostic]
pub shadowee: SupertraitItemShadowee,
}

#[derive(Subdiagnostic)]
pub(crate) enum SupertraitItemShadowee {
#[note(hir_analysis_supertrait_item_shadowee)]
Labeled {
#[primary_span]
span: Span,
supertrait: Symbol,
},
#[note(hir_analysis_supertrait_item_multiple_shadowee)]
Several {
#[primary_span]
spans: MultiSpan,
traits: DiagSymbolList,
},
}
8 changes: 8 additions & 0 deletions compiler/rustc_hir_typeck/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,14 @@ hir_typeck_suggest_boxing_when_appropriate = store this in the heap by calling `
hir_typeck_suggest_ptr_null_mut = consider using `core::ptr::null_mut` instead
hir_typeck_supertrait_item_multiple_shadowee = items from several supertraits are shadowed: {$traits}
hir_typeck_supertrait_item_shadowee = item from `{$supertrait}` is shadowed by a subtrait item
hir_typeck_supertrait_item_shadower = item from `{$subtrait}` shadows a supertrait item
hir_typeck_supertrait_item_shadowing = trait item `{$item}` from `{$subtrait}` shadows identically named item from supertrait
hir_typeck_trivial_cast = trivial {$numeric ->
[true] numeric cast
*[false] cast
Expand Down
35 changes: 35 additions & 0 deletions compiler/rustc_hir_typeck/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -868,3 +868,38 @@ pub(crate) struct ReplaceCommaWithSemicolon {
pub comma_span: Span,
pub descr: &'static str,
}

#[derive(LintDiagnostic)]
#[diag(hir_typeck_supertrait_item_shadowing)]
pub(crate) struct SupertraitItemShadowing {
pub item: Symbol,
pub subtrait: Symbol,
#[subdiagnostic]
pub shadower: SupertraitItemShadower,
#[subdiagnostic]
pub shadowee: SupertraitItemShadowee,
}

#[derive(Subdiagnostic)]
#[note(hir_typeck_supertrait_item_shadower)]
pub(crate) struct SupertraitItemShadower {
pub subtrait: Symbol,
#[primary_span]
pub span: Span,
}

#[derive(Subdiagnostic)]
pub(crate) enum SupertraitItemShadowee {
#[note(hir_typeck_supertrait_item_shadowee)]
Labeled {
#[primary_span]
span: Span,
supertrait: Symbol,
},
#[note(hir_typeck_supertrait_item_multiple_shadowee)]
Several {
#[primary_span]
spans: MultiSpan,
traits: DiagSymbolList,
},
}
48 changes: 46 additions & 2 deletions compiler/rustc_hir_typeck/src/method/confirm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use rustc_hir_analysis::hir_ty_lowering::{
FeedConstTy, GenericArgsLowerer, HirTyLowerer, IsMethodCall, RegionInferReason,
};
use rustc_infer::infer::{self, DefineOpaqueTypes, InferOk};
use rustc_lint::builtin::SUPERTRAIT_ITEM_SHADOWING_USAGE;
use rustc_middle::traits::{ObligationCauseCode, UnifyReceiverContext};
use rustc_middle::ty::adjustment::{
Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCoercion,
Expand All @@ -24,6 +25,7 @@ use rustc_trait_selection::traits;
use tracing::debug;

use super::{MethodCallee, probe};
use crate::errors::{SupertraitItemShadowee, SupertraitItemShadower, SupertraitItemShadowing};
use crate::{FnCtxt, callee};

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

// Make sure nobody calls `drop()` explicitly.
self.enforce_illegal_method_limitations(pick);
self.check_for_illegal_method_calls(pick);

// Lint when an item is shadowing a supertrait item.
self.lint_shadowed_supertrait_items(pick, segment);

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

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

fn lint_shadowed_supertrait_items(
&self,
pick: &probe::Pick<'_>,
segment: &hir::PathSegment<'tcx>,
) {
if pick.shadowed_candidates.is_empty() {
return;
}

let shadower_span = self.tcx.def_span(pick.item.def_id);
let subtrait = self.tcx.item_name(pick.item.trait_container(self.tcx).unwrap());
let shadower = SupertraitItemShadower { span: shadower_span, subtrait };

let shadowee = if let [shadowee] = &pick.shadowed_candidates[..] {
let shadowee_span = self.tcx.def_span(shadowee.def_id);
let supertrait = self.tcx.item_name(shadowee.trait_container(self.tcx).unwrap());
SupertraitItemShadowee::Labeled { span: shadowee_span, supertrait }
} else {
let (traits, spans): (Vec<_>, Vec<_>) = pick
.shadowed_candidates
.iter()
.map(|item| {
(
self.tcx.item_name(item.trait_container(self.tcx).unwrap()),
self.tcx.def_span(item.def_id),
)
})
.unzip();
SupertraitItemShadowee::Several { traits: traits.into(), spans: spans.into() }
};

self.tcx.emit_node_span_lint(
SUPERTRAIT_ITEM_SHADOWING_USAGE,
segment.hir_id,
segment.ident.span,
SupertraitItemShadowing { shadower, shadowee, item: segment.ident.name, subtrait },
);
}

fn upcast(
&mut self,
source_trait_ref: ty::PolyTraitRef<'tcx>,
Expand Down
Loading

0 comments on commit 3cb0272

Please sign in to comment.