Skip to content

Commit ef8e725

Browse files
Implement shadowing lint
1 parent 1051286 commit ef8e725

File tree

10 files changed

+245
-12
lines changed

10 files changed

+245
-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::{MethodCallee, probe};
29+
use crate::errors::{
30+
SupertraitMethodShadowee, SupertraitMethodShadower, SupertraitMethodShadowing,
31+
};
2832
use crate::{FnCtxt, callee};
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.
@@ -663,6 +669,45 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
663669
}
664670
}
665671

672+
fn enforce_shadowed_supertrait_methods(
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 = SupertraitMethodShadower { 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+
SupertraitMethodShadowee::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+
SupertraitMethodShadowee::Several { traits: traits.into(), spans: spans.into() }
701+
};
702+
703+
self.tcx.emit_node_span_lint(
704+
SUPERTRAIT_ITEM_SHADOWING,
705+
segment.hir_id,
706+
segment.ident.span,
707+
SupertraitMethodShadowing { shadower, shadowee, method: segment.ident.name, subtrait },
708+
);
709+
}
710+
666711
fn upcast(
667712
&mut self,
668713
source_trait_ref: ty::PolyTraitRef<'tcx>,

compiler/rustc_hir_typeck/src/method/probe.rs

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

184184
/// Unstable candidates alongside the stable ones.
185185
unstable_candidates: Vec<(Candidate<'tcx>, Symbol)>,
186+
187+
/// Candidates that were shadowed by supertraits.
188+
pub shadowed_candidates: Vec<ty::AssocItem>,
186189
}
187190

188191
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -1319,18 +1322,10 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
13191322
debug!("applicable_candidates: {:?}", applicable_candidates);
13201323

13211324
if applicable_candidates.len() > 1 {
1322-
if self.tcx.features().supertrait_item_shadowing {
1323-
if let Some(pick) =
1324-
self.collapse_candidates_to_subtrait_pick(self_ty, &applicable_candidates)
1325-
{
1326-
return Some(Ok(pick));
1327-
}
1328-
} else {
1329-
if let Some(pick) =
1330-
self.collapse_candidates_to_trait_pick(self_ty, &applicable_candidates)
1331-
{
1332-
return Some(Ok(pick));
1333-
}
1325+
if let Some(pick) =
1326+
self.collapse_candidates_to_trait_pick(self_ty, &applicable_candidates)
1327+
{
1328+
return Some(Ok(pick));
13341329
}
13351330
}
13361331

@@ -1347,6 +1342,17 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
13471342
}
13481343

13491344
if applicable_candidates.len() > 1 {
1345+
// We collapse to a subtrait pick *after* filtering unstable candidates
1346+
// to make sure we don't prefer a unstable subtrait method over a stable
1347+
// supertrait method.
1348+
if self.tcx.features().supertrait_item_shadowing {
1349+
if let Some(pick) =
1350+
self.collapse_candidates_to_subtrait_pick(self_ty, &applicable_candidates)
1351+
{
1352+
return Some(Ok(pick));
1353+
}
1354+
}
1355+
13501356
let sources = candidates.iter().map(|p| self.candidate_source(p, self_ty)).collect();
13511357
return Some(Err(MethodError::Ambiguity(sources)));
13521358
}
@@ -1385,6 +1391,7 @@ impl<'tcx> Pick<'tcx> {
13851391
autoref_or_ptr_adjustment: _,
13861392
self_ty,
13871393
unstable_candidates: _,
1394+
shadowed_candidates: _,
13881395
} = *self;
13891396
self_ty != other.self_ty || def_id != other.item.def_id
13901397
}
@@ -1761,6 +1768,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
17611768
autoref_or_ptr_adjustment: None,
17621769
self_ty,
17631770
unstable_candidates: vec![],
1771+
shadowed_candidates: vec![],
17641772
})
17651773
}
17661774

@@ -1806,6 +1814,11 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
18061814
autoref_or_ptr_adjustment: None,
18071815
self_ty,
18081816
unstable_candidates: vec![],
1817+
shadowed_candidates: probes
1818+
.iter()
1819+
.map(|(c, _)| c.item)
1820+
.filter(|item| item.def_id != child_pick.item.def_id)
1821+
.collect(),
18091822
})
18101823
}
18111824

@@ -2099,6 +2112,7 @@ impl<'tcx> Candidate<'tcx> {
20992112
autoref_or_ptr_adjustment: None,
21002113
self_ty,
21012114
unstable_candidates,
2115+
shadowed_candidates: vec![],
21022116
}
21032117
}
21042118
}

compiler/rustc_lint_defs/src/builtin.rs

+38
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ declare_lint_pass! {
100100
SINGLE_USE_LIFETIMES,
101101
SOFT_UNSTABLE,
102102
STABLE_FEATURES,
103+
SUPERTRAIT_ITEM_SHADOWING,
103104
TEST_UNSTABLE_LINT,
104105
TEXT_DIRECTION_CODEPOINT_IN_COMMENT,
105106
TRIVIAL_CASTS,
@@ -4964,6 +4965,43 @@ declare_lint! {
49644965
};
49654966
}
49664967

4968+
declare_lint! {
4969+
/// The `supertrait_item_shadowing` lint detects when.
4970+
///
4971+
/// ### Example
4972+
///
4973+
/// ```rust
4974+
/// #![feature(supertrait_item_shadowing)]
4975+
///
4976+
/// trait Upstream {
4977+
/// fn hello(&self) {}
4978+
/// }
4979+
/// impl<T> Upstream for T {}
4980+
///
4981+
/// trait Downstream: Upstream {
4982+
/// fn hello(&self) {}
4983+
/// }
4984+
/// impl<T> Downstream for T {}
4985+
///
4986+
/// struct MyType;
4987+
/// MyType.hello();
4988+
/// ```
4989+
///
4990+
/// {{produces}}
4991+
///
4992+
/// ### Explanation
4993+
///
4994+
/// RFC 3624 specified a heuristic in which a supertrait method would be
4995+
/// shadowed by a subtrait method when ambiguity occurs during method
4996+
/// selection. In order to mitigate side-effects of this happening
4997+
/// silently, it was also decided that this would, since the code should
4998+
/// eventually be fixed to no longer trigger this ambiguity.
4999+
pub SUPERTRAIT_ITEM_SHADOWING,
5000+
Warn,
5001+
"detects when a supertrait method is shadowed by a subtrait method",
5002+
@feature_gate = supertrait_item_shadowing;
5003+
}
5004+
49675005
declare_lint! {
49685006
/// The `ptr_to_integer_transmute_in_consts` lint detects pointer to integer
49695007
/// transmute in const functions and associated constants.
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)