Skip to content

Add more detail to type inference error #61361

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jun 3, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 83 additions & 18 deletions src/librustc/infer/error_reporting/need_type_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,18 @@ struct FindLocalByTypeVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
hir_map: &'a hir::map::Map<'gcx>,
found_local_pattern: Option<&'gcx Pat>,
found_arg_pattern: Option<&'gcx Pat>,
found_ty: Option<Ty<'tcx>>,
}

impl<'a, 'gcx, 'tcx> FindLocalByTypeVisitor<'a, 'gcx, 'tcx> {
fn node_matches_type(&mut self, hir_id: HirId) -> bool {
fn node_matches_type(&mut self, hir_id: HirId) -> Option<Ty<'tcx>> {
let ty_opt = self.infcx.in_progress_tables.and_then(|tables| {
tables.borrow().node_type_opt(hir_id)
});
match ty_opt {
Some(ty) => {
let ty = self.infcx.resolve_vars_if_possible(&ty);
ty.walk().any(|inner_ty| {
if ty.walk().any(|inner_ty| {
inner_ty == self.target_ty || match (&inner_ty.sty, &self.target_ty.sty) {
(&Infer(TyVar(a_vid)), &Infer(TyVar(b_vid))) => {
self.infcx
Expand All @@ -35,9 +36,13 @@ impl<'a, 'gcx, 'tcx> FindLocalByTypeVisitor<'a, 'gcx, 'tcx> {
}
_ => false,
}
})
}) {
Some(ty)
} else {
None
}
}
None => false,
None => None,
}
}
}
Expand All @@ -48,16 +53,21 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for FindLocalByTypeVisitor<'a, 'gcx, 'tcx> {
}

