Skip to content

Commit 840bdc3

Browse files
committed
Auto merge of #67665 - Patryk27:master, r=zackmdavis
Improve reporting errors and suggestions for trait bounds Fix #66802 - When printing errors for unsized function parameter, properly point at the parameter instead of function's body. - Improve `consider further restricting this bound` (and related) messages by separating human-oriented hints from the machine-oriented ones.
2 parents 71c7e14 + a8d34c1 commit 840bdc3

File tree

71 files changed

+1039
-375
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

71 files changed

+1039
-375
lines changed

src/librustc/traits/error_reporting/mod.rs

+228
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
2727
use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
2828
use rustc_hir as hir;
2929
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
30+
use rustc_hir::{QPath, TyKind, WhereBoundPredicate, WherePredicate};
31+
use rustc_span::source_map::SourceMap;
3032
use rustc_span::{ExpnKind, Span, DUMMY_SP};
3133
use std::fmt;
3234
use syntax::ast;
@@ -1426,3 +1428,229 @@ impl ArgKind {
14261428
}
14271429
}
14281430
}
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+
}

src/librustc/traits/error_reporting/suggestions.rs

+8-84
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ use super::{
44
};
55

66
use crate::infer::InferCtxt;
7+
use crate::traits::error_reporting::suggest_constraining_type_param;
78
use crate::traits::object_safety::object_safety_violations;
89
use crate::ty::TypeckTables;
910
use crate::ty::{self, AdtKind, DefIdTree, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness};
10-
1111
use rustc_errors::{
1212
error_code, pluralize, struct_span_err, Applicability, DiagnosticBuilder, Style,
1313
};
@@ -16,7 +16,6 @@ use rustc_hir::def::DefKind;
1616
use rustc_hir::def_id::DefId;
1717
use rustc_hir::intravisit::Visitor;
1818
use rustc_hir::Node;
19-
use rustc_span::source_map::SourceMap;
2019
use rustc_span::symbol::{kw, sym};
2120
use rustc_span::{MultiSpan, Span, DUMMY_SP};
2221
use std::fmt;
@@ -430,12 +429,16 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
430429
.span_take_while(span, |c| c.is_whitespace() || *c == '&');
431430

432431
let remove_refs = refs_remaining + 1;
433-
let format_str =
434-
format!("consider removing {} leading `&`-references", remove_refs);
432+
433+
let msg = if remove_refs == 1 {
434+
"consider removing the leading `&`-reference".to_string()
435+
} else {
436+
format!("consider removing {} leading `&`-references", remove_refs)
437+
};
435438

436439
err.span_suggestion_short(
437440
sp,
438-
&format_str,
441+
&msg,
439442
String::new(),
440443
Applicability::MachineApplicable,
441444
);
@@ -1652,85 +1655,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
16521655
}
16531656
}
16541657

