1
Fork 0

Auto merge of #45630 - joshleeb:iss35241, r=estebank

Improve display of error E0308

Ref. Forgetting to call a variant constructor causes a confusing error message #35241.

This PR modifies [`note_type_err`](b7041bfab3/src/librustc/infer/error_reporting/mod.rs (L669-L674)) to display a `help` message when a `TyFnPtr` or `TyFnDef` are found and the return type, of the function or function pointer, is the same as the type that is expected.

The output of compiling

```rust
struct Foo(u32);

fn test() -> Foo { Foo }

fn main() {}
```

is now

```bash
$ rustc src/test/ui/issue-35241.rs
error[E0308]: mismatched types
  --> src/test/ui/issue-35241.rs:13:20
   |
13 | fn test() -> Foo { Foo }
   |              ---   ^^^ expected struct `Foo`, found fn item
   |              |
   |              expected `Foo` because of return type
   |
   = help: did you mean `Foo { /* fields */ }`?
   = note: expected type `Foo`
              found type `fn(u32) -> Foo {Foo::{{constructor}}}`

error: aborting due to previous error
```
This commit is contained in:
bors 2017-11-02 08:30:03 +00:00
commit a7d98c7837
3 changed files with 70 additions and 13 deletions

View file

@ -66,7 +66,7 @@ use hir::map as hir_map;
use hir::def_id::DefId; use hir::def_id::DefId;
use middle::region; use middle::region;
use traits::{ObligationCause, ObligationCauseCode}; use traits::{ObligationCause, ObligationCauseCode};
use ty::{self, Region, Ty, TyCtxt, TypeFoldable}; use ty::{self, Region, Ty, TyCtxt, TypeFoldable, TypeVariants};
use ty::error::TypeError; use ty::error::TypeError;
use syntax::ast::DUMMY_NODE_ID; use syntax::ast::DUMMY_NODE_ID;
use syntax_pos::{Pos, Span}; use syntax_pos::{Pos, Span};
@ -673,14 +673,17 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
values: Option<ValuePairs<'tcx>>, values: Option<ValuePairs<'tcx>>,
terr: &TypeError<'tcx>) terr: &TypeError<'tcx>)
{ {
let (expected_found, is_simple_error) = match values { let (expected_found, exp_found, is_simple_error) = match values {
None => (None, false), None => (None, None, false),
Some(values) => { Some(values) => {
let is_simple_error = match values { let (is_simple_error, exp_found) = match values {
ValuePairs::Types(exp_found) => { ValuePairs::Types(exp_found) => {
exp_found.expected.is_primitive() && exp_found.found.is_primitive() let is_simple_err = exp_found.expected.is_primitive()
&& exp_found.found.is_primitive();
(is_simple_err, Some(exp_found))
} }
_ => false, _ => (false, None),
}; };
let vals = match self.values_str(&values) { let vals = match self.values_str(&values) {
Some((expected, found)) => Some((expected, found)), Some((expected, found)) => Some((expected, found)),
@ -690,12 +693,17 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
return return
} }
}; };
(vals, is_simple_error) (vals, exp_found, is_simple_error)
} }
}; };
let span = cause.span; let span = cause.span;
diag.span_label(span, terr.to_string());
if let Some((sp, msg)) = secondary_span {
diag.span_label(sp, msg);
}
if let Some((expected, found)) = expected_found { if let Some((expected, found)) = expected_found {
match (terr, is_simple_error, expected == found) { match (terr, is_simple_error, expected == found) {
(&TypeError::Sorts(ref values), false, true) => { (&TypeError::Sorts(ref values), false, true) => {
@ -704,18 +712,37 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
&format!(" ({})", values.expected.sort_string(self.tcx)), &format!(" ({})", values.expected.sort_string(self.tcx)),
&format!(" ({})", values.found.sort_string(self.tcx))); &format!(" ({})", values.found.sort_string(self.tcx)));
} }
(_, false, _) => { (_, false, _) => {
if let Some(exp_found) = exp_found {
let (def_id, ret_ty) = match exp_found.found.sty {
TypeVariants::TyFnDef(def, _) => {
(Some(def), Some(self.tcx.fn_sig(def).output()))
}
_ => (None, None)
};
let exp_is_struct = match exp_found.expected.sty {
TypeVariants::TyAdt(def, _) => def.is_struct(),
_ => false
};
if let (Some(def_id), Some(ret_ty)) = (def_id, ret_ty) {
if exp_is_struct && exp_found.expected == ret_ty.0 {
let message = format!(
"did you mean `{}(/* fields */)`?",
self.tcx.item_path_str(def_id)
);
diag.span_label(cause.span, message);
}
}
}
diag.note_expected_found(&"type", expected, found); diag.note_expected_found(&"type", expected, found);
} }
_ => (), _ => (),
} }
} }
diag.span_label(span, terr.to_string());
if let Some((sp, msg)) = secondary_span {
diag.span_label(sp, msg);
}
self.note_error_origin(diag, &cause); self.note_error_origin(diag, &cause);
self.check_and_note_conflicting_crates(diag, terr, span); self.check_and_note_conflicting_crates(diag, terr, span);
self.tcx.note_and_explain_type_err(diag, terr, span); self.tcx.note_and_explain_type_err(diag, terr, span);

View file

@ -0,0 +1,15 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
struct Foo(u32);
fn test() -> Foo { Foo }
fn main() {}

View file

@ -0,0 +1,15 @@
error[E0308]: mismatched types
--> $DIR/issue-35241.rs:13:20
|
13 | fn test() -> Foo { Foo }
| --- ^^^
| | |
| | expected struct `Foo`, found fn item
| | did you mean `Foo(/* fields */)`?
| expected `Foo` because of return type
|
= note: expected type `Foo`
found type `fn(u32) -> Foo {Foo::{{constructor}}}`
error: aborting due to previous error