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