Skip to content

Commit

Permalink
Auto merge of #45630 - joshleeb:iss35241, r=estebank
Browse files Browse the repository at this point in the history
Improve display of error E0308

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

This PR modifies [`note_type_err`](https://github.com/rust-lang/rust/blob/b7041bfab3a83702a8026fb7a18d8ea7d54cc648/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
```
  • Loading branch information
bors committed Nov 2, 2017
2 parents 2379faa + 87c951d commit a7d98c7
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 13 deletions.
53 changes: 40 additions & 13 deletions src/librustc/infer/error_reporting/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ use hir::map as hir_map;
use hir::def_id::DefId;
use middle::region;
use traits::{ObligationCause, ObligationCauseCode};
use ty::{self, Region, Ty, TyCtxt, TypeFoldable};
use ty::{self, Region, Ty, TyCtxt, TypeFoldable, TypeVariants};
use ty::error::TypeError;
use syntax::ast::DUMMY_NODE_ID;
use syntax_pos::{Pos, Span};
Expand Down Expand Up @@ -673,14 +673,17 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
values: Option<ValuePairs<'tcx>>,
terr: &TypeError<'tcx>)
{
let (expected_found, is_simple_error) = match values {
None => (None, false),
let (expected_found, exp_found, is_simple_error) = match values {
None => (None, None, false),
Some(values) => {
let is_simple_error = match values {
let (is_simple_error, exp_found) = match values {
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) {
Some((expected, found)) => Some((expected, found)),
Expand All @@ -690,12 +693,17 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
return
}
};
(vals, is_simple_error)
(vals, exp_found, is_simple_error)
}
};

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 {
match (terr, is_simple_error, expected == found) {
(&TypeError::Sorts(ref values), false, true) => {
Expand All @@ -704,18 +712,37 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
&format!(" ({})", values.expected.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.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.check_and_note_conflicting_crates(diag, terr, span);
self.tcx.note_and_explain_type_err(diag, terr, span);
Expand Down
15 changes: 15 additions & 0 deletions src/test/ui/issue-35241.rs
Original file line number Diff line number Diff line change
@@ -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() {}
15 changes: 15 additions & 0 deletions src/test/ui/issue-35241.stderr
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit a7d98c7

Please sign in to comment.