@@ -27,7 +27,7 @@ use syntax::errors::pluralize;
27
27
use syntax:: feature_gate:: feature_err;
28
28
use syntax:: util:: lev_distance:: find_best_match_for_name;
29
29
use syntax:: symbol:: sym;
30
- use syntax_pos:: { DUMMY_SP , Span , MultiSpan } ;
30
+ use syntax_pos:: { DUMMY_SP , MultiSpan , Span } ;
31
31
use crate :: util:: common:: ErrorReported ;
32
32
use crate :: util:: nodemap:: FxHashMap ;
33
33
@@ -1393,20 +1393,23 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
1393
1393
}
1394
1394
1395
1395
// Use a `BTreeSet` to keep output in a more consistent order.
1396
- let mut associated_types = BTreeSet :: default ( ) ;
1396
+ let mut associated_types: FxHashMap < Span , BTreeSet < DefId > > = FxHashMap :: default ( ) ;
1397
1397
1398
- let regular_traits_refs = bounds. trait_bounds
1398
+ let regular_traits_refs_spans = bounds. trait_bounds
1399
1399
. into_iter ( )
1400
- . filter ( |( trait_ref, _) | !tcx. trait_is_auto ( trait_ref. def_id ( ) ) )
1401
- . map ( |( trait_ref, _) | trait_ref) ;
1402
- for trait_ref in traits:: elaborate_trait_refs ( tcx, regular_traits_refs) {
1400
+ . filter ( |( trait_ref, _) | !tcx. trait_is_auto ( trait_ref. def_id ( ) ) ) ;
1401
+
1402
+ 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 ! [ ] ;
1405
+ for trait_ref in traits:: elaborate_trait_ref ( tcx, base_trait_ref) {
1403
1406
debug ! ( "conv_object_ty_poly_trait_ref: observing object predicate `{:?}`" , trait_ref) ;
1404
1407
match trait_ref {
1405
1408
ty:: Predicate :: Trait ( pred) => {
1406
- associated_types
1409
+ associated_types. entry ( span ) . or_default ( )
1407
1410
. extend ( tcx. associated_items ( pred. def_id ( ) )
1408
- . filter ( |item| item. kind == ty:: AssocKind :: Type )
1409
- . map ( |item| item. def_id ) ) ;
1411
+ . filter ( |item| item. kind == ty:: AssocKind :: Type )
1412
+ . map ( |item| item. def_id ) ) ;
1410
1413
}
1411
1414
ty:: Predicate :: Projection ( pred) => {
1412
1415
// A `Self` within the original bound will be substituted with a
@@ -1432,19 +1435,22 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
1432
1435
// which is uglier but works. See the discussion in #56288 for alternatives.
1433
1436
if !references_self {
1434
1437
// Include projections defined on supertraits.
1435
- bounds . projection_bounds . push ( ( pred, DUMMY_SP ) )
1438
+ new_bounds . push ( ( pred, span ) ) ;
1436
1439
}
1437
1440
}
1438
1441
_ => ( )
1439
1442
}
1443
+ }
1444
+ bounds. projection_bounds . extend ( new_bounds) ;
1440
1445
}
1441
1446
1442
1447
for ( projection_bound, _) in & bounds. projection_bounds {
1443
- associated_types. remove ( & projection_bound. projection_def_id ( ) ) ;
1448
+ for ( _, def_ids) in & mut associated_types {
1449
+ def_ids. remove ( & projection_bound. projection_def_id ( ) ) ;
1450
+ }
1444
1451
}
1445
1452
1446
1453
self . complain_about_missing_associated_types (
1447
- span,
1448
1454
associated_types,
1449
1455
potential_assoc_types,
1450
1456
trait_bounds,
@@ -1530,125 +1536,188 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
1530
1536
1531
1537
fn complain_about_missing_associated_types (
1532
1538
& self ,
1533
- span : Span ,
1534
- associated_types : BTreeSet < DefId > ,
1539
+ mut associated_types : FxHashMap < Span , BTreeSet < DefId > > ,
1535
1540
potential_assoc_types : Vec < Span > ,
1536
1541
trait_bounds : & [ hir:: PolyTraitRef ] ,
1537
1542
) {
1538
- if associated_types. is_empty ( ) {
1543
+ if ! associated_types. values ( ) . any ( |v| v . len ( ) > 0 ) {
1539
1544
return ;
1540
1545
}
1541
- // Account for things like `dyn Foo + 'a` by pointing at the `TraitRef.path`
1542
- // `Span` instead of the `PolyTraitRef` `Span`. That way the suggestion will
1543
- // be valid, otherwise we would suggest `dyn Foo + 'a<A = Type>`. See tests
1544
- // `issue-22434.rs` and `issue-22560.rs` for examples.
1545
- let sugg_span = match ( & potential_assoc_types[ ..] , & trait_bounds) {
1546
+ let tcx = self . tcx ( ) ;
1547
+ let mut names = vec ! [ ] ;
1548
+
1549
+ // Account for things like `dyn Foo + 'a`, like in tests `issue-22434.rs` and
1550
+ // `issue-22560.rs`.
1551
+ let mut trait_bound_spans: Vec < Span > = vec ! [ ] ;
1552
+ for ( span, item_def_ids) in & associated_types {
1553
+ if !item_def_ids. is_empty ( ) {
1554
+ trait_bound_spans. push ( * span) ;
1555
+ }
1556
+ for item_def_id in item_def_ids {
1557
+ let assoc_item = tcx. associated_item ( * item_def_id) ;
1558
+ let trait_def_id = assoc_item. container . id ( ) ;
1559
+ names. push ( format ! (
1560
+ "`{}` (from trait `{}`)" ,
1561
+ assoc_item. ident,
1562
+ tcx. def_path_str( trait_def_id) ,
1563
+ ) ) ;
1564
+ }
1565
+ }
1566
+
1567
+ match ( & potential_assoc_types[ ..] , & trait_bounds) {
1546
1568
( [ ] , [ bound] ) => match & bound. trait_ref . path . segments [ ..] {
1547
1569
// FIXME: `trait_ref.path.span` can point to a full path with multiple
1548
1570
// segments, even though `trait_ref.path.segments` is of length `1`. Work
1549
1571
// around that bug here, even though it should be fixed elsewhere.
1550
1572
// This would otherwise cause an invalid suggestion. For an example, look at
1551
- // `src/test/ui/issues/issue-28344.rs`.
1552
- [ segment] if segment. args . is_none ( ) => segment. ident . span ,
1553
- _ => bound. trait_ref . path . span ,
1573
+ // `src/test/ui/issues/issue-28344.rs` where instead of the following:
1574
+ //
1575
+ // error[E0191]: the value of the associated type `Output`
1576
+ // (from trait `std::ops::BitXor`) must be specified
1577
+ // --> $DIR/issue-28344.rs:4:17
1578
+ // |
1579
+ // LL | let x: u8 = BitXor::bitor(0 as u8, 0 as u8);
1580
+ // | ^^^^^^ help: specify the associated type:
1581
+ // | `BitXor<Output = Type>`
1582
+ //
1583
+ // we would output:
1584
+ //
1585
+ // error[E0191]: the value of the associated type `Output`
1586
+ // (from trait `std::ops::BitXor`) must be specified
1587
+ // --> $DIR/issue-28344.rs:4:17
1588
+ // |
1589
+ // LL | let x: u8 = BitXor::bitor(0 as u8, 0 as u8);
1590
+ // | ^^^^^^^^^^^^^ help: specify the associated type:
1591
+ // | `BitXor::bitor<Output = Type>`
1592
+ [ segment] if segment. args . is_none ( ) => {
1593
+ trait_bound_spans = vec ! [ segment. ident. span] ;
1594
+ associated_types = associated_types. into_iter ( )
1595
+ . map ( |( _, defs) | ( segment. ident . span , defs) )
1596
+ . collect ( ) ;
1597
+ }
1598
+ _ => { }
1554
1599
} ,
1555
- _ => span,
1556
- } ;
1557
- let tcx = self . tcx ( ) ;
1558
- let names = associated_types. iter ( ) . map ( |item_def_id| {
1559
- let assoc_item = tcx. associated_item ( * item_def_id) ;
1560
- let trait_def_id = assoc_item. container . id ( ) ;
1561
- format ! ( "`{}` (from trait `{}`)" , assoc_item. ident, tcx. def_path_str( trait_def_id) )
1562
- } ) . collect :: < Vec < _ > > ( ) . join ( ", " ) ;
1600
+ _ => { }
1601
+ }
1563
1602
let mut err = struct_span_err ! (
1564
1603
tcx. sess,
1565
- sugg_span ,
1604
+ trait_bound_spans ,
1566
1605
E0191 ,
1567
1606
"the value of the associated type{} {} must be specified" ,
1568
- pluralize!( associated_types . len( ) ) ,
1569
- names,
1607
+ pluralize!( names . len( ) ) ,
1608
+ names. join ( ", " ) ,
1570
1609
) ;
1571
- let mut suggestions = Vec :: new ( ) ;
1572
- let mut applicability = Applicability :: MaybeIncorrect ;
1573
- for ( i, item_def_id) in associated_types. iter ( ) . enumerate ( ) {
1574
- let assoc_item = tcx. associated_item ( * item_def_id) ;
1575
- if let Some ( sp) = tcx. hir ( ) . span_if_local ( * item_def_id) {
1576
- err. span_label ( sp, format ! ( "`{}` defined here" , assoc_item. ident) ) ;
1577
- }
1578
- if potential_assoc_types. len ( ) == associated_types. len ( ) {
1610
+ let mut suggestions = vec ! [ ] ;
1611
+ let mut types_count = 0 ;
1612
+ 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 ( ) ;
1617
+ let mut names: FxHashMap < _ , usize > = FxHashMap :: default ( ) ;
1618
+ for item in & assoc_items {
1619
+ types_count += 1 ;
1620
+ * names. entry ( item. ident . name ) . or_insert ( 0 ) += 1 ;
1621
+ }
1622
+ let mut dupes = false ;
1623
+ for item in & assoc_items {
1624
+ let prefix = if names[ & item. ident . name ] > 1 {
1625
+ let trait_def_id = item. container . id ( ) ;
1626
+ dupes = true ;
1627
+ format ! ( "{}::" , tcx. def_path_str( trait_def_id) )
1628
+ } else {
1629
+ String :: new ( )
1630
+ } ;
1631
+ if let Some ( sp) = tcx. hir ( ) . span_if_local ( item. def_id ) {
1632
+ err. span_label ( sp, format ! ( "`{}{}` defined here" , prefix, item. ident) ) ;
1633
+ }
1634
+ }
1635
+ if potential_assoc_types. len ( ) == assoc_items. len ( ) {
1579
1636
// Only suggest when the amount of missing associated types equals the number of
1580
1637
// extra type arguments present, as that gives us a relatively high confidence
1581
1638
// that the user forgot to give the associtated type's name. The canonical
1582
1639
// example would be trying to use `Iterator<isize>` instead of
1583
1640
// `Iterator<Item = isize>`.
1584
- if let Ok ( snippet) = tcx. sess . source_map ( ) . span_to_snippet (
1585
- potential_assoc_types[ i] ,
1586
- ) {
1587
- suggestions. push ( (
1588
- potential_assoc_types[ i] ,
1589
- format ! ( "{} = {}" , assoc_item. ident, snippet) ,
1590
- ) ) ;
1641
+ for ( potential, item) in potential_assoc_types. iter ( ) . zip ( assoc_items. iter ( ) ) {
1642
+ if let Ok ( snippet) = tcx. sess . source_map ( ) . span_to_snippet (
1643
+ * potential,
1644
+ ) {
1645
+ suggestions. push ( (
1646
+ * potential,
1647
+ format ! ( "{} = {}" , item. ident, snippet) ,
1648
+ ) ) ;
1649
+ }
1591
1650
}
1592
- }
1593
- }
1594
- let mut suggestions_len = suggestions. len ( ) ;
1595
- if let Ok ( snippet) = tcx. sess . source_map ( ) . span_to_snippet ( sugg_span) {
1596
- let assoc_types: Vec < String > = associated_types. iter ( )
1597
- . map ( |item_def_id| {
1598
- let assoc_item = tcx. associated_item ( * item_def_id) ;
1599
- format ! ( "{} = Type" , assoc_item. ident)
1600
- } )
1601
- . collect ( ) ;
1602
- let dedup = assoc_types. clone ( ) . drain ( ..) . collect :: < FxHashSet < _ > > ( ) ;
1603
-
1604
- if dedup. len ( ) != assoc_types. len ( ) && trait_bounds. len ( ) == 1 {
1605
- // If there are duplicates associated type names and a single trait bound do not
1606
- // use structured suggestion, it means that there are multiple super-traits with
1607
- // the same associated type name.
1608
- err. help ( "consider introducing a new type parameter, adding `where` constraints \
1609
- using the fully-qualified path to the associated type") ;
1610
- } else if dedup. len ( ) == assoc_types. len ( ) &&
1611
- potential_assoc_types. is_empty ( ) &&
1612
- trait_bounds. len ( ) == 1 &&
1613
- // Do not attempt to suggest when we don't know which path segment needs the
1614
- // type parameter set.
1615
- trait_bounds[ 0 ] . trait_ref . path . segments . len ( ) == 1
1616
- {
1617
- applicability = Applicability :: HasPlaceholders ;
1618
- let sugg = assoc_types. join ( ", " ) ;
1619
- if snippet. ends_with ( '>' ) {
1651
+ } else if let ( Ok ( snippet) , false ) = (
1652
+ tcx. sess . source_map ( ) . span_to_snippet ( * span) ,
1653
+ dupes,
1654
+ ) {
1655
+ let types: Vec < _ > = assoc_items. iter ( )
1656
+ . map ( |item| format ! ( "{} = Type" , item. ident) )
1657
+ . collect ( ) ;
1658
+ let code = if snippet. ends_with ( ">" ) {
1620
1659
// The user wrote `Trait<'a>` or similar and we don't have a type we can
1621
1660
// suggest, but at least we can clue them to the correct syntax
1622
1661
// `Trait<'a, Item = Type>` while accounting for the `<'a>` in the
1623
1662
// suggestion.
1624
- suggestions. push ( ( sugg_span, format ! (
1625
- "{}, {}>" ,
1626
- & snippet[ ..snippet. len( ) -1 ] ,
1627
- sugg,
1628
- ) ) ) ;
1663
+ format ! ( "{}, {}>" , & snippet[ ..snippet. len( ) - 1 ] , types. join( ", " ) )
1629
1664
} else {
1630
1665
// The user wrote `Iterator`, so we don't have a type we can suggest, but at
1631
1666
// least we can clue them to the correct syntax `Iterator<Item = Type>`.
1632
- suggestions. push ( ( sugg_span, format ! ( "{}<{}>" , snippet, sugg) ) ) ;
1633
- }
1634
- suggestions_len = assoc_types. len ( ) ;
1667
+ format ! ( "{}<{}>" , snippet, types. join( ", " ) )
1668
+ } ;
1669
+ suggestions. push ( ( * span, code) ) ;
1670
+ } else if dupes {
1671
+ where_constraints. push ( * span) ;
1635
1672
}
1636
1673
}
1674
+ let where_msg = "consider introducing a new type parameter, adding `where` constraints \
1675
+ using the fully-qualified path to the associated types";
1676
+ if !where_constraints. is_empty ( ) && suggestions. is_empty ( ) {
1677
+ // If there are duplicates associated type names and a single trait bound do not
1678
+ // use structured suggestion, it means that there are multiple super-traits with
1679
+ // the same associated type name.
1680
+ err. help ( where_msg) ;
1681
+ }
1637
1682
if suggestions. len ( ) != 1 {
1638
1683
// We don't need this label if there's an inline suggestion, show otherwise.
1639
- let names = associated_types. iter ( )
1640
- . map ( |t| format ! ( "`{}`" , tcx. associated_item( * t) . ident) )
1641
- . collect :: < Vec < _ > > ( )
1642
- . join ( ", " ) ;
1643
- err. span_label ( sugg_span, format ! (
1644
- "associated type{} {} must be specified" ,
1645
- pluralize!( associated_types. len( ) ) ,
1646
- names,
1647
- ) ) ;
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 ( ) ;
1688
+ let mut names: FxHashMap < _ , usize > = FxHashMap :: default ( ) ;
1689
+ for item in & assoc_items {
1690
+ types_count += 1 ;
1691
+ * names. entry ( item. ident . name ) . or_insert ( 0 ) += 1 ;
1692
+ }
1693
+ let mut label = vec ! [ ] ;
1694
+ for item in & assoc_items {
1695
+ let postfix = if names[ & item. ident . name ] > 1 {
1696
+ let trait_def_id = item. container . id ( ) ;
1697
+ format ! ( " (from trait `{}`)" , tcx. def_path_str( trait_def_id) )
1698
+ } else {
1699
+ String :: new ( )
1700
+ } ;
1701
+ label. push ( format ! ( "`{}`{}" , item. ident, postfix) ) ;
1702
+ }
1703
+ if !label. is_empty ( ) {
1704
+ err. span_label ( * span, format ! (
1705
+ "associated type{} {} must be specified" ,
1706
+ pluralize!( label. len( ) ) ,
1707
+ label. join( ", " ) ,
1708
+ ) ) ;
1709
+ }
1710
+ }
1648
1711
}
1649
1712
if !suggestions. is_empty ( ) {
1650
- let msg = format ! ( "specify the associated type{}" , pluralize!( suggestions_len) ) ;
1651
- err. multipart_suggestion ( & msg, suggestions, applicability) ;
1713
+ err. multipart_suggestion (
1714
+ & format ! ( "specify the associated type{}" , pluralize!( types_count) ) ,
1715
+ suggestions,
1716
+ Applicability :: HasPlaceholders ,
1717
+ ) ;
1718
+ if !where_constraints. is_empty ( ) {
1719
+ err. span_help ( where_constraints, where_msg) ;
1720
+ }
1652
1721
}
1653
1722
err. emit ( ) ;
1654
1723
}
0 commit comments