Skip to content

Commit 87bf6bd

Browse files
committed
Cleanup: move code to their own methods and deduplicate actions
1 parent a4883ba commit 87bf6bd

File tree

2 files changed

+94
-75
lines changed

2 files changed

+94
-75
lines changed

src/librustc_typeck/astconv.rs

Lines changed: 92 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -783,6 +783,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
783783
(substs, assoc_bindings, potential_assoc_types)
784784
}
785785

786+
/// On missing type parameters, emit an E0393 error and provide a structured suggestion using
787+
/// the type parameter's name as a placeholder.
786788
fn complain_about_missing_type_params(
787789
&self,
788790
missing_type_params: Vec<String>,
@@ -956,8 +958,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
956958
trait_def_id: DefId,
957959
self_ty: Ty<'tcx>,
958960
trait_segment: &hir::PathSegment
959-
) -> ty::TraitRef<'tcx>
960-
{
961+
) -> ty::TraitRef<'tcx> {
961962
let (substs, assoc_bindings, _) =
962963
self.create_substs_for_ast_trait_ref(span,
963964
trait_def_id,
@@ -967,15 +968,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
967968
ty::TraitRef::new(trait_def_id, substs)
968969
}
969970

970-
fn create_substs_for_ast_trait_ref<'a>(
971+
/// When the code is using the `Fn` traits directly, instead of the `Fn(A) -> B` syntax, emit
972+
/// an error and attempt to build a reasonable structured suggestion.
973+
fn complain_about_internal_fn_trait(
971974
&self,
972975
span: Span,
973976
trait_def_id: DefId,
974-
self_ty: Ty<'tcx>,
975977
trait_segment: &'a hir::PathSegment,
976-
) -> (SubstsRef<'tcx>, Vec<ConvertedBinding<'a, 'tcx>>, Option<Vec<Span>>) {
977-
debug!("create_substs_for_ast_trait_ref(trait_segment={:?})", trait_segment);
978-
978+
) {
979979
let trait_def = self.tcx().trait_def(trait_def_id);
980980

981981
if !self.tcx().features().unboxed_closures &&
@@ -1020,19 +1020,33 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
10201020
}
10211021
err.emit();
10221022
}
1023+
}
1024+
1025+
fn create_substs_for_ast_trait_ref<'a>(
1026+
&self,
1027+
span: Span,
1028+
trait_def_id: DefId,
1029+
self_ty: Ty<'tcx>,
1030+
trait_segment: &'a hir::PathSegment,
1031+
) -> (SubstsRef<'tcx>, Vec<ConvertedBinding<'a, 'tcx>>, Option<Vec<Span>>) {
1032+
debug!("create_substs_for_ast_trait_ref(trait_segment={:?})", trait_segment);
1033+
1034+
self.complain_about_internal_fn_trait(span, trait_def_id, trait_segment);
10231035

1024-
self.create_substs_for_ast_path(span,
1025-
trait_def_id,
1026-
trait_segment.generic_args(),
1027-
trait_segment.infer_args,
1028-
Some(self_ty))
1036+
self.create_substs_for_ast_path(
1037+
span,
1038+
trait_def_id,
1039+
trait_segment.generic_args(),
1040+
trait_segment.infer_args,
1041+
Some(self_ty),
1042+
)
10291043
}
10301044

