Skip to content

Commit 94c300a

Browse files
committed
Suggest tuple-parentheses when passing N arguments to an N-tuple argument
1 parent 92ed874 commit 94c300a

File tree

1 file changed

+67
-6
lines changed
  • compiler/rustc_typeck/src/check/fn_ctxt

1 file changed

+67
-6
lines changed

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

+67-6
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ use crate::structured_errors::StructuredDiagnostic;
2828
use std::iter;
2929
use std::slice;
3030

31+
enum FnArgsAsTuple<'hir> {
32+
Single(&'hir hir::Expr<'hir>),
33+
Multi { first: &'hir hir::Expr<'hir>, last: &'hir hir::Expr<'hir> },
34+
}
35+
3136
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
3237
pub(in super::super) fn check_casts(&self) {
3338
let mut deferred_cast_checks = self.deferred_cast_checks.borrow_mut();
@@ -127,8 +132,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
127132

128133
let expected_arg_count = formal_input_tys.len();
129134

130-
// expected_count, arg_count, error_code, sugg_unit
131-
let mut error: Option<(usize, usize, &str, bool)> = None;
135+
// expected_count, arg_count, error_code, sugg_unit, sugg_tuple_wrap_args
136+
let mut error: Option<(usize, usize, &str, bool, Option<FnArgsAsTuple<'_>>)> = None;
132137

133138
// If the arguments should be wrapped in a tuple (ex: closures), unwrap them here
134139
let (formal_input_tys, expected_input_tys) = if tuple_arguments == TupleArguments {
@@ -138,7 +143,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
138143
ty::Tuple(arg_types) => {
139144
// Argument length differs
140145
if arg_types.len() != provided_args.len() {
141-
error = Some((arg_types.len(), provided_args.len(), "E0057", false));
146+
error = Some((arg_types.len(), provided_args.len(), "E0057", false, None));
142147
}
143148
let expected_input_tys = match expected_input_tys.get(0) {
144149
Some(&ty) => match ty.kind() {
@@ -169,7 +174,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
169174
if supplied_arg_count >= expected_arg_count {
170175
(formal_input_tys.to_vec(), expected_input_tys)
171176
} else {
172-
error = Some((expected_arg_count, supplied_arg_count, "E0060", false));
177+
error = Some((expected_arg_count, supplied_arg_count, "E0060", false, None));
173178
(self.err_args(supplied_arg_count), vec![])
174179
}
175180
} else {
@@ -181,7 +186,43 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
181186
} else {
182187
false
183188
};
184-
error = Some((expected_arg_count, supplied_arg_count, "E0061", sugg_unit));
189+
190+
// are we passing elements of a tuple without the tuple parentheses?
191+
let chosen_arg_tys = if expected_input_tys.is_empty() {
192+
// In most cases we can use expected_arg_tys, but some callers won't have the type
193+
// information, in which case we fall back to the types from the input expressions.
194+
formal_input_tys
195+
} else {
196+
&*expected_input_tys
197+
};
198+
199+
let sugg_tuple_wrap_args = chosen_arg_tys
200+
.get(0)
201+
.cloned()
202+
.map(|arg_ty| self.resolve_vars_if_possible(arg_ty))
203+
.and_then(|arg_ty| match arg_ty.kind() {
204+
ty::Tuple(tup_elems) => Some(tup_elems),
205+
_ => None,
206+
})
207+
.and_then(|tup_elems| {
208+
if tup_elems.len() == supplied_arg_count && chosen_arg_tys.len() == 1 {
209+
match provided_args {
210+
[] => None,
211+
[single] => Some(FnArgsAsTuple::Single(single)),
212+
[first, .., last] => Some(FnArgsAsTuple::Multi { first, last }),
213+
}
214+
} else {
215+
None
216+
}
217+
});
218+
219+
error = Some((
220+
expected_arg_count,
221+
supplied_arg_count,
222+
"E0061",
223+
sugg_unit,
224+
sugg_tuple_wrap_args,
225+
));
185226
(self.err_args(supplied_arg_count), vec![])
186227
};
187228

@@ -305,7 +346,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
305346
}
306347

307348
// If there was an error in parameter count, emit that here
308-
if let Some((expected_count, arg_count, err_code, sugg_unit)) = error {
349+
if let Some((expected_count, arg_count, err_code, sugg_unit, sugg_tuple_wrap_args)) = error
350+
{
309351
let (span, start_span, args, ctor_of) = match &call_expr.kind {
310352
hir::ExprKind::Call(
311353
hir::Expr {
@@ -408,6 +450,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
408450
String::from("()"),
409451
Applicability::MachineApplicable,
410452
);
453+
} else if let Some(tuple_fn_arg) = sugg_tuple_wrap_args {
454+
use FnArgsAsTuple::*;
455+
456+
let spans = match tuple_fn_arg {
457+
Multi { first, last } => vec![
458+
(first.span.shrink_to_lo(), '('.to_string()),
459+
(last.span.shrink_to_hi(), ')'.to_string()),
460+
],
461+
Single(single) => vec![
462+
(single.span.shrink_to_lo(), '('.to_string()),
463+
(single.span.shrink_to_hi(), ",)".to_string()),
464+
],
465+
};
466+
467+
err.multipart_suggestion(
468+
"use parentheses to construct a tuple",
469+
spans,
470+
Applicability::MachineApplicable,
471+
);
411472
} else {
412473
err.span_label(
413474
span,

0 commit comments

Comments
 (0)