1 use crate::hir::{self, Local, Pat, Body, HirId};
2 use crate::hir::intravisit::{self, Visitor, NestedVisitorMap};
3 use crate::infer::InferCtxt;
4 use crate::infer::type_variable::TypeVariableOrigin;
5 use crate::ty::{self, Ty, Infer, TyVar};
7 use syntax::source_map::CompilerDesugaringKind;
9 use errors::DiagnosticBuilder;
11 struct FindLocalByTypeVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
12 infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
13 target_ty: &'a Ty<'tcx>,
14 hir_map: &'a hir::map::Map<'gcx>,
15 found_local_pattern: Option<&'gcx Pat>,
16 found_arg_pattern: Option<&'gcx Pat>,
19 impl<'a, 'gcx, 'tcx> FindLocalByTypeVisitor<'a, 'gcx, 'tcx> {
20 fn node_matches_type(&mut self, hir_id: HirId) -> bool {
21 let ty_opt = self.infcx.in_progress_tables.and_then(|tables| {
22 tables.borrow().node_type_opt(hir_id)
26 let ty = self.infcx.resolve_type_vars_if_possible(&ty);
27 ty.walk().any(|inner_ty| {
28 inner_ty == *self.target_ty || match (&inner_ty.sty, &self.target_ty.sty) {
29 (&Infer(TyVar(a_vid)), &Infer(TyVar(b_vid))) => {
33 .sub_unified(a_vid, b_vid)
44 impl<'a, 'gcx, 'tcx> Visitor<'gcx> for FindLocalByTypeVisitor<'a, 'gcx, 'tcx> {
45 fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'gcx> {
46 NestedVisitorMap::OnlyBodies(&self.hir_map)
49 fn visit_local(&mut self, local: &'gcx Local) {
50 if self.found_local_pattern.is_none() && self.node_matches_type(local.hir_id) {
51 self.found_local_pattern = Some(&*local.pat);
53 intravisit::walk_local(self, local);
56 fn visit_body(&mut self, body: &'gcx Body) {
57 for argument in &body.arguments {
58 if self.found_arg_pattern.is_none() && self.node_matches_type(argument.hir_id) {
59 self.found_arg_pattern = Some(&*argument.pat);
62 intravisit::walk_body(self, body);
67 impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
68 pub fn extract_type_name(
71 highlight: Option<ty::print::RegionHighlightMode>,
73 if let ty::Infer(ty::TyVar(ty_vid)) = (*ty).sty {
74 let ty_vars = self.type_variables.borrow();
75 if let TypeVariableOrigin::TypeParameterDefinition(_, name) =
76 *ty_vars.var_origin(ty_vid) {
77 return name.to_string();
81 let mut s = String::new();
82 let mut printer = ty::print::FmtPrinter::new(&mut s);
83 if let Some(highlight) = highlight {
84 printer.region_highlight_mode = highlight;
86 let _ = ty::print::PrintCx::with(self.tcx, printer, |cx| {
92 pub fn need_type_info_err(&self,
93 body_id: Option<hir::BodyId>,
96 -> DiagnosticBuilder<'gcx> {
97 let ty = self.resolve_type_vars_if_possible(&ty);
98 let name = self.extract_type_name(&ty, None);
100 let mut err_span = span;
101 let mut labels = vec![(
104 "cannot infer type".to_owned()
106 format!("cannot infer type for `{}`", name)
110 let mut local_visitor = FindLocalByTypeVisitor {
113 hir_map: &self.tcx.hir(),
114 found_local_pattern: None,
115 found_arg_pattern: None,
118 if let Some(body_id) = body_id {
119 let expr = self.tcx.hir().expect_expr_by_hir_id(body_id.hir_id);
120 local_visitor.visit_expr(expr);
123 if let Some(pattern) = local_visitor.found_arg_pattern {
124 err_span = pattern.span;
125 // We don't want to show the default label for closures.
127 // So, before clearing, the output would look something like this:
130 // - ^^^^ cannot infer type for `[_; 0]`
132 // consider giving this closure parameter a type
135 // After clearing, it looks something like this:
138 // ^ consider giving this closure parameter a type
142 (pattern.span, "consider giving this closure parameter a type".to_owned()));
143 } else if let Some(pattern) = local_visitor.found_local_pattern {
144 if let Some(simple_ident) = pattern.simple_ident() {
145 match pattern.span.compiler_desugaring_kind() {
146 None => labels.push((pattern.span,
147 format!("consider giving `{}` a type", simple_ident))),
148 Some(CompilerDesugaringKind::ForLoop) => labels.push((
150 "the element type for this iterator is not specified".to_owned(),
155 labels.push((pattern.span, "consider giving the pattern a type".to_owned()));
159 let mut err = struct_span_err!(self.tcx.sess,
162 "type annotations needed");
164 for (target_span, label_message) in labels {
165 err.span_label(target_span, label_message);