Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit b1b8aeb

Browse files
committedNov 21, 2024
Implement shadowing lint
1 parent 2c43098 commit b1b8aeb

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
@@ -797,3 +797,38 @@ pub(crate) struct PassToVariadicFunction<'a, 'tcx> {
797797
#[note(hir_typeck_teach_help)]
798798
pub(crate) teach: bool,
799799
}
800+
801+
#[derive(LintDiagnostic)]
802+
#[diag(hir_typeck_supertrait_method_shadowing)]
803+
pub(crate) struct SupertraitMethodShadowing {
804+
pub method: Symbol,
805+
pub subtrait: Symbol,
806+
#[subdiagnostic]
807+
pub shadower: SupertraitMethodShadower,
808+
#[subdiagnostic]
809+
pub shadowee: SupertraitMethodShadowee,
810+
}
811+
812+
#[derive(Subdiagnostic)]
813+
#[note(hir_typeck_supertrait_method_shadower)]
814+
pub(crate) struct SupertraitMethodShadower {
815+
pub subtrait: Symbol,
816+
#[primary_span]
817+
pub span: Span,
818+
}
819+
820+
#[derive(Subdiagnostic)]
821+
pub(crate) enum SupertraitMethodShadowee {
822+
#[note(hir_typeck_supertrait_method_shadowee)]
823+
Labeled {
824+
#[primary_span]
825+
span: Span,
826+
supertrait: Symbol,
827+
},
828+
#[note(hir_typeck_supertrait_method_multiple_shadowee)]
829+
Several {
830+
#[primary_span]
831+
spans: MultiSpan,
832+
traits: DiagSymbolList,
833+
},
834+
}

‎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.
@@ -664,6 +670,45 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
664670
}
665671
}
666672

673+
fn enforce_shadowed_supertrait_methods(
674+
&self,
675+
pick: &probe::Pick<'_>,
676+
segment: &hir::PathSegment<'tcx>,
677+
) {
678+
if pick.shadowed_candidates.is_empty() {
679+
return;
680+
}
681+
682+
let shadower_span = self.tcx.def_span(pick.item.def_id);
683+
let subtrait = self.tcx.item_name(pick.item.trait_container(self.tcx).unwrap());
684+
let shadower = SupertraitMethodShadower { span: shadower_span, subtrait };
685+
686+
let shadowee = if let [shadowee] = &pick.shadowed_candidates[..] {
687+
let shadowee_span = self.tcx.def_span(shadowee.def_id);
688+
let supertrait = self.tcx.item_name(shadowee.trait_container(self.tcx).unwrap());
689+
SupertraitMethodShadowee::Labeled { span: shadowee_span, supertrait }
690+
} else {
691+
let (traits, spans): (Vec<_>, Vec<_>) = pick
692+
.shadowed_candidates
693+
.iter()
694+
.map(|item| {
695+
(
696+
self.tcx.item_name(item.trait_container(self.tcx).unwrap()),
697+
self.tcx.def_span(item.def_id),
698+
)
699+
})
700+
.unzip();
701+
SupertraitMethodShadowee::Several { traits: traits.into(), spans: spans.into() }
702+
};
703+
704+
self.tcx.emit_node_span_lint(
705+
SUPERTRAIT_ITEM_SHADOWING,
706+
segment.hir_id,
707+
segment.ident.span,
708+
SupertraitMethodShadowing { shadower, shadowee, method: segment.ident.name, subtrait },
709+
);
710+
}
711+
667712
fn upcast(
668713
&mut self,
669714
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)]
@@ -1317,18 +1320,10 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
13171320
debug!("applicable_candidates: {:?}", applicable_candidates);
13181321

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

@@ -1345,6 +1340,17 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
13451340
}
13461341

13471342
if applicable_candidates.len() > 1 {
1343+
// We collapse to a subtrait pick *after* filtering unstable candidates
1344+
// to make sure we don't prefer a unstable subtrait method over a stable
1345+
// supertrait method.
1346+
if self.tcx.features().supertrait_item_shadowing {
1347+
if let Some(pick) =
1348+
self.collapse_candidates_to_subtrait_pick(self_ty, &applicable_candidates)
1349+
{
1350+
return Some(Ok(pick));
1351+
}
1352+
}
1353+
13481354
let sources = candidates.iter().map(|p| self.candidate_source(p, self_ty)).collect();
13491355
return Some(Err(MethodError::Ambiguity(sources)));
13501356
}
@@ -1382,6 +1388,7 @@ impl<'tcx> Pick<'tcx> {
13821388
autoref_or_ptr_adjustment: _,
13831389
self_ty,
13841390
unstable_candidates: _,
1391+
shadowed_candidates: _,
13851392
} = *self;
13861393
self_ty != other.self_ty || def_id != other.item.def_id
13871394
}
@@ -1758,6 +1765,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
17581765
autoref_or_ptr_adjustment: None,
17591766
self_ty,
17601767
unstable_candidates: vec![],
1768+
shadowed_candidates: vec![],
17611769
})
17621770
}
17631771

@@ -1803,6 +1811,11 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
18031811
autoref_or_ptr_adjustment: None,
18041812
self_ty,
18051813
unstable_candidates: vec![],
1814+
shadowed_candidates: probes
1815+
.iter()
1816+
.map(|(c, _)| c.item)
1817+
.filter(|item| item.def_id != child_pick.item.def_id)
1818+
.collect(),
18061819
})
18071820
}
18081821

@@ -2096,6 +2109,7 @@ impl<'tcx> Candidate<'tcx> {
20962109
autoref_or_ptr_adjustment: None,
20972110
self_ty,
20982111
unstable_candidates,
2112+
shadowed_candidates: vec![],
20992113
}
21002114
}
21012115
}

‎compiler/rustc_lint_defs/src/builtin.rs

+38
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,
104105
TAIL_EXPR_DROP_ORDER,
105106
TEST_UNSTABLE_LINT,
106107
TEXT_DIRECTION_CODEPOINT_IN_COMMENT,
@@ -4961,6 +4962,43 @@ declare_lint! {
49614962
};
49624963
}
49634964

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