Skip to content

Commit

Permalink
Auto merge of #66389 - estebank:type-err-labels, r=petrochenkov
Browse files Browse the repository at this point in the history
Specific labels when referring to "expected" and "found" types
  • Loading branch information
bors committed Nov 21, 2019
2 parents 35ef33a + 468722b commit 53712f8
Show file tree
Hide file tree
Showing 581 changed files with 2,544 additions and 3,540 deletions.
71 changes: 46 additions & 25 deletions src/librustc/infer/error_reporting/mod.rs
Expand Up @@ -1163,8 +1163,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
Some(values) => {
let (is_simple_error, exp_found) = match values {
ValuePairs::Types(exp_found) => {
let is_simple_err =
exp_found.expected.is_primitive() && exp_found.found.is_primitive();
let is_simple_err = exp_found.expected.is_simple_text()
&& exp_found.found.is_simple_text();

(is_simple_err, Some(exp_found))
}
Expand Down Expand Up @@ -1197,40 +1197,61 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
};

if let Some((expected, found)) = expected_found {
match (terr, is_simple_error, expected == found) {
(&TypeError::Sorts(ref values), false, true) => {
let sort_string = | a_type: Ty<'tcx> |
if let ty::Opaque(def_id, _) = a_type.kind {
format!(" (opaque type at {})", self.tcx.sess.source_map()
.mk_substr_filename(self.tcx.def_span(def_id)))
} else {
format!(" ({})", a_type.sort_string(self.tcx))
};
diag.note_expected_found_extra(
&"type",
expected,
found,
&sort_string(values.expected),
&sort_string(values.found),
);
let expected_label = exp_found.map_or("type".into(), |ef| ef.expected.prefix_string());
let found_label = exp_found.map_or("type".into(), |ef| ef.found.prefix_string());
match (&terr, expected == found) {
(TypeError::Sorts(values), extra) => {
let sort_string = |ty: Ty<'tcx>| match (extra, &ty.kind) {
(true, ty::Opaque(def_id, _)) => format!(
" (opaque type at {})",
self.tcx.sess.source_map()
.mk_substr_filename(self.tcx.def_span(*def_id)),
),
(true, _) => format!(" ({})", ty.sort_string(self.tcx)),
(false, _) => "".to_string(),
};
if !(values.expected.is_simple_text() && values.found.is_simple_text()) || (
exp_found.map_or(false, |ef| {
// This happens when the type error is a subset of the expectation,
// like when you have two references but one is `usize` and the other
// is `f32`. In those cases we still want to show the `note`. If the
// value from `ef` is `Infer(_)`, then we ignore it.
if !ef.expected.is_ty_infer() {
ef.expected != values.expected
} else if !ef.found.is_ty_infer() {
ef.found != values.found
} else {
false
}
})
) {
diag.note_expected_found_extra(
&expected_label,
expected,
&found_label,
found,
&sort_string(values.expected),
&sort_string(values.found),
);
}
}
(TypeError::ObjectUnsafeCoercion(_), ..) => {
(TypeError::ObjectUnsafeCoercion(_), _) => {
diag.note_unsuccessfull_coercion(found, expected);
}
(_, false, _) => {
(_, _) => {
debug!(
"note_type_err: exp_found={:?}, expected={:?} found={:?}",
exp_found, expected, found
);
if let Some(exp_found) = exp_found {
self.suggest_as_ref_where_appropriate(span, &exp_found, diag);
if !is_simple_error || terr.must_include_note() {
diag.note_expected_found(&expected_label, expected, &found_label, found);
}

diag.note_expected_found(&"type", expected, found);
}
_ => (),
}
}
if let Some(exp_found) = exp_found {
self.suggest_as_ref_where_appropriate(span, &exp_found, diag);
}

// In some (most?) cases cause.body_id points to actual body, but in some cases
// it's a actual definition. According to the comments (e.g. in
Expand Down
56 changes: 56 additions & 0 deletions src/librustc/ty/diagnostics.rs
@@ -0,0 +1,56 @@
//! Diagnostics related methods for `TyS`.

use crate::ty::TyS;
use crate::ty::TyKind::*;
use crate::ty::sty::InferTy;

impl<'tcx> TyS<'tcx> {
/// Similar to `TyS::is_primitive`, but also considers inferred numeric values to be primitive.
pub fn is_primitive_ty(&self) -> bool {
match self.kind {
Bool | Char | Str | Int(_) | Uint(_) | Float(_) |
Infer(InferTy::IntVar(_)) | Infer(InferTy::FloatVar(_)) |
Infer(InferTy::FreshIntTy(_)) | Infer(InferTy::FreshFloatTy(_)) => true,
_ => false,
}
}

/// Whether the type is succinctly representable as a type instead of just refered to with a
/// description in error messages. This is used in the main error message.
pub fn is_simple_ty(&self) -> bool {
match self.kind {
Bool | Char | Str | Int(_) | Uint(_) | Float(_) |
Infer(InferTy::IntVar(_)) | Infer(InferTy::FloatVar(_)) |
Infer(InferTy::FreshIntTy(_)) | Infer(InferTy::FreshFloatTy(_)) => true,
Ref(_, x, _) | Array(x, _) | Slice(x) => x.peel_refs().is_simple_ty(),
Tuple(tys) if tys.is_empty() => true,
_ => false,
}
}

/// Whether the type is succinctly representable as a type instead of just refered to with a
/// description in error messages. This is used in the primary span label. Beyond what
/// `is_simple_ty` includes, it also accepts ADTs with no type arguments and references to
/// ADTs with no type arguments.
pub fn is_simple_text(&self) -> bool {
match self.kind {
Adt(_, substs) => substs.types().next().is_none(),
Ref(_, ty, _) => ty.is_simple_text(),
_ => self.is_simple_ty(),
}
}

/// Whether the type can be safely suggested during error recovery.
pub fn is_suggestable(&self) -> bool {
match self.kind {
Opaque(..) |
FnDef(..) |
FnPtr(..) |
Dynamic(..) |
Closure(..) |
Infer(..) |
Projection(..) => false,
_ => true,
}
}
}
104 changes: 84 additions & 20 deletions src/librustc/ty/error.rs
Expand Up @@ -64,8 +64,11 @@ pub enum UnconstrainedNumeric {
impl<'tcx> fmt::Display for TypeError<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use self::TypeError::*;
fn report_maybe_different(f: &mut fmt::Formatter<'_>,
expected: &str, found: &str) -> fmt::Result {
fn report_maybe_different(
f: &mut fmt::Formatter<'_>,
expected: &str,
found: &str,
) -> fmt::Result {
// A naive approach to making sure that we're not reporting silly errors such as:
// (expected closure, found closure).
if expected == found {
Expand Down Expand Up @@ -183,46 +186,77 @@ impl<'tcx> fmt::Display for TypeError<'tcx> {
}
}

impl<'tcx> TypeError<'tcx> {
pub fn must_include_note(&self) -> bool {
use self::TypeError::*;
match self {
CyclicTy(_) |
UnsafetyMismatch(_) |
Mismatch |
AbiMismatch(_) |
FixedArraySize(_) |
Sorts(_) |
IntMismatch(_) |
FloatMismatch(_) |
VariadicMismatch(_) => false,

Mutability |
TupleSize(_) |
ArgCount |
RegionsDoesNotOutlive(..) |
RegionsInsufficientlyPolymorphic(..) |
RegionsOverlyPolymorphic(..) |
RegionsPlaceholderMismatch |
Traits(_) |
ProjectionMismatched(_) |
ProjectionBoundsLength(_) |
ExistentialMismatch(_) |
ConstMismatch(_) |
IntrinsicCast |
ObjectUnsafeCoercion(_) => true,
}
}
}

impl<'tcx> ty::TyS<'tcx> {
pub fn sort_string(&self, tcx: TyCtxt<'_>) -> Cow<'static, str> {
match self.kind {
ty::Bool | ty::Char | ty::Int(_) |
ty::Uint(_) | ty::Float(_) | ty::Str | ty::Never => self.to_string().into(),
ty::Tuple(ref tys) if tys.is_empty() => self.to_string().into(),
ty::Uint(_) | ty::Float(_) | ty::Str | ty::Never => format!("`{}`", self).into(),
ty::Tuple(ref tys) if tys.is_empty() => format!("`{}`", self).into(),

ty::Adt(def, _) => format!("{} `{}`", def.descr(), tcx.def_path_str(def.did)).into(),
ty::Foreign(def_id) => format!("extern type `{}`", tcx.def_path_str(def_id)).into(),
ty::Array(_, n) => {
ty::Array(t, n) => {
let n = tcx.lift(&n).unwrap();
match n.try_eval_usize(tcx, ty::ParamEnv::empty()) {
Some(n) => {
format!("array of {} element{}", n, pluralize!(n)).into()
}
_ if t.is_simple_ty() => format!("array `{}`", self).into(),
Some(n) => format!("array of {} element{} ", n, pluralize!(n)).into(),
None => "array".into(),
}
}
ty::Slice(ty) if ty.is_simple_ty() => format!("slice `{}`", self).into(),
ty::Slice(_) => "slice".into(),
ty::RawPtr(_) => "*-ptr".into(),
ty::Ref(region, ty, mutbl) => {
ty::Ref(_, ty, mutbl) => {
let tymut = ty::TypeAndMut { ty, mutbl };
let tymut_string = tymut.to_string();
if tymut_string == "_" || //unknown type name,
tymut_string.len() > 10 || //name longer than saying "reference",
region.to_string() != "'_" //... or a complex type
{
format!("{}reference", match mutbl {
hir::Mutability::Mutable => "mutable ",
_ => ""
}).into()
} else {
format!("&{}", tymut_string).into()
if tymut_string != "_" && (
ty.is_simple_text() || tymut_string.len() < "mutable reference".len()
) {
format!("`&{}`", tymut_string).into()
} else { // Unknown type name, it's long or has type arguments
match mutbl {
hir::Mutability::Mutable => "mutable reference",
_ => "reference",
}.into()
}
}
ty::FnDef(..) => "fn item".into(),
ty::FnPtr(_) => "fn pointer".into(),
ty::Dynamic(ref inner, ..) => {
if let Some(principal) = inner.principal() {
format!("trait {}", tcx.def_path_str(principal.def_id())).into()
format!("trait `{}`", tcx.def_path_str(principal.def_id())).into()
} else {
"trait".into()
}
Expand All @@ -246,6 +280,36 @@ impl<'tcx> ty::TyS<'tcx> {
ty::Error => "type error".into(),
}
}

pub fn prefix_string(&self) -> Cow<'static, str> {
match self.kind {
ty::Infer(_) | ty::Error | ty::Bool | ty::Char | ty::Int(_) |
ty::Uint(_) | ty::Float(_) | ty::Str | ty::Never => "type".into(),
ty::Tuple(ref tys) if tys.is_empty() => "unit type".into(),
ty::Adt(def, _) => def.descr().into(),
ty::Foreign(_) => "extern type".into(),
ty::Array(..) => "array".into(),
ty::Slice(_) => "slice".into(),
ty::RawPtr(_) => "raw pointer".into(),
ty::Ref(.., mutbl) => match mutbl {
hir::Mutability::Mutable => "mutable reference",
_ => "reference"
}.into(),
ty::FnDef(..) => "fn item".into(),
ty::FnPtr(_) => "fn pointer".into(),
ty::Dynamic(..) => "trait object".into(),
ty::Closure(..) => "closure".into(),
ty::Generator(..) => "generator".into(),
ty::GeneratorWitness(..) => "generator witness".into(),
ty::Tuple(..) => "tuple".into(),
ty::Placeholder(..) => "higher-ranked type".into(),
ty::Bound(..) => "bound type variable".into(),
ty::Projection(_) => "associated type".into(),
ty::UnnormalizedProjection(_) => "associated type".into(),
ty::Param(_) => "type parameter".into(),
ty::Opaque(..) => "opaque type".into(),
}
}
}

impl<'tcx> TyCtxt<'tcx> {
Expand Down
33 changes: 2 additions & 31 deletions src/librustc/ty/mod.rs
Expand Up @@ -71,6 +71,7 @@ pub use self::sty::BoundRegion::*;
pub use self::sty::InferTy::*;
pub use self::sty::RegionKind::*;
pub use self::sty::TyKind::*;
pub use crate::ty::diagnostics::*;

pub use self::binding::BindingMode;
pub use self::binding::BindingMode::*;
Expand Down Expand Up @@ -122,6 +123,7 @@ mod instance;
mod structural_impls;
mod structural_match;
mod sty;
mod diagnostics;

// Data types

Expand Down Expand Up @@ -552,37 +554,6 @@ impl<'tcx> Hash for TyS<'tcx> {
}
}

impl<'tcx> TyS<'tcx> {
pub fn is_primitive_ty(&self) -> bool {
match self.kind {
Bool |
Char |
Int(_) |
Uint(_) |
Float(_) |
Infer(InferTy::IntVar(_)) |
Infer(InferTy::FloatVar(_)) |
Infer(InferTy::FreshIntTy(_)) |
Infer(InferTy::FreshFloatTy(_)) => true,
Ref(_, x, _) => x.is_primitive_ty(),
_ => false,
}
}

pub fn is_suggestable(&self) -> bool {
match self.kind {
Opaque(..) |
FnDef(..) |
FnPtr(..) |
Dynamic(..) |
Closure(..) |
Infer(..) |
Projection(..) => false,
_ => true,
}
}
}

impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for ty::TyS<'tcx> {
fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
let ty::TyS {
Expand Down
4 changes: 2 additions & 2 deletions src/librustc/ty/wf.rs
Expand Up @@ -209,7 +209,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
// LL | impl Bar for Foo {
// | ---------------- in this `impl` item
// LL | type Ok = ();
// | ^^^^^^^^^^^^^ expected u32, found ()
// | ^^^^^^^^^^^^^ expected `u32`, found `()`
// |
// = note: expected type `u32`
// found type `()`
Expand All @@ -228,7 +228,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
// LL | impl Bar for Foo {
// | ---------------- in this `impl` item
// LL | type Ok = ();
// | ^^^^^^^^^^^^^ expected u32, found ()
// | ^^^^^^^^^^^^^ expected `u32`, found `()`
// ...
// LL | impl Bar2 for Foo2 {
// | ---------------- in this `impl` item
Expand Down

0 comments on commit 53712f8

Please sign in to comment.