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