Skip to content

Commit f8c51d3

Browse files
Implement shadowing lint
1 parent 0c85044 commit f8c51d3

File tree

10 files changed

+255
-12
lines changed

10 files changed

+255
-12
lines changed

Diff for: 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

Diff for: 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+
}

Diff for: compiler/rustc_hir_typeck/src/method/confirm.rs

+43
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> {
@@ -143,6 +145,8 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
143145
// Make sure nobody calls `drop()` explicitly.
144146
self.enforce_illegal_method_limitations(pick);
145147

148+
self.enforce_shadowed_supertrait_items(pick, segment);
149+
146150
// Add any trait/regions obligations specified on the method's type parameters.
147151
// We won't add these if we encountered an illegal sized bound, so that we can use
148152
// a custom error in that case.
@@ -672,6 +676,45 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
672676
}
673677
}
674678

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

Diff for: compiler/rustc_hir_typeck/src/method/probe.rs

+26-12
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,9 @@ pub(crate) struct Pick<'tcx> {
226226
/// to identify this method. Used only for deshadowing errors.
227227
/// Only applies for inherent impls.
228228
pub receiver_steps: Option<usize>,
229+
230+
/// Candidates that were shadowed by supertraits.
231+
pub shadowed_candidates: Vec<ty::AssocItem>,
229232
}
230233

231234
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -1616,18 +1619,10 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
16161619
debug!("applicable_candidates: {:?}", applicable_candidates);
16171620

16181621
if applicable_candidates.len() > 1 {
1619-
if self.tcx.features().supertrait_item_shadowing() {
1620-
if let Some(pick) =
1621-
self.collapse_candidates_to_subtrait_pick(self_ty, &applicable_candidates)
1622-
{
1623-
return Some(Ok(pick));
1624-
}
1625-
} else {
1626-
if let Some(pick) =
1627-
self.collapse_candidates_to_trait_pick(self_ty, &applicable_candidates)
1628-
{
1629-
return Some(Ok(pick));
1630-
}
1622+
if let Some(pick) =
1623+
self.collapse_candidates_to_trait_pick(self_ty, &applicable_candidates)
1624+
{
1625+
return Some(Ok(pick));
16311626
}
16321627
}
16331628

@@ -1644,6 +1639,17 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
16441639
}
16451640

16461641
if applicable_candidates.len() > 1 {
1642+
// We collapse to a subtrait pick *after* filtering unstable candidates
1643+
// to make sure we don't prefer a unstable subtrait method over a stable
1644+
// supertrait method.
1645+
if self.tcx.features().supertrait_item_shadowing() {
1646+
if let Some(pick) =
1647+
self.collapse_candidates_to_subtrait_pick(self_ty, &applicable_candidates)
1648+
{
1649+
return Some(Ok(pick));
1650+
}
1651+
}
1652+
16471653
let sources = candidates.iter().map(|p| self.candidate_source(p, self_ty)).collect();
16481654
return Some(Err(MethodError::Ambiguity(sources)));
16491655
}
@@ -1682,6 +1688,7 @@ impl<'tcx> Pick<'tcx> {
16821688
self_ty,
16831689
unstable_candidates: _,
16841690
receiver_steps: _,
1691+
shadowed_candidates: _,
16851692
} = *self;
16861693
self_ty != other.self_ty || def_id != other.item.def_id
16871694
}
@@ -2091,6 +2098,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
20912098
self_ty,
20922099
unstable_candidates: vec![],
20932100
receiver_steps: None,
2101+
shadowed_candidates: vec![],
20942102
})
20952103
}
20962104

@@ -2136,6 +2144,11 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
21362144
autoref_or_ptr_adjustment: None,
21372145
self_ty,
21382146
unstable_candidates: vec![],
2147+
shadowed_candidates: probes
2148+
.iter()
2149+
.map(|(c, _)| c.item)
2150+
.filter(|item| item.def_id != child_pick.item.def_id)
2151+
.collect(),
21392152
receiver_steps: None,
21402153
})
21412154
}
@@ -2434,6 +2447,7 @@ impl<'tcx> Candidate<'tcx> {
24342447
InherentImplCandidate { receiver_steps, .. } => Some(receiver_steps),
24352448
_ => None,
24362449
},
2450+
shadowed_candidates: vec![],
24372451
}
24382452
}
24392453
}

Diff for: compiler/rustc_lint_defs/src/builtin.rs

+43
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ declare_lint_pass! {
101101
SINGLE_USE_LIFETIMES,
102102
SOFT_UNSTABLE,
103103
STABLE_FEATURES,
104+
SUPERTRAIT_ITEM_SHADOWING_USAGE,
104105
TAIL_EXPR_DROP_ORDER,
105106
TEST_UNSTABLE_LINT,
106107
TEXT_DIRECTION_CODEPOINT_IN_COMMENT,
@@ -4916,6 +4917,48 @@ declare_lint! {
49164917
};
49174918
}
49184919

