Skip to content

Commit 824875a

Browse files
Implement shadowing lint
1 parent 3481f18 commit 824875a

File tree

10 files changed

+243
-12
lines changed

10 files changed

+243
-12
lines changed

compiler/rustc_hir_typeck/messages.ftl

+8
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,14 @@ hir_typeck_suggest_boxing_when_appropriate = store this in the heap by calling `
180180
181181
hir_typeck_suggest_ptr_null_mut = consider using `core::ptr::null_mut` instead
182182
183+
hir_typeck_supertrait_method_multiple_shadowee = methods from several supertraits are shadowed: {$traits}
184+
185+
hir_typeck_supertrait_method_shadowee = method from `{$supertrait}` is shadowed by a subtrait method
186+
187+
hir_typeck_supertrait_method_shadower = method from `{$subtrait}` shadows a supertrait method
188+
189+
hir_typeck_supertrait_method_shadowing = trait method `{$method}` from `{$subtrait}` shadows identically named method from supertrait
190+
183191
hir_typeck_trivial_cast = trivial {$numeric ->
184192
[true] numeric cast
185193
*[false] cast

compiler/rustc_hir_typeck/src/errors.rs

+35
Original file line numberDiff line numberDiff line change
@@ -724,3 +724,38 @@ pub(crate) struct PassToVariadicFunction<'a, 'tcx> {
724724
#[note(hir_typeck_teach_help)]
725725
pub(crate) teach: bool,
726726
}
727+
728+
#[derive(LintDiagnostic)]
729+
#[diag(hir_typeck_supertrait_method_shadowing)]
730+
pub(crate) struct SupertraitMethodShadowing {
731+
pub method: Symbol,
732+
pub subtrait: Symbol,
733+
#[subdiagnostic]
734+
pub shadower: SupertraitMethodShadower,
735+
#[subdiagnostic]
736+
pub shadowee: SupertraitMethodShadowee,
737+
}
738+
739+
#[derive(Subdiagnostic)]
740+
#[note(hir_typeck_supertrait_method_shadower)]
741+
pub(crate) struct SupertraitMethodShadower {
742+
pub subtrait: Symbol,
743+
#[primary_span]
744+
pub span: Span,
745+
}
746+
747+
#[derive(Subdiagnostic)]
748+
pub(crate) enum SupertraitMethodShadowee {
749+
#[note(hir_typeck_supertrait_method_shadowee)]
750+
Labeled {
751+
#[primary_span]
752+
span: Span,
753+
supertrait: Symbol,
754+
},
755+
#[note(hir_typeck_supertrait_method_multiple_shadowee)]
756+
Several {
757+
#[primary_span]
758+
spans: MultiSpan,
759+
traits: DiagSymbolList,
760+
},
761+
}

compiler/rustc_hir_typeck/src/method/confirm.rs

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

2728
use super::{probe, MethodCallee};
29+
use crate::errors::{
30+
SupertraitMethodShadowee, SupertraitMethodShadower, SupertraitMethodShadowing,
31+
};
2832
use crate::{callee, FnCtxt};
2933

3034
struct ConfirmContext<'a, 'tcx> {
@@ -144,6 +148,8 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
144148
// Make sure nobody calls `drop()` explicitly.
145149
self.enforce_illegal_method_limitations(pick);
146150

151+
self.enforce_shadowed_supertrait_methods(pick, segment);
152+
147153
// Add any trait/regions obligations specified on the method's type parameters.
148154
// We won't add these if we encountered an illegal sized bound, so that we can use
149155
// a custom error in that case.
@@ -646,6 +652,45 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
646652
}
647653
}
648654

655+
fn enforce_shadowed_supertrait_methods(
656+
&self,
657+
pick: &probe::Pick<'_>,
658+
segment: &hir::PathSegment<'tcx>,
659+
) {
660+
if pick.shadowed_candidates.is_empty() {
661+
return;
662+
}
663+
664+
let shadower_span = self.tcx.def_span(pick.item.def_id);
665+
let subtrait = self.tcx.item_name(pick.item.trait_container(self.tcx).unwrap());
666+
let shadower = SupertraitMethodShadower { span: shadower_span, subtrait };
667+
668+
let shadowee = if let [shadowee] = &pick.shadowed_candidates[..] {
669+
let shadowee_span = self.tcx.def_span(shadowee.def_id);
670+
let supertrait = self.tcx.item_name(shadowee.trait_container(self.tcx).unwrap());
671+
SupertraitMethodShadowee::Labeled { span: shadowee_span, supertrait }
672+
} else {
673+
let (traits, spans): (Vec<_>, Vec<_>) = pick
674+
.shadowed_candidates
675+
.iter()
676+
.map(|item| {
677+
(
678+
self.tcx.item_name(item.trait_container(self.tcx).unwrap()),
679+
self.tcx.def_span(item.def_id),
680+
)
681+
})
682+
.unzip();
683+
SupertraitMethodShadowee::Several { traits: traits.into(), spans: spans.into() }
684+
};
685+
686+
self.tcx.emit_node_span_lint(
687+
SUPERTRAIT_ITEM_SHADOWING,
688+
segment.hir_id,
689+
segment.ident.span,
690+
SupertraitMethodShadowing { shadower, shadowee, method: segment.ident.name, subtrait },
691+
);
692+
}
693+
649694
fn upcast(
650695
&mut self,
651696
source_trait_ref: ty::PolyTraitRef<'tcx>,

compiler/rustc_hir_typeck/src/method/probe.rs

+26-12
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,9 @@ pub(crate) struct Pick<'tcx> {
179179

180180
/// Unstable candidates alongside the stable ones.
181181
unstable_candidates: Vec<(Candidate<'tcx>, Symbol)>,
182+
183+
/// Candidates that were shadowed by supertraits.
184+
pub shadowed_candidates: Vec<ty::AssocItem>,
182185
}
183186

184187
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -1262,18 +1265,10 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
12621265
debug!("applicable_candidates: {:?}", applicable_candidates);
12631266

12641267
if applicable_candidates.len() > 1 {
1265-
if self.tcx.features().supertrait_item_shadowing {
1266-
if let Some(pick) =
1267-
self.collapse_candidates_to_subtrait_pick(self_ty, &applicable_candidates)
1268-
{
1269-
return Some(Ok(pick));
1270-
}
1271-
} else {
1272-
if let Some(pick) =
1273-
self.collapse_candidates_to_trait_pick(self_ty, &applicable_candidates)
1274-
{
1275-
return Some(Ok(pick));
1276-
}
1268+
if let Some(pick) =
1269+
self.collapse_candidates_to_trait_pick(self_ty, &applicable_candidates)
1270+
{
1271+
return Some(Ok(pick));
12771272
}
12781273
}
12791274

@@ -1290,6 +1285,17 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
12901285
}
12911286

12921287
if applicable_candidates.len() > 1 {
1288+
// We collapse to a subtrait pick *after* filtering unstable candidates
1289+
// to make sure we don't prefer a unstable subtrait method over a stable
1290+
// supertrait method.
1291+
if self.tcx.features().supertrait_item_shadowing {
1292+
if let Some(pick) =
1293+
self.collapse_candidates_to_subtrait_pick(self_ty, &applicable_candidates)
1294+
{
1295+
return Some(Ok(pick));
1296+
}
1297+
}
1298+
12931299
let sources = candidates.iter().map(|p| self.candidate_source(p, self_ty)).collect();
12941300
return Some(Err(MethodError::Ambiguity(sources)));
12951301
}
@@ -1328,6 +1334,7 @@ impl<'tcx> Pick<'tcx> {
13281334
autoref_or_ptr_adjustment: _,
13291335
self_ty,
13301336
unstable_candidates: _,
1337+
shadowed_candidates: _,
13311338
} = *self;
13321339
self_ty != other.self_ty || def_id != other.item.def_id
13331340
}
@@ -1704,6 +1711,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
17041711
autoref_or_ptr_adjustment: None,
17051712
self_ty,
17061713
unstable_candidates: vec![],
1714+
shadowed_candidates: vec![],
17071715
})
17081716
}
17091717

@@ -1749,6 +1757,11 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
17491757
autoref_or_ptr_adjustment: None,
17501758
self_ty,
17511759
unstable_candidates: vec![],
1760+
shadowed_candidates: probes
1761+
.iter()
1762+
.map(|(c, _)| c.item)
1763+
.filter(|item| item.def_id != child_pick.item.def_id)
1764+
.collect(),
17521765
})
17531766
}
17541767

@@ -2042,6 +2055,7 @@ impl<'tcx> Candidate<'tcx> {
20422055
autoref_or_ptr_adjustment: None,
20432056
self_ty,
20442057
unstable_candidates,
2058+
shadowed_candidates: vec![],
20452059
}
20462060
}
20472061
}

compiler/rustc_lint_defs/src/builtin.rs

+36
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ declare_lint_pass! {
100100
SOFT_UNSTABLE,
101101
STABLE_FEATURES,
102102
STATIC_MUT_REFS,
103+
SUPERTRAIT_ITEM_SHADOWING,
103104
TEST_UNSTABLE_LINT,
104105
TEXT_DIRECTION_CODEPOINT_IN_COMMENT,
105106
TRIVIAL_CASTS,
@@ -5050,3 +5051,38 @@ declare_lint! {
50505051
reference: "issue #124535 <https://github.com/rust-lang/rust/issues/124535>",
50515052
};
50525053
}
5054+
5055+
declare_lint! {
5056+
/// The `supertrait_item_shadowing` lint detects when.
5057+
///
5058+
/// ### Example
5059+
///
5060+
/// ```rust
5061+
/// trait Upstream {
5062+
/// fn hello(&self) {}
5063+
/// }
5064+
/// impl<T> Upstream for T {}
5065+
///
5066+
/// trait Downstream: Upstream {
5067+
/// fn hello(&self) {}
5068+
/// }
5069+
/// impl<T> Downstream for T {}
5070+
///
5071+
/// struct MyType;
5072+
/// MyType.hello();
5073+
/// ```
5074+
///
5075+
/// {{produces}}
5076+
///
5077+
/// ### Explanation
5078+
///
5079+
/// RFC 3624 specified a heuristic in which a supertrait method would be
5080+
/// shadowed by a subtrait method when ambiguity occurs during method
5081+
/// selection. In order to mitigate side-effects of this happening
5082+
/// silently, it was also decided that this would, since the code should
5083+
/// eventually be fixed to no longer trigger this ambiguity.
5084+
pub SUPERTRAIT_ITEM_SHADOWING,
5085+
Warn,
5086+
"detects when a supertrait method is shadowed by a subtrait method",
5087+
@feature_gate = supertrait_item_shadowing;
5088+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//@ run-pass
2+
//@ check-run-results
3+
4+
#![feature(supertrait_item_shadowing)]
5+
#![allow(dead_code)]
6+
7+
trait A {
8+
fn hello(&self) {
9+
println!("A");
10+
}
11+
}
12+
impl<T> A for T {}
13+
14+
trait B: A {
15+
fn hello(&self) {
16+
println!("B");
17+
}
18+
}
19+
impl<T> B for T {}
20+
21+
fn main() {
22+
().hello();
23+
//~^ WARN trait method `hello` from `B` shadows identically named method from supertrait
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
B
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
warning: trait method `hello` from `B` shadows identically named method from supertrait
2+
--> $DIR/common-ancestor.rs:22:8
3+
|
4+
LL | ().hello();
5+
| ^^^^^
6+
|
7+
note: method from `B` shadows a supertrait method
8+
--> $DIR/common-ancestor.rs:15:5
9+
|
10+
LL | fn hello(&self) {
11+
| ^^^^^^^^^^^^^^^
12+
note: method from `A` is shadowed by a subtrait method
13+
--> $DIR/common-ancestor.rs:8:5
14+
|
15+
LL | fn hello(&self) {
16+
| ^^^^^^^^^^^^^^^
17+
= note: `#[warn(supertrait_item_shadowing)]` on by default
18+
19+
warning: 1 warning emitted
20+
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)