Skip to content

Commit 54d2d30

Browse files
committed
Compare tuple element & arg types before suggesting a tuple
1 parent 80059f9 commit 54d2d30

File tree

6 files changed

+79
-32
lines changed

6 files changed

+79
-32
lines changed

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

+31-22
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use rustc_hir::def_id::DefId;
1818
use rustc_hir::{ExprKind, Node, QPath};
1919
use rustc_middle::ty::adjustment::AllowTwoPhase;
2020
use rustc_middle::ty::fold::TypeFoldable;
21-
use rustc_middle::ty::{self, Ty};
21+
use rustc_middle::ty::{self, ParamEnv, Ty};
2222
use rustc_session::Session;
2323
use rustc_span::symbol::Ident;
2424
use rustc_span::{self, MultiSpan, Span};
@@ -188,33 +188,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
188188
};
189189

190190
// 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
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
193193
// information, in which case we fall back to the types from the input expressions.
194194
formal_input_tys
195195
} else {
196196
&*expected_input_tys
197197
};
198198

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-
});
199+
let sugg_tuple_wrap_args = self.suggested_tuple_wrap(expected_input_tys, provided_args);
218200

219201
error = Some((
220202
expected_arg_count,
@@ -518,6 +500,33 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
518500
}
519501
}
520502

503+
fn suggested_tuple_wrap(
504+
&self,
505+
expected_input_tys: &[Ty<'tcx>],
506+
provided_args: &'tcx [hir::Expr<'tcx>],
507+
) -> Option<FnArgsAsTuple<'_>> {
508+
let [expected_arg_type] = &expected_input_tys[..] else { return None };
509+
510+
let ty::Tuple(expected_elems) = self.resolve_vars_if_possible(*expected_arg_type).kind()
511+
else { return None };
512+
513+
let expected_types: Vec<_> = expected_elems.iter().map(|k| k.expect_ty()).collect();
514+
let supplied_types: Vec<_> = provided_args.iter().map(|arg| self.check_expr(arg)).collect();
515+
516+
let all_match = iter::zip(expected_types, supplied_types)
517+
.all(|(expected, supplied)| self.can_eq(ParamEnv::empty(), expected, supplied).is_ok());
518+
519+
if all_match {
520+
match provided_args {
521+
[] => None,
522+
[single] => Some(FnArgsAsTuple::Single(single)),
523+
[first, .., last] => Some(FnArgsAsTuple::Multi { first, last }),
524+
}
525+
} else {
526+
None
527+
}
528+
}
529+
521530
// AST fragment checking
522531
pub(in super::super) fn check_lit(
523532
&self,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
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+
12+
fn int_bool(_: (i32, bool)) {
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
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:12:4
19+
|
20+
LL | fn int_bool(_: (i32, bool)) {
21+
| ^^^^^^^^ --------------
22+
23+
error: aborting due to 2 previous errors
24+
25+
For more information about this error, try `rustc --explain E0061`.

src/test/ui/suggestions/args-instead-of-tuple.fixed

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ fn main() {
1111
let _: Option<()> = Some(());
1212
//~^ ERROR this enum variant takes 1 argument but 0 arguments were supplied
1313

14-
f((1, 2)); //~ ERROR this function takes 1 argument
14+
two_ints((1, 2)); //~ ERROR this function takes 1 argument
1515
}
1616

17-
fn f(_: (i32, i32)) {
17+
fn two_ints(_: (i32, i32)) {
1818
}

src/test/ui/suggestions/args-instead-of-tuple.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ fn main() {
1111
let _: Option<()> = Some();
1212
//~^ ERROR this enum variant takes 1 argument but 0 arguments were supplied
1313

14-
f(1, 2); //~ ERROR this function takes 1 argument
14+
two_ints(1, 2); //~ ERROR this function takes 1 argument
1515
}
1616

17-
fn f(_: (i32, i32)) {
17+
fn two_ints(_: (i32, i32)) {
1818
}

src/test/ui/suggestions/args-instead-of-tuple.stderr

+6-6
Original file line numberDiff line numberDiff line change
@@ -34,18 +34,18 @@ LL | let _: Option<()> = Some(());
3434
error[E0061]: this function takes 1 argument but 2 arguments were supplied
3535
--> $DIR/args-instead-of-tuple.rs:14:5
3636
|
37-
LL | f(1, 2);
38-
| ^ - - supplied 2 arguments
37+
LL | two_ints(1, 2);
38+
| ^^^^^^^^ - - supplied 2 arguments
3939
|
4040
note: function defined here
4141
--> $DIR/args-instead-of-tuple.rs:17:4
4242
|
43-
LL | fn f(_: (i32, i32)) {
44-
| ^ -------------
43+
LL | fn two_ints(_: (i32, i32)) {
44+
| ^^^^^^^^ -------------
4545
help: use parentheses to construct a tuple
4646
|
47-
LL | f((1, 2));
48-
| + +
47+
LL | two_ints((1, 2));
48+
| + +
4949

5050
error: aborting due to 4 previous errors
5151

0 commit comments

Comments
 (0)