Skip to content

Commit 06d4f70

Browse files
committed
refactor type error reporting
1 parent 4702ee0 commit 06d4f70

File tree

8 files changed

+141
-203
lines changed

8 files changed

+141
-203
lines changed

src/librustc/infer/error_reporting.rs

Lines changed: 82 additions & 153 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ use hir::def_id::DefId;
8383
use infer::{self, TypeOrigin};
8484
use middle::region;
8585
use ty::subst;
86-
use ty::{self, Ty, TyCtxt, TypeFoldable};
86+
use ty::{self, TyCtxt, TypeFoldable};
8787
use ty::{Region, ReFree};
8888
use ty::error::TypeError;
8989

@@ -462,52 +462,6 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
462462
}
463463
}
464464

465-
fn report_type_error(&self,
466-
trace: TypeTrace<'tcx>,
467-
terr: &TypeError<'tcx>)
468-
-> DiagnosticBuilder<'tcx> {
469-
let (expected, found) = match self.values_str(&trace.values) {
470-
Some(v) => v,
471-
None => {
472-
return self.tcx.sess.diagnostic().struct_dummy(); /* derived error */
473-
}
474-
};
475-
476-
let is_simple_error = if let &TypeError::Sorts(ref values) = terr {
477-
values.expected.is_primitive() && values.found.is_primitive()
478-
} else {
479-
false
480-
};
481-
482-
let mut err = struct_span_err!(self.tcx.sess,
483-
trace.origin.span(),
484-
E0308,
485-
"{}",
486-
trace.origin);
487-
488-
if !is_simple_error || check_old_school() {
489-
err.note_expected_found(&"type", &expected, &found);
490-
}
491-
492-
err.span_label(trace.origin.span(), &terr);
493-
494-
self.check_and_note_conflicting_crates(&mut err, terr, trace.origin.span());
495-
496-
match trace.origin {
497-
TypeOrigin::MatchExpressionArm(_, arm_span, source) => match source {
498-
hir::MatchSource::IfLetDesugar{..} => {
499-
err.span_note(arm_span, "`if let` arm with an incompatible type");
500-
}
501-
_ => {
502-
err.span_note(arm_span, "match arm with an incompatible type");
503-
}
504-
},
505-
_ => ()
506-
}
507-
508-
err
509-
}
510-
511465
/// Adds a note if the types come from similarly named crates
512466
fn check_and_note_conflicting_crates(&self,
513467
err: &mut DiagnosticBuilder,
@@ -550,43 +504,91 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
550504
}
551505
}
552506

