]> git.lizzy.rs Git - rust.git/blob - src/librustc/infer/error_reporting/need_type_info.rs
Use the appropriate number of type arguments in suggestion
[rust.git] / src / librustc / infer / error_reporting / need_type_info.rs
1 use crate::hir::def::Namespace;
2 use crate::hir::{self, Body, FunctionRetTy, Expr, ExprKind, HirId, Local, Pat};
3 use crate::hir::intravisit::{self, Visitor, NestedVisitorMap};
4 use crate::infer::InferCtxt;
5 use crate::infer::type_variable::TypeVariableOriginKind;
6 use crate::ty::{self, Ty, Infer, TyVar};
7 use crate::ty::print::Print;
8 use syntax::source_map::DesugaringKind;
9 use syntax::symbol::kw;
10 use syntax_pos::Span;
11 use errors::{Applicability, DiagnosticBuilder};
12
13 use rustc_error_codes::*;
14
15 struct FindLocalByTypeVisitor<'a, 'tcx> {
16     infcx: &'a InferCtxt<'a, 'tcx>,
17     target_ty: Ty<'tcx>,
18     hir_map: &'a hir::map::Map<'tcx>,
19     found_local_pattern: Option<&'tcx Pat>,
20     found_arg_pattern: Option<&'tcx Pat>,
21     found_ty: Option<Ty<'tcx>>,
22     found_closure: Option<&'tcx ExprKind>,
23     found_method_call: Option<&'tcx Expr>,
24 }
25
26 impl<'a, 'tcx> FindLocalByTypeVisitor<'a, 'tcx> {
27     fn new(
28         infcx: &'a InferCtxt<'a, 'tcx>,
29         target_ty: Ty<'tcx>,
30         hir_map: &'a hir::map::Map<'tcx>,
31     ) -> Self {
32         Self {
33             infcx,
34             target_ty,
35             hir_map,
36             found_local_pattern: None,
37             found_arg_pattern: None,
38             found_ty: None,
39             found_closure: None,
40             found_method_call: None,
41         }
42     }
43
44     fn node_matches_type(&mut self, hir_id: HirId) -> Option<Ty<'tcx>> {
45         let ty_opt = self.infcx.in_progress_tables.and_then(|tables| {
46             tables.borrow().node_type_opt(hir_id)
47         });
48         match ty_opt {
49             Some(ty) => {
50                 let ty = self.infcx.resolve_vars_if_possible(&ty);
51                 if ty.walk().any(|inner_ty| {
52                     inner_ty == self.target_ty || match (&inner_ty.kind, &self.target_ty.kind) {
53                         (&Infer(TyVar(a_vid)), &Infer(TyVar(b_vid))) => {
54                             self.infcx
55                                 .type_variables
56                                 .borrow_mut()
57                                 .sub_unified(a_vid, b_vid)
58                         }
59                         _ => false,
60                     }
61                 }) {
62                     Some(ty)
63                 } else {
64                     None
65                 }
66             }
67             None => None,
68         }
69     }
70 }
71
72 impl<'a, 'tcx> Visitor<'tcx> for FindLocalByTypeVisitor<'a, 'tcx> {
73     fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
74         NestedVisitorMap::OnlyBodies(&self.hir_map)
75     }
76
77     fn visit_local(&mut self, local: &'tcx Local) {
78         if let (None, Some(ty)) = (self.found_local_pattern, self.node_matches_type(local.hir_id)) {
79             self.found_local_pattern = Some(&*local.pat);
80             self.found_ty = Some(ty);
81         }
82         intravisit::walk_local(self, local);
83     }
84
85     fn visit_body(&mut self, body: &'tcx Body) {
86         for param in &body.params {
87             if let (None, Some(ty)) = (
88                 self.found_arg_pattern,
89                 self.node_matches_type(param.hir_id),
90             ) {
91                 self.found_arg_pattern = Some(&*param.pat);
92                 self.found_ty = Some(ty);
93             }
94         }
95         intravisit::walk_body(self, body);
96     }
97
98     fn visit_expr(&mut self, expr: &'tcx Expr) {
99         if self.node_matches_type(expr.hir_id).is_some() {
100             match expr.kind {
101                 ExprKind::Closure(..) => self.found_closure = Some(&expr.kind),
102                 ExprKind::MethodCall(..) => self.found_method_call = Some(&expr),
103                 _ => {}
104             }
105         }
106         intravisit::walk_expr(self, expr);
107     }
108 }
109
110 /// Suggest giving an appropriate return type to a closure expression.
111 fn closure_return_type_suggestion(
112     span: Span,
113     err: &mut DiagnosticBuilder<'_>,
114     output: &FunctionRetTy,
115     body: &Body,
116     name: &str,
117     ret: &str,
118 ) {
119     let (arrow, post) = match output {
120         FunctionRetTy::DefaultReturn(_) => ("-> ", " "),
121         _ => ("", ""),
122     };
123     let suggestion = match body.value.kind {
124         ExprKind::Block(..) => {
125             vec![(output.span(), format!("{}{}{}", arrow, ret, post))]
126         }
127         _ => {
128             vec![
129                 (output.span(), format!("{}{}{}{{ ", arrow, ret, post)),
130                 (body.value.span.shrink_to_hi(), " }".to_string()),
131             ]
132         }
133     };
134     err.multipart_suggestion(
135         "give this closure an explicit return type without `_` placeholders",
136         suggestion,
137         Applicability::HasPlaceholders,
138     );
139     err.span_label(span, InferCtxt::missing_type_msg(&name));
140 }
141
142 /// Given a closure signature, return a `String` containing a list of all its argument types.
143 fn closure_args(fn_sig: &ty::PolyFnSig<'_>) -> String {
144     fn_sig.inputs()
145         .skip_binder()
146         .iter()
147         .next()
148         .map(|args| args.tuple_fields()
149             .map(|arg| arg.to_string())
150             .collect::<Vec<_>>().join(", "))
151         .unwrap_or_default()
152 }
153
154 pub enum TypeAnnotationNeeded {
155     E0282,
156     E0283,
157     E0284,
158 }
159
160 impl Into<errors::DiagnosticId> for TypeAnnotationNeeded {
161     fn into(self) -> errors::DiagnosticId {
162         syntax::diagnostic_used!(E0282);
163         syntax::diagnostic_used!(E0283);
164         syntax::diagnostic_used!(E0284);
165         errors::DiagnosticId::Error(match self {
166             Self::E0282 => "E0282".to_string(),
167             Self::E0283 => "E0283".to_string(),
168             Self::E0284 => "E0284".to_string(),
169         })
170     }
171 }
172
173 impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
174     pub fn extract_type_name(
175         &self,
176         ty: Ty<'tcx>,
177         highlight: Option<ty::print::RegionHighlightMode>,
178     ) -> (String, Option<Span>) {
179         if let ty::Infer(ty::TyVar(ty_vid)) = ty.kind {
180             let ty_vars = self.type_variables.borrow();
181             let var_origin = ty_vars.var_origin(ty_vid);
182             if let TypeVariableOriginKind::TypeParameterDefinition(name) = var_origin.kind {
183                 if name != kw::SelfUpper {
184                     return (name.to_string(), Some(var_origin.span));
185                 }
186             }
187         }
188
189         let mut s = String::new();
190         let mut printer = ty::print::FmtPrinter::new(self.tcx, &mut s, Namespace::TypeNS);
191         if let Some(highlight) = highlight {
192             printer.region_highlight_mode = highlight;
193         }
194         let _ = ty.print(printer);
195         (s, None)
196     }
197
198     pub fn need_type_info_err(
199         &self,
200         body_id: Option<hir::BodyId>,
201         span: Span,
202         ty: Ty<'tcx>,
203         error_code: TypeAnnotationNeeded,
204     ) -> DiagnosticBuilder<'tcx> {
205         let ty = self.resolve_vars_if_possible(&ty);
206         let (name, name_sp) = self.extract_type_name(&ty, None);
207
208         let mut local_visitor = FindLocalByTypeVisitor::new(&self, ty, &self.tcx.hir());
209         let ty_to_string = |ty: Ty<'tcx>| -> String {
210             let mut s = String::new();
211             let mut printer = ty::print::FmtPrinter::new(self.tcx, &mut s, Namespace::TypeNS);
212             let ty_vars = self.type_variables.borrow();
213             let getter = move |ty_vid| {
214                 let var_origin = ty_vars.var_origin(ty_vid);
215                 if let TypeVariableOriginKind::TypeParameterDefinition(name) = var_origin.kind {
216                     return Some(name.to_string());
217                 }
218                 None
219             };
220             printer.name_resolver = Some(Box::new(&getter));
221             let _ = ty.print(printer);
222             s
223         };
224
225         if let Some(body_id) = body_id {
226             let expr = self.tcx.hir().expect_expr(body_id.hir_id);
227             local_visitor.visit_expr(expr);
228         }
229         let err_span = if let Some(pattern) = local_visitor.found_arg_pattern {
230             pattern.span
231         } else if let Some(span) = name_sp {
232             // `span` here lets us point at `sum` instead of the entire right hand side expr:
233             // error[E0282]: type annotations needed
234             //  --> file2.rs:3:15
235             //   |
236             // 3 |     let _ = x.sum() as f64;
237             //   |               ^^^ cannot infer type for `S`
238             span
239         } else if let Some(
240             ExprKind::MethodCall(_, call_span, _),
241         ) = local_visitor.found_method_call.map(|e| &e.kind) {
242             // Point at the call instead of the whole expression:
243             // error[E0284]: type annotations needed
244             //  --> file.rs:2:5
245             //   |
246             // 2 |     vec![Ok(2)].into_iter().collect()?;
247             //   |                             ^^^^^^^ cannot infer type
248             //   |
249             //   = note: cannot resolve `<_ as std::ops::Try>::Ok == _`
250             if span.contains(*call_span) {
251                 *call_span
252             } else {
253                 span
254             }
255         } else {
256             span
257         };
258
259         let is_named_and_not_impl_trait = |ty: Ty<'_>| {
260             &ty.to_string() != "_" &&
261                 // FIXME: Remove this check after `impl_trait_in_bindings` is stabilized. #63527
262                 (!ty.is_impl_trait() || self.tcx.features().impl_trait_in_bindings)
263         };
264
265         let ty_msg = match local_visitor.found_ty {
266             Some(ty::TyS { kind: ty::Closure(def_id, substs), .. }) => {
267                 let fn_sig = substs.as_closure().sig(*def_id, self.tcx);
268                 let args = closure_args(&fn_sig);
269                 let ret = fn_sig.output().skip_binder().to_string();
270                 format!(" for the closure `fn({}) -> {}`", args, ret)
271             }
272             Some(ty) if is_named_and_not_impl_trait(ty) => {
273                 let ty = ty_to_string(ty);
274                 format!(" for `{}`", ty)
275             }
276             _ => String::new(),
277         };
278
279         // When `name` corresponds to a type argument, show the path of the full type we're
280         // trying to infer. In the following example, `ty_msg` contains
281         // " in `std::result::Result<i32, E>`":
282         // ```
283         // error[E0282]: type annotations needed for `std::result::Result<i32, E>`
284         //  --> file.rs:L:CC
285         //   |
286         // L |     let b = Ok(4);
287         //   |         -   ^^ cannot infer type for `E` in `std::result::Result<i32, E>`
288         //   |         |
289         //   |         consider giving `b` the explicit type `std::result::Result<i32, E>`, where
290         //   |         the type parameter `E` is specified
291         // ```
292         let error_code = error_code.into();
293         let mut err = self.tcx.sess.struct_span_err_with_code(
294             err_span,
295             &format!("type annotations needed{}", ty_msg),
296             error_code,
297         );
298
299         let suffix = match local_visitor.found_ty {
300             Some(ty::TyS { kind: ty::Closure(def_id, substs), .. }) => {
301                 let fn_sig = substs.as_closure().sig(*def_id, self.tcx);
302                 let ret = fn_sig.output().skip_binder().to_string();
303
304                 if let Some(ExprKind::Closure(_, decl, body_id, ..)) = local_visitor.found_closure {
305                     if let Some(body) = self.tcx.hir().krate().bodies.get(body_id) {
306                         closure_return_type_suggestion(
307                             span,
308                             &mut err,
309                             &decl.output,
310                             &body,
311                             &name,
312                             &ret,
313                         );
314                         // We don't want to give the other suggestions when the problem is the
315                         // closure return type.
316                         return err;
317                     }
318                 }
319
320                 // This shouldn't be reachable, but just in case we leave a reasonable fallback.
321                 let args = closure_args(&fn_sig);
322                 // This suggestion is incomplete, as the user will get further type inference
323                 // errors due to the `_` placeholders and the introduction of `Box`, but it does
324                 // nudge them in the right direction.
325                 format!("a boxed closure type like `Box<dyn Fn({}) -> {}>`", args, ret)
326             }
327             Some(ty) if is_named_and_not_impl_trait(ty) && name == "_" => {
328                 let ty = ty_to_string(ty);
329                 format!("the explicit type `{}`, with the type parameters specified", ty)
330             }
331             Some(ty) if is_named_and_not_impl_trait(ty) && ty.to_string() != name => {
332                 let ty = ty_to_string(ty);
333                 format!(
334                     "the explicit type `{}`, where the type parameter `{}` is specified",
335                     ty,
336                     name,
337                 )
338             }
339             _ => "a type".to_string(),
340         };
341
342         if let Some(pattern) = local_visitor.found_arg_pattern {
343             // We don't want to show the default label for closures.
344             //
345             // So, before clearing, the output would look something like this:
346             // ```
347             // let x = |_| {  };
348             //          -  ^^^^ cannot infer type for `[_; 0]`
349             //          |
350             //          consider giving this closure parameter a type
351             // ```
352             //
353             // After clearing, it looks something like this:
354             // ```
355             // let x = |_| {  };
356             //          ^ consider giving this closure parameter the type `[_; 0]`
357             //            with the type parameter `_` specified
358             // ```
359             err.span_label(
360                 pattern.span,
361                 format!("consider giving this closure parameter {}", suffix),
362             );
363         } else if let Some(pattern) = local_visitor.found_local_pattern {
364             let msg = if let Some(simple_ident) = pattern.simple_ident() {
365                 match pattern.span.desugaring_kind() {
366                     None => {
367                         format!("consider giving `{}` {}", simple_ident, suffix)
368                     }
369                     Some(DesugaringKind::ForLoop) => {
370                         "the element type for this iterator is not specified".to_string()
371                     }
372                     _ => format!("this needs {}", suffix),
373                 }
374             } else {
375                 format!("consider giving this pattern {}", suffix)
376             };
377             err.span_label(pattern.span, msg);
378         } else if let Some(e) = local_visitor.found_method_call {
379             if let ExprKind::MethodCall(segment, _call_sp, _args) = &e.kind {
380                 if let (Ok(snippet), Some(tables), None) = (
381                     self.tcx.sess.source_map().span_to_snippet(segment.ident.span),
382                     self.in_progress_tables,
383                     &segment.args,
384                  ) {
385                     let borrow = tables.borrow();
386                     let sigs = borrow.node_method_sig();
387                     if let Some(sig) = sigs.get(e.hir_id) {
388                         let mut params = vec![];
389                         for arg in sig.inputs_and_output().skip_binder().iter() {
390                             if let ty::Param(param) = arg.kind {
391                                 if param.name != kw::SelfUpper {
392                                     let name = param.name.to_string();
393                                     if !params.contains(&name) {
394                                         params.push(name);
395                                     }
396                                 }
397                             }
398                         }
399                         if !params.is_empty() {
400                             err.span_suggestion(
401                                 segment.ident.span,
402                                 &format!(
403                                     "consider specifying the type argument{} in the method call",
404                                     if params.len() > 1 {
405                                         "s"
406                                     } else {
407                                         ""
408                                     },
409                                 ),
410                                 format!("{}::<{}>", snippet, params.join(", ")),
411                                 Applicability::HasPlaceholders,
412                             );
413                         } else {
414                             err.span_label(e.span, &format!(
415                                 "this method call resolves to `{:?}`",
416                                 sig.output().skip_binder(),
417                             ));
418                         }
419                     }
420                 }
421             }
422         }
423         // Instead of the following:
424         // error[E0282]: type annotations needed
425         //  --> file2.rs:3:15
426         //   |
427         // 3 |     let _ = x.sum() as f64;
428         //   |             --^^^--------- cannot infer type for `S`
429         //   |
430         //   = note: type must be known at this point
431         // We want:
432         // error[E0282]: type annotations needed
433         //  --> file2.rs:3:15
434         //   |
435         // 3 |     let _ = x.sum() as f64;
436         //   |               ^^^ cannot infer type for `S`
437         //   |
438         //   = note: type must be known at this point
439         let span = name_sp.unwrap_or(err_span);
440         if !err.span.span_labels().iter().any(|span_label| {
441                 span_label.label.is_some() && span_label.span == span
442             }) && local_visitor.found_arg_pattern.is_none()
443         { // Avoid multiple labels pointing at `span`.
444             err.span_label(span, InferCtxt::missing_type_msg(&name));
445         }
446
447         err
448     }
449
450     pub fn need_type_info_err_in_generator(
451         &self,
452         kind: hir::GeneratorKind,
453         span: Span,
454         ty: Ty<'tcx>,
455     ) -> DiagnosticBuilder<'tcx> {
456         let ty = self.resolve_vars_if_possible(&ty);
457         let name = self.extract_type_name(&ty, None).0;
458         let mut err = struct_span_err!(
459             self.tcx.sess, span, E0698, "type inside {} must be known in this context", kind,
460         );
461         err.span_label(span, InferCtxt::missing_type_msg(&name));
462         err
463     }
464
465     fn missing_type_msg(type_name: &str) -> String {
466         if type_name == "_" {
467             "cannot infer type".to_owned()
468         } else {
469             format!("cannot infer type for `{}`", type_name)
470         }
471     }
472 }