Compare tuple element & arg types before suggesting a tuple

This commit is contained in:
Rob Pilling 2021-12-05 21:41:33 +00:00
parent 80059f9942
commit 54d2d30662
6 changed files with 79 additions and 32 deletions

View file

@ -18,7 +18,7 @@
use rustc_hir::{ExprKind, Node, QPath};
use rustc_middle::ty::adjustment::AllowTwoPhase;
use rustc_middle::ty::fold::TypeFoldable;
use rustc_middle::ty::{self, Ty};
use rustc_middle::ty::{self, ParamEnv, Ty};
use rustc_session::Session;
use rustc_span::symbol::Ident;
use rustc_span::{self, MultiSpan, Span};
@ -188,33 +188,15 @@ pub(in super::super) fn check_argument_types(
};
// are we passing elements of a tuple without the tuple parentheses?
let chosen_arg_tys = if expected_input_tys.is_empty() {
// In most cases we can use expected_arg_tys, but some callers won't have the type
let expected_input_tys = if expected_input_tys.is_empty() {
// In most cases we can use expected_input_tys, but some callers won't have the type
// information, in which case we fall back to the types from the input expressions.
formal_input_tys
} else {
&*expected_input_tys
};
let sugg_tuple_wrap_args = chosen_arg_tys
.get(0)
.cloned()
.map(|arg_ty| self.resolve_vars_if_possible(arg_ty))
.and_then(|arg_ty| match arg_ty.kind() {
ty::Tuple(tup_elems) => Some(tup_elems),
_ => None,
})
.and_then(|tup_elems| {
if tup_elems.len() == supplied_arg_count && chosen_arg_tys.len() == 1 {
match provided_args {
[] => None,
[single] => Some(FnArgsAsTuple::Single(single)),
[first, .., last] => Some(FnArgsAsTuple::Multi { first, last }),
}
} else {
None
}
});
let sugg_tuple_wrap_args = self.suggested_tuple_wrap(expected_input_tys, provided_args);
error = Some((
expected_arg_count,
@ -518,6 +500,33 @@ fn variadic_error<'tcx>(sess: &Session, span: Span, ty: Ty<'tcx>, cast_ty: &str)
}
}
fn suggested_tuple_wrap(
&self,
expected_input_tys: &[Ty<'tcx>],
provided_args: &'tcx [hir::Expr<'tcx>],
) -> Option<FnArgsAsTuple<'_>> {
let [expected_arg_type] = &expected_input_tys[..] else { return None };
let ty::Tuple(expected_elems) = self.resolve_vars_if_possible(*expected_arg_type).kind()
else { return None };
let expected_types: Vec<_> = expected_elems.iter().map(|k| k.expect_ty()).collect();
let supplied_types: Vec<_> = provided_args.iter().map(|arg| self.check_expr(arg)).collect();
let all_match = iter::zip(expected_types, supplied_types)
.all(|(expected, supplied)| self.can_eq(ParamEnv::empty(), expected, supplied).is_ok());
if all_match {
match provided_args {
[] => None,
[single] => Some(FnArgsAsTuple::Single(single)),
[first, .., last] => Some(FnArgsAsTuple::Multi { first, last }),
}
} else {
None
}
}
// AST fragment checking
pub(in super::super) fn check_lit(
&self,

View file

@ -0,0 +1,13 @@
// Ensure we don't suggest tuple-wrapping when we'd end up with a type error
fn main() {
// we shouldn't suggest to fix these - `2` isn't a `bool`
let _: Option<(i32, bool)> = Some(1, 2);
//~^ ERROR this enum variant takes 1 argument but 2 arguments were supplied
int_bool(1, 2);
//~^ ERROR this function takes 1 argument but 2 arguments were supplied
}
fn int_bool(_: (i32, bool)) {
}

View file

@ -0,0 +1,25 @@
error[E0061]: this enum variant takes 1 argument but 2 arguments were supplied
--> $DIR/args-instead-of-tuple-errors.rs:6:34
|
LL | let _: Option<(i32, bool)> = Some(1, 2);
| ^^^^ - - supplied 2 arguments
| |
| expected 1 argument
error[E0061]: this function takes 1 argument but 2 arguments were supplied
--> $DIR/args-instead-of-tuple-errors.rs:8:5
|
LL | int_bool(1, 2);
| ^^^^^^^^ - - supplied 2 arguments
| |
| expected 1 argument
|
note: function defined here
--> $DIR/args-instead-of-tuple-errors.rs:12:4
|
LL | fn int_bool(_: (i32, bool)) {
| ^^^^^^^^ --------------
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0061`.

View file

@ -11,8 +11,8 @@ fn main() {
let _: Option<()> = Some(());
//~^ ERROR this enum variant takes 1 argument but 0 arguments were supplied
f((1, 2)); //~ ERROR this function takes 1 argument
two_ints((1, 2)); //~ ERROR this function takes 1 argument
}
fn f(_: (i32, i32)) {
fn two_ints(_: (i32, i32)) {
}

View file

@ -11,8 +11,8 @@ fn main() {
let _: Option<()> = Some();
//~^ ERROR this enum variant takes 1 argument but 0 arguments were supplied
f(1, 2); //~ ERROR this function takes 1 argument
two_ints(1, 2); //~ ERROR this function takes 1 argument
}
fn f(_: (i32, i32)) {
fn two_ints(_: (i32, i32)) {
}

View file

@ -34,18 +34,18 @@ LL | let _: Option<()> = Some(());
error[E0061]: this function takes 1 argument but 2 arguments were supplied
--> $DIR/args-instead-of-tuple.rs:14:5
|
LL | f(1, 2);
| ^ - - supplied 2 arguments
LL | two_ints(1, 2);
| ^^^^^^^^ - - supplied 2 arguments
|
note: function defined here
--> $DIR/args-instead-of-tuple.rs:17:4
|
LL | fn f(_: (i32, i32)) {
| ^ -------------
LL | fn two_ints(_: (i32, i32)) {
| ^^^^^^^^ -------------
help: use parentheses to construct a tuple
|
LL | f((1, 2));
| + +
LL | two_ints((1, 2));
| + +
error: aborting due to 4 previous errors