4920+
declare_lint! {
4921+
/// The `supertrait_item_shadowing_usage` lint detects when the
4922+
/// usage of an item that is provided by both a subtrait and supertrait
4923+
/// is shadowed, preferring the subtrait.
4924+
///
4925+
/// ### Example
4926+
///
4927+
/// ```rust
4928+
/// #![feature(supertrait_item_shadowing)]
4929+
/// #![deny(supertrait_item_shadowing_usage)]
4930+
///
4931+
/// trait Upstream {
4932+
/// fn hello(&self) {}
4933+
/// }
4934+
/// impl<T> Upstream for T {}
4935+
///
4936+
/// trait Downstream: Upstream {
4937+
/// fn hello(&self) {}
4938+
/// }
4939+
/// impl<T> Downstream for T {}
4940+
///
4941+
/// struct MyType;
4942+
/// MyType.hello();
4943+
/// ```
4944+
///
4945+
/// {{produces}}
4946+
///
4947+
/// ### Explanation
4948+
///
4949+
/// RFC 3624 specified a heuristic in which a supertrait item would be
4950+
/// shadowed by a subtrait item when ambiguity occurs during item
4951+
/// selection. In order to mitigate side-effects of this happening
4952+
/// silently, this lint detects these cases when users want to deny them
4953+
/// or fix the call sites.
4954+
pub SUPERTRAIT_ITEM_SHADOWING_USAGE,
4955+
// FIXME(supertrait_item_shadowing): It is not decided if this should
4956+
// warn by default at the call site.
4957+
Allow,
4958+
"detects when a supertrait item is shadowed by a subtrait item",
4959+
@feature_gate = supertrait_item_shadowing;
4960+
}
4961+
49194962
declare_lint! {
49204963
/// The `ptr_to_integer_transmute_in_consts` lint detects pointer to integer
49214964
/// transmute in const functions and associated constants.
+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//@ run-pass
2+
//@ check-run-results
3+
4+
#![feature(supertrait_item_shadowing)]
5+
#![warn(supertrait_item_shadowing_usage)]
6+
#![allow(dead_code)]
7+
8+
trait A {
9+
fn hello(&self) {
10+
println!("A");
11+
}
12+
}
13+
impl<T> A for T {}
14+
15+
trait B: A {
16+
fn hello(&self) {
17+
println!("B");
18+
}
19+
}
20+
impl<T> B for T {}
21+
22+
fn main() {
23+
().hello();
24+
//~^ WARN trait item `hello` from `B` shadows identically named item from supertrait
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
B
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
warning: trait item `hello` from `B` shadows identically named item from supertrait
2+
--> $DIR/common-ancestor.rs:23:8
3+
|
4+
LL | ().hello();
5+
| ^^^^^
6+
|
7+
note: item from `B` shadows a supertrait item
8+
--> $DIR/common-ancestor.rs:16:5
9+
|
10+
LL | fn hello(&self) {
11+
| ^^^^^^^^^^^^^^^
12+
note: item from `A` is shadowed by a subtrait item
13+
--> $DIR/common-ancestor.rs:9:5
14+
|
15+
LL | fn hello(&self) {
16+
| ^^^^^^^^^^^^^^^
17+
note: the lint level is defined here
18+
--> $DIR/common-ancestor.rs:5:9
19+
|
20+
LL | #![warn(supertrait_item_shadowing_usage)]
21+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
22+
23+
warning: 1 warning emitted
24+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#![feature(supertrait_item_shadowing)]
2+
3+
trait A {
4+
fn hello(&self) {
5+
println!("A");
6+
}
7+
}
8+
impl<T> A for T {}
9+
10+
trait B {
11+
fn hello(&self) {
12+
println!("B");
13+
}
14+
}
15+
impl<T> B for T {}
16+
17+
fn main() {
18+
().hello();
19+
//~^ ERROR multiple applicable items in scope
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
error[E0034]: multiple applicable items in scope
2+
--> $DIR/no-common-ancestor.rs:18:8
3+
|
4+
LL | ().hello();
5+
| ^^^^^ multiple `hello` found
6+
|
7+
note: candidate #1 is defined in an impl of the trait `A` for the type `T`
8+
--> $DIR/no-common-ancestor.rs:4:5
9+
|
10+
LL | fn hello(&self) {
11+
| ^^^^^^^^^^^^^^^
12+
note: candidate #2 is defined in an impl of the trait `B` for the type `T`
13+
--> $DIR/no-common-ancestor.rs:11:5
14+
|
15+
LL | fn hello(&self) {
16+
| ^^^^^^^^^^^^^^^
17+
help: disambiguate the method for candidate #1
18+
|
19+
LL - ().hello();
20+
LL + A::hello(&());
21+
|
22+
help: disambiguate the method for candidate #2
23+
|
24+
LL - ().hello();
25+
LL + B::hello(&());
26+
|
27+
28+
error: aborting due to 1 previous error
29+
30+
For more information about this error, try `rustc --explain E0034`.

0 commit comments

Comments
 (0)