Skip to content

Commit c8ede15

Browse files
authored
Rollup merge of #93118 - jackh726:param-heuristics-3, r=estebank
Move param count error emission to end of `check_argument_types` The error emission here isn't exactly what is done in #92364, but replicating that is hard . The general move should make for a smaller diff. Also included the `(usize, Ty, Ty)` to -> `Option<(Ty, Ty)>` commit. r? ``@estebank``
2 parents cf70411 + ce31f68 commit c8ede15

File tree

3 files changed

+152
-136
lines changed

3 files changed

+152
-136
lines changed

compiler/rustc_typeck/src/check/fn_ctxt/checks.rs

+144-135
Original file line numberDiff line numberDiff line change
@@ -127,136 +127,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
127127

128128
let expected_arg_count = formal_input_tys.len();
129129

130-
let param_count_error = |expected_count: usize,
131-
arg_count: usize,
132-
error_code: &str,
133-
c_variadic: bool,
134-
sugg_unit: bool| {
135-
let (span, start_span, args, ctor_of) = match &call_expr.kind {
136-
hir::ExprKind::Call(
137-
hir::Expr {
138-
span,
139-
kind:
140-
hir::ExprKind::Path(hir::QPath::Resolved(
141-
_,
142-
hir::Path { res: Res::Def(DefKind::Ctor(of, _), _), .. },
143-
)),
144-
..
145-
},
146-
args,
147-
) => (*span, *span, &args[..], Some(of)),
148-
hir::ExprKind::Call(hir::Expr { span, .. }, args) => {
149-
(*span, *span, &args[..], None)
150-
}
151-
hir::ExprKind::MethodCall(path_segment, args, _) => (
152-
path_segment.ident.span,
153-
// `sp` doesn't point at the whole `foo.bar()`, only at `bar`.
154-
path_segment
155-
.args
156-
.and_then(|args| args.args.iter().last())
157-
// Account for `foo.bar::<T>()`.
158-
.map(|arg| {
159-
// Skip the closing `>`.
160-
tcx.sess
161-
.source_map()
162-
.next_point(tcx.sess.source_map().next_point(arg.span()))
163-
})
164-
.unwrap_or(path_segment.ident.span),
165-
&args[1..], // Skip the receiver.
166-
None, // methods are never ctors
167-
),
168-
k => span_bug!(call_span, "checking argument types on a non-call: `{:?}`", k),
169-
};
170-
let arg_spans = if provided_args.is_empty() {
171-
// foo()
172-
// ^^^-- supplied 0 arguments
173-
// |
174-
// expected 2 arguments
175-
vec![tcx.sess.source_map().next_point(start_span).with_hi(call_span.hi())]
176-
} else {
177-
// foo(1, 2, 3)
178-
// ^^^ - - - supplied 3 arguments
179-
// |
180-
// expected 2 arguments
181-
args.iter().map(|arg| arg.span).collect::<Vec<Span>>()
182-
};
183-
184-
let mut err = tcx.sess.struct_span_err_with_code(
185-
span,
186-
&format!(
187-
"this {} takes {}{} but {} {} supplied",
188-
match ctor_of {
189-
Some(CtorOf::Struct) => "struct",
190-
Some(CtorOf::Variant) => "enum variant",
191-
None => "function",
192-
},
193-
if c_variadic { "at least " } else { "" },
194-
potentially_plural_count(expected_count, "argument"),
195-
potentially_plural_count(arg_count, "argument"),
196-
if arg_count == 1 { "was" } else { "were" }
197-
),
198-
DiagnosticId::Error(error_code.to_owned()),
199-
);
200-
let label = format!("supplied {}", potentially_plural_count(arg_count, "argument"));
201-
for (i, span) in arg_spans.into_iter().enumerate() {
202-
err.span_label(
203-
span,
204-
if arg_count == 0 || i + 1 == arg_count { &label } else { "" },
205-
);
206-
}
207-
208-
if let Some(def_id) = fn_def_id {
209-
if let Some(def_span) = tcx.def_ident_span(def_id) {
210-
let mut spans: MultiSpan = def_span.into();
211-
212-
let params = tcx
213-
.hir()
214-
.get_if_local(def_id)
215-
.and_then(|node| node.body_id())
216-
.into_iter()
217-
.map(|id| tcx.hir().body(id).params)
218-
.flatten();
219-
220-
for param in params {
221-
spans.push_span_label(param.span, String::new());
222-
}
223-
224-
let def_kind = tcx.def_kind(def_id);
225-
err.span_note(spans, &format!("{} defined here", def_kind.descr(def_id)));
226-
}
227-
}
228-
229-
if sugg_unit {
230-
let sugg_span = tcx.sess.source_map().end_point(call_expr.span);
231-
// remove closing `)` from the span
232-
let sugg_span = sugg_span.shrink_to_lo();
233-
err.span_suggestion(
234-
sugg_span,
235-
"expected the unit value `()`; create it with empty parentheses",
236-
String::from("()"),
237-
Applicability::MachineApplicable,
238-
);
239-
} else {
240-
err.span_label(
241-
span,
242-
format!(
243-
"expected {}{}",
244-
if c_variadic { "at least " } else { "" },
245-
potentially_plural_count(expected_count, "argument")
246-
),
247-
);
248-
}
249-
err.emit();
250-
};
130+
// expected_count, arg_count, error_code, sugg_unit
131+
let mut error: Option<(usize, usize, &str, bool)> = None;
251132

133+
// If the arguments should be wrapped in a tuple (ex: closures), unwrap them here
252134
let (formal_input_tys, expected_input_tys) = if tuple_arguments == TupleArguments {
253135
let tuple_type = self.structurally_resolved_type(call_span, formal_input_tys[0]);
254136
match tuple_type.kind() {
255-
ty::Tuple(arg_types) if arg_types.len() != provided_args.len() => {
256-
param_count_error(arg_types.len(), provided_args.len(), "E0057", false, false);
257-
(self.err_args(provided_args.len()), vec![])
258-
}
137+
// We expected a tuple and got a tuple
259138
ty::Tuple(arg_types) => {
139+
// Argument length differs
140+
if arg_types.len() != provided_args.len() {
141+
error = Some((arg_types.len(), provided_args.len(), "E0057", false));
142+
}
260143
let expected_input_tys = match expected_input_tys.get(0) {
261144
Some(&ty) => match ty.kind() {
262145
ty::Tuple(ref tys) => tys.iter().map(|k| k.expect_ty()).collect(),
@@ -267,6 +150,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
267150
(arg_types.iter().map(|k| k.expect_ty()).collect(), expected_input_tys)
268151
}
269152
_ => {
153+
// Otherwise, there's a mismatch, so clear out what we're expecting, and set
154+
// our input typs to err_args so we don't blow up the error messages
270155
struct_span_err!(
271156
tcx.sess,
272157
call_span,
@@ -284,7 +169,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
284169
if supplied_arg_count >= expected_arg_count {
285170
(formal_input_tys.to_vec(), expected_input_tys)
286171
} else {
287-
param_count_error(expected_arg_count, supplied_arg_count, "E0060", true, false);
172+
error = Some((expected_arg_count, supplied_arg_count, "E0060", false));
288173
(self.err_args(supplied_arg_count), vec![])
289174
}
290175
} else {
@@ -296,8 +181,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
296181
} else {
297182
false
298183
};
299-
param_count_error(expected_arg_count, supplied_arg_count, "E0061", false, sugg_unit);
300-
184+
error = Some((expected_arg_count, supplied_arg_count, "E0061", sugg_unit));
301185
(self.err_args(supplied_arg_count), vec![])
302186
};
303187

@@ -315,13 +199,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
315199

316200
assert_eq!(expected_input_tys.len(), formal_input_tys.len());
317201

202+
let provided_arg_count: usize = provided_args.len();
203+
318204
// Keep track of the fully coerced argument types
319-
let mut final_arg_types: Vec<(usize, Ty<'_>, Ty<'_>)> = vec![];
205+
let mut final_arg_types: Vec<Option<(Ty<'_>, Ty<'_>)>> = vec![None; provided_arg_count];
320206

321207
// We introduce a helper function to demand that a given argument satisfy a given input
322208
// This is more complicated than just checking type equality, as arguments could be coerced
323209
// This version writes those types back so further type checking uses the narrowed types
324-
let demand_compatible = |idx, final_arg_types: &mut Vec<(usize, Ty<'tcx>, Ty<'tcx>)>| {
210+
let demand_compatible = |idx, final_arg_types: &mut Vec<Option<(Ty<'tcx>, Ty<'tcx>)>>| {
325211
let formal_input_ty: Ty<'tcx> = formal_input_tys[idx];
326212
let expected_input_ty: Ty<'tcx> = expected_input_tys[idx];
327213
let provided_arg = &provided_args[idx];
@@ -340,13 +226,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
340226
let coerced_ty = expectation.only_has_type(self).unwrap_or(formal_input_ty);
341227

342228
// Keep track of these for below
343-
final_arg_types.push((idx, checked_ty, coerced_ty));
229+
final_arg_types[idx] = Some((checked_ty, coerced_ty));
344230

345231
// Cause selection errors caused by resolving a single argument to point at the
346232
// argument and not the call. This is otherwise redundant with the `demand_coerce`
347233
// call immediately after, but it lets us customize the span pointed to in the
348234
// fulfillment error to be more accurate.
349-
let _ =
235+
let coerced_ty =
350236
self.resolve_vars_with_obligations_and_mutate_fulfillment(coerced_ty, |errors| {
351237
self.point_at_type_arg_instead_of_call_if_possible(errors, call_expr);
352238
self.point_at_arg_instead_of_call_if_possible(
@@ -358,6 +244,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
358244
);
359245
});
360246

247+
final_arg_types[idx] = Some((checked_ty, coerced_ty));
248+
361249
// We're processing function arguments so we definitely want to use
362250
// two-phase borrows.
363251
self.demand_coerce(&provided_arg, checked_ty, coerced_ty, None, AllowTwoPhase::Yes);
@@ -416,6 +304,123 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
416304
}
417305
}
418306

307+
// If there was an error in parameter count, emit that here
308+
if let Some((expected_count, arg_count, err_code, sugg_unit)) = error {
309+
let (span, start_span, args, ctor_of) = match &call_expr.kind {
310+
hir::ExprKind::Call(
311+
hir::Expr {
312+
span,
313+
kind:
314+
hir::ExprKind::Path(hir::QPath::Resolved(
315+
_,
316+
hir::Path { res: Res::Def(DefKind::Ctor(of, _), _), .. },
317+
)),
318+
..
319+
},
320+
args,
321+
) => (*span, *span, &args[..], Some(of)),
322+
hir::ExprKind::Call(hir::Expr { span, .. }, args) => {
323+
(*span, *span, &args[..], None)
324+
}
325+
hir::ExprKind::MethodCall(path_segment, args, _) => (
326+
path_segment.ident.span,
327+
// `sp` doesn't point at the whole `foo.bar()`, only at `bar`.
328+
path_segment
329+
.args
330+
.and_then(|args| args.args.iter().last())
331+
// Account for `foo.bar::<T>()`.
332+
.map(|arg| {
333+
// Skip the closing `>`.
334+
tcx.sess
335+
.source_map()
336+
.next_point(tcx.sess.source_map().next_point(arg.span()))
337+
})
338+
.unwrap_or(path_segment.ident.span),
339+
&args[1..], // Skip the receiver.
340+
None, // methods are never ctors
341+
),
342+
k => span_bug!(call_span, "checking argument types on a non-call: `{:?}`", k),
343+
};
344+
let arg_spans = if provided_args.is_empty() {
345+
// foo()
346+
// ^^^-- supplied 0 arguments
347+
// |
348+
// expected 2 arguments
349+
vec![tcx.sess.source_map().next_point(start_span).with_hi(call_span.hi())]
350+
} else {
351+
// foo(1, 2, 3)
352+
// ^^^ - - - supplied 3 arguments
353+
// |
354+
// expected 2 arguments
355+
args.iter().map(|arg| arg.span).collect::<Vec<Span>>()
356+
};
357+
let call_name = match ctor_of {
358+
Some(CtorOf::Struct) => "struct",
359+
Some(CtorOf::Variant) => "enum variant",
360+
None => "function",
361+
};
362+
let mut err = tcx.sess.struct_span_err_with_code(
363+
span,
364+
&format!(
365+
"this {} takes {}{} but {} {} supplied",
366+
call_name,
367+
if c_variadic { "at least " } else { "" },
368+
potentially_plural_count(expected_count, "argument"),
369+
potentially_plural_count(arg_count, "argument"),
370+
if arg_count == 1 { "was" } else { "were" }
371+
),
372+
DiagnosticId::Error(err_code.to_owned()),
373+
);
374+
let label = format!("supplied {}", potentially_plural_count(arg_count, "argument"));
375+
for (i, span) in arg_spans.into_iter().enumerate() {
376+
err.span_label(
377+
span,
378+
if arg_count == 0 || i + 1 == arg_count { &label } else { "" },
379+
);
380+
}
381+
if let Some(def_id) = fn_def_id {
382+
if let Some(def_span) = tcx.def_ident_span(def_id) {
383+
let mut spans: MultiSpan = def_span.into();
384+
385+
let params = tcx
386+
.hir()
387+
.get_if_local(def_id)
388+
.and_then(|node| node.body_id())
389+
.into_iter()
390+
.map(|id| tcx.hir().body(id).params)
391+
.flatten();
392+
393+
for param in params {
394+
spans.push_span_label(param.span, String::new());
395+
}
396+
397+
let def_kind = tcx.def_kind(def_id);
398+
err.span_note(spans, &format!("{} defined here", def_kind.descr(def_id)));
399+
}
400+
}
401+
if sugg_unit {
402+
let sugg_span = tcx.sess.source_map().end_point(call_expr.span);
403+
// remove closing `)` from the span
404+
let sugg_span = sugg_span.shrink_to_lo();
405+
err.span_suggestion(
406+
sugg_span,
407+
"expected the unit value `()`; create it with empty parentheses",
408+
String::from("()"),
409+
Applicability::MachineApplicable,
410+
);
411+
} else {
412+
err.span_label(
413+
span,
414+
format!(
415+
"expected {}{}",
416+
if c_variadic { "at least " } else { "" },
417+
potentially_plural_count(expected_count, "argument")
418+
),
419+
);
420+
}
421+
err.emit();
422+
}
423+
419424
// We also need to make sure we at least write the ty of the other
420425
// arguments which we skipped above.
421426
if c_variadic {
@@ -975,7 +980,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
975980
fn point_at_arg_instead_of_call_if_possible(
976981
&self,
977982
errors: &mut Vec<traits::FulfillmentError<'tcx>>,
978-
final_arg_types: &[(usize, Ty<'tcx>, Ty<'tcx>)],
983+
final_arg_types: &[Option<(Ty<'tcx>, Ty<'tcx>)>],
979984
expr: &'tcx hir::Expr<'tcx>,
980985
call_sp: Span,
981986
args: &'tcx [hir::Expr<'tcx>],
@@ -1030,8 +1035,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
10301035
// `FulfillmentError`.
10311036
let mut referenced_in = final_arg_types
10321037
.iter()
1033-
.map(|&(i, checked_ty, _)| (i, checked_ty))
1034-
.chain(final_arg_types.iter().map(|&(i, _, coerced_ty)| (i, coerced_ty)))
1038+
.enumerate()
1039+
.filter_map(|(i, arg)| match arg {
1040+
Some((checked_ty, coerce_ty)) => Some([(i, *checked_ty), (i, *coerce_ty)]),
1041+
_ => None,
1042+
})
1043+
.flatten()
10351044
.flat_map(|(i, ty)| {
10361045
let ty = self.resolve_vars_if_possible(ty);
10371046
// We walk the argument type because the argument's type could have

src/test/ui/mismatched_types/overloaded-calls-bad.rs

+1
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,5 @@ fn main() {
3030
//~^ ERROR this function takes 1 argument but 0 arguments were supplied
3131
let ans = s("burma", "shave");
3232
//~^ ERROR this function takes 1 argument but 2 arguments were supplied
33+
//~| ERROR mismatched types
3334
}

src/test/ui/mismatched_types/overloaded-calls-bad.stderr

+7-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@ note: associated function defined here
1818
LL | extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output;
1919
| ^^^^^^^^
2020

21+
error[E0308]: mismatched types
22+
--> $DIR/overloaded-calls-bad.rs:31:17
23+
|
24+
LL | let ans = s("burma", "shave");
25+
| ^^^^^^^ expected `isize`, found `&str`
26+
2127
error[E0057]: this function takes 1 argument but 2 arguments were supplied
2228
--> $DIR/overloaded-calls-bad.rs:31:15
2329
|
@@ -32,7 +38,7 @@ note: associated function defined here
3238
LL | extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output;
3339
| ^^^^^^^^
3440

35-
error: aborting due to 3 previous errors
41+
error: aborting due to 4 previous errors
3642

3743
Some errors have detailed explanations: E0057, E0308.
3844
For more information about an error, try `rustc --explain E0057`.

0 commit comments

Comments
 (0)