]> git.lizzy.rs Git - rust.git/blob - src/librustc/infer/error_reporting/need_type_info.rs
fix rebase fallout
[rust.git] / src / librustc / infer / error_reporting / need_type_info.rs
1 use crate::hir::{self, Local, Pat, Body, HirId};
2 use crate::hir::intravisit::{self, Visitor, NestedVisitorMap};
3 use crate::infer::InferCtxt;
4 use crate::infer::type_variable::TypeVariableOrigin;
5 use crate::ty::{self, Ty, Infer, TyVar};
6 use syntax::source_map::CompilerDesugaringKind;
7 use syntax_pos::Span;
8 use errors::DiagnosticBuilder;
9
10 struct FindLocalByTypeVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
11     infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
12     target_ty: &'a Ty<'tcx>,
13     hir_map: &'a hir::map::Map<'gcx>,
14     found_local_pattern: Option<&'gcx Pat>,
15     found_arg_pattern: Option<&'gcx Pat>,
16 }
17
18 impl<'a, 'gcx, 'tcx> FindLocalByTypeVisitor<'a, 'gcx, 'tcx> {
19     fn node_matches_type(&mut self, hir_id: HirId) -> bool {
20         let ty_opt = self.infcx.in_progress_tables.and_then(|tables| {
21             tables.borrow().node_type_opt(hir_id)
22         });
23         match ty_opt {
24             Some(ty) => {
25                 let ty = self.infcx.resolve_type_vars_if_possible(&ty);
26                 ty.walk().any(|inner_ty| {
27                     inner_ty == *self.target_ty || match (&inner_ty.sty, &self.target_ty.sty) {
28                         (&Infer(TyVar(a_vid)), &Infer(TyVar(b_vid))) => {
29                             self.infcx
30                                 .type_variables
31                                 .borrow_mut()
32                                 .sub_unified(a_vid, b_vid)
33                         }
34                         _ => false,
35                     }
36                 })
37             }
38             None => false,
39         }
40     }
41 }
42
43 impl<'a, 'gcx, 'tcx> Visitor<'gcx> for FindLocalByTypeVisitor<'a, 'gcx, 'tcx> {
44     fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'gcx> {
45         NestedVisitorMap::OnlyBodies(&self.hir_map)
46     }
47
48     fn visit_local(&mut self, local: &'gcx Local) {
49         if self.found_local_pattern.is_none() && self.node_matches_type(local.hir_id) {
50             self.found_local_pattern = Some(&*local.pat);
51         }
52         intravisit::walk_local(self, local);
53     }
54
55     fn visit_body(&mut self, body: &'gcx Body) {
56         for argument in &body.arguments {
57             if self.found_arg_pattern.is_none() && self.node_matches_type(argument.hir_id) {
58                 self.found_arg_pattern = Some(&*argument.pat);
59             }
60         }
61         intravisit::walk_body(self, body);
62     }
63 }
64
65
66 impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
67     pub fn extract_type_name(&self, ty: &'a Ty<'tcx>) -> String {
68         if let ty::Infer(ty::TyVar(ty_vid)) = (*ty).sty {
69             let ty_vars = self.type_variables.borrow();
70             if let TypeVariableOrigin::TypeParameterDefinition(_, name) =
71                 *ty_vars.var_origin(ty_vid) {
72                 name.to_string()
73             } else {
74                 ty.to_string()
75             }
76         } else {
77             ty.to_string()
78         }
79     }
80
81     pub fn need_type_info_err(&self,
82                             body_id: Option<hir::BodyId>,
83                             span: Span,
84                             ty: Ty<'tcx>)
85                             -> DiagnosticBuilder<'gcx> {
86         let ty = self.resolve_type_vars_if_possible(&ty);
87         let name = self.extract_type_name(&ty);
88
89         let mut err_span = span;
90         let mut labels = vec![(
91             span,
92             if &name == "_" {
93                 "cannot infer type".to_owned()
94             } else {
95                 format!("cannot infer type for `{}`", name)
96             },
97         )];
98
99         let mut local_visitor = FindLocalByTypeVisitor {
100             infcx: &self,
101             target_ty: &ty,
102             hir_map: &self.tcx.hir(),
103             found_local_pattern: None,
104             found_arg_pattern: None,
105         };
106
107         if let Some(body_id) = body_id {
108             let expr = self.tcx.hir().expect_expr(body_id.node_id);
109             local_visitor.visit_expr(expr);
110         }
111
112         if let Some(pattern) = local_visitor.found_arg_pattern {
113             err_span = pattern.span;
114             // We don't want to show the default label for closures.
115             //
116             // So, before clearing, the output would look something like this:
117             // ```
118             // let x = |_| {  };
119             //          -  ^^^^ cannot infer type for `[_; 0]`
120             //          |
121             //          consider giving this closure parameter a type
122             // ```
123             //
124             // After clearing, it looks something like this:
125             // ```
126             // let x = |_| {  };
127             //          ^ consider giving this closure parameter a type
128             // ```
129             labels.clear();
130             labels.push(
131                 (pattern.span, "consider giving this closure parameter a type".to_owned()));
132         } else if let Some(pattern) = local_visitor.found_local_pattern {
133             if let Some(simple_ident) = pattern.simple_ident() {
134                 match pattern.span.compiler_desugaring_kind() {
135                     None => labels.push((pattern.span,
136                                          format!("consider giving `{}` a type", simple_ident))),
137                     Some(CompilerDesugaringKind::ForLoop) => labels.push((
138                         pattern.span,
139                         "the element type for this iterator is not specified".to_owned(),
140                     )),
141                     _ => {}
142                 }
143             } else {
144                 labels.push((pattern.span, "consider giving the pattern a type".to_owned()));
145             }
146         }
147
148         let mut err = struct_span_err!(self.tcx.sess,
149                                        err_span,
150                                        E0282,
151                                        "type annotations needed");
152
153         for (target_span, label_message) in labels {
154             err.span_label(target_span, label_message);
155         }
156
157         err
158     }
159 }