Skip to content

Commit 8f5c57d

Browse files
Implement shadowing lint
1 parent ccdf686 commit 8f5c57d

File tree

10 files changed

+253
-12
lines changed

10 files changed

+253
-12
lines changed

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
@@ -866,3 +866,38 @@ pub(crate) struct ReplaceCommaWithSemicolon {
866866
pub comma_span: Span,
867867
pub descr: &'static str,
868868
}
869+
870+
#[derive(LintDiagnostic)]
871+
#[diag(hir_typeck_supertrait_item_shadowing)]
872+
pub(crate) struct SupertraitItemShadowing {
873+
pub item: Symbol,
874+
pub subtrait: Symbol,
875+
#[subdiagnostic]
876+
pub shadower: SupertraitItemShadower,
877+
#[subdiagnostic]
878+
pub shadowee: SupertraitItemShadowee,
879+
}
880+
881+
#[derive(Subdiagnostic)]
882+
#[note(hir_typeck_supertrait_item_shadower)]
883+
pub(crate) struct SupertraitItemShadower {
884+
pub subtrait: Symbol,
885+
#[primary_span]
886+
pub span: Span,
887+
}
888+
889+
#[derive(Subdiagnostic)]
890+
pub(crate) enum SupertraitItemShadowee {
891+
#[note(hir_typeck_supertrait_item_shadowee)]
892+
Labeled {
893+
#[primary_span]
894+
span: Span,
895+
supertrait: Symbol,
896+
},
897+
#[note(hir_typeck_supertrait_item_multiple_shadowee)]
898+
Several {
899+
#[primary_span]
900+
spans: MultiSpan,
901+
traits: DiagSymbolList,
902+
},
903+
}

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.
@@ -665,6 +669,45 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
665669
}
666670
}
667671

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

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
}

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,
@@ -4966,6 +4967,48 @@ declare_lint! {
49664967
};
49674968
}
49684969

4970+
declare_lint! {
4971+
/// The `supertrait_item_shadowing_usage` lint detects when the
4972+
/// usage of an item that is provided by both a subtrait and supertrait
4973+
/// is shadowed, preferring the subtrait.
4974+
///
4975+
/// ### Example
4976+
///
4977+
/// ```rust
4978+
/// #![feature(supertrait_item_shadowing)]
4979+
/// #![deny(supertrait_item_shadowing_usage)]
4980+
///
4981+
/// trait Upstream {
4982+
/// fn hello(&self) {}
4983+
/// }
4984+
/// impl<T> Upstream for T {}
4985+
///
4986+
/// trait Downstream: Upstream {
4987+
/// fn hello(&self) {}
4988+
/// }
4989+
/// impl<T> Downstream for T {}
4990+
///
4991+
/// struct MyType;
4992+
/// MyType.hello();
4993+
/// ```
4994+
///
4995+
/// {{produces}}
4996+
///
4997+
/// ### Explanation
4998+
///
4999+
/// RFC 3624 specified a heuristic in which a supertrait item would be
5000+
/// shadowed by a subtrait item when ambiguity occurs during item
5001+
/// selection. In order to mitigate side-effects of this happening
5002+
/// silently, this lint detects these cases when users want to deny them
5003+
/// or fix the call sites.
5004+
pub SUPERTRAIT_ITEM_SHADOWING_USAGE,
5005+
// FIXME(supertrait_item_shadowing): It is not decided if this should
5006+
// warn by default at the call site.
5007+
Allow,
5008+
"detects when a supertrait item is shadowed by a subtrait item",
5009+
@feature_gate = supertrait_item_shadowing;
5010+
}
5011+
49695012
declare_lint! {
49705013
/// The `ptr_to_integer_transmute_in_consts` lint detects pointer to integer
49715014
/// transmute in const functions and associated constants.
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,28 @@
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 | A::hello(&());
20+
| ~~~~~~~~~~~~~
21+
help: disambiguate the method for candidate #2
22+
|
23+
LL | B::hello(&());
24+
| ~~~~~~~~~~~~~
25+
26+
error: aborting due to 1 previous error
27+
28+
For more information about this error, try `rustc --explain E0034`.

0 commit comments

Comments
 (0)