@@ -1444,59 +1444,68 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
1444
1444
}
1445
1445
1446
1446
// Use a `BTreeSet` to keep output in a more consistent order.
1447
- let mut associated_types = BTreeSet :: default ( ) ;
1447
+ let mut associated_types: FxHashMap < Span , BTreeSet < DefId > > = FxHashMap :: default ( ) ;
1448
1448
1449
- let regular_traits_refs = bounds
1449
+ let regular_traits_refs_spans = bounds
1450
1450
. trait_bounds
1451
1451
. into_iter ( )
1452
- . filter ( |( trait_ref, _) | !tcx. trait_is_auto ( trait_ref. def_id ( ) ) )
1453
- . map ( |( trait_ref, _) | trait_ref) ;
1454
- for trait_ref in traits:: elaborate_trait_refs ( tcx, regular_traits_refs) {
1455
- debug ! ( "conv_object_ty_poly_trait_ref: observing object predicate `{:?}`" , trait_ref) ;
1456
- match trait_ref {
1457
- ty:: Predicate :: Trait ( pred) => {
1458
- associated_types. extend (
1459
- tcx. associated_items ( pred. def_id ( ) )
1460
- . filter ( |item| item. kind == ty:: AssocKind :: Type )
1461
- . map ( |item| item. def_id ) ,
1462
- ) ;
1463
- }
1464
- ty:: Predicate :: Projection ( pred) => {
1465
- // A `Self` within the original bound will be substituted with a
1466
- // `trait_object_dummy_self`, so check for that.
1467
- let references_self = pred. skip_binder ( ) . ty . walk ( ) . any ( |t| t == dummy_self) ;
1468
-
1469
- // If the projection output contains `Self`, force the user to
1470
- // elaborate it explicitly to avoid a lot of complexity.
1471
- //
1472
- // The "classicaly useful" case is the following:
1473
- // ```
1474
- // trait MyTrait: FnMut() -> <Self as MyTrait>::MyOutput {
1475
- // type MyOutput;
1476
- // }
1477
- // ```
1478
- //
1479
- // Here, the user could theoretically write `dyn MyTrait<Output = X>`,
1480
- // but actually supporting that would "expand" to an infinitely-long type
1481
- // `fix $ τ → dyn MyTrait<MyOutput = X, Output = <τ as MyTrait>::MyOutput`.
1482
- //
1483
- // Instead, we force the user to write `dyn MyTrait<MyOutput = X, Output = X>`,
1484
- // which is uglier but works. See the discussion in #56288 for alternatives.
1485
- if !references_self {
1486
- // Include projections defined on supertraits.
1487
- bounds. projection_bounds . push ( ( pred, DUMMY_SP ) )
1452
+ . filter ( |( trait_ref, _) | !tcx. trait_is_auto ( trait_ref. def_id ( ) ) ) ;
1453
+
1454
+ for ( base_trait_ref, span) in regular_traits_refs_spans {
1455
+ debug ! ( "conv_object_ty_poly_trait_ref regular_trait_ref `{:?}`" , base_trait_ref) ;
1456
+ let mut new_bounds = vec ! [ ] ;
1457
+ for trait_ref in traits:: elaborate_trait_ref ( tcx, base_trait_ref) {
1458
+ debug ! (
1459
+ "conv_object_ty_poly_trait_ref: observing object predicate `{:?}`" ,
1460
+ trait_ref
1461
+ ) ;
1462
+ match trait_ref {
1463
+ ty:: Predicate :: Trait ( pred) => {
1464
+ associated_types. entry ( span) . or_default ( ) . extend (
1465
+ tcx. associated_items ( pred. def_id ( ) )
1466
+ . filter ( |item| item. kind == ty:: AssocKind :: Type )
1467
+ . map ( |item| item. def_id ) ,
1468
+ ) ;
1488
1469
}
1470
+ ty:: Predicate :: Projection ( pred) => {
1471
+ // A `Self` within the original bound will be substituted with a
1472
+ // `trait_object_dummy_self`, so check for that.
1473
+ let references_self = pred. skip_binder ( ) . ty . walk ( ) . any ( |t| t == dummy_self) ;
1474
+
1475
+ // If the projection output contains `Self`, force the user to
1476
+ // elaborate it explicitly to avoid a lot of complexity.
1477
+ //
1478
+ // The "classicaly useful" case is the following:
1479
+ // ```
1480
+ // trait MyTrait: FnMut() -> <Self as MyTrait>::MyOutput {
1481
+ // type MyOutput;
1482
+ // }
1483
+ // ```
1484
+ //
1485
+ // Here, the user could theoretically write `dyn MyTrait<Output = X>`,
1486
+ // but actually supporting that would "expand" to an infinitely-long type
1487
+ // `fix $ τ → dyn MyTrait<MyOutput = X, Output = <τ as MyTrait>::MyOutput`.
1488
+ //
1489
+ // Instead, we force the user to write `dyn MyTrait<MyOutput = X, Output = X>`,
1490
+ // which is uglier but works. See the discussion in #56288 for alternatives.
1491
+ if !references_self {
1492
+ // Include projections defined on supertraits.
1493
+ new_bounds. push ( ( pred, span) ) ;
1494
+ }
1495
+ }
1496
+ _ => ( ) ,
1489
1497
}
1490
- _ => ( ) ,
1491
1498
}
1499
+ bounds. projection_bounds . extend ( new_bounds) ;
1492
1500
}
1493
1501
1494
1502
for ( projection_bound, _) in & bounds. projection_bounds {
1495
- associated_types. remove ( & projection_bound. projection_def_id ( ) ) ;
1503
+ for ( _, def_ids) in & mut associated_types {
1504
+ def_ids. remove ( & projection_bound. projection_def_id ( ) ) ;
1505
+ }
1496
1506
}
1497
1507
1498
1508
self . complain_about_missing_associated_types (
1499
- span,
1500
1509
associated_types,
1501
1510
potential_assoc_types,
1502
1511
trait_bounds,
@@ -1591,134 +1600,183 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
1591
1600
1592
1601
fn complain_about_missing_associated_types (
1593
1602
& self ,
1594
- span : Span ,
1595
- associated_types : BTreeSet < DefId > ,
1603
+ mut associated_types : FxHashMap < Span , BTreeSet < DefId > > ,
1596
1604
potential_assoc_types : Vec < Span > ,
1597
1605
trait_bounds : & [ hir:: PolyTraitRef ] ,
1598
1606
) {
1599
- if associated_types. is_empty ( ) {
1607
+ if ! associated_types. values ( ) . any ( |v| v . len ( ) > 0 ) {
1600
1608
return ;
1601
1609
}
1602
- // Account for things like `dyn Foo + 'a` by pointing at the `TraitRef.path`
1603
- // `Span` instead of the `PolyTraitRef` `Span`. That way the suggestion will
1604
- // be valid, otherwise we would suggest `dyn Foo + 'a<A = Type>`. See tests
1605
- // `issue-22434.rs` and `issue-22560.rs` for examples.
1606
- let sugg_span = match ( & potential_assoc_types[ ..] , & trait_bounds) {
1610
+ let tcx = self . tcx ( ) ;
1611
+ let mut names = vec ! [ ] ;
1612
+
1613
+ // Account for things like `dyn Foo + 'a`, like in tests `issue-22434.rs` and
1614
+ // `issue-22560.rs`.
1615
+ let mut trait_bound_spans: Vec < Span > = vec ! [ ] ;
1616
+ for ( span, item_def_ids) in & associated_types {
1617
+ if !item_def_ids. is_empty ( ) {
1618
+ trait_bound_spans. push ( * span) ;
1619
+ }
1620
+ for item_def_id in item_def_ids {
1621
+ let assoc_item = tcx. associated_item ( * item_def_id) ;
1622
+ let trait_def_id = assoc_item. container . id ( ) ;
1623
+ names. push ( format ! (
1624
+ "`{}` (from trait `{}`)" ,
1625
+ assoc_item. ident,
1626
+ tcx. def_path_str( trait_def_id) ,
1627
+ ) ) ;
1628
+ }
1629
+ }
1630
+
1631
+ match ( & potential_assoc_types[ ..] , & trait_bounds) {
1607
1632
( [ ] , [ bound] ) => match & bound. trait_ref . path . segments [ ..] {
1608
1633
// FIXME: `trait_ref.path.span` can point to a full path with multiple
1609
1634
// segments, even though `trait_ref.path.segments` is of length `1`. Work
1610
1635
// around that bug here, even though it should be fixed elsewhere.
1611
1636
// This would otherwise cause an invalid suggestion. For an example, look at
1612
- // `src/test/ui/issues/issue-28344.rs`.
1613
- [ segment] if segment. args . is_none ( ) => segment. ident . span ,
1614
- _ => bound. trait_ref . path . span ,
1637
+ // `src/test/ui/issues/issue-28344.rs` where instead of the following:
1638
+ //
1639
+ // error[E0191]: the value of the associated type `Output`
1640
+ // (from trait `std::ops::BitXor`) must be specified
1641
+ // --> $DIR/issue-28344.rs:4:17
1642
+ // |
1643
+ // LL | let x: u8 = BitXor::bitor(0 as u8, 0 as u8);
1644
+ // | ^^^^^^ help: specify the associated type:
1645
+ // | `BitXor<Output = Type>`
1646
+ //
1647
+ // we would output:
1648
+ //
1649
+ // error[E0191]: the value of the associated type `Output`
1650
+ // (from trait `std::ops::BitXor`) must be specified
1651
+ // --> $DIR/issue-28344.rs:4:17
1652
+ // |
1653
+ // LL | let x: u8 = BitXor::bitor(0 as u8, 0 as u8);
1654
+ // | ^^^^^^^^^^^^^ help: specify the associated type:
1655
+ // | `BitXor::bitor<Output = Type>`
1656
+ [ segment] if segment. args . is_none ( ) => {
1657
+ trait_bound_spans = vec ! [ segment. ident. span] ;
1658
+ associated_types = associated_types
1659
+ . into_iter ( )
1660
+ . map ( |( _, defs) | ( segment. ident . span , defs) )
1661
+ . collect ( ) ;
1662
+ }
1663
+ _ => { }
1615
1664
} ,
1616
- _ => span,
1617
- } ;
1618
- let tcx = self . tcx ( ) ;
1619
- let names = associated_types
1620
- . iter ( )
1621
- . map ( |item_def_id| {
1622
- let assoc_item = tcx. associated_item ( * item_def_id) ;
1623
- let trait_def_id = assoc_item. container . id ( ) ;
1624
- format ! ( "`{}` (from trait `{}`)" , assoc_item. ident, tcx. def_path_str( trait_def_id) )
1625
- } )
1626
- . collect :: < Vec < _ > > ( )
1627
- . join ( ", " ) ;
1665
+ _ => { }
1666
+ }
1628
1667
let mut err = struct_span_err ! (
1629
1668
tcx. sess,
1630
- sugg_span ,
1669
+ trait_bound_spans ,
1631
1670
E0191 ,
1632
1671
"the value of the associated type{} {} must be specified" ,
1633
- pluralize!( associated_types . len( ) ) ,
1634
- names,
1672
+ pluralize!( names . len( ) ) ,
1673
+ names. join ( ", " ) ,
1635
1674
) ;
1636
- let mut suggestions = Vec :: new ( ) ;
1637
- let mut applicability = Applicability :: MaybeIncorrect ;
1638
- for ( i, item_def_id) in associated_types. iter ( ) . enumerate ( ) {
1639
- let assoc_item = tcx. associated_item ( * item_def_id) ;
1640
- if let Some ( sp) = tcx. hir ( ) . span_if_local ( * item_def_id) {
1641
- err. span_label ( sp, format ! ( "`{}` defined here" , assoc_item. ident) ) ;
1642
- }
1643
- if potential_assoc_types. len ( ) == associated_types. len ( ) {
1675
+ let mut suggestions = vec ! [ ] ;
1676
+ let mut types_count = 0 ;
1677
+ let mut where_constraints = vec ! [ ] ;
1678
+ for ( span, def_ids) in & associated_types {
1679
+ let assoc_items: Vec < _ > =
1680
+ def_ids. iter ( ) . map ( |def_id| tcx. associated_item ( * def_id) ) . collect ( ) ;
1681
+ let mut names: FxHashMap < _ , usize > = FxHashMap :: default ( ) ;
1682
+ for item in & assoc_items {
1683
+ types_count += 1 ;
1684
+ * names. entry ( item. ident . name ) . or_insert ( 0 ) += 1 ;
1685
+ }
1686
+ let mut dupes = false ;
1687
+ for item in & assoc_items {
1688
+ let prefix = if names[ & item. ident . name ] > 1 {
1689
+ let trait_def_id = item. container . id ( ) ;
1690
+ dupes = true ;
1691
+ format ! ( "{}::" , tcx. def_path_str( trait_def_id) )
1692
+ } else {
1693
+ String :: new ( )
1694
+ } ;
1695
+ if let Some ( sp) = tcx. hir ( ) . span_if_local ( item. def_id ) {
1696
+ err. span_label ( sp, format ! ( "`{}{}` defined here" , prefix, item. ident) ) ;
1697
+ }
1698
+ }
1699
+ if potential_assoc_types. len ( ) == assoc_items. len ( ) {
1644
1700
// Only suggest when the amount of missing associated types equals the number of
1645
1701
// extra type arguments present, as that gives us a relatively high confidence
1646
1702
// that the user forgot to give the associtated type's name. The canonical
1647
1703
// example would be trying to use `Iterator<isize>` instead of
1648
1704
// `Iterator<Item = isize>`.
1649
- if let Ok ( snippet) = tcx. sess . source_map ( ) . span_to_snippet ( potential_assoc_types[ i] )
1650
- {
1651
- suggestions. push ( (
1652
- potential_assoc_types[ i] ,
1653
- format ! ( "{} = {}" , assoc_item. ident, snippet) ,
1654
- ) ) ;
1705
+ for ( potential, item) in potential_assoc_types. iter ( ) . zip ( assoc_items. iter ( ) ) {
1706
+ if let Ok ( snippet) = tcx. sess . source_map ( ) . span_to_snippet ( * potential) {
1707
+ suggestions. push ( ( * potential, format ! ( "{} = {}" , item. ident, snippet) ) ) ;
1708
+ }
1655
1709
}
1656
- }
1657
- }
1658
- let mut suggestions_len = suggestions. len ( ) ;
1659
- if let Ok ( snippet) = tcx. sess . source_map ( ) . span_to_snippet ( sugg_span) {
1660
- let assoc_types: Vec < String > = associated_types
1661
- . iter ( )
1662
- . map ( |item_def_id| {
1663
- let assoc_item = tcx. associated_item ( * item_def_id) ;
1664
- format ! ( "{} = Type" , assoc_item. ident)
1665
- } )
1666
- . collect ( ) ;
1667
- let dedup = assoc_types. clone ( ) . drain ( ..) . collect :: < FxHashSet < _ > > ( ) ;
1668
-
1669
- if dedup. len ( ) != assoc_types. len ( ) && trait_bounds. len ( ) == 1 {
1670
- // If there are duplicates associated type names and a single trait bound do not
1671
- // use structured suggestion, it means that there are multiple super-traits with
1672
- // the same associated type name.
1673
- err. help (
1674
- "consider introducing a new type parameter, adding `where` constraints \
1675
- using the fully-qualified path to the associated type",
1676
- ) ;
1677
- } else if dedup. len ( ) == assoc_types. len ( ) &&
1678
- potential_assoc_types. is_empty ( ) &&
1679
- trait_bounds. len ( ) == 1 &&
1680
- // Do not attempt to suggest when we don't know which path segment needs the
1681
- // type parameter set.
1682
- trait_bounds[ 0 ] . trait_ref . path . segments . len ( ) == 1
1710
+ } else if let ( Ok ( snippet) , false ) =
1711
+ ( tcx. sess . source_map ( ) . span_to_snippet ( * span) , dupes)
1683
1712
{
1684
- applicability = Applicability :: HasPlaceholders ;
1685
- let sugg = assoc_types . join ( ", " ) ;
1686
- if snippet. ends_with ( '>' ) {
1713
+ let types : Vec < _ > =
1714
+ assoc_items . iter ( ) . map ( |item| format ! ( "{} = Type" , item . ident ) ) . collect ( ) ;
1715
+ let code = if snippet. ends_with ( ">" ) {
1687
1716
// The user wrote `Trait<'a>` or similar and we don't have a type we can
1688
1717
// suggest, but at least we can clue them to the correct syntax
1689
1718
// `Trait<'a, Item = Type>` while accounting for the `<'a>` in the
1690
1719
// suggestion.
1691
- suggestions. push ( (
1692
- sugg_span,
1693
- format ! ( "{}, {}>" , & snippet[ ..snippet. len( ) - 1 ] , sugg, ) ,
1694
- ) ) ;
1720
+ format ! ( "{}, {}>" , & snippet[ ..snippet. len( ) - 1 ] , types. join( ", " ) )
1695
1721
} else {
1696
1722
// The user wrote `Iterator`, so we don't have a type we can suggest, but at
1697
1723
// least we can clue them to the correct syntax `Iterator<Item = Type>`.
1698
- suggestions. push ( ( sugg_span, format ! ( "{}<{}>" , snippet, sugg) ) ) ;
1699
- }
1700
- suggestions_len = assoc_types. len ( ) ;
1724
+ format ! ( "{}<{}>" , snippet, types. join( ", " ) )
1725
+ } ;
1726
+ suggestions. push ( ( * span, code) ) ;
1727
+ } else if dupes {
1728
+ where_constraints. push ( * span) ;
1701
1729
}
1702
1730
}
1731
+ let where_msg = "consider introducing a new type parameter, adding `where` constraints \
1732
+ using the fully-qualified path to the associated types";
1733
+ if !where_constraints. is_empty ( ) && suggestions. is_empty ( ) {
1734
+ // If there are duplicates associated type names and a single trait bound do not
1735
+ // use structured suggestion, it means that there are multiple super-traits with
1736
+ // the same associated type name.
1737
+ err. help ( where_msg) ;
1738
+ }
1703
1739
if suggestions. len ( ) != 1 {
1704
1740
// We don't need this label if there's an inline suggestion, show otherwise.
1705
- let names = associated_types
1706
- . iter ( )
1707
- . map ( |t| format ! ( "`{}`" , tcx. associated_item( * t) . ident) )
1708
- . collect :: < Vec < _ > > ( )
1709
- . join ( ", " ) ;
1710
- err. span_label (
1711
- sugg_span,
1712
- format ! (
1713
- "associated type{} {} must be specified" ,
1714
- pluralize!( associated_types. len( ) ) ,
1715
- names,
1716
- ) ,
1717
- ) ;
1741
+ for ( span, def_ids) in & associated_types {
1742
+ let assoc_items: Vec < _ > =
1743
+ def_ids. iter ( ) . map ( |def_id| tcx. associated_item ( * def_id) ) . collect ( ) ;
1744
+ let mut names: FxHashMap < _ , usize > = FxHashMap :: default ( ) ;
1745
+ for item in & assoc_items {
1746
+ types_count += 1 ;
1747
+ * names. entry ( item. ident . name ) . or_insert ( 0 ) += 1 ;
1748
+ }
1749
+ let mut label = vec ! [ ] ;
1750
+ for item in & assoc_items {
1751
+ let postfix = if names[ & item. ident . name ] > 1 {
1752
+ let trait_def_id = item. container . id ( ) ;
1753
+ format ! ( " (from trait `{}`)" , tcx. def_path_str( trait_def_id) )
1754
+ } else {
1755
+ String :: new ( )
1756
+ } ;
1757
+ label. push ( format ! ( "`{}`{}" , item. ident, postfix) ) ;
1758
+ }
1759
+ if !label. is_empty ( ) {
1760
+ err. span_label (
1761
+ * span,
1762
+ format ! (
1763
+ "associated type{} {} must be specified" ,
1764
+ pluralize!( label. len( ) ) ,
1765
+ label. join( ", " ) ,
1766
+ ) ,
1767
+ ) ;
1768
+ }
1769
+ }
1718
1770
}
1719
1771
if !suggestions. is_empty ( ) {
1720
- let msg = format ! ( "specify the associated type{}" , pluralize!( suggestions_len) ) ;
1721
- err. multipart_suggestion ( & msg, suggestions, applicability) ;
1772
+ err. multipart_suggestion (
1773
+ & format ! ( "specify the associated type{}" , pluralize!( types_count) ) ,
1774
+ suggestions,
1775
+ Applicability :: HasPlaceholders ,
1776
+ ) ;
1777
+ if !where_constraints. is_empty ( ) {
1778
+ err. span_help ( where_constraints, where_msg) ;
1779
+ }
1722
1780
}
1723
1781
err. emit ( ) ;
1724
1782
}
0 commit comments