Skip to content

Commit e0e70c0

Browse files
committed
Auto merge of #90677 - bobrippling:suggest-tuple-parens, r=camelid
Suggest tuple-parentheses for enum variants This follows on from #86493 / #86481, making the parentheses suggestion. To summarise, given the following code: ```rust fn f() -> Option<(i32, i8)> { Some(1, 2) } ``` The current output is: ``` error[E0061]: this enum variant takes 1 argument but 2 arguments were supplied --> b.rs:2:5 | 2 | Some(1, 2) | ^^^^ - - supplied 2 arguments | | | expected 1 argument error: aborting due to previous error For more information about this error, try `rustc --explain E0061`. ``` With this change, `rustc` will now suggest parentheses when: - The callee is expecting a single tuple argument - The number of arguments passed matches the element count in the above tuple - The arguments' types match the tuple's fields ``` error[E0061]: this enum variant takes 1 argument but 2 arguments were supplied --> b.rs:2:5 | 2 | Some(1, 2) | ^^^^ - - supplied 2 arguments | help: use parentheses to construct a tuple | 2 | Some((1, 2)) | + + ```
2 parents 312a799 + a8bac98 commit e0e70c0

File tree

6 files changed

+255
-6
lines changed

6 files changed

+255
-6
lines changed

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

+68-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+
struct FnArgsAsTuple<'hir> {
32+
first: &'hir hir::Expr<'hir>,
33+
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,25 @@ 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 expected_input_tys = if expected_input_tys.is_empty() {
192+
// In most cases we can use expected_input_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 = self.suggested_tuple_wrap(expected_input_tys, provided_args);
200+
201+
error = Some((
202+
expected_arg_count,
203+
supplied_arg_count,
204+
"E0061",
205+
sugg_unit,
206+
sugg_tuple_wrap_args,
207+
));
185208
(self.err_args(supplied_arg_count), vec![])
186209
};
187210

@@ -305,7 +328,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
305328
}
306329

