-
Notifications
You must be signed in to change notification settings - Fork 14.1k
Better error for missing tuple pattern in args #45069
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 3 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 |
|---|---|---|
|
|
@@ -711,41 +711,105 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { | |
| } | ||
| } | ||
|
|
||
| OutputTypeParameterMismatch(ref expected_trait_ref, ref actual_trait_ref, _) => { | ||
| OutputTypeParameterMismatch(ref found_trait_ref, ref expected_trait_ref, _) => { | ||
| let found_trait_ref = self.resolve_type_vars_if_possible(&*found_trait_ref); | ||
| let expected_trait_ref = self.resolve_type_vars_if_possible(&*expected_trait_ref); | ||
| let actual_trait_ref = self.resolve_type_vars_if_possible(&*actual_trait_ref); | ||
| if actual_trait_ref.self_ty().references_error() { | ||
| if expected_trait_ref.self_ty().references_error() { | ||
| return; | ||
| } | ||
| let expected_trait_ty = expected_trait_ref.self_ty(); | ||
| let found_span = expected_trait_ty.ty_to_def_id().and_then(|did| { | ||
| let found_trait_ty = found_trait_ref.self_ty(); | ||
|
|
||
| let found_did = found_trait_ty.ty_to_def_id(); | ||
| let found_span = found_did.and_then(|did| { | ||
| self.tcx.hir.span_if_local(did) | ||
| }); | ||
|
|
||
| let self_ty_count = | ||
| match expected_trait_ref.skip_binder().substs.type_at(1).sty { | ||
| let found_ty_count = | ||
| match found_trait_ref.skip_binder().substs.type_at(1).sty { | ||
| ty::TyTuple(ref tys, _) => tys.len(), | ||
| _ => 1, | ||
| }; | ||
| let arg_ty_count = | ||
| match actual_trait_ref.skip_binder().substs.type_at(1).sty { | ||
| ty::TyTuple(ref tys, _) => tys.len(), | ||
| _ => 1, | ||
| let (expected_tys, expected_ty_count) = | ||
| match expected_trait_ref.skip_binder().substs.type_at(1).sty { | ||
| ty::TyTuple(ref tys, _) => | ||
| (tys.iter().map(|t| &t.sty).collect(), tys.len()), | ||
| ref sty => (vec![sty], 1), | ||
| }; | ||
| if self_ty_count == arg_ty_count { | ||
| if found_ty_count == expected_ty_count { | ||
| self.report_closure_arg_mismatch(span, | ||
| found_span, | ||
| expected_trait_ref, | ||
| actual_trait_ref) | ||
| found_trait_ref, | ||
| expected_trait_ref) | ||
| } else { | ||
| // Expected `|| { }`, found `|x, y| { }` | ||
| // Expected `fn(x) -> ()`, found `|| { }` | ||
| let expected_tuple = if expected_ty_count == 1 { | ||
| expected_tys.first().and_then(|t| { | ||
| if let &&ty::TyTuple(ref tuptys, _) = t { | ||
| Some(tuptys.len()) | ||
| } else { | ||
| None | ||
| } | ||
| }) | ||
| } else { | ||
| None | ||
| }; | ||
|
|
||
| // FIXME(#44150): Expand this to "N args expected but a N-tuple found." | ||
| // Type of the 1st expected argument is somehow provided as type of a | ||
| // found one in that case. | ||
| // | ||
| // ``` | ||
| // [1i32, 2, 3].sort_by(|(a, b)| ..) | ||
| // // ^^^^^^^^ | ||
| // // expected_trait_ref: std::ops::FnMut<(&i32, &i32)> | ||
| // // found_trait_ref: std::ops::FnMut<(&i32,)> | ||
| // ``` | ||
|
|
||
| let (closure_span, closure_args) = found_did | ||
| .and_then(|did| self.tcx.hir.get_if_local(did)) | ||
| .and_then(|node| { | ||
| if let hir::map::NodeExpr( | ||
| &hir::Expr { | ||
| node: hir::ExprClosure(_, ref decl, id, span, _), | ||
| .. | ||
| }) = node | ||
| { | ||
| let ty_snips = decl.inputs.iter() | ||
| .map(|ty| { | ||
| self.tcx.sess.codemap().span_to_snippet(ty.span).ok() | ||
| .and_then(|snip| { | ||
| // filter out dummy spans | ||
| if snip == "," || snip == "|" { | ||
| None | ||
| } else { | ||
| Some(snip) | ||
| } | ||
| }) | ||
| }) | ||
| .collect::<Vec<Option<String>>>(); | ||
|
|
||
| let body = self.tcx.hir.body(id); | ||
| let pat_snips = body.arguments.iter() | ||
| .map(|arg| | ||
| self.tcx.sess.codemap().span_to_snippet(arg.pat.span).ok()) | ||
| .collect::<Option<Vec<String>>>(); | ||
|
|
||
| Some((span, pat_snips, ty_snips)) | ||
| } else { | ||
| None | ||
| } | ||
| }) | ||
| .map(|(span, pat, ty)| (Some(span), Some((pat, ty)))) | ||
| .unwrap_or((None, None)); | ||
| let closure_args = closure_args.and_then(|(pat, ty)| Some((pat?, ty))); | ||
|
|
||
| self.report_arg_count_mismatch( | ||
| span, | ||
| found_span, | ||
| arg_ty_count, | ||
| self_ty_count, | ||
| expected_trait_ty.is_closure() | ||
| closure_span.or(found_span), | ||
| expected_ty_count, | ||
| expected_tuple, | ||
| found_ty_count, | ||
| closure_args, | ||
| found_trait_ty.is_closure() | ||
| ) | ||
| } | ||
| } | ||
|
|
@@ -767,32 +831,87 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { | |
| err.emit(); | ||
| } | ||
|
|
||
| fn report_arg_count_mismatch(&self, | ||
| span: Span, | ||
| found_span: Option<Span>, | ||
| expected: usize, | ||
| found: usize, | ||
| is_closure: bool) | ||
| -> DiagnosticBuilder<'tcx> | ||
| { | ||
| fn report_arg_count_mismatch( | ||
| &self, | ||
| span: Span, | ||
| found_span: Option<Span>, | ||
| expected: usize, | ||
| expected_tuple: Option<usize>, | ||
| found: usize, | ||
| closure_args: Option<(Vec<String>, Vec<Option<String>>)>, | ||
| is_closure: bool | ||
| ) -> DiagnosticBuilder<'tcx> { | ||
| use std::borrow::Cow; | ||
|
|
||
| let kind = if is_closure { "closure" } else { "function" }; | ||
|
|
||
| let args_str = |n| format!( | ||
| "{} argument{}", | ||
| n, | ||
| if n == 1 { "" } else { "s" } | ||
| ); | ||
|
|
||
| let mut err = struct_span_err!(self.tcx.sess, span, E0593, | ||
| "{} takes {} argument{} but {} argument{} {} required", | ||
| if is_closure { "closure" } else { "function" }, | ||
| found, | ||
| if found == 1 { "" } else { "s" }, | ||
| expected, | ||
| if expected == 1 { "" } else { "s" }, | ||
| "{} takes {}, but {} {} required", | ||
| kind, | ||
| if expected_tuple.is_some() { | ||
| Cow::from("multiple arguments") | ||
| } else { | ||
| Cow::from(args_str(found)) | ||
| }, | ||
| if expected_tuple.is_some() { | ||
| Cow::from("a tuple argument") | ||
|
||
| } else { | ||
| Cow::from(args_str(expected)) | ||
| }, | ||
| if expected == 1 { "is" } else { "are" }); | ||
|
|
||
| err.span_label(span, format!("expected {} that takes {} argument{}", | ||
| if is_closure { "closure" } else { "function" }, | ||
| expected, | ||
| if expected == 1 { "" } else { "s" })); | ||
| err.span_label( | ||
| span, | ||
| format!( | ||
| "expected {} that takes {}{}", | ||
| kind, | ||
| args_str(expected), | ||
| if let Some(n) = expected_tuple { | ||
| assert!(expected == 1); | ||
| Cow::from(format!(", a {}-tuple", n)) | ||
| } else { | ||
| Cow::from("") | ||
| } | ||
| ) | ||
| ); | ||
|
|
||
| if let Some(span) = found_span { | ||
| err.span_label(span, format!("takes {} argument{}", | ||
| found, | ||
| if found == 1 { "" } else { "s" })); | ||
| if let (Some(expected_tuple), Some((pats, tys))) = (expected_tuple, closure_args) { | ||
| if expected_tuple != found || pats.len() != found { | ||
| err.span_label(span, format!("takes {}", args_str(found))); | ||
| } else { | ||
| let sugg = format!( | ||
| "|({}){}|", | ||
| pats.join(", "), | ||
|
|
||
| // add type annotations if available | ||
| if tys.iter().any(|ty| ty.is_some()) { | ||
| Cow::from(format!( | ||
| ": ({})", | ||
| tys.into_iter().map(|ty| if let Some(ty) = ty { | ||
| ty | ||
| } else { | ||
| "_".to_string() | ||
| }).collect::<Vec<String>>().join(", ") | ||
| )) | ||
| } else { | ||
| Cow::from("") | ||
| }, | ||
| ); | ||
|
|
||
| err.span_suggestion(span, "consider changing to", sugg); | ||
| } | ||
| } else { | ||
| err.span_label(span, format!("takes {}", args_str(found))); | ||
| } | ||
| } | ||
|
|
||
| err | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,16 +1,16 @@ | ||
| error[E0593]: closure takes 0 arguments but 2 arguments are required | ||
| error[E0593]: closure takes 0 arguments, but 2 arguments are required | ||
| --> $DIR/closure-arg-count.rs:15:15 | ||
| | | ||
| 15 | [1, 2, 3].sort_by(|| panic!()); | ||
| | ^^^^^^^ ----------- takes 0 arguments | ||
| | ^^^^^^^ -- takes 0 arguments | ||
| | | | ||
| | expected closure that takes 2 arguments | ||
|
|
||
| error[E0593]: closure takes 1 argument but 2 arguments are required | ||
| error[E0593]: closure takes 1 argument, but 2 arguments are required | ||
| --> $DIR/closure-arg-count.rs:16:15 | ||
| | | ||
| 16 | [1, 2, 3].sort_by(|tuple| panic!()); | ||
| | ^^^^^^^ ---------------- takes 1 argument | ||
| | ^^^^^^^ ------- takes 1 argument | ||
| | | | ||
| | expected closure that takes 2 arguments | ||
|
|
||
|
|
@@ -23,23 +23,47 @@ error[E0308]: mismatched types | |
| = note: expected type `&{integer}` | ||
| found type `(_, _)` | ||
|
|
||
| error[E0593]: closure takes 1 argument but 2 arguments are required | ||
| error[E0593]: closure takes 1 argument, but 2 arguments are required | ||
| --> $DIR/closure-arg-count.rs:17:15 | ||
| | | ||
| 17 | [1, 2, 3].sort_by(|(tuple, tuple2)| panic!()); | ||
| | ^^^^^^^ -------------------------- takes 1 argument | ||
| | ^^^^^^^ ----------------- takes 1 argument | ||
| | | | ||
| | expected closure that takes 2 arguments | ||
|
|
||
| error[E0593]: closure takes 0 arguments but 1 argument is required | ||
| error[E0593]: closure takes 0 arguments, but 1 argument is required | ||
| --> $DIR/closure-arg-count.rs:18:5 | ||
| | | ||
| 18 | f(|| panic!()); | ||
| | ^ ----------- takes 0 arguments | ||
| | ^ -- takes 0 arguments | ||
| | | | ||
| | expected closure that takes 1 argument | ||
| | | ||
| = note: required by `f` | ||
|
|
||
| error: aborting due to 5 previous errors | ||
| error[E0593]: closure takes multiple arguments, but a tuple argument is required | ||
| --> $DIR/closure-arg-count.rs:20:53 | ||
| | | ||
| 20 | let _it = vec![1, 2, 3].into_iter().enumerate().map(|i, x| i); | ||
| | ^^^ ------ help: consider changing to: `|(i, x)|` | ||
|
||
| | | | ||
| | expected closure that takes 1 argument, a 2-tuple | ||
|
||
|
|
||
| error[E0593]: closure takes multiple arguments, but a tuple argument is required | ||
| --> $DIR/closure-arg-count.rs:21:53 | ||
| | | ||
| 21 | let _it = vec![1, 2, 3].into_iter().enumerate().map(|i: usize, x| i); | ||
| | ^^^ ------------- help: consider changing to: `|(i, x): (usize, _)|` | ||
|
||
| | | | ||
| | expected closure that takes 1 argument, a 2-tuple | ||
|
|
||
| error[E0593]: closure takes multiple arguments, but a tuple argument is required | ||
| --> $DIR/closure-arg-count.rs:22:53 | ||
| | | ||
| 22 | let _it = vec![1, 2, 3].into_iter().enumerate().map(|i, x, y| i); | ||
| | ^^^ --------- takes 3 arguments | ||
| | | | ||
| | expected closure that takes 1 argument, a 2-tuple | ||
|
|
||
| error: aborting due to 8 previous errors | ||
|
|
||
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 does this say "FIXME"?
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.
This PR improves message for "expected |(x, y)|, found |x, y|" case but not "expected |x, y|, found |(x, y)|" case. In the latter case,
found_trait_refshould beFnMut<((_, _),)>, but it becomesFnMut<(_,)>and I don't know how to fix.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.
@nikomatsakis, I believe @sinkuu is referring to the following case:
We should create an issue after this PR lands to track that case.