553-
pub fn report_and_explain_type_error(&self,
554-
trace: TypeTrace<'tcx>,
555-
terr: &TypeError<'tcx>)
556-
-> DiagnosticBuilder<'tcx> {
557-
let trace = self.resolve_type_vars_if_possible(&trace);
507+
fn note_error_origin(&self,
508+
err: &mut DiagnosticBuilder<'tcx>,
509+
origin: &TypeOrigin)
510+
{
511+
match origin {
512+
&TypeOrigin::MatchExpressionArm(_, arm_span, source) => match source {
513+
hir::MatchSource::IfLetDesugar {..} => {
514+
err.span_note(arm_span, "`if let` arm with an incompatible type");
515+
}
516+
_ => {
517+
err.span_note(arm_span, "match arm with an incompatible type");
518+
}
519+
},
520+
_ => ()
521+
}
522+
}
523+
524+
pub fn report_and_explain_type_error_with_code(&self,
525+
trace: TypeTrace<'tcx>,
526+
terr: &TypeError<'tcx>,
527+
message: &str,
528+
code: &str)
529+
-> DiagnosticBuilder<'tcx>
530+
{
531+
let (expected, found) = match self.values_str(&trace.values) {
532+
Some((expected, found)) => (expected, found),
533+
None => return self.tcx.sess.diagnostic().struct_dummy() /* derived error */
534+
};
535+
558536
let span = trace.origin.span();
559-
let mut err = self.report_type_error(trace, terr);
537+
538+
let is_simple_error = if let &TypeError::Sorts(ref values) = terr {
539+
values.expected.is_primitive() && values.found.is_primitive()
540+
} else {
541+
false
542+
};
543+
544+
let mut err = self.tcx.sess.struct_span_err_with_code(
545+
trace.origin.span(),
546+
message,
547+
code);
548+
549+
if !is_simple_error || check_old_school() {
550+
err.note_expected_found(&"type", &expected, &found);
551+
}
552+
553+
err.span_label(span, &terr);
554+
555+
self.note_error_origin(&mut err, &trace.origin);
556+
self.check_and_note_conflicting_crates(&mut err, terr, span);
560557
self.tcx.note_and_explain_type_err(&mut err, terr, span);
558+
561559
err
562560
}
563561

564-
/// Returns a string of the form "expected `{}`, found `{}`", or None if this is a derived
565-
/// error.
562+
pub fn report_and_explain_type_error(&self,
563+
trace: TypeTrace<'tcx>,
564+
terr: &TypeError<'tcx>)
565+
-> DiagnosticBuilder<'tcx>
566+
{
567+
// FIXME: do we want to use a different error code for each origin?
568+
let failure_str = trace.origin.as_failure_str();
569+
type_err!(self, trace, terr, E0308, "{}", failure_str)
570+
}
571+
572+
/// Returns a string of the form "expected `{}`, found `{}`".
566573
fn values_str(&self, values: &ValuePairs<'tcx>) -> Option<(String, String)> {
567574
match *values {
568575
infer::Types(ref exp_found) => self.expected_found_str(exp_found),
569576
infer::TraitRefs(ref exp_found) => self.expected_found_str(exp_found),
570-
infer::PolyTraitRefs(ref exp_found) => self.expected_found_str(exp_found)
577+
infer::PolyTraitRefs(ref exp_found) => self.expected_found_str(exp_found),
571578
}
572579
}
573580

574-
fn expected_found_str<T: fmt::Display + Resolvable<'tcx> + TypeFoldable<'tcx>>(
581+
fn expected_found_str<T: fmt::Display + TypeFoldable<'tcx>>(
575582
&self,
576583
exp_found: &ty::error::ExpectedFound<T>)
577584
-> Option<(String, String)>
578585
{
579-
let expected = exp_found.expected.resolve(self);
580-
if expected.references_error() {
586+
let exp_found = self.resolve_type_vars_if_possible(exp_found);
587+
if exp_found.references_error() {
581588
return None;
582589
}
583590

584-
let found = exp_found.found.resolve(self);
585-
if found.references_error() {
586-
return None;
587-
}
588-
589-
Some((format!("{}", expected), format!("{}", found)))
591+
Some((format!("{}", exp_found.expected), format!("{}", exp_found.found)))
590592
}
591593

592594
fn report_generic_bound_failure(&self,
@@ -1609,68 +1611,21 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
16091611
fn note_region_origin(&self, err: &mut DiagnosticBuilder, origin: &SubregionOrigin<'tcx>) {
16101612
match *origin {
16111613
infer::Subtype(ref trace) => {
1612-
let desc = match trace.origin {
1613-
TypeOrigin::Misc(_) => {
1614-
"types are compatible"
1615-
}
1616-
TypeOrigin::MethodCompatCheck(_) => {
1617-
"method type is compatible with trait"
1618-
}
1619-
TypeOrigin::ExprAssignable(_) => {
1620-
"expression is assignable"
1621-
}
1622-
TypeOrigin::RelateTraitRefs(_) => {
1623-
"traits are compatible"
1624-
}
1625-
TypeOrigin::RelateSelfType(_) => {
1626-
"self type matches impl self type"
1627-
}
1628-
TypeOrigin::RelateOutputImplTypes(_) => {
1629-
"trait type parameters matches those \
1630-
specified on the impl"
1631-
}
1632-
TypeOrigin::MatchExpressionArm(_, _, _) => {
1633-
"match arms have compatible types"
1634-
}
1635-
TypeOrigin::IfExpression(_) => {
1636-
"if and else have compatible types"
1637-
}
1638-
TypeOrigin::IfExpressionWithNoElse(_) => {
1639-
"if may be missing an else clause"
1640-
}
1641-
TypeOrigin::RangeExpression(_) => {
1642-
"start and end of range have compatible types"
1643-
}
1644-
TypeOrigin::EquatePredicate(_) => {
1645-
"equality where clause is satisfied"
1646-
}
1647-
TypeOrigin::MainFunctionType(_) => {
1648-
"the `main` function has the correct type"
1649-
}
1650-
TypeOrigin::StartFunctionType(_) => {
1651-
"the `start` function has the correct type"
1652-
}
1653-
TypeOrigin::IntrinsicType(_) => {
1654-
"the intrinsic has the correct type"
1655-
}
1656-
};
1657-
1658-
match self.values_str(&trace.values) {
1659-
Some((expected, found)) => {
1660-
err.span_note(
1661-
trace.origin.span(),
1662-
&format!("...so that {} (expected {}, found {})",
1663-
desc, expected, found));
1664-
}
1665-
None => {
1666-
// Really should avoid printing this error at
1667-
// all, since it is derived, but that would
1668-
// require more refactoring than I feel like
1669-
// doing right now. - nmatsakis
1670-
err.span_note(
1671-
trace.origin.span(),
1672-
&format!("...so that {}", desc));
1673-
}
1614+
if let Some((expected, found)) = self.values_str(&trace.values) {
1615+
// FIXME: do we want a "the" here?
1616+
err.span_note(
1617+
trace.origin.span(),
1618+
&format!("...so that {} (expected {}, found {})",
1619+
trace.origin.as_requirement_str(), expected, found));
1620+
} else {
1621+
// FIXME: this really should be handled at some earlier stage. Our
1622+
// handling of region checking when type errors are present is
1623+
// *terrible*.
1624+
1625+
err.span_note(
1626+
trace.origin.span(),
1627+
&format!("...so that {}",
1628+
trace.origin.as_requirement_str()));
16741629
}
16751630
}
16761631
infer::Reborrow(span) => {
@@ -1813,32 +1768,6 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
18131768
}
18141769
}
18151770

1816-
pub trait Resolvable<'tcx> {
1817-
fn resolve<'a, 'gcx>(&self, infcx: &InferCtxt<'a, 'gcx, 'tcx>) -> Self;
1818-
}
1819-
1820-
impl<'tcx> Resolvable<'tcx> for Ty<'tcx> {
1821-
fn resolve<'a, 'gcx>(&self, infcx: &InferCtxt<'a, 'gcx, 'tcx>) -> Ty<'tcx> {
1822-
infcx.resolve_type_vars_if_possible(self)
1823-
}
1824-
}
1825-
1826-
impl<'tcx> Resolvable<'tcx> for ty::TraitRef<'tcx> {
1827-
fn resolve<'a, 'gcx>(&self, infcx: &InferCtxt<'a, 'gcx, 'tcx>)
1828-
-> ty::TraitRef<'tcx> {
1829-
infcx.resolve_type_vars_if_possible(self)
1830-
}
1831-
}
1832-
1833-
impl<'tcx> Resolvable<'tcx> for ty::PolyTraitRef<'tcx> {
1834-
fn resolve<'a, 'gcx>(&self,
1835-
infcx: &InferCtxt<'a, 'gcx, 'tcx>)
1836-
-> ty::PolyTraitRef<'tcx>
1837-
{
1838-
infcx.resolve_type_vars_if_possible(self)
1839-
}
1840-
}
1841-
18421771
fn lifetimes_in_scope<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
18431772
scope_id: ast::NodeId)
18441773
-> Vec<hir::LifetimeDef> {

src/librustc/infer/mod.rs

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ pub enum TypeOrigin {
231231
}
232232

233233
impl TypeOrigin {
234-
fn as_str(&self) -> &'static str {
234+
fn as_failure_str(&self) -> &'static str {
235235
match self {
236236
&TypeOrigin::Misc(_) |
237237
&TypeOrigin::RelateSelfType(_) |
@@ -252,11 +252,26 @@ impl TypeOrigin {
252252
&TypeOrigin::IntrinsicType(_) => "intrinsic has wrong type",
253253
}
254254
}
255-
}
256255

257-
impl fmt::Display for TypeOrigin {
258-
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(),fmt::Error> {
259-
fmt::Display::fmt(self.as_str(), f)
256+
fn as_requirement_str(&self) -> &'static str {
257+
match self {
258+
&TypeOrigin::Misc(_) => "types are compatible",
259+
&TypeOrigin::MethodCompatCheck(_) => "method type is compatible with trait",
260+
&TypeOrigin::ExprAssignable(_) => "expression is assignable",
261+
&TypeOrigin::RelateTraitRefs(_) => "traits are compatible",
262+
&TypeOrigin::RelateSelfType(_) => "self type matches impl self type",
263+
&TypeOrigin::RelateOutputImplTypes(_) => {
264+
"trait type parameters matches those specified on the impl"
265+
}
266+
&TypeOrigin::MatchExpressionArm(_, _, _) => "match arms have compatible types",
267+
&TypeOrigin::IfExpression(_) => "if and else have compatible types",
268+
&TypeOrigin::IfExpressionWithNoElse(_) => "if missing an else returns ()",
269+
&TypeOrigin::RangeExpression(_) => "start and end of range have compatible types",
270+
&TypeOrigin::EquatePredicate(_) => "equality where clause is satisfied",
271+
&TypeOrigin::MainFunctionType(_) => "`main` function has the correct type",
272+
&TypeOrigin::StartFunctionType(_) => "`start` function has the correct type",
273+
&TypeOrigin::IntrinsicType(_) => "intrinsic has the correct type",
274+
}
260275
}
261276
}
262277

@@ -1489,22 +1504,20 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
14891504
pub fn type_error_message<M>(&self,
14901505
sp: Span,
14911506
mk_msg: M,
1492-
actual_ty: Ty<'tcx>,
1493-
err: Option<&TypeError<'tcx>>)
1507+
actual_ty: Ty<'tcx>)
14941508
where M: FnOnce(String) -> String,
14951509
{
1496-
self.type_error_struct(sp, mk_msg, actual_ty, err).emit();
1510+
self.type_error_struct(sp, mk_msg, actual_ty).emit();
14971511
}
14981512

14991513
pub fn type_error_struct<M>(&self,
15001514
sp: Span,
15011515
mk_msg: M,
1502-
actual_ty: Ty<'tcx>,
1503-
err: Option<&TypeError<'tcx>>)
1516+
actual_ty: Ty<'tcx>)
15041517
-> DiagnosticBuilder<'tcx>
15051518
where M: FnOnce(String) -> String,
15061519
{
1507-
debug!("type_error_struct({:?}, {:?}, {:?})", sp, actual_ty, err);
1520+
debug!("type_error_struct({:?}, {:?})", sp, actual_ty);
15081521

15091522
let actual_ty = self.resolve_type_vars_if_possible(&actual_ty);
15101523

@@ -1513,21 +1526,10 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
15131526
return self.tcx.sess.diagnostic().struct_dummy();
15141527
}
15151528

1516-
let error_str = err.map_or("".to_string(), |t_err| {
1517-
format!(" ({})", t_err)
1518-
});
1519-
15201529
let msg = mk_msg(self.ty_to_string(actual_ty));
15211530

15221531
// FIXME: use an error code.
1523-
let mut db = self.tcx.sess.struct_span_err(
1524-
sp, &format!("{} {}", msg, error_str));
1525-
1526-
if let Some(err) = err {
1527-
self.tcx.note_and_explain_type_err(&mut db, err, sp);
1528-
}
1529-
1530-
db
1532+
self.tcx.sess.struct_span_err(sp, &msg)
15311533
}
15321534

15331535
pub fn report_mismatched_types(&self,

src/librustc/macros.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,15 @@ macro_rules! span_bug {
5959
$crate::session::span_bug_fmt(file!(), line!(), $span, format_args!($($message)*))
6060
})
6161
}
62+
63+
#[macro_export]
64+
macro_rules! type_err {
65+
($infcx:expr, $trace: expr, $terr: expr, $code:ident, $($message:tt)*) => ({
66+
__diagnostic_used!($code);
67+
$infcx.report_and_explain_type_error_with_code(
68+
$trace,
69+
$terr,
70+
&format!($($message)*),
71+
stringify!($code))
72+
})
73+
}

0 commit comments

Comments
 (0)