Auto merge of #61361 - estebank:infer-type, r=varkor

Add more detail to type inference error

When encountering code where type inference fails, add more actionable
information:

```
fn main() {
    let foo = Vec::new();
}
```

```
error[E0282]: type annotations needed in `std::vec::Vec<T>`
  --> $DIR/vector-no-ann.rs:2:16
   |
LL |     let foo = Vec::new();
   |         ---   ^^^^^^^^ cannot infer type for `T` in `std::vec::Vec<T>`
   |         |
   |         consider giving `foo` a type
```

Fix #25633.
This commit is contained in:
bors 2019-06-03 02:45:35 +00:00
commit d59dcb261e
20 changed files with 136 additions and 50 deletions

View File

@ -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
@ -35,9 +36,13 @@ impl<'a, 'gcx, 'tcx> FindLocalByTypeVisitor<'a, 'gcx, 'tcx> {
}
_ => false,
}
})
}) {
Some(ty)
} else {
None
}
}
None => false,
None => None,
}
}
}
@ -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);
@ -98,7 +108,6 @@ 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,
@ -106,6 +115,22 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
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 {
@ -113,6 +138,38 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
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.
@ -128,16 +185,21 @@ 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(),
@ -145,14 +207,17 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
_ => {}
}
} 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);

View File

@ -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) {
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) => {
@ -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>>,
@ -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>>>,
}
impl<F> Deref for FmtPrinter<'a, 'gcx, 'tcx, F> {
@ -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,
}))
}
}
@ -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))
}
fn print_value_path(
mut self,
def_id: DefId,

View File

@ -4,5 +4,5 @@ fn new<T>() -> &'static T {
fn main() {
let &v = new();
//~^ ERROR type annotations needed [E0282]
//~^ ERROR type annotations needed
}

View File

@ -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

View File

@ -4,5 +4,5 @@ fn new<'r, T>() -> &'r T {
fn main() {
let &v = new();
//~^ ERROR type annotations needed [E0282]
//~^ ERROR type annotations needed
}

View File

@ -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

View File

@ -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

View File

@ -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() {

View File

@ -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

View File

@ -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`

View File

@ -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

View File

@ -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();
}

View File

@ -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

View File

@ -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`
|

View File

@ -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

View File

@ -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)

View File

@ -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_in_tuples.rs:2:18
|
LL | let (x, ) = (vec![], );
| ----- ^^^^^^ cannot infer type for `T`
| |
| consider giving the pattern a type
| consider giving this pattern 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)

View File

@ -1,8 +1,8 @@
error[E0282]: type annotations needed
error[E0282]: type annotations needed for `std::option::Option<T>`
--> $DIR/unboxed-closures-failed-recursive-fn-2.rs:16:32
|
LL | let mut closure0 = None;
| ------------ consider giving `closure0` a type
| ------------ consider giving `closure0` the explicit type `std::option::Option<T>`, with the type parameters specified
...
LL | return c();
| ^^^ cannot infer type

View File

@ -1,4 +1,4 @@
fn main() {
let _foo = Vec::new();
//~^ ERROR type annotations needed [E0282]
//~^ ERROR type annotations needed
}

View File

@ -1,10 +1,10 @@
error[E0282]: type annotations needed
error[E0282]: type annotations needed for `std::vec::Vec<T>`
--> $DIR/vector-no-ann.rs:2:16
|
LL | let _foo = Vec::new();
| ---- ^^^^^^^^ cannot infer type for `T`
| |
| consider giving `_foo` a type
| consider giving `_foo` the explicit type `std::vec::Vec<T>`, where the type parameter `T` is specified
error: aborting due to previous error