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