Move param count error emission to near end of check_argument_types
This commit is contained in:
parent
762bdbfce7
commit
ce31f68f9e
3 changed files with 140 additions and 130 deletions
|
@ -127,136 +127,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
|
||||
let expected_arg_count = formal_input_tys.len();
|
||||
|
||||
let param_count_error = |expected_count: usize,
|
||||
arg_count: usize,
|
||||
error_code: &str,
|
||||
c_variadic: bool,
|
||||
sugg_unit: bool| {
|
||||
let (span, start_span, args, ctor_of) = match &call_expr.kind {
|
||||
hir::ExprKind::Call(
|
||||
hir::Expr {
|
||||
span,
|
||||
kind:
|
||||
hir::ExprKind::Path(hir::QPath::Resolved(
|
||||
_,
|
||||
hir::Path { res: Res::Def(DefKind::Ctor(of, _), _), .. },
|
||||
)),
|
||||
..
|
||||
},
|
||||
args,
|
||||
) => (*span, *span, &args[..], Some(of)),
|
||||
hir::ExprKind::Call(hir::Expr { span, .. }, args) => {
|
||||
(*span, *span, &args[..], None)
|
||||
}
|
||||
hir::ExprKind::MethodCall(path_segment, args, _) => (
|
||||
path_segment.ident.span,
|
||||
// `sp` doesn't point at the whole `foo.bar()`, only at `bar`.
|
||||
path_segment
|
||||
.args
|
||||
.and_then(|args| args.args.iter().last())
|
||||
// Account for `foo.bar::<T>()`.
|
||||
.map(|arg| {
|
||||
// Skip the closing `>`.
|
||||
tcx.sess
|
||||
.source_map()
|
||||
.next_point(tcx.sess.source_map().next_point(arg.span()))
|
||||
})
|
||||
.unwrap_or(path_segment.ident.span),
|
||||
&args[1..], // Skip the receiver.
|
||||
None, // methods are never ctors
|
||||
),
|
||||
k => span_bug!(call_span, "checking argument types on a non-call: `{:?}`", k),
|
||||
};
|
||||
let arg_spans = if provided_args.is_empty() {
|
||||
// foo()
|
||||
// ^^^-- supplied 0 arguments
|
||||
// |
|
||||
// expected 2 arguments
|
||||
vec![tcx.sess.source_map().next_point(start_span).with_hi(call_span.hi())]
|
||||
} else {
|
||||
// foo(1, 2, 3)
|
||||
// ^^^ - - - supplied 3 arguments
|
||||
// |
|
||||
// expected 2 arguments
|
||||
args.iter().map(|arg| arg.span).collect::<Vec<Span>>()
|
||||
};
|
||||
|
||||
let mut err = tcx.sess.struct_span_err_with_code(
|
||||
span,
|
||||
&format!(
|
||||
"this {} takes {}{} but {} {} supplied",
|
||||
match ctor_of {
|
||||
Some(CtorOf::Struct) => "struct",
|
||||
Some(CtorOf::Variant) => "enum variant",
|
||||
None => "function",
|
||||
},
|
||||
if c_variadic { "at least " } else { "" },
|
||||
potentially_plural_count(expected_count, "argument"),
|
||||
potentially_plural_count(arg_count, "argument"),
|
||||
if arg_count == 1 { "was" } else { "were" }
|
||||
),
|
||||
DiagnosticId::Error(error_code.to_owned()),
|
||||
);
|
||||
let label = format!("supplied {}", potentially_plural_count(arg_count, "argument"));
|
||||
for (i, span) in arg_spans.into_iter().enumerate() {
|
||||
err.span_label(
|
||||
span,
|
||||
if arg_count == 0 || i + 1 == arg_count { &label } else { "" },
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(def_id) = fn_def_id {
|
||||
if let Some(def_span) = tcx.def_ident_span(def_id) {
|
||||
let mut spans: MultiSpan = def_span.into();
|
||||
|
||||
let params = tcx
|
||||
.hir()
|
||||
.get_if_local(def_id)
|
||||
.and_then(|node| node.body_id())
|
||||
.into_iter()
|
||||
.map(|id| tcx.hir().body(id).params)
|
||||
.flatten();
|
||||
|
||||
for param in params {
|
||||
spans.push_span_label(param.span, String::new());
|
||||
}
|
||||
|
||||
let def_kind = tcx.def_kind(def_id);
|
||||
err.span_note(spans, &format!("{} defined here", def_kind.descr(def_id)));
|
||||
}
|
||||
}
|
||||
|
||||
if sugg_unit {
|
||||
let sugg_span = tcx.sess.source_map().end_point(call_expr.span);
|
||||
// remove closing `)` from the span
|
||||
let sugg_span = sugg_span.shrink_to_lo();
|
||||
err.span_suggestion(
|
||||
sugg_span,
|
||||
"expected the unit value `()`; create it with empty parentheses",
|
||||
String::from("()"),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else {
|
||||
err.span_label(
|
||||
span,
|
||||
format!(
|
||||
"expected {}{}",
|
||||
if c_variadic { "at least " } else { "" },
|
||||
potentially_plural_count(expected_count, "argument")
|
||||
),
|
||||
);
|
||||
}
|
||||
err.emit();
|
||||
};
|
||||
// expected_count, arg_count, error_code, sugg_unit
|
||||
let mut error: Option<(usize, usize, &str, bool)> = None;
|
||||
|
||||
// If the arguments should be wrapped in a tuple (ex: closures), unwrap them here
|
||||
let (formal_input_tys, expected_input_tys) = if tuple_arguments == TupleArguments {
|
||||
let tuple_type = self.structurally_resolved_type(call_span, formal_input_tys[0]);
|
||||
match tuple_type.kind() {
|
||||
ty::Tuple(arg_types) if arg_types.len() != provided_args.len() => {
|
||||
param_count_error(arg_types.len(), provided_args.len(), "E0057", false, false);
|
||||
(self.err_args(provided_args.len()), vec![])
|
||||
}
|
||||
// We expected a tuple and got a tuple
|
||||
ty::Tuple(arg_types) => {
|
||||
// Argument length differs
|
||||
if arg_types.len() != provided_args.len() {
|
||||
error = Some((arg_types.len(), provided_args.len(), "E0057", false));
|
||||
}
|
||||
let expected_input_tys = match expected_input_tys.get(0) {
|
||||
Some(&ty) => match ty.kind() {
|
||||
ty::Tuple(ref tys) => tys.iter().map(|k| k.expect_ty()).collect(),
|
||||
|
@ -267,6 +150,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
(arg_types.iter().map(|k| k.expect_ty()).collect(), expected_input_tys)
|
||||
}
|
||||
_ => {
|
||||
// Otherwise, there's a mismatch, so clear out what we're expecting, and set
|
||||
// our input typs to err_args so we don't blow up the error messages
|
||||
struct_span_err!(
|
||||
tcx.sess,
|
||||
call_span,
|
||||
|
@ -284,7 +169,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
if supplied_arg_count >= expected_arg_count {
|
||||
(formal_input_tys.to_vec(), expected_input_tys)
|
||||
} else {
|
||||
param_count_error(expected_arg_count, supplied_arg_count, "E0060", true, false);
|
||||
error = Some((expected_arg_count, supplied_arg_count, "E0060", false));
|
||||
(self.err_args(supplied_arg_count), vec![])
|
||||
}
|
||||
} else {
|
||||
|
@ -296,8 +181,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
} else {
|
||||
false
|
||||
};
|
||||
param_count_error(expected_arg_count, supplied_arg_count, "E0061", false, sugg_unit);
|
||||
|
||||
error = Some((expected_arg_count, supplied_arg_count, "E0061", sugg_unit));
|
||||
(self.err_args(supplied_arg_count), vec![])
|
||||
};
|
||||
|
||||
|
@ -348,7 +232,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
// argument and not the call. This is otherwise redundant with the `demand_coerce`
|
||||
// call immediately after, but it lets us customize the span pointed to in the
|
||||
// fulfillment error to be more accurate.
|
||||
let _ =
|
||||
let coerced_ty =
|
||||
self.resolve_vars_with_obligations_and_mutate_fulfillment(coerced_ty, |errors| {
|
||||
self.point_at_type_arg_instead_of_call_if_possible(errors, call_expr);
|
||||
self.point_at_arg_instead_of_call_if_possible(
|
||||
|
@ -360,6 +244,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
);
|
||||
});
|
||||
|
||||
final_arg_types[idx] = Some((checked_ty, coerced_ty));
|
||||
|
||||
// We're processing function arguments so we definitely want to use
|
||||
// two-phase borrows.
|
||||
self.demand_coerce(&provided_arg, checked_ty, coerced_ty, None, AllowTwoPhase::Yes);
|
||||
|
@ -418,6 +304,123 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
// If there was an error in parameter count, emit that here
|
||||
if let Some((expected_count, arg_count, err_code, sugg_unit)) = error {
|
||||
let (span, start_span, args, ctor_of) = match &call_expr.kind {
|
||||
hir::ExprKind::Call(
|
||||
hir::Expr {
|
||||
span,
|
||||
kind:
|
||||
hir::ExprKind::Path(hir::QPath::Resolved(
|
||||
_,
|
||||
hir::Path { res: Res::Def(DefKind::Ctor(of, _), _), .. },
|
||||
)),
|
||||
..
|
||||
},
|
||||
args,
|
||||
) => (*span, *span, &args[..], Some(of)),
|
||||
hir::ExprKind::Call(hir::Expr { span, .. }, args) => {
|
||||
(*span, *span, &args[..], None)
|
||||
}
|
||||
hir::ExprKind::MethodCall(path_segment, args, _) => (
|
||||
path_segment.ident.span,
|
||||
// `sp` doesn't point at the whole `foo.bar()`, only at `bar`.
|
||||
path_segment
|
||||
.args
|
||||
.and_then(|args| args.args.iter().last())
|
||||
// Account for `foo.bar::<T>()`.
|
||||
.map(|arg| {
|
||||
// Skip the closing `>`.
|
||||
tcx.sess
|
||||
.source_map()
|
||||
.next_point(tcx.sess.source_map().next_point(arg.span()))
|
||||
})
|
||||
.unwrap_or(path_segment.ident.span),
|
||||
&args[1..], // Skip the receiver.
|
||||
None, // methods are never ctors
|
||||
),
|
||||
k => span_bug!(call_span, "checking argument types on a non-call: `{:?}`", k),
|
||||
};
|
||||
let arg_spans = if provided_args.is_empty() {
|
||||
// foo()
|
||||
// ^^^-- supplied 0 arguments
|
||||
// |
|
||||
// expected 2 arguments
|
||||
vec![tcx.sess.source_map().next_point(start_span).with_hi(call_span.hi())]
|
||||
} else {
|
||||
// foo(1, 2, 3)
|
||||
// ^^^ - - - supplied 3 arguments
|
||||
// |
|
||||
// expected 2 arguments
|
||||
args.iter().map(|arg| arg.span).collect::<Vec<Span>>()
|
||||
};
|
||||
let call_name = match ctor_of {
|
||||
Some(CtorOf::Struct) => "struct",
|
||||
Some(CtorOf::Variant) => "enum variant",
|
||||
None => "function",
|
||||
};
|
||||
let mut err = tcx.sess.struct_span_err_with_code(
|
||||
span,
|
||||
&format!(
|
||||
"this {} takes {}{} but {} {} supplied",
|
||||
call_name,
|
||||
if c_variadic { "at least " } else { "" },
|
||||
potentially_plural_count(expected_count, "argument"),
|
||||
potentially_plural_count(arg_count, "argument"),
|
||||
if arg_count == 1 { "was" } else { "were" }
|
||||
),
|
||||
DiagnosticId::Error(err_code.to_owned()),
|
||||
);
|
||||
let label = format!("supplied {}", potentially_plural_count(arg_count, "argument"));
|
||||
for (i, span) in arg_spans.into_iter().enumerate() {
|
||||
err.span_label(
|
||||
span,
|
||||
if arg_count == 0 || i + 1 == arg_count { &label } else { "" },
|
||||
);
|
||||
}
|
||||
if let Some(def_id) = fn_def_id {
|
||||
if let Some(def_span) = tcx.def_ident_span(def_id) {
|
||||
let mut spans: MultiSpan = def_span.into();
|
||||
|
||||
let params = tcx
|
||||
.hir()
|
||||
.get_if_local(def_id)
|
||||
.and_then(|node| node.body_id())
|
||||
.into_iter()
|
||||
.map(|id| tcx.hir().body(id).params)
|
||||
.flatten();
|
||||
|
||||
for param in params {
|
||||
spans.push_span_label(param.span, String::new());
|
||||
}
|
||||
|
||||
let def_kind = tcx.def_kind(def_id);
|
||||
err.span_note(spans, &format!("{} defined here", def_kind.descr(def_id)));
|
||||
}
|
||||
}
|
||||
if sugg_unit {
|
||||
let sugg_span = tcx.sess.source_map().end_point(call_expr.span);
|
||||
// remove closing `)` from the span
|
||||
let sugg_span = sugg_span.shrink_to_lo();
|
||||
err.span_suggestion(
|
||||
sugg_span,
|
||||
"expected the unit value `()`; create it with empty parentheses",
|
||||
String::from("()"),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else {
|
||||
err.span_label(
|
||||
span,
|
||||
format!(
|
||||
"expected {}{}",
|
||||
if c_variadic { "at least " } else { "" },
|
||||
potentially_plural_count(expected_count, "argument")
|
||||
),
|
||||
);
|
||||
}
|
||||
err.emit();
|
||||
}
|
||||
|
||||
// We also need to make sure we at least write the ty of the other
|
||||
// arguments which we skipped above.
|
||||
if c_variadic {
|
||||
|
|
|
@ -30,4 +30,5 @@ fn main() {
|
|||
//~^ ERROR this function takes 1 argument but 0 arguments were supplied
|
||||
let ans = s("burma", "shave");
|
||||
//~^ ERROR this function takes 1 argument but 2 arguments were supplied
|
||||
//~| ERROR mismatched types
|
||||
}
|
||||
|
|
|
@ -18,6 +18,12 @@ note: associated function defined here
|
|||
LL | extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output;
|
||||
| ^^^^^^^^
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/overloaded-calls-bad.rs:31:17
|
||||
|
|
||||
LL | let ans = s("burma", "shave");
|
||||
| ^^^^^^^ expected `isize`, found `&str`
|
||||
|
||||
error[E0057]: this function takes 1 argument but 2 arguments were supplied
|
||||
--> $DIR/overloaded-calls-bad.rs:31:15
|
||||
|
|
||||
|
@ -32,7 +38,7 @@ note: associated function defined here
|
|||
LL | extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output;
|
||||
| ^^^^^^^^
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0057, E0308.
|
||||
For more information about an error, try `rustc --explain E0057`.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue