-
-
Notifications
You must be signed in to change notification settings - Fork 14.2k
Move E0101 and E0102 logic into new E0282 mechanism #40013 #41236
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -25,16 +25,16 @@ use super::{ | |
|
|
||
| use errors::DiagnosticBuilder; | ||
| use fmt_macros::{Parser, Piece, Position}; | ||
| use hir::{intravisit, Local, Pat}; | ||
| use hir::{self, intravisit, Local, Pat, Body}; | ||
| use hir::intravisit::{Visitor, NestedVisitorMap}; | ||
| use hir::map::NodeExpr; | ||
| use hir::def_id::DefId; | ||
| use infer::{self, InferCtxt}; | ||
| use infer::type_variable::TypeVariableOrigin; | ||
| use rustc::lint::builtin::EXTRA_REQUIREMENT_IN_IMPL; | ||
| use std::fmt; | ||
| use syntax::ast; | ||
| use ty::{self, AdtKind, ToPredicate, ToPolyTraitRef, Ty, TyCtxt, TypeFoldable}; | ||
| use syntax::ast::{self, NodeId}; | ||
| use ty::{self, AdtKind, ToPredicate, ToPolyTraitRef, Ty, TyCtxt, TypeFoldable, TyInfer, TyVar}; | ||
| use ty::error::ExpectedFound; | ||
| use ty::fast_reject; | ||
| use ty::fold::TypeFolder; | ||
|
|
@@ -66,37 +66,52 @@ impl<'a, 'gcx, 'tcx> TraitErrorKey<'tcx> { | |
| struct FindLocalByTypeVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { | ||
| infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, | ||
| target_ty: &'a Ty<'tcx>, | ||
| found_pattern: Option<&'a Pat>, | ||
| hir_map: &'a hir::map::Map<'gcx>, | ||
| found_local_pattern: Option<&'gcx Pat>, | ||
| found_arg_pattern: Option<&'gcx Pat>, | ||
| } | ||
|
|
||
| impl<'a, 'gcx, 'tcx> FindLocalByTypeVisitor<'a, 'gcx, 'tcx> { | ||
| fn is_match(&self, ty: Ty<'tcx>) -> bool { | ||
| ty == *self.target_ty || match (&ty.sty, &self.target_ty.sty) { | ||
| (&ty::TyInfer(ty::TyVar(a_vid)), &ty::TyInfer(ty::TyVar(b_vid))) => | ||
| self.infcx.type_variables | ||
| .borrow_mut() | ||
| .sub_unified(a_vid, b_vid), | ||
|
|
||
| fn node_matches_type(&mut self, node_id: &'gcx NodeId) -> bool { | ||
| match self.infcx.tables.borrow().node_types.get(node_id) { | ||
| Some(&ty) => { | ||
| let ty = self.infcx.resolve_type_vars_if_possible(&ty); | ||
| ty.walk().any(|inner_ty| { | ||
| inner_ty == *self.target_ty || match (&inner_ty.sty, &self.target_ty.sty) { | ||
| (&TyInfer(TyVar(a_vid)), &TyInfer(TyVar(b_vid))) => { | ||
| self.infcx | ||
| .type_variables | ||
| .borrow_mut() | ||
| .sub_unified(a_vid, b_vid) | ||
| } | ||
| _ => false, | ||
| } | ||
| }) | ||
| } | ||
| _ => false, | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl<'a, 'gcx, 'tcx> Visitor<'a> for FindLocalByTypeVisitor<'a, 'gcx, 'tcx> { | ||
| fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'a> { | ||
| NestedVisitorMap::None | ||
| impl<'a, 'gcx, 'tcx> Visitor<'gcx> for FindLocalByTypeVisitor<'a, 'gcx, 'tcx> { | ||
| fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'gcx> { | ||
| NestedVisitorMap::OnlyBodies(&self.hir_map) | ||
| } | ||
|
|
||
| fn visit_local(&mut self, local: &'a Local) { | ||
| if let Some(&ty) = self.infcx.tables.borrow().node_types.get(&local.id) { | ||
| let ty = self.infcx.resolve_type_vars_if_possible(&ty); | ||
| let is_match = ty.walk().any(|t| self.is_match(t)); | ||
| fn visit_local(&mut self, local: &'gcx Local) { | ||
| if self.found_local_pattern.is_none() && self.node_matches_type(&local.id) { | ||
| self.found_local_pattern = Some(&*local.pat); | ||
| } | ||
| intravisit::walk_local(self, local); | ||
| } | ||
|
|
||
| if is_match && self.found_pattern.is_none() { | ||
| self.found_pattern = Some(&*local.pat); | ||
| fn visit_body(&mut self, body: &'gcx Body) { | ||
| for argument in &body.arguments { | ||
| if self.found_arg_pattern.is_none() && self.node_matches_type(&argument.id) { | ||
| self.found_arg_pattern = Some(&*argument.pat); | ||
| } | ||
| } | ||
| intravisit::walk_local(self, local); | ||
| intravisit::walk_body(self, body); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -721,6 +736,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { | |
| // coherence violation, so we don't report it here. | ||
|
|
||
| let predicate = self.resolve_type_vars_if_possible(&obligation.predicate); | ||
| let body_id = hir::BodyId { node_id: obligation.cause.body_id }; | ||
| let span = obligation.cause.span; | ||
|
|
||
| debug!("maybe_report_ambiguity(predicate={:?}, obligation={:?})", | ||
| predicate, | ||
|
|
@@ -768,10 +785,10 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { | |
| self.tcx.lang_items.sized_trait() | ||
| .map_or(false, |sized_id| sized_id == trait_ref.def_id()) | ||
| { | ||
| self.need_type_info(obligation, self_ty); | ||
| self.need_type_info(body_id, span, self_ty); | ||
| } else { | ||
| let mut err = struct_span_err!(self.tcx.sess, | ||
| obligation.cause.span, E0283, | ||
| span, E0283, | ||
| "type annotations required: \ | ||
| cannot resolve `{}`", | ||
| predicate); | ||
|
|
@@ -785,7 +802,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { | |
| // Same hacky approach as above to avoid deluging user | ||
| // with error messages. | ||
| if !ty.references_error() && !self.tcx.sess.has_errors() { | ||
| self.need_type_info(obligation, ty); | ||
| self.need_type_info(body_id, span, ty); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -796,7 +813,9 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { | |
| let &SubtypePredicate { a_is_expected: _, a, b } = data.skip_binder(); | ||
| // both must be type variables, or the other would've been instantiated | ||
| assert!(a.is_ty_var() && b.is_ty_var()); | ||
| self.need_type_info(obligation, a); | ||
| self.need_type_info(hir::BodyId { node_id: obligation.cause.body_id }, | ||
| obligation.cause.span, | ||
| a); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -874,42 +893,66 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { | |
| } | ||
| } | ||
|
|
||
| fn need_type_info(&self, obligation: &PredicateObligation<'tcx>, ty: Ty<'tcx>) { | ||
| pub fn need_type_info(&self, body_id: hir::BodyId, span: Span, ty: Ty<'tcx>) { | ||
|
||
| let ty = self.resolve_type_vars_if_possible(&ty); | ||
| let name = self.extract_type_name(&ty); | ||
| let ref cause = obligation.cause; | ||
|
|
||
| let mut err = struct_span_err!(self.tcx.sess, | ||
| cause.span, | ||
| E0282, | ||
| "type annotations needed"); | ||
|
|
||
| err.span_label(cause.span, &format!("cannot infer type for `{}`", name)); | ||
| let mut err_span = span; | ||
| let mut labels = vec![(span, format!("cannot infer type for `{}`", name))]; | ||
|
||
|
|
||
| let mut local_visitor = FindLocalByTypeVisitor { | ||
| infcx: &self, | ||
| target_ty: &ty, | ||
| found_pattern: None, | ||
| hir_map: &self.tcx.hir, | ||
| found_local_pattern: None, | ||
| found_arg_pattern: None, | ||
| }; | ||
|
|
||
| // #40294: cause.body_id can also be a fn declaration. | ||
| // Currently, if it's anything other than NodeExpr, we just ignore it | ||
| match self.tcx.hir.find(cause.body_id) { | ||
| match self.tcx.hir.find(body_id.node_id) { | ||
| Some(NodeExpr(expr)) => local_visitor.visit_expr(expr), | ||
| _ => () | ||
| } | ||
|
|
||
| if let Some(pattern) = local_visitor.found_pattern { | ||
| let pattern_span = pattern.span; | ||
| if let Some(pattern) = local_visitor.found_arg_pattern { | ||
| err_span = pattern.span; | ||
| // We don't want to show the default label for closures. | ||
| // | ||
| // So, before clearing, the output would look something like this: | ||
| // ``` | ||
| // let x = |_| { }; | ||
| // - ^^^^ cannot infer type for `[_; 0]` | ||
| // | | ||
| // consider giving this closure parameter a type | ||
| // ``` | ||
| // | ||
| // After clearing, it looks something like this: | ||
| // ``` | ||
| // let x = |_| { }; | ||
| // ^ consider giving this closure parameter a type | ||
| // ``` | ||
| labels.clear(); | ||
| labels.push((pattern.span, format!("consider giving this closure parameter a type"))); | ||
| } | ||
|
|
||
| if let Some(pattern) = local_visitor.found_local_pattern { | ||
| if let Some(simple_name) = pattern.simple_name() { | ||
| err.span_label(pattern_span, | ||
| &format!("consider giving `{}` a type", | ||
| simple_name)); | ||
| labels.push((pattern.span, format!("consider giving `{}` a type", simple_name))); | ||
| } else { | ||
| err.span_label(pattern_span, &format!("consider giving a type to pattern")); | ||
| labels.push((pattern.span, format!("consider giving the pattern a type"))); | ||
| } | ||
| } | ||
|
|
||
| let mut err = struct_span_err!(self.tcx.sess, | ||
| err_span, | ||
| E0282, | ||
| "type annotations needed"); | ||
|
|
||
| for (target_span, label_message) in labels { | ||
| err.span_label(target_span, &label_message); | ||
| } | ||
|
|
||
| err.emit(); | ||
| } | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you have separate options for
found_local_patternandfound_arg_pattern? I'll have a singlefound_localenum.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They're not only holding target patterns, but also changing the flow of the result.
Can you share your suggestion with a concrete example please?
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And find the maximum relevant
TypeVariableHolder.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't seem like it makes much difference either way to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually it's mainly something I want to build on.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense. I would be fine landing as is, but also fine with adopting the single enum approach -- I imagine as we add more cases, we'll probably want a single enum, as you suggest. (Though I was wondering if it may be the case that we can't detect until the end whether certain patterns are met, and hence we wouldn't be able to decide the final state until the end.) Either way, easy to evolve as needed.