1031-
fn trait_defines_associated_type_named(&self,
1032-
trait_def_id: DefId,
1033-
assoc_name: ast::Ident)
1034-
-> bool
1035-
{
1045+
fn trait_defines_associated_type_named(
1046+
&self,
1047+
trait_def_id: DefId,
1048+
assoc_name: ast::Ident,
1049+
) -> bool {
10361050
self.tcx().associated_items(trait_def_id).any(|item| {
10371051
item.kind == ty::AssocKind::Type &&
10381052
self.tcx().hygienic_eq(assoc_name, item.ident, trait_def_id)
@@ -1400,48 +1414,49 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
14001414
.filter(|(trait_ref, _)| !tcx.trait_is_auto(trait_ref.def_id()));
14011415

14021416
for (base_trait_ref, span) in regular_traits_refs_spans {
1403-
debug!("conv_object_ty_poly_trait_ref regular_trait_ref `{:?}`", base_trait_ref);
1404-
let mut new_bounds = vec![];
14051417
for trait_ref in traits::elaborate_trait_ref(tcx, base_trait_ref) {
1406-
debug!("conv_object_ty_poly_trait_ref: observing object predicate `{:?}`", trait_ref);
1407-
match trait_ref {
1408-
ty::Predicate::Trait(pred) => {
1409-
associated_types.entry(span).or_default()
1410-
.extend(tcx.associated_items(pred.def_id())
1411-
.filter(|item| item.kind == ty::AssocKind::Type)
1412-
.map(|item| item.def_id));
1413-
}
1414-
ty::Predicate::Projection(pred) => {
1415-
// A `Self` within the original bound will be substituted with a
1416-
// `trait_object_dummy_self`, so check for that.
1417-
let references_self =
1418-
pred.skip_binder().ty.walk().any(|t| t == dummy_self);
1419-
1420-
// If the projection output contains `Self`, force the user to
1421-
// elaborate it explicitly to avoid a lot of complexity.
1422-
//
1423-
// The "classicaly useful" case is the following:
1424-
// ```
1425-
// trait MyTrait: FnMut() -> <Self as MyTrait>::MyOutput {
1426-
// type MyOutput;
1427-
// }
1428-
// ```
1429-
//
1430-
// Here, the user could theoretically write `dyn MyTrait<Output = X>`,
1431-
// but actually supporting that would "expand" to an infinitely-long type
1432-
// `fix $ τ → dyn MyTrait<MyOutput = X, Output = <τ as MyTrait>::MyOutput`.
1433-
//
1434-
// Instead, we force the user to write `dyn MyTrait<MyOutput = X, Output = X>`,
1435-
// which is uglier but works. See the discussion in #56288 for alternatives.
1436-
if !references_self {
1437-
// Include projections defined on supertraits.
1438-
new_bounds.push((pred, span));
1418+
debug!(
1419+
"conv_object_ty_poly_trait_ref: observing object predicate `{:?}`",
1420+
trait_ref
1421+
);
1422+
match trait_ref {
1423+
ty::Predicate::Trait(pred) => {
1424+
associated_types.entry(span).or_default()
1425+
.extend(tcx.associated_items(pred.def_id())
1426+
.filter(|item| item.kind == ty::AssocKind::Type)
1427+
.map(|item| item.def_id));
1428+
}
1429+
ty::Predicate::Projection(pred) => {
1430+
// A `Self` within the original bound will be substituted with a
1431+
// `trait_object_dummy_self`, so check for that.
1432+
let references_self =
1433+
pred.skip_binder().ty.walk().any(|t| t == dummy_self);
1434+
1435+
// If the projection output contains `Self`, force the user to
1436+
// elaborate it explicitly to avoid a lot of complexity.
1437+
//
1438+
// The "classicaly useful" case is the following:
1439+
// ```
1440+
// trait MyTrait: FnMut() -> <Self as MyTrait>::MyOutput {
1441+
// type MyOutput;
1442+
// }
1443+
// ```
1444+
//
1445+
// Here, the user could theoretically write `dyn MyTrait<Output = X>`,
1446+
// but actually supporting that would "expand" to an infinitely-long type
1447+
// `fix $ τ → dyn MyTrait<MyOutput = X, Output = <τ as MyTrait>::MyOutput`.
1448+
//
1449+
// Instead, we force the user to write
1450+
// `dyn MyTrait<MyOutput = X, Output = X>`, which is uglier but works. See
1451+
// the discussion in #56288 for alternatives.
1452+
if !references_self {
1453+
// Include projections defined on supertraits.
1454+
bounds.projection_bounds.push((pred, span));
1455+
}
14391456
}
1457+
_ => ()
14401458
}
1441-
_ => ()
1442-
}
14431459
}
1444-
bounds.projection_bounds.extend(new_bounds);
14451460
}
14461461

14471462
for (projection_bound, _) in &bounds.projection_bounds {
@@ -1534,27 +1549,37 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
15341549
ty
15351550
}
15361551

1552+
/// When there are any missing associated types, emit an E0191 error and attempt to supply a
1553+
/// reasonable suggestion on how to write it. For the case of multiple associated types in the
1554+
/// same trait bound have the same name (as they come from different super-traits), we instead
1555+
/// emit a generic note suggesting using a `where` clause to constraint instead.
15371556
fn complain_about_missing_associated_types(
15381557
&self,
1539-
mut associated_types: FxHashMap<Span, BTreeSet<DefId>>,
1558+
associated_types: FxHashMap<Span, BTreeSet<DefId>>,
15401559
potential_assoc_types: Vec<Span>,
15411560
trait_bounds: &[hir::PolyTraitRef],
15421561
) {
15431562
if !associated_types.values().any(|v| v.len() > 0) {
15441563
return;
15451564
}
15461565
let tcx = self.tcx();
1566+
// FIXME: Marked `mut` so that we can replace the spans further below with a more
1567+
// appropriate one, but this should be handled earlier in the span assignment.
1568+
let mut associated_types: FxHashMap<Span, Vec<_>> = associated_types.into_iter()
1569+
.map(|(span, def_ids)| (
1570+
span,
1571+
def_ids.into_iter().map(|did| tcx.associated_item(did)).collect(),
1572+
)).collect();
15471573
let mut names = vec![];
15481574

15491575
// Account for things like `dyn Foo + 'a`, like in tests `issue-22434.rs` and
15501576
// `issue-22560.rs`.
15511577
let mut trait_bound_spans: Vec<Span> = vec![];
1552-
for (span, item_def_ids) in &associated_types {
1553-
if !item_def_ids.is_empty() {
1578+
for (span, items) in &associated_types {
1579+
if !items.is_empty() {
15541580
trait_bound_spans.push(*span);
15551581
}
1556-
for item_def_id in item_def_ids {
1557-
let assoc_item = tcx.associated_item(*item_def_id);
1582+
for assoc_item in items {
15581583
let trait_def_id = assoc_item.container.id();
15591584
names.push(format!(
15601585
"`{}` (from trait `{}`)",
@@ -1592,7 +1617,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
15921617
[segment] if segment.args.is_none() => {
15931618
trait_bound_spans = vec![segment.ident.span];
15941619
associated_types = associated_types.into_iter()
1595-
.map(|(_, defs)| (segment.ident.span, defs))
1620+
.map(|(_, items)| (segment.ident.span, items))
15961621
.collect();
15971622
}
15981623
_ => {}
@@ -1610,17 +1635,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
16101635
let mut suggestions = vec![];
16111636
let mut types_count = 0;
16121637
let mut where_constraints = vec![];
1613-
for (span, def_ids) in &associated_types {
1614-
let assoc_items: Vec<_> = def_ids.iter()
1615-
.map(|def_id| tcx.associated_item(*def_id))
1616-
.collect();
1638+
for (span, assoc_items) in &associated_types {
16171639
let mut names: FxHashMap<_, usize> = FxHashMap::default();
1618-
for item in &assoc_items {
1640+
for item in assoc_items {
16191641
types_count += 1;
16201642
*names.entry(item.ident.name).or_insert(0) += 1;
16211643
}
16221644
let mut dupes = false;
1623-
for item in &assoc_items {
1645+
for item in assoc_items {
16241646
let prefix = if names[&item.ident.name] > 1 {
16251647
let trait_def_id = item.container.id();
16261648
dupes = true;
@@ -1681,17 +1703,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
16811703
}
16821704
if suggestions.len() != 1 {
16831705
// We don't need this label if there's an inline suggestion, show otherwise.
1684-
for (span, def_ids) in &associated_types {
1685-
let assoc_items: Vec<_> = def_ids.iter()
1686-
.map(|def_id| tcx.associated_item(*def_id))
1687-
.collect();
1706+
for (span, assoc_items) in &associated_types {
16881707
let mut names: FxHashMap<_, usize> = FxHashMap::default();
1689-
for item in &assoc_items {
1708+
for item in assoc_items {
16901709
types_count += 1;
16911710
*names.entry(item.ident.name).or_insert(0) += 1;
16921711
}
16931712
let mut label = vec![];
1694-
for item in &assoc_items {
1713+
for item in assoc_items {
16951714
let postfix = if names[&item.ident.name] > 1 {
16961715
let trait_def_id = item.container.id();
16971716
format!(" (from trait `{}`)", tcx.def_path_str(trait_def_id))

src/test/ui/associated-types/missing-associated-types.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ LL | type Bar<Rhs> = dyn Add<Rhs> + Sub<Rhs> + X<Rhs> + Z<Rhs>;
3838
| first non-auto trait
3939
| trait alias used in trait object type (first use)
4040

41-
error[E0191]: the value of the associated types `Output` (from trait `std::ops::Add`), `Output` (from trait `std::ops::Sub`), `A` (from trait `Z`), `B` (from trait `Z`), `Output` (from trait `std::ops::Div`), `Output` (from trait `std::ops::Mul`), `Output` (from trait `std::ops::Div`) must be specified
42-
--> $DIR/missing-associated-types.rs:15:21
41+
error[E0191]: the value of the associated types `Output` (from trait `std::ops::Mul`), `Output` (from trait `std::ops::Div`), `Output` (from trait `std::ops::Sub`), `A` (from trait `Z`), `B` (from trait `Z`), `Output` (from trait `std::ops::Div`), `Output` (from trait `std::ops::Add`) must be specified
42+
--> $DIR/missing-associated-types.rs:15:43
4343
|
4444
LL | type A;
4545
| ------- `A` defined here

0 commit comments

Comments
 (0)