307330
// If there was an error in parameter count, emit that here
308-
if let Some((expected_count, arg_count, err_code, sugg_unit)) = error {
331+
if let Some((expected_count, arg_count, err_code, sugg_unit, sugg_tuple_wrap_args)) = error
332+
{
309333
let (span, start_span, args, ctor_of) = match &call_expr.kind {
310334
hir::ExprKind::Call(
311335
hir::Expr {
@@ -408,6 +432,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
408432
String::from("()"),
409433
Applicability::MachineApplicable,
410434
);
435+
} else if let Some(FnArgsAsTuple { first, last }) = sugg_tuple_wrap_args {
436+
err.multipart_suggestion(
437+
"use parentheses to construct a tuple",
438+
vec![
439+
(first.span.shrink_to_lo(), '('.to_string()),
440+
(last.span.shrink_to_hi(), ')'.to_string()),
441+
],
442+
Applicability::MachineApplicable,
443+
);
411444
} else {
412445
err.span_label(
413446
span,
@@ -457,6 +490,35 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
457490
}
458491
}
459492

493+
fn suggested_tuple_wrap(
494+
&self,
495+
expected_input_tys: &[Ty<'tcx>],
496+
provided_args: &'tcx [hir::Expr<'tcx>],
497+
) -> Option<FnArgsAsTuple<'_>> {
498+
let [expected_arg_type] = &expected_input_tys[..] else { return None };
499+
500+
let ty::Tuple(expected_elems) = self.resolve_vars_if_possible(*expected_arg_type).kind()
501+
else { return None };
502+
503+
let expected_types: Vec<_> = expected_elems.iter().map(|k| k.expect_ty()).collect();
504+
let supplied_types: Vec<_> = provided_args.iter().map(|arg| self.check_expr(arg)).collect();
505+
506+
let all_match = iter::zip(expected_types, supplied_types)
507+
.all(|(expected, supplied)| self.can_eq(self.param_env, expected, supplied).is_ok());
508+
509+
if all_match {
510+
match provided_args {
511+
[] => None,
512+
[_] => unreachable!(
513+
"shouldn't reach here - need count mismatch between 1-tuple and 1-argument"
514+
),
515+
[first, .., last] => Some(FnArgsAsTuple { first, last }),
516+
}
517+
} else {
518+
None
519+
}
520+
}
521+
460522
// AST fragment checking
461523
pub(in super::super) fn check_lit(
462524
&self,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Ensure we don't suggest tuple-wrapping when we'd end up with a type error
2+
3+
fn main() {
4+
// we shouldn't suggest to fix these - `2` isn't a `bool`
5+
6+
let _: Option<(i32, bool)> = Some(1, 2);
7+
//~^ ERROR this enum variant takes 1 argument but 2 arguments were supplied
8+
int_bool(1, 2);
9+
//~^ ERROR this function takes 1 argument but 2 arguments were supplied
10+
11+
let _: Option<(i8,)> = Some();
12+
//~^ ERROR this enum variant takes 1 argument but 0 arguments were supplied
13+
}
14+
15+
fn int_bool(_: (i32, bool)) {
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
error[E0061]: this enum variant takes 1 argument but 2 arguments were supplied
2+
--> $DIR/args-instead-of-tuple-errors.rs:6:34
3+
|
4+
LL | let _: Option<(i32, bool)> = Some(1, 2);
5+
| ^^^^ - - supplied 2 arguments
6+
| |
7+
| expected 1 argument
8+
9+
error[E0061]: this function takes 1 argument but 2 arguments were supplied
10+
--> $DIR/args-instead-of-tuple-errors.rs:8:5
11+
|
12+
LL | int_bool(1, 2);
13+
| ^^^^^^^^ - - supplied 2 arguments
14+
| |
15+
| expected 1 argument
16+
|
17+
note: function defined here
18+
--> $DIR/args-instead-of-tuple-errors.rs:15:4
19+
|
20+
LL | fn int_bool(_: (i32, bool)) {
21+
| ^^^^^^^^ --------------
22+
23+
error[E0061]: this enum variant takes 1 argument but 0 arguments were supplied
24+
--> $DIR/args-instead-of-tuple-errors.rs:11:28
25+
|
26+
LL | let _: Option<(i8,)> = Some();
27+
| ^^^^-- supplied 0 arguments
28+
| |
29+
| expected 1 argument
30+
31+
error: aborting due to 3 previous errors
32+
33+
For more information about this error, try `rustc --explain E0061`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Test suggesting tuples where bare arguments may have been passed
2+
// See issue #86481 for details.
3+
4+
// run-rustfix
5+
6+
fn main() {
7+
let _: Result<(i32, i8), ()> = Ok((1, 2));
8+
//~^ ERROR this enum variant takes 1 argument but 2 arguments were supplied
9+
let _: Option<(i32, i8, &'static str)> = Some((1, 2, "hi"));
10+
//~^ ERROR this enum variant takes 1 argument but 3 arguments were supplied
11+
let _: Option<()> = Some(());
12+
//~^ ERROR this enum variant takes 1 argument but 0 arguments were supplied
13+
14+
two_ints((1, 2)); //~ ERROR this function takes 1 argument
15+
16+
with_generic((3, 4)); //~ ERROR this function takes 1 argument
17+
}
18+
19+
fn two_ints(_: (i32, i32)) {
20+
}
21+
22+
fn with_generic<T: Copy + Send>((a, b): (i32, T)) {
23+
if false {
24+
// test generics/bound handling
25+
with_generic((a, b)); //~ ERROR this function takes 1 argument
26+
}
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Test suggesting tuples where bare arguments may have been passed
2+
// See issue #86481 for details.
3+
4+
// run-rustfix
5+
6+
fn main() {
7+
let _: Result<(i32, i8), ()> = Ok(1, 2);
8+
//~^ ERROR this enum variant takes 1 argument but 2 arguments were supplied
9+
let _: Option<(i32, i8, &'static str)> = Some(1, 2, "hi");
10+
//~^ ERROR this enum variant takes 1 argument but 3 arguments were supplied
11+
let _: Option<()> = Some();
12+
//~^ ERROR this enum variant takes 1 argument but 0 arguments were supplied
13+
14+
two_ints(1, 2); //~ ERROR this function takes 1 argument
15+
16+
with_generic(3, 4); //~ ERROR this function takes 1 argument
17+
}
18+
19+
fn two_ints(_: (i32, i32)) {
20+
}
21+
22+
fn with_generic<T: Copy + Send>((a, b): (i32, T)) {
23+
if false {
24+
// test generics/bound handling
25+
with_generic(a, b); //~ ERROR this function takes 1 argument
26+
}
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
error[E0061]: this enum variant takes 1 argument but 2 arguments were supplied
2+
--> $DIR/args-instead-of-tuple.rs:7:36
3+
|
4+
LL | let _: Result<(i32, i8), ()> = Ok(1, 2);
5+
| ^^ - - supplied 2 arguments
6+
|
7+
help: use parentheses to construct a tuple
8+
|
9+
LL | let _: Result<(i32, i8), ()> = Ok((1, 2));
10+
| + +
11+
12+
error[E0061]: this enum variant takes 1 argument but 3 arguments were supplied
13+
--> $DIR/args-instead-of-tuple.rs:9:46
14+
|
15+
LL | let _: Option<(i32, i8, &'static str)> = Some(1, 2, "hi");
16+
| ^^^^ - - ---- supplied 3 arguments
17+
|
18+
help: use parentheses to construct a tuple
19+
|
20+
LL | let _: Option<(i32, i8, &'static str)> = Some((1, 2, "hi"));
21+
| + +
22+
23+
error[E0061]: this enum variant takes 1 argument but 0 arguments were supplied
24+
--> $DIR/args-instead-of-tuple.rs:11:25
25+
|
26+
LL | let _: Option<()> = Some();
27+
| ^^^^-- supplied 0 arguments
28+
|
29+
help: expected the unit value `()`; create it with empty parentheses
30+
|
31+
LL | let _: Option<()> = Some(());
32+
| ++
33+
34+
error[E0061]: this function takes 1 argument but 2 arguments were supplied
35+
--> $DIR/args-instead-of-tuple.rs:14:5
36+
|
37+
LL | two_ints(1, 2);
38+
| ^^^^^^^^ - - supplied 2 arguments
39+
|
40+
note: function defined here
41+
--> $DIR/args-instead-of-tuple.rs:19:4
42+
|
43+
LL | fn two_ints(_: (i32, i32)) {
44+
| ^^^^^^^^ -------------
45+
help: use parentheses to construct a tuple
46+
|
47+
LL | two_ints((1, 2));
48+
| + +
49+
50+
error[E0061]: this function takes 1 argument but 2 arguments were supplied
51+
--> $DIR/args-instead-of-tuple.rs:16:5
52+
|
53+
LL | with_generic(3, 4);
54+
| ^^^^^^^^^^^^ - - supplied 2 arguments
55+
|
56+
note: function defined here
57+
--> $DIR/args-instead-of-tuple.rs:22:4
58+
|
59+
LL | fn with_generic<T: Copy + Send>((a, b): (i32, T)) {
60+
| ^^^^^^^^^^^^ ----------------
61+
help: use parentheses to construct a tuple
62+
|
63+
LL | with_generic((3, 4));
64+
| + +
65+
66+
error[E0061]: this function takes 1 argument but 2 arguments were supplied
67+
--> $DIR/args-instead-of-tuple.rs:25:9
68+
|
69+
LL | with_generic(a, b);
70+
| ^^^^^^^^^^^^ - - supplied 2 arguments
71+
|
72+
note: function defined here
73+
--> $DIR/args-instead-of-tuple.rs:22:4
74+
|
75+
LL | fn with_generic<T: Copy + Send>((a, b): (i32, T)) {
76+
| ^^^^^^^^^^^^ ----------------
77+
help: use parentheses to construct a tuple
78+
|
79+
LL | with_generic((a, b));
80+
| + +
81+
82+
error: aborting due to 6 previous errors
83+
84+
For more information about this error, try `rustc --explain E0061`.

0 commit comments

Comments
 (0)