1655-
/// Suggest restricting a type param with a new bound.
1656-
pub fn suggest_constraining_type_param(
1657-
tcx: TyCtxt<'_>,
1658-
generics: &hir::Generics<'_>,
1659-
err: &mut DiagnosticBuilder<'_>,
1660-
param_name: &str,
1661-
constraint: &str,
1662-
source_map: &SourceMap,
1663-
span: Span,
1664-
def_id: Option<DefId>,
1665-
) -> bool {
1666-
let restrict_msg = "consider further restricting this bound";
1667-
if let Some(param) =
1668-
generics.params.iter().filter(|p| p.name.ident().as_str() == param_name).next()
1669-
{
1670-
if def_id == tcx.lang_items().sized_trait() {
1671-
// Type parameters are already `Sized` by default.
1672-
err.span_label(
1673-
param.span,
1674-
&format!("this type parameter needs to be `{}`", constraint),
1675-
);
1676-
} else if param_name.starts_with("impl ") {
1677-
// `impl Trait` in argument:
1678-
// `fn foo(x: impl Trait) {}` → `fn foo(t: impl Trait + Trait2) {}`
1679-
err.span_suggestion(
1680-
param.span,
1681-
restrict_msg,
1682-
// `impl CurrentTrait + MissingTrait`
1683-
format!("{} + {}", param_name, constraint),
1684-
Applicability::MachineApplicable,
1685-
);
1686-
} else if generics.where_clause.predicates.is_empty() && param.bounds.is_empty() {
1687-
// If there are no bounds whatsoever, suggest adding a constraint
1688-
// to the type parameter:
1689-
// `fn foo<T>(t: T) {}` → `fn foo<T: Trait>(t: T) {}`
1690-
err.span_suggestion(
1691-
param.span,
1692-
"consider restricting this bound",
1693-
format!("{}: {}", param_name, constraint),
1694-
Applicability::MachineApplicable,
1695-
);
1696-
} else if !generics.where_clause.predicates.is_empty() {
1697-
// There is a `where` clause, so suggest expanding it:
1698-
// `fn foo<T>(t: T) where T: Debug {}` →
1699-
// `fn foo<T>(t: T) where T: Debug, T: Trait {}`
1700-
err.span_suggestion(
1701-
generics.where_clause.span().unwrap().shrink_to_hi(),
1702-
&format!("consider further restricting type parameter `{}`", param_name),
1703-
format!(", {}: {}", param_name, constraint),
1704-
Applicability::MachineApplicable,
1705-
);
1706-
} else {
1707-
// If there is no `where` clause lean towards constraining to the
1708-
// type parameter:
1709-
// `fn foo<X: Bar, T>(t: T, x: X) {}` → `fn foo<T: Trait>(t: T) {}`
1710-
// `fn foo<T: Bar>(t: T) {}` → `fn foo<T: Bar + Trait>(t: T) {}`
1711-
let sp = param.span.with_hi(span.hi());
1712-
let span = source_map.span_through_char(sp, ':');
1713-
if sp != param.span && sp != span {
1714-
// Only suggest if we have high certainty that the span
1715-
// covers the colon in `foo<T: Trait>`.
1716-
err.span_suggestion(
1717-
span,
1718-
restrict_msg,
1719-
format!("{}: {} + ", param_name, constraint),
1720-
Applicability::MachineApplicable,
1721-
);
1722-
} else {
1723-
err.span_label(
1724-
param.span,
1725-
&format!("consider adding a `where {}: {}` bound", param_name, constraint),
1726-
);
1727-
}
1728-
}
1729-
return true;
1730-
}
1731-
false
1732-
}
1733-
17341658
/// Collect all the returned expressions within the input expression.
17351659
/// Used to point at the return spans when we want to suggest some change to them.
17361660
#[derive(Default)]

src/librustc_hir/hir.rs

+11-1
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,16 @@ pub struct GenericParam<'hir> {
441441
pub kind: GenericParamKind<'hir>,
442442
}
443443

444+
impl GenericParam<'hir> {
445+
pub fn bounds_span(&self) -> Option<Span> {
446+
self.bounds.iter().fold(None, |span, bound| {
447+
let span = span.map(|s| s.to(bound.span())).unwrap_or_else(|| bound.span());
448+
449+
Some(span)
450+
})
451+
}
452+
}
453+
444454
#[derive(Default)]
445455
pub struct GenericParamCount {
446456
pub lifetimes: usize,
@@ -513,7 +523,7 @@ pub enum SyntheticTyParamKind {
513523
#[derive(RustcEncodable, RustcDecodable, Debug, HashStable_Generic)]
514524
pub struct WhereClause<'hir> {
515525
pub predicates: &'hir [WherePredicate<'hir>],
516-
// Only valid if predicates isn't empty.
526+
// Only valid if predicates aren't empty.
517527
pub span: Span,
518528
}
519529

src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use rustc::mir::{
33
FakeReadCause, Local, LocalDecl, LocalInfo, LocalKind, Location, Operand, Place, PlaceRef,
44
ProjectionElem, Rvalue, Statement, StatementKind, TerminatorKind, VarBindingForm,
55
};
6-
use rustc::traits::error_reporting::suggestions::suggest_constraining_type_param;
6+
use rustc::traits::error_reporting::suggest_constraining_type_param;
77
use rustc::ty::{self, Ty};
88
use rustc_data_structures::fx::FxHashSet;
99
use rustc_errors::{Applicability, DiagnosticBuilder};

0 commit comments

Comments
 (0)