@@ -27,6 +27,8 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
27
27
use rustc_errors:: { struct_span_err, Applicability , DiagnosticBuilder } ;
28
28
use rustc_hir as hir;
29
29
use rustc_hir:: def_id:: { DefId , LOCAL_CRATE } ;
30
+ use rustc_hir:: { QPath , TyKind , WhereBoundPredicate , WherePredicate } ;
31
+ use rustc_span:: source_map:: SourceMap ;
30
32
use rustc_span:: { ExpnKind , Span , DUMMY_SP } ;
31
33
use std:: fmt;
32
34
use syntax:: ast;
@@ -1426,3 +1428,229 @@ impl ArgKind {
1426
1428
}
1427
1429
}
1428
1430
}
1431
+
1432
+ /// Suggest restricting a type param with a new bound.
1433
+ pub fn suggest_constraining_type_param (
1434
+ tcx : TyCtxt < ' _ > ,
1435
+ generics : & hir:: Generics < ' _ > ,
1436
+ err : & mut DiagnosticBuilder < ' _ > ,
1437
+ param_name : & str ,
1438
+ constraint : & str ,
1439
+ source_map : & SourceMap ,
1440
+ span : Span ,
1441
+ def_id : Option < DefId > ,
1442
+ ) -> bool {
1443
+ const MSG_RESTRICT_BOUND_FURTHER : & str = "consider further restricting this bound with" ;
1444
+ const MSG_RESTRICT_TYPE : & str = "consider restricting this type parameter with" ;
1445
+ const MSG_RESTRICT_TYPE_FURTHER : & str = "consider further restricting this type parameter with" ;
1446
+
1447
+ let param = generics. params . iter ( ) . filter ( |p| p. name . ident ( ) . as_str ( ) == param_name) . next ( ) ;
1448
+
1449
+ let param = if let Some ( param) = param {
1450
+ param
1451
+ } else {
1452
+ return false ;
1453
+ } ;
1454
+
1455
+ if def_id == tcx. lang_items ( ) . sized_trait ( ) {
1456
+ // Type parameters are already `Sized` by default.
1457
+ err. span_label ( param. span , & format ! ( "this type parameter needs to be `{}`" , constraint) ) ;
1458
+ return true ;
1459
+ }
1460
+
1461
+ if param_name. starts_with ( "impl " ) {
1462
+ // If there's an `impl Trait` used in argument position, suggest
1463
+ // restricting it:
1464
+ //
1465
+ // fn foo(t: impl Foo) { ... }
1466
+ // --------
1467
+ // |
1468
+ // help: consider further restricting this bound with `+ Bar`
1469
+ //
1470
+ // Suggestion for tools in this case is:
1471
+ //
1472
+ // fn foo(t: impl Foo) { ... }
1473
+ // --------
1474
+ // |
1475
+ // replace with: `impl Foo + Bar`
1476
+
1477
+ err. span_help ( param. span , & format ! ( "{} `+ {}`" , MSG_RESTRICT_BOUND_FURTHER , constraint) ) ;
1478
+
1479
+ err. tool_only_span_suggestion (
1480
+ param. span ,
1481
+ MSG_RESTRICT_BOUND_FURTHER ,
1482
+ format ! ( "{} + {}" , param_name, constraint) ,
1483
+ Applicability :: MachineApplicable ,
1484
+ ) ;
1485
+
1486
+ return true ;
1487
+ }
1488
+
1489
+ if generics. where_clause . predicates . is_empty ( ) {
1490
+ if let Some ( bounds_span) = param. bounds_span ( ) {
1491
+ // If user has provided some bounds, suggest restricting them:
1492
+ //
1493
+ // fn foo<T: Foo>(t: T) { ... }
1494
+ // ---
1495
+ // |
1496
+ // help: consider further restricting this bound with `+ Bar`
1497
+ //
1498
+ // Suggestion for tools in this case is:
1499
+ //
1500
+ // fn foo<T: Foo>(t: T) { ... }
1501
+ // --
1502
+ // |
1503
+ // replace with: `T: Bar +`
1504
+
1505
+ err. span_help (
1506
+ bounds_span,
1507
+ & format ! ( "{} `+ {}`" , MSG_RESTRICT_BOUND_FURTHER , constraint) ,
1508
+ ) ;
1509
+
1510
+ let span_hi = param. span . with_hi ( span. hi ( ) ) ;
1511
+ let span_with_colon = source_map. span_through_char ( span_hi, ':' ) ;
1512
+
1513
+ if span_hi != param. span && span_with_colon != span_hi {
1514
+ err. tool_only_span_suggestion (
1515
+ span_with_colon,
1516
+ MSG_RESTRICT_BOUND_FURTHER ,
1517
+ format ! ( "{}: {} + " , param_name, constraint) ,
1518
+ Applicability :: MachineApplicable ,
1519
+ ) ;
1520
+ }
1521
+ } else {
1522
+ // If user hasn't provided any bounds, suggest adding a new one:
1523
+ //
1524
+ // fn foo<T>(t: T) { ... }
1525
+ // - help: consider restricting this type parameter with `T: Foo`
1526
+
1527
+ err. span_help (
1528
+ param. span ,
1529
+ & format ! ( "{} `{}: {}`" , MSG_RESTRICT_TYPE , param_name, constraint) ,
1530
+ ) ;
1531
+
1532
+ err. tool_only_span_suggestion (
1533
+ param. span ,
1534
+ MSG_RESTRICT_TYPE ,
1535
+ format ! ( "{}: {}" , param_name, constraint) ,
1536
+ Applicability :: MachineApplicable ,
1537
+ ) ;
1538
+ }
1539
+
1540
+ true
1541
+ } else {
1542
+ // This part is a bit tricky, because using the `where` clause user can
1543
+ // provide zero, one or many bounds for the same type parameter, so we
1544
+ // have following cases to consider:
1545
+ //
1546
+ // 1) When the type parameter has been provided zero bounds
1547
+ //
1548
+ // Message:
1549
+ // fn foo<X, Y>(x: X, y: Y) where Y: Foo { ... }
1550
+ // - help: consider restricting this type parameter with `where X: Bar`
1551
+ //
1552
+ // Suggestion:
1553
+ // fn foo<X, Y>(x: X, y: Y) where Y: Foo { ... }
1554
+ // - insert: `, X: Bar`
1555
+ //
1556
+ //
1557
+ // 2) When the type parameter has been provided one bound
1558
+ //
1559
+ // Message:
1560
+ // fn foo<T>(t: T) where T: Foo { ... }
1561
+ // ^^^^^^
1562
+ // |
1563
+ // help: consider further restricting this bound with `+ Bar`
1564
+ //
1565
+ // Suggestion:
1566
+ // fn foo<T>(t: T) where T: Foo { ... }
1567
+ // ^^
1568
+ // |
1569
+ // replace with: `T: Bar +`
1570
+ //
1571
+ //
1572
+ // 3) When the type parameter has been provided many bounds
1573
+ //
1574
+ // Message:
1575
+ // fn foo<T>(t: T) where T: Foo, T: Bar {... }
1576
+ // - help: consider further restricting this type parameter with `where T: Zar`
1577
+ //
1578
+ // Suggestion:
1579
+ // fn foo<T>(t: T) where T: Foo, T: Bar {... }
1580
+ // - insert: `, T: Zar`
1581
+
1582
+ let mut param_spans = Vec :: new ( ) ;
1583
+
1584
+ for predicate in generics. where_clause . predicates {
1585
+ if let WherePredicate :: BoundPredicate ( WhereBoundPredicate {
1586
+ span, bounded_ty, ..
1587
+ } ) = predicate
1588
+ {
1589
+ if let TyKind :: Path ( QPath :: Resolved ( _, path) ) = & bounded_ty. kind {
1590
+ if let Some ( segment) = path. segments . first ( ) {
1591
+ if segment. ident . to_string ( ) == param_name {
1592
+ param_spans. push ( span) ;
1593
+ }
1594
+ }
1595
+ }
1596
+ }
1597
+ }
1598
+
1599
+ let where_clause_span =
1600
+ generics. where_clause . span_for_predicates_or_empty_place ( ) . shrink_to_hi ( ) ;
1601
+
1602
+ match & param_spans[ ..] {
1603
+ & [ ] => {
1604
+ err. span_help (
1605
+ param. span ,
1606
+ & format ! ( "{} `where {}: {}`" , MSG_RESTRICT_TYPE , param_name, constraint) ,
1607
+ ) ;
1608
+
1609
+ err. tool_only_span_suggestion (
1610
+ where_clause_span,
1611
+ MSG_RESTRICT_TYPE ,
1612
+ format ! ( ", {}: {}" , param_name, constraint) ,
1613
+ Applicability :: MachineApplicable ,
1614
+ ) ;
1615
+ }
1616
+
1617
+ & [ & param_span] => {
1618
+ err. span_help (
1619
+ param_span,
1620
+ & format ! ( "{} `+ {}`" , MSG_RESTRICT_BOUND_FURTHER , constraint) ,
1621
+ ) ;
1622
+
1623
+ let span_hi = param_span. with_hi ( span. hi ( ) ) ;
1624
+ let span_with_colon = source_map. span_through_char ( span_hi, ':' ) ;
1625
+
1626
+ if span_hi != param_span && span_with_colon != span_hi {
1627
+ err. tool_only_span_suggestion (
1628
+ span_with_colon,
1629
+ MSG_RESTRICT_BOUND_FURTHER ,
1630
+ format ! ( "{}: {} +" , param_name, constraint) ,
1631
+ Applicability :: MachineApplicable ,
1632
+ ) ;
1633
+ }
1634
+ }
1635
+
1636
+ _ => {
1637
+ err. span_help (
1638
+ param. span ,
1639
+ & format ! (
1640
+ "{} `where {}: {}`" ,
1641
+ MSG_RESTRICT_TYPE_FURTHER , param_name, constraint,
1642
+ ) ,
1643
+ ) ;
1644
+
1645
+ err. tool_only_span_suggestion (
1646
+ where_clause_span,
1647
+ MSG_RESTRICT_BOUND_FURTHER ,
1648
+ format ! ( ", {}: {}" , param_name, constraint) ,
1649
+ Applicability :: MachineApplicable ,
1650
+ ) ;
1651
+ }
1652
+ }
1653
+
1654
+ true
1655
+ }
1656
+ }
0 commit comments