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