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