Skip to content

Commit 6d8c642

Browse files
Point out shadowed associated types
When trying to set the value of a shadowed associated type, the error message was confusing, as it simply said that there is a missing type. Now the compiler notifies the user about the shadow, and suggests renaming as a fix
1 parent 9c8a269 commit 6d8c642

File tree

4 files changed

+110
-1
lines changed

4 files changed

+110
-1
lines changed

compiler/rustc_hir_analysis/src/astconv/errors.rs

+60-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use rustc_errors::{pluralize, struct_span_err, Applicability, Diagnostic, ErrorG
99
use rustc_hir as hir;
1010
use rustc_hir::def_id::{DefId, LocalDefId};
1111
use rustc_infer::traits::FulfillmentError;
12-
use rustc_middle::ty::{self, suggest_constraining_type_param, Ty, TyCtxt};
12+
use rustc_middle::ty::{self, suggest_constraining_type_param, AssocItem, AssocKind, Ty, TyCtxt};
1313
use rustc_session::parse::feature_err;
1414
use rustc_span::edit_distance::find_best_match_for_name;
1515
use rustc_span::symbol::{sym, Ident};
@@ -513,6 +513,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
513513
if associated_types.values().all(|v| v.is_empty()) {
514514
return;
515515
}
516+
516517
let tcx = self.tcx();
517518
// FIXME: Marked `mut` so that we can replace the spans further below with a more
518519
// appropriate one, but this should be handled earlier in the span assignment.
@@ -585,6 +586,35 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
585586
}
586587
}
587588

589+
// We get all the associated items that _are_ set,
590+
// so that we can check if any of their names match one of the ones we are missing.
591+
// This would mean that they are shadowing the associated type we are missing,
592+
// and we can then use their span to indicate this to the user.
593+
let bound_names = trait_bounds
594+
.iter()
595+
.filter_map(|poly_trait_ref| {
596+
poly_trait_ref.trait_ref.path.segments.last().and_then(|path| {
597+
let args = path.args?;
598+
Some((path, args))
599+
})
600+
})
601+
.flat_map(|(path, args)| {
602+
args.bindings.iter().map(|binding| {
603+
let ident = binding.ident;
604+
let trait_def = path.res.opt_def_id();
605+
let assoc_item = trait_def.and_then(|did| {
606+
tcx.associated_items(did).find_by_name_and_kinds(
607+
tcx,
608+
ident,
609+
&[AssocKind::Fn, AssocKind::Type, AssocKind::Const],
610+
did,
611+
)
612+
});
613+
(ident.name, assoc_item)
614+
})
615+
})
616+
.collect::<FxHashMap<Symbol, Option<&AssocItem>>>();
617+
588618
let mut names = names
589619
.into_iter()
590620
.map(|(trait_, mut assocs)| {
@@ -614,6 +644,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
614644
pluralize!(names_len),
615645
names,
616646
);
647+
let mut rename_suggestions = vec![];
617648
let mut suggestions = vec![];
618649
let mut types_count = 0;
619650
let mut where_constraints = vec![];
@@ -625,23 +656,47 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
625656
*names.entry(item.name).or_insert(0) += 1;
626657
}
627658
let mut dupes = false;
659+
let mut shadows = false;
628660
for item in assoc_items {
629661
let prefix = if names[&item.name] > 1 {
630662
let trait_def_id = item.container_id(tcx);
631663
dupes = true;
632664
format!("{}::", tcx.def_path_str(trait_def_id))
665+
} else if bound_names.contains_key(&item.name) {
666+
let trait_def_id = item.container_id(tcx);
667+
shadows = true;
668+
format!("{}::", tcx.def_path_str(trait_def_id))
633669
} else {
634670
String::new()
635671
};
636672
if let Some(sp) = tcx.hir().span_if_local(item.def_id) {
637673
err.span_label(sp, format!("`{}{}` defined here", prefix, item.name));
638674
}
675+
676+
if let Some(Some(assoc_item)) = bound_names.get(&item.name) {
677+
err.span_label(
678+
tcx.def_span(assoc_item.def_id),
679+
format!("`{}{}` shadowed here", prefix, item.name),
680+
);
681+
}
639682
}
640683
if potential_assoc_types.len() == assoc_items.len() {
641684
// When the amount of missing associated types equals the number of
642685
// extra type arguments present. A suggesting to replace the generic args with
643686
// associated types is already emitted.
644687
already_has_generics_args_suggestion = true;
688+
} else if shadows {
689+
for item in assoc_items {
690+
if let Some(Some(assoc_item)) = bound_names.get(&item.name) {
691+
if let Some(sp) = tcx.hir().span_if_local(item.def_id) {
692+
rename_suggestions.push(sp);
693+
}
694+
695+
if let Some(sp) = tcx.hir().span_if_local(assoc_item.def_id) {
696+
rename_suggestions.push(sp);
697+
}
698+
}
699+
}
645700
} else if let (Ok(snippet), false) =
646701
(tcx.sess.source_map().span_to_snippet(*span), dupes)
647702
{
@@ -725,6 +780,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
725780
err.span_help(where_constraints, where_msg);
726781
}
727782
}
783+
784+
for span in rename_suggestions {
785+
err.span_help(span, "consider renaming this associated type");
786+
}
728787
err.emit();
729788
}
730789
}

tests/ui/associated-type-bounds/overlaping-bound-suggestion.stderr

+5
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ LL | inner: <IntoIterator<Item: IntoIterator<Item: >>::IntoIterator as Item>
66
| | |
77
| | associated types `Item`, `IntoIter` must be specified
88
| associated types `Item`, `IntoIter` must be specified
9+
--> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
10+
|
11+
= note: `IntoIterator::Item` shadowed here
12+
|
13+
= note: `IntoIterator::Item` shadowed here
914

1015
error[E0223]: ambiguous associated type
1116
--> $DIR/overlaping-bound-suggestion.rs:7:13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Test Setting the value of an associated type
2+
// that is shadowed from a supertrait
3+
4+
pub trait Super {
5+
type X;
6+
}
7+
8+
pub trait Sub: Super {
9+
type X;
10+
}
11+
12+
impl<T> Clone for Box<dyn Sub<X = T>> {
13+
//~^ ERROR associated type `X` in `Super` must be specified
14+
fn clone(&self) -> Self {
15+
unimplemented!();
16+
}
17+
}
18+
19+
pub fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
error[E0191]: the value of the associated type `X` in `Super` must be specified
2+
--> $DIR/associated-type-shadowed-from-supertrait.rs:12:27
3+
|
4+
LL | type X;
5+
| ------ `Super::X` defined here
6+
...
7+
LL | type X;
8+
| ------ `Super::X` shadowed here
9+
...
10+
LL | impl<T> Clone for Box<dyn Sub<X = T>> {
11+
| ^^^^^^^^^^ associated type `X` must be specified
12+
|
13+
help: consider renaming this associated type
14+
--> $DIR/associated-type-shadowed-from-supertrait.rs:5:5
15+
|
16+
LL | type X;
17+
| ^^^^^^
18+
help: consider renaming this associated type
19+
--> $DIR/associated-type-shadowed-from-supertrait.rs:9:5
20+
|
21+
LL | type X;
22+
| ^^^^^^
23+
24+
error: aborting due to previous error
25+
26+
For more information about this error, try `rustc --explain E0191`.

0 commit comments

Comments
 (0)