1 use crate::hir::def::Namespace;
2 use crate::hir::{self, Body, FunctionRetTy, Expr, ExprKind, HirId, Local, Pat};
3 use crate::hir::intravisit::{self, Visitor, NestedVisitorMap};
4 use crate::infer::InferCtxt;
5 use crate::infer::type_variable::TypeVariableOriginKind;
6 use crate::ty::{self, Ty, Infer, TyVar};
7 use crate::ty::print::Print;
8 use syntax::source_map::DesugaringKind;
9 use syntax::symbol::kw;
11 use errors::{Applicability, DiagnosticBuilder};
13 use rustc_error_codes::*;
15 struct FindLocalByTypeVisitor<'a, 'tcx> {
16 infcx: &'a InferCtxt<'a, 'tcx>,
18 hir_map: &'a hir::map::Map<'tcx>,
19 found_local_pattern: Option<&'tcx Pat>,
20 found_arg_pattern: Option<&'tcx Pat>,
21 found_ty: Option<Ty<'tcx>>,
22 found_closure: Option<&'tcx ExprKind>,
23 found_method_call: Option<&'tcx Expr>,
26 impl<'a, 'tcx> FindLocalByTypeVisitor<'a, 'tcx> {
28 infcx: &'a InferCtxt<'a, 'tcx>,
30 hir_map: &'a hir::map::Map<'tcx>,
36 found_local_pattern: None,
37 found_arg_pattern: None,
40 found_method_call: None,
44 fn node_matches_type(&mut self, hir_id: HirId) -> Option<Ty<'tcx>> {
45 let ty_opt = self.infcx.in_progress_tables.and_then(|tables| {
46 tables.borrow().node_type_opt(hir_id)
50 let ty = self.infcx.resolve_vars_if_possible(&ty);
51 if ty.walk().any(|inner_ty| {
52 inner_ty == self.target_ty || match (&inner_ty.kind, &self.target_ty.kind) {
53 (&Infer(TyVar(a_vid)), &Infer(TyVar(b_vid))) => {
57 .sub_unified(a_vid, b_vid)
72 impl<'a, 'tcx> Visitor<'tcx> for FindLocalByTypeVisitor<'a, 'tcx> {
73 fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
74 NestedVisitorMap::OnlyBodies(&self.hir_map)
77 fn visit_local(&mut self, local: &'tcx Local) {
78 if let (None, Some(ty)) = (self.found_local_pattern, self.node_matches_type(local.hir_id)) {
79 self.found_local_pattern = Some(&*local.pat);
80 self.found_ty = Some(ty);
82 intravisit::walk_local(self, local);
85 fn visit_body(&mut self, body: &'tcx Body) {
86 for param in &body.params {
87 if let (None, Some(ty)) = (
88 self.found_arg_pattern,
89 self.node_matches_type(param.hir_id),
91 self.found_arg_pattern = Some(&*param.pat);
92 self.found_ty = Some(ty);
95 intravisit::walk_body(self, body);
98 fn visit_expr(&mut self, expr: &'tcx Expr) {
99 if self.node_matches_type(expr.hir_id).is_some() {
101 ExprKind::Closure(..) => self.found_closure = Some(&expr.kind),
102 ExprKind::MethodCall(..) => self.found_method_call = Some(&expr),
106 intravisit::walk_expr(self, expr);
110 /// Suggest giving an appropriate return type to a closure expression.
111 fn closure_return_type_suggestion(
113 err: &mut DiagnosticBuilder<'_>,
114 output: &FunctionRetTy,
119 let (arrow, post) = match output {
120 FunctionRetTy::DefaultReturn(_) => ("-> ", " "),
123 let suggestion = match body.value.kind {
124 ExprKind::Block(..) => {
125 vec![(output.span(), format!("{}{}{}", arrow, ret, post))]
129 (output.span(), format!("{}{}{}{{ ", arrow, ret, post)),
130 (body.value.span.shrink_to_hi(), " }".to_string()),
134 err.multipart_suggestion(
135 "give this closure an explicit return type without `_` placeholders",
137 Applicability::HasPlaceholders,
139 err.span_label(span, InferCtxt::missing_type_msg(&name));
142 /// Given a closure signature, return a `String` containing a list of all its argument types.
143 fn closure_args(fn_sig: &ty::PolyFnSig<'_>) -> String {
148 .map(|args| args.tuple_fields()
149 .map(|arg| arg.to_string())
150 .collect::<Vec<_>>().join(", "))
154 pub enum TypeAnnotationNeeded {
160 impl Into<errors::DiagnosticId> for TypeAnnotationNeeded {
161 fn into(self) -> errors::DiagnosticId {
162 syntax::diagnostic_used!(E0282);
163 syntax::diagnostic_used!(E0283);
164 syntax::diagnostic_used!(E0284);
165 errors::DiagnosticId::Error(match self {
166 Self::E0282 => "E0282".to_string(),
167 Self::E0283 => "E0283".to_string(),
168 Self::E0284 => "E0284".to_string(),
173 impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
174 pub fn extract_type_name(
177 highlight: Option<ty::print::RegionHighlightMode>,
178 ) -> (String, Option<Span>) {
179 if let ty::Infer(ty::TyVar(ty_vid)) = ty.kind {
180 let ty_vars = self.type_variables.borrow();
181 let var_origin = ty_vars.var_origin(ty_vid);
182 if let TypeVariableOriginKind::TypeParameterDefinition(name) = var_origin.kind {
183 if name != kw::SelfUpper {
184 return (name.to_string(), Some(var_origin.span));
189 let mut s = String::new();
190 let mut printer = ty::print::FmtPrinter::new(self.tcx, &mut s, Namespace::TypeNS);
191 if let Some(highlight) = highlight {
192 printer.region_highlight_mode = highlight;
194 let _ = ty.print(printer);
198 pub fn need_type_info_err(
200 body_id: Option<hir::BodyId>,
203 error_code: TypeAnnotationNeeded,
204 ) -> DiagnosticBuilder<'tcx> {
205 let ty = self.resolve_vars_if_possible(&ty);
206 let (name, name_sp) = self.extract_type_name(&ty, None);
208 let mut local_visitor = FindLocalByTypeVisitor::new(&self, ty, &self.tcx.hir());
209 let ty_to_string = |ty: Ty<'tcx>| -> String {
210 let mut s = String::new();
211 let mut printer = ty::print::FmtPrinter::new(self.tcx, &mut s, Namespace::TypeNS);
212 let ty_vars = self.type_variables.borrow();
213 let getter = move |ty_vid| {
214 let var_origin = ty_vars.var_origin(ty_vid);
215 if let TypeVariableOriginKind::TypeParameterDefinition(name) = var_origin.kind {
216 return Some(name.to_string());
220 printer.name_resolver = Some(Box::new(&getter));
221 let _ = ty.print(printer);
225 if let Some(body_id) = body_id {
226 let expr = self.tcx.hir().expect_expr(body_id.hir_id);
227 local_visitor.visit_expr(expr);
229 let err_span = if let Some(pattern) = local_visitor.found_arg_pattern {
231 } else if let Some(span) = name_sp {
232 // `span` here lets us point at `sum` instead of the entire right hand side expr:
233 // error[E0282]: type annotations needed
236 // 3 | let _ = x.sum() as f64;
237 // | ^^^ cannot infer type for `S`
240 ExprKind::MethodCall(_, call_span, _),
241 ) = local_visitor.found_method_call.map(|e| &e.kind) {
242 // Point at the call instead of the whole expression:
243 // error[E0284]: type annotations needed
246 // 2 | vec![Ok(2)].into_iter().collect()?;
247 // | ^^^^^^^ cannot infer type
249 // = note: cannot resolve `<_ as std::ops::Try>::Ok == _`
250 if span.contains(*call_span) {
259 let is_named_and_not_impl_trait = |ty: Ty<'_>| {
260 &ty.to_string() != "_" &&
261 // FIXME: Remove this check after `impl_trait_in_bindings` is stabilized. #63527
262 (!ty.is_impl_trait() || self.tcx.features().impl_trait_in_bindings)
265 let ty_msg = match local_visitor.found_ty {
266 Some(ty::TyS { kind: ty::Closure(def_id, substs), .. }) => {
267 let fn_sig = substs.as_closure().sig(*def_id, self.tcx);
268 let args = closure_args(&fn_sig);
269 let ret = fn_sig.output().skip_binder().to_string();
270 format!(" for the closure `fn({}) -> {}`", args, ret)
272 Some(ty) if is_named_and_not_impl_trait(ty) => {
273 let ty = ty_to_string(ty);
274 format!(" for `{}`", ty)
279 // When `name` corresponds to a type argument, show the path of the full type we're
280 // trying to infer. In the following example, `ty_msg` contains
281 // " in `std::result::Result<i32, E>`":
283 // error[E0282]: type annotations needed for `std::result::Result<i32, E>`
286 // L | let b = Ok(4);
287 // | - ^^ cannot infer type for `E` in `std::result::Result<i32, E>`
289 // | consider giving `b` the explicit type `std::result::Result<i32, E>`, where
290 // | the type parameter `E` is specified
292 let error_code = error_code.into();
293 let mut err = self.tcx.sess.struct_span_err_with_code(
295 &format!("type annotations needed{}", ty_msg),
299 let suffix = match local_visitor.found_ty {
300 Some(ty::TyS { kind: ty::Closure(def_id, substs), .. }) => {
301 let fn_sig = substs.as_closure().sig(*def_id, self.tcx);
302 let ret = fn_sig.output().skip_binder().to_string();
304 if let Some(ExprKind::Closure(_, decl, body_id, ..)) = local_visitor.found_closure {
305 if let Some(body) = self.tcx.hir().krate().bodies.get(body_id) {
306 closure_return_type_suggestion(
314 // We don't want to give the other suggestions when the problem is the
315 // closure return type.
320 // This shouldn't be reachable, but just in case we leave a reasonable fallback.
321 let args = closure_args(&fn_sig);
322 // This suggestion is incomplete, as the user will get further type inference
323 // errors due to the `_` placeholders and the introduction of `Box`, but it does
324 // nudge them in the right direction.
325 format!("a boxed closure type like `Box<dyn Fn({}) -> {}>`", args, ret)
327 Some(ty) if is_named_and_not_impl_trait(ty) && name == "_" => {
328 let ty = ty_to_string(ty);
329 format!("the explicit type `{}`, with the type parameters specified", ty)
331 Some(ty) if is_named_and_not_impl_trait(ty) && ty.to_string() != name => {
332 let ty = ty_to_string(ty);
334 "the explicit type `{}`, where the type parameter `{}` is specified",
339 _ => "a type".to_string(),
342 if let Some(pattern) = local_visitor.found_arg_pattern {
343 // We don't want to show the default label for closures.
345 // So, before clearing, the output would look something like this:
348 // - ^^^^ cannot infer type for `[_; 0]`
350 // consider giving this closure parameter a type
353 // After clearing, it looks something like this:
356 // ^ consider giving this closure parameter the type `[_; 0]`
357 // with the type parameter `_` specified
361 format!("consider giving this closure parameter {}", suffix),
363 } else if let Some(pattern) = local_visitor.found_local_pattern {
364 let msg = if let Some(simple_ident) = pattern.simple_ident() {
365 match pattern.span.desugaring_kind() {
367 format!("consider giving `{}` {}", simple_ident, suffix)
369 Some(DesugaringKind::ForLoop) => {
370 "the element type for this iterator is not specified".to_string()
372 _ => format!("this needs {}", suffix),
375 format!("consider giving this pattern {}", suffix)
377 err.span_label(pattern.span, msg);
378 } else if let Some(e) = local_visitor.found_method_call {
379 if let ExprKind::MethodCall(segment, _call_sp, _args) = &e.kind {
380 if let (Ok(snippet), Some(tables), None) = (
381 self.tcx.sess.source_map().span_to_snippet(segment.ident.span),
382 self.in_progress_tables,
385 let borrow = tables.borrow();
386 let sigs = borrow.node_method_sig();
387 if let Some(sig) = sigs.get(e.hir_id) {
388 let mut params = vec![];
389 for arg in sig.inputs_and_output().skip_binder().iter() {
390 if let ty::Param(param) = arg.kind {
391 if param.name != kw::SelfUpper {
392 let name = param.name.to_string();
393 if !params.contains(&name) {
399 if !params.is_empty() {
403 "consider specifying the type argument{} in the method call",
404 if params.len() > 1 {
410 format!("{}::<{}>", snippet, params.join(", ")),
411 Applicability::HasPlaceholders,
414 err.span_label(e.span, &format!(
415 "this method call resolves to `{:?}`",
416 sig.output().skip_binder(),
423 // Instead of the following:
424 // error[E0282]: type annotations needed
427 // 3 | let _ = x.sum() as f64;
428 // | --^^^--------- cannot infer type for `S`
430 // = note: type must be known at this point
432 // error[E0282]: type annotations needed
435 // 3 | let _ = x.sum() as f64;
436 // | ^^^ cannot infer type for `S`
438 // = note: type must be known at this point
439 let span = name_sp.unwrap_or(err_span);
440 if !err.span.span_labels().iter().any(|span_label| {
441 span_label.label.is_some() && span_label.span == span
442 }) && local_visitor.found_arg_pattern.is_none()
443 { // Avoid multiple labels pointing at `span`.
444 err.span_label(span, InferCtxt::missing_type_msg(&name));
450 pub fn need_type_info_err_in_generator(
452 kind: hir::GeneratorKind,
455 ) -> DiagnosticBuilder<'tcx> {
456 let ty = self.resolve_vars_if_possible(&ty);
457 let name = self.extract_type_name(&ty, None).0;
458 let mut err = struct_span_err!(
459 self.tcx.sess, span, E0698, "type inside {} must be known in this context", kind,
461 err.span_label(span, InferCtxt::missing_type_msg(&name));
465 fn missing_type_msg(type_name: &str) -> String {
466 if type_name == "_" {
467 "cannot infer type".to_owned()
469 format!("cannot infer type for `{}`", type_name)