fn visit_local(&mut self, local: &'gcx Local) {
if self.found_local_pattern.is_none() && self.node_matches_type(local.hir_id) {
if let (None, Some(ty)) = (self.found_local_pattern, self.node_matches_type(local.hir_id)) {
self.found_local_pattern = Some(&*local.pat);
self.found_ty = Some(ty);
}
intravisit::walk_local(self, local);
}

fn visit_body(&mut self, body: &'gcx Body) {
for argument in &body.arguments {
if self.found_arg_pattern.is_none() && self.node_matches_type(argument.hir_id) {
if let (None, Some(ty)) = (
self.found_arg_pattern,
self.node_matches_type(argument.hir_id),
) {
self.found_arg_pattern = Some(&*argument.pat);
self.found_ty = Some(ty);
}
}
intravisit::walk_body(self, body);
Expand Down Expand Up @@ -98,21 +108,68 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
let name = self.extract_type_name(&ty, None);

let mut err_span = span;
let mut labels = vec![(span, InferCtxt::missing_type_msg(&name))];

let mut local_visitor = FindLocalByTypeVisitor {
infcx: &self,
target_ty: ty,
hir_map: &self.tcx.hir(),
found_local_pattern: None,
found_arg_pattern: None,
found_ty: None,
};
let ty_to_string = |ty: Ty<'tcx>| -> String {
let mut s = String::new();
let mut printer = ty::print::FmtPrinter::new(self.tcx, &mut s, Namespace::TypeNS);
let ty_vars = self.type_variables.borrow();
let getter = move |ty_vid| {
if let TypeVariableOrigin::TypeParameterDefinition(_, name) =
*ty_vars.var_origin(ty_vid) {
return Some(name.to_string());
}
None
};
printer.name_resolver = Some(Box::new(&getter));
let _ = ty.print(printer);
s
};

if let Some(body_id) = body_id {
let expr = self.tcx.hir().expect_expr_by_hir_id(body_id.hir_id);
local_visitor.visit_expr(expr);
}

// When `name` corresponds to a type argument, show the path of the full type we're
// trying to infer. In the following example, `ty_msg` contains
// " in `std::result::Result<i32, E>`":
// ```
// error[E0282]: type annotations needed for `std::result::Result<i32, E>`
// --> file.rs:L:CC
// |
// L | let b = Ok(4);
// | - ^^ cannot infer type for `E` in `std::result::Result<i32, E>`
// | |
// | consider giving `b` the explicit type `std::result::Result<i32, E>`, where
// | the type parameter `E` is specified
// ```
let (ty_msg, suffix) = match &local_visitor.found_ty {
Some(ty) if &ty.to_string() != "_" && name == "_" => {
let ty = ty_to_string(ty);
(format!(" for `{}`", ty),
format!("the explicit type `{}`, with the type parameters specified", ty))
}
Some(ty) if &ty.to_string() != "_" && ty.to_string() != name => {
let ty = ty_to_string(ty);
(format!(" for `{}`", ty),
format!(
"the explicit type `{}`, where the type parameter `{}` is specified",
ty,
name,
))
}
_ => (String::new(), "a type".to_owned()),
};
let mut labels = vec![(span, InferCtxt::missing_type_msg(&name))];

if let Some(pattern) = local_visitor.found_arg_pattern {
err_span = pattern.span;
// We don't want to show the default label for closures.
Expand All @@ -128,31 +185,39 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
// After clearing, it looks something like this:
// ```
// let x = |_| { };
// ^ consider giving this closure parameter a type
// ^ consider giving this closure parameter the type `[_; 0]`
// with the type parameter `_` specified
// ```
labels.clear();
labels.push(
(pattern.span, "consider giving this closure parameter a type".to_owned()));
labels.push((
pattern.span,
format!("consider giving this closure parameter {}", suffix),
));
} else if let Some(pattern) = local_visitor.found_local_pattern {
if let Some(simple_ident) = pattern.simple_ident() {
match pattern.span.compiler_desugaring_kind() {
None => labels.push((pattern.span,
format!("consider giving `{}` a type", simple_ident))),
None => labels.push((
pattern.span,
format!("consider giving `{}` {}", simple_ident, suffix),
)),
Some(CompilerDesugaringKind::ForLoop) => labels.push((
pattern.span,
"the element type for this iterator is not specified".to_owned(),
)),
_ => {}
}
} else {
labels.push((pattern.span, "consider giving the pattern a type".to_owned()));
labels.push((pattern.span, format!("consider giving this pattern {}", suffix)));
}
}
};

let mut err = struct_span_err!(self.tcx.sess,
err_span,
E0282,
"type annotations needed");
let mut err = struct_span_err!(
self.tcx.sess,
err_span,
E0282,
"type annotations needed{}",
ty_msg,
);

for (target_span, label_message) in labels {
err.span_label(target_span, label_message);
Expand Down
23 changes: 22 additions & 1 deletion src/librustc/ty/print/pretty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -483,7 +483,17 @@ pub trait PrettyPrinter<'gcx: 'tcx, 'tcx>:
ty::FnPtr(ref bare_fn) => {
p!(print(bare_fn))
}
ty::Infer(infer_ty) => p!(write("{}", infer_ty)),
ty::Infer(infer_ty) => {
if let ty::TyVar(ty_vid) = infer_ty {
if let Some(name) = self.infer_ty_name(ty_vid) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd probably override this in FmtPrinter's print_ty tbh, instead of adding infer_ty_name.

p!(write("{}", name))
} else {
p!(write("{}", infer_ty))
}
} else {
p!(write("{}", infer_ty))
}
},
ty::Error => p!(write("[type error]")),
ty::Param(ref param_ty) => p!(write("{}", param_ty)),
ty::Bound(debruijn, bound_ty) => {
Expand Down Expand Up @@ -681,6 +691,10 @@ pub trait PrettyPrinter<'gcx: 'tcx, 'tcx>:
Ok(self)
}

fn infer_ty_name(&self, _: ty::TyVid) -> Option<String> {
None
}

fn pretty_print_dyn_existential(
mut self,
predicates: &'tcx ty::List<ty::ExistentialPredicate<'tcx>>,
Expand Down Expand Up @@ -931,6 +945,8 @@ pub struct FmtPrinterData<'a, 'gcx, 'tcx, F> {
binder_depth: usize,

pub region_highlight_mode: RegionHighlightMode,

pub name_resolver: Option<Box<&'a dyn Fn(ty::sty::TyVid) -> Option<String>>>,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please cc me on ty::print changes - but also, I have a few questions, such as "why is this a Box<&dyn Trait>?" - you can just remove the Box and everything would still work.

}

impl<F> Deref for FmtPrinter<'a, 'gcx, 'tcx, F> {
Expand All @@ -957,6 +973,7 @@ impl<F> FmtPrinter<'a, 'gcx, 'tcx, F> {
region_index: 0,
binder_depth: 0,
region_highlight_mode: RegionHighlightMode::default(),
name_resolver: None,
}))
}
}
Expand Down Expand Up @@ -1206,6 +1223,10 @@ impl<F: fmt::Write> Printer<'gcx, 'tcx> for FmtPrinter<'_, 'gcx, 'tcx, F> {
}

impl<F: fmt::Write> PrettyPrinter<'gcx, 'tcx> for FmtPrinter<'_, 'gcx, 'tcx, F> {
fn infer_ty_name(&self, id: ty::TyVid) -> Option<String> {
self.0.name_resolver.as_ref().and_then(|func| func(id))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a Deref impl, you don't need to write self.0.

}

