1 // Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 use hir::{self, Local, Pat, Body, HirId};
12 use hir::intravisit::{self, Visitor, NestedVisitorMap};
14 use infer::type_variable::TypeVariableOrigin;
15 use ty::{self, Ty, TyInfer, TyVar};
17 use errors::DiagnosticBuilder;
19 struct FindLocalByTypeVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
20 infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
21 target_ty: &'a Ty<'tcx>,
22 hir_map: &'a hir::map::Map<'gcx>,
23 found_local_pattern: Option<&'gcx Pat>,
24 found_arg_pattern: Option<&'gcx Pat>,
27 impl<'a, 'gcx, 'tcx> FindLocalByTypeVisitor<'a, 'gcx, 'tcx> {
28 fn node_matches_type(&mut self, node_id: HirId) -> bool {
29 let ty_opt = self.infcx.in_progress_tables.and_then(|tables| {
30 tables.borrow().node_id_to_type_opt(node_id)
34 let ty = self.infcx.resolve_type_vars_if_possible(&ty);
35 ty.walk().any(|inner_ty| {
36 inner_ty == *self.target_ty || match (&inner_ty.sty, &self.target_ty.sty) {
37 (&TyInfer(TyVar(a_vid)), &TyInfer(TyVar(b_vid))) => {
41 .sub_unified(a_vid, b_vid)
52 impl<'a, 'gcx, 'tcx> Visitor<'gcx> for FindLocalByTypeVisitor<'a, 'gcx, 'tcx> {
53 fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'gcx> {
54 NestedVisitorMap::OnlyBodies(&self.hir_map)
57 fn visit_local(&mut self, local: &'gcx Local) {
58 if self.found_local_pattern.is_none() && self.node_matches_type(local.hir_id) {
59 self.found_local_pattern = Some(&*local.pat);
61 intravisit::walk_local(self, local);
64 fn visit_body(&mut self, body: &'gcx Body) {
65 for argument in &body.arguments {
66 if self.found_arg_pattern.is_none() && self.node_matches_type(argument.hir_id) {
67 self.found_arg_pattern = Some(&*argument.pat);
70 intravisit::walk_body(self, body);
75 impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
76 fn extract_type_name(&self, ty: &'a Ty<'tcx>) -> String {
77 if let ty::TyInfer(ty::TyVar(ty_vid)) = (*ty).sty {
78 let ty_vars = self.type_variables.borrow();
79 if let TypeVariableOrigin::TypeParameterDefinition(_, name) =
80 *ty_vars.var_origin(ty_vid) {
90 pub fn need_type_info_err(&self,
91 body_id: Option<hir::BodyId>,
94 -> DiagnosticBuilder<'gcx> {
95 let ty = self.resolve_type_vars_if_possible(&ty);
96 let name = self.extract_type_name(&ty);
98 let mut err_span = span;
99 let mut labels = vec![(span, format!("cannot infer type for `{}`", name))];
101 let mut local_visitor = FindLocalByTypeVisitor {
104 hir_map: &self.tcx.hir,
105 found_local_pattern: None,
106 found_arg_pattern: None,
109 if let Some(body_id) = body_id {
110 let expr = self.tcx.hir.expect_expr(body_id.node_id);
111 local_visitor.visit_expr(expr);
114 if let Some(pattern) = local_visitor.found_arg_pattern {
115 err_span = pattern.span;
116 // We don't want to show the default label for closures.
118 // So, before clearing, the output would look something like this:
121 // - ^^^^ cannot infer type for `[_; 0]`
123 // consider giving this closure parameter a type
126 // After clearing, it looks something like this:
129 // ^ consider giving this closure parameter a type
132 labels.push((pattern.span, format!("consider giving this closure parameter a type")));
133 } else if let Some(pattern) = local_visitor.found_local_pattern {
134 if let Some(simple_ident) = pattern.simple_ident() {
135 labels.push((pattern.span, format!("consider giving `{}` a type", simple_ident)));
137 labels.push((pattern.span, format!("consider giving the pattern a type")));
141 let mut err = struct_span_err!(self.tcx.sess,
144 "type annotations needed");
146 for (target_span, label_message) in labels {
147 err.span_label(target_span, label_message);