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