fn print_value_path(
mut self,
def_id: DefId,
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/issues/issue-12187-1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ fn new<T>() -> &'static T {

fn main() {
let &v = new();
//~^ ERROR type annotations needed [E0282]
//~^ ERROR type annotations needed
}
4 changes: 2 additions & 2 deletions src/test/ui/issues/issue-12187-1.stderr
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
error[E0282]: type annotations needed
error[E0282]: type annotations needed for `&T`
--> $DIR/issue-12187-1.rs:6:10
|
LL | let &v = new();
| -^
| ||
| |cannot infer type
| consider giving the pattern a type
| consider giving this pattern the explicit type `&T`, with the type parameters specified

error: aborting due to previous error

Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/issues/issue-12187-2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ fn new<'r, T>() -> &'r T {

fn main() {
let &v = new();
//~^ ERROR type annotations needed [E0282]
//~^ ERROR type annotations needed
}
4 changes: 2 additions & 2 deletions src/test/ui/issues/issue-12187-2.stderr
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
error[E0282]: type annotations needed
error[E0282]: type annotations needed for `&T`
--> $DIR/issue-12187-2.rs:6:10
|
LL | let &v = new();
| -^
| ||
| |cannot infer type
| consider giving the pattern a type
| consider giving this pattern the explicit type `&T`, with the type parameters specified

error: aborting due to previous error

Expand Down
4 changes: 2 additions & 2 deletions src/test/ui/issues/issue-17551.stderr
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
error[E0282]: type annotations needed
error[E0282]: type annotations needed for `B<T>`
--> $DIR/issue-17551.rs:6:15
|
LL | let foo = B(marker::PhantomData);
| --- ^ cannot infer type for `T`
| |
| consider giving `foo` a type
| consider giving `foo` the explicit type `B<T>`, where the type parameter `T` is specified

error: aborting due to previous error

Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/issues/issue-20261.stderr
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
error[E0282]: type annotations needed
error[E0282]: type annotations needed for `&(_,)`
--> $DIR/issue-20261.rs:4:11
|
LL | for (ref i,) in [].iter() {
Expand Down
4 changes: 2 additions & 2 deletions src/test/ui/issues/issue-23046.stderr
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
error[E0282]: type annotations needed
error[E0282]: type annotations needed for `Expr<'_, VAR>`
--> $DIR/issue-23046.rs:17:15
|
LL | let ex = |x| {
| ^ consider giving this closure parameter a type
| ^ consider giving this closure parameter the explicit type `Expr<'_, VAR>`, where the type parameter `VAR` is specified

error: aborting due to previous error

Expand Down
4 changes: 2 additions & 2 deletions src/test/ui/issues/issue-25368.stderr
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
error[E0282]: type annotations needed
error[E0282]: type annotations needed for `(std::sync::mpsc::Sender<Foo<T>>, std::sync::mpsc::Receiver<Foo<T>>)`
--> $DIR/issue-25368.rs:11:17
|
LL | let (tx, rx) = channel();
| -------- consider giving the pattern a type
| -------- consider giving this pattern the explicit type `(std::sync::mpsc::Sender<Foo<T>>, std::sync::mpsc::Receiver<Foo<T>>)`, where the type parameter `T` is specified
...
LL | tx.send(Foo{ foo: PhantomData });
| ^^^ cannot infer type for `T`
Expand Down
4 changes: 2 additions & 2 deletions src/test/ui/issues/issue-7813.stderr
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
error[E0282]: type annotations needed
error[E0282]: type annotations needed for `&[_; 0]`
--> $DIR/issue-7813.rs:2:13
|
LL | let v = &[];
| - ^^^ cannot infer type
| |
| consider giving `v` a type
| consider giving `v` the explicit type `&[_; 0]`, with the type parameters specified

error: aborting due to previous error

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ impl Foo for Vec<isize> {
fn m1() {
// we couldn't infer the type of the vector just based on calling foo()...
let mut x = Vec::new();
//~^ ERROR type annotations needed [E0282]
//~^ ERROR type annotations needed
x.foo();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
error[E0282]: type annotations needed
error[E0282]: type annotations needed for `std::vec::Vec<T>`
--> $DIR/method-ambig-one-trait-unknown-int-type.rs:24:17
|
LL | let mut x = Vec::new();
| ----- ^^^^^^^^ cannot infer type for `T`
| |
| consider giving `x` a type
| consider giving `x` the explicit type `std::vec::Vec<T>`, where the type parameter `T` is specified

error[E0308]: mismatched types
--> $DIR/method-ambig-one-trait-unknown-int-type.rs:33:20
Expand Down
4 changes: 2 additions & 2 deletions src/test/ui/span/issue-42234-unknown-receiver-type.stderr
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
error[E0282]: type annotations needed
error[E0282]: type annotations needed for `std::option::Option<_>`
--> $DIR/issue-42234-unknown-receiver-type.rs:7:5
|
LL | let x: Option<_> = None;
| - consider giving `x` a type
| - consider giving `x` the explicit type `std::option::Option<_>`, where the type parameter `T` is specified
LL | x.unwrap().method_that_could_exist_on_some_type();
| ^^^^^^^^^^ cannot infer type for `T`
|
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
error[E0282]: type annotations needed
error[E0282]: type annotations needed for `[_; 0]`
--> $DIR/cannot_infer_local_or_array.rs:2:13
|
LL | let x = [];
| - ^^ cannot infer type
| |
| consider giving `x` a type
| consider giving `x` the explicit type `[_; 0]`, with the type parameters specified

error: aborting due to previous error

Expand Down
4 changes: 2 additions & 2 deletions src/test/ui/type/type-check/cannot_infer_local_or_vec.stderr
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
error[E0282]: type annotations needed
error[E0282]: type annotations needed for `std::vec::Vec<T>`
--> $DIR/cannot_infer_local_or_vec.rs:2:13
|
LL | let x = vec![];
| - ^^^^^^ cannot infer type for `T`
| |
| consider giving `x` a type
| consider giving `x` the explicit type `std::vec::Vec<T>`, where the type parameter `T` is specified
|
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)

Expand Down
Loading