diff options
| author | Rob Pilling <robpilling@gmail.com> | 2021-12-05 21:41:33 +0000 |
|---|---|---|
| committer | Rob Pilling <robpilling@gmail.com> | 2022-01-25 22:51:19 +0000 |
| commit | 54d2d30662c2832554bb127f017f7a311bb62b4e (patch) | |
| tree | a19c767d98cfa7d68e24b72bc678365f00b32472 | |
| parent | 80059f99424c64d10f073ab5c502856d46b1c26d (diff) | |
| download | rust-54d2d30662c2832554bb127f017f7a311bb62b4e.tar.gz rust-54d2d30662c2832554bb127f017f7a311bb62b4e.zip | |
Compare tuple element & arg types before suggesting a tuple
6 files changed, 79 insertions, 32 deletions
diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs index 5f3507846ba..a94e6b480d6 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs @@ -18,7 +18,7 @@ use rustc_hir::def_id::DefId; 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 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; // 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 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + 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, diff --git a/src/test/ui/suggestions/args-instead-of-tuple-errors.rs b/src/test/ui/suggestions/args-instead-of-tuple-errors.rs new file mode 100644 index 00000000000..c4e9c68e219 --- /dev/null +++ b/src/test/ui/suggestions/args-instead-of-tuple-errors.rs @@ -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)) { +} diff --git a/src/test/ui/suggestions/args-instead-of-tuple-errors.stderr b/src/test/ui/suggestions/args-instead-of-tuple-errors.stderr new file mode 100644 index 00000000000..c53c8bbdcc9 --- /dev/null +++ b/src/test/ui/suggestions/args-instead-of-tuple-errors.stderr @@ -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`. diff --git a/src/test/ui/suggestions/args-instead-of-tuple.fixed b/src/test/ui/suggestions/args-instead-of-tuple.fixed index adb832b8a7b..095be95f185 100644 --- a/src/test/ui/suggestions/args-instead-of-tuple.fixed +++ b/src/test/ui/suggestions/args-instead-of-tuple.fixed @@ -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)) { } diff --git a/src/test/ui/suggestions/args-instead-of-tuple.rs b/src/test/ui/suggestions/args-instead-of-tuple.rs index 8dbc58daeb1..3466a46df84 100644 --- a/src/test/ui/suggestions/args-instead-of-tuple.rs +++ b/src/test/ui/suggestions/args-instead-of-tuple.rs @@ -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)) { } diff --git a/src/test/ui/suggestions/args-instead-of-tuple.stderr b/src/test/ui/suggestions/args-instead-of-tuple.stderr index 95bbbdb2749..1bf7e7a8d17 100644 --- a/src/test/ui/suggestions/args-instead-of-tuple.stderr +++ b/src/test/ui/suggestions/args-instead-of-tuple.stderr @@ -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 |
