Skip to content

Commit 06985c6

Browse files
authored
Rollup merge of rust-lang#67406 - ohadravid:suggest-assoc-type, r=estebank
Suggest associated type when the specified one cannot be found Fixes rust-lang#67386, so code like this: ``` use std::ops::Deref; fn homura<T: Deref<Trget = i32>>(_: T) {} fn main() {} ``` results in: ``` error[E0220]: associated type `Trget` not found for `std::ops::Deref` --> type-binding.rs:6:20 | 6 | fn homura<T: Deref<Trget = i32>>(_: T) {} | ^^^^^^^^^^^ help: there is an associated type with a similar name: `Target` error: aborting due to previous error ``` (The `help` is new) I used an `all_candidates: impl Fn() -> Iterator<...>` instead of `collect`ing to avoid the cost of allocating the Vec when no errors are found, at the expense of a little added complexity. r? @estebank
2 parents cfa7581 + a4a2fc0 commit 06985c6

File tree

2 files changed

+73
-32
lines changed

2 files changed

+73
-32
lines changed

src/librustc_typeck/astconv.rs

+72-31
Original file line numberDiff line numberDiff line change
@@ -1145,11 +1145,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
11451145
} else {
11461146
// Otherwise, we have to walk through the supertraits to find
11471147
// those that do.
1148-
let candidates = traits::supertraits(tcx, trait_ref).filter(|r| {
1149-
self.trait_defines_associated_type_named(r.def_id(), binding.item_name)
1150-
});
11511148
self.one_bound_for_assoc_type(
1152-
candidates,
1149+
|| traits::supertraits(tcx, trait_ref),
11531150
&trait_ref.print_only_trait_path().to_string(),
11541151
binding.item_name,
11551152
binding.span
@@ -1531,50 +1528,48 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
15311528

15321529
debug!("find_bound_for_assoc_item: predicates={:#?}", predicates);
15331530

1534-
let bounds = predicates.iter().filter_map(|(p, _)| p.to_opt_poly_trait_ref());
1535-
1536-
// Check that there is exactly one way to find an associated type with the
1537-
// correct name.
1538-
let suitable_bounds = traits::transitive_bounds(tcx, bounds)
1539-
.filter(|b| self.trait_defines_associated_type_named(b.def_id(), assoc_name));
1540-
15411531
let param_hir_id = tcx.hir().as_local_hir_id(ty_param_def_id).unwrap();
15421532
let param_name = tcx.hir().ty_param_name(param_hir_id);
1543-
self.one_bound_for_assoc_type(suitable_bounds,
1544-
&param_name.as_str(),
1545-
assoc_name,
1546-
span)
1533+
self.one_bound_for_assoc_type(
1534+
|| traits::transitive_bounds(tcx, predicates
1535+
.iter().filter_map(|(p, _)| p.to_opt_poly_trait_ref())),
1536+
&param_name.as_str(),
1537+
assoc_name,
1538+
span,
1539+
)
15471540
}
15481541

1549-
// Checks that `bounds` contains exactly one element and reports appropriate
1550-
// errors otherwise.
15511542
fn one_bound_for_assoc_type<I>(&self,
1552-
mut bounds: I,
1543+
all_candidates: impl Fn() -> I,
15531544
ty_param_name: &str,
15541545
assoc_name: ast::Ident,
15551546
span: Span)
15561547
-> Result<ty::PolyTraitRef<'tcx>, ErrorReported>
15571548
where I: Iterator<Item = ty::PolyTraitRef<'tcx>>
15581549
{
1559-
let bound = match bounds.next() {
1550+
let mut matching_candidates = all_candidates().filter(|r| {
1551+
self.trait_defines_associated_type_named(r.def_id(), assoc_name)
1552+
});
1553+
1554+
let bound = match matching_candidates.next() {
15601555
Some(bound) => bound,
15611556
None => {
1562-
struct_span_err!(self.tcx().sess, span, E0220,
1563-
"associated type `{}` not found for `{}`",
1564-
assoc_name,
1565-
ty_param_name)
1566-
.span_label(span, format!("associated type `{}` not found", assoc_name))
1567-
.emit();
1557+
self.complain_about_assoc_type_not_found(
1558+
all_candidates,
1559+
ty_param_name,
1560+
assoc_name,
1561+
span
1562+
);
15681563
return Err(ErrorReported);
15691564
}
15701565
};
15711566

15721567
debug!("one_bound_for_assoc_type: bound = {:?}", bound);
15731568

1574-
if let Some(bound2) = bounds.next() {
1569+
if let Some(bound2) = matching_candidates.next() {
15751570
debug!("one_bound_for_assoc_type: bound2 = {:?}", bound2);
15761571

1577-
let bounds = iter::once(bound).chain(iter::once(bound2)).chain(bounds);
1572+
let bounds = iter::once(bound).chain(iter::once(bound2)).chain(matching_candidates);
15781573
let mut err = struct_span_err!(
15791574
self.tcx().sess, span, E0221,
15801575
"ambiguous associated type `{}` in bounds of `{}`",
@@ -1606,6 +1601,50 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
16061601
return Ok(bound);
16071602
}
16081603

1604+
fn complain_about_assoc_type_not_found<I>(&self,
1605+
all_candidates: impl Fn() -> I,
1606+
ty_param_name: &str,
1607+
assoc_name: ast::Ident,
1608+
span: Span)
1609+
where I: Iterator<Item = ty::PolyTraitRef<'tcx>> {
1610+
let mut err = struct_span_err!(self.tcx().sess, span, E0220,
1611+
"associated type `{}` not found for `{}`",
1612+
assoc_name,
1613+
ty_param_name);
1614+
1615+
let all_candidate_names: Vec<_> = all_candidates()
1616+
.map(|r| self.tcx().associated_items(r.def_id()))
1617+
.flatten()
1618+
.filter_map(|item|
1619+
if item.kind == ty::AssocKind::Type {
1620+
Some(item.ident.name)
1621+
} else {
1622+
None
1623+
}
1624+
)
1625+
.collect();
1626+
1627+
if let Some(suggested_name) = find_best_match_for_name(
1628+
all_candidate_names.iter(),
1629+
&assoc_name.as_str(),
1630+
None,
1631+
) {
1632+
err.span_suggestion(
1633+
span,
1634+
"there is an associated type with a similar name",
1635+
suggested_name.to_string(),
1636+
Applicability::MaybeIncorrect,
1637+
);
1638+
} else {
1639+
err.span_label(
1640+
span,
1641+
format!("associated type `{}` not found", assoc_name)
1642+
);
1643+
}
1644+
1645+
err.emit();
1646+
}
1647+
16091648
// Create a type from a path to an associated type.
16101649
// For a path `A::B::C::D`, `qself_ty` and `qself_def` are the type and def for `A::B::C`
16111650
// and item_segment is the path segment for `D`. We return a type and a def for
@@ -1660,10 +1699,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
16601699
}
16611700
};
16621701

1663-
let candidates = traits::supertraits(tcx, ty::Binder::bind(trait_ref))
1664-
.filter(|r| self.trait_defines_associated_type_named(r.def_id(), assoc_ident));
1665-
1666-
self.one_bound_for_assoc_type(candidates, "Self", assoc_ident, span)?
1702+
self.one_bound_for_assoc_type(
1703+
|| traits::supertraits(tcx, ty::Binder::bind(trait_ref)),
1704+
"Self",
1705+
assoc_ident,
1706+
span
1707+
)?
16671708
}
16681709
(&ty::Param(_), Res::SelfTy(Some(param_did), None)) |
16691710
(&ty::Param(_), Res::Def(DefKind::TyParam, param_did)) => {

src/test/ui/span/type-binding.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ error[E0220]: associated type `Trget` not found for `std::ops::Deref`
22
--> $DIR/type-binding.rs:6:20
33
|
44
LL | fn homura<T: Deref<Trget = i32>>(_: T) {}
5-
| ^^^^^^^^^^^ associated type `Trget` not found
5+
| ^^^^^^^^^^^ help: there is an associated type with a similar name: `Target`
66

77
error: aborting due to previous error
88

0 commit comments

Comments
 (0)