about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRob Pilling <robpilling@gmail.com>2021-12-05 21:41:33 +0000
committerRob Pilling <robpilling@gmail.com>2022-01-25 22:51:19 +0000
commit54d2d30662c2832554bb127f017f7a311bb62b4e (patch)
treea19c767d98cfa7d68e24b72bc678365f00b32472
parent80059f99424c64d10f073ab5c502856d46b1c26d (diff)
downloadrust-54d2d30662c2832554bb127f017f7a311bb62b4e.tar.gz
rust-54d2d30662c2832554bb127f017f7a311bb62b4e.zip
Compare tuple element & arg types before suggesting a tuple
-rw-r--r--compiler/rustc_typeck/src/check/fn_ctxt/checks.rs53
-rw-r--r--src/test/ui/suggestions/args-instead-of-tuple-errors.rs13
-rw-r--r--src/test/ui/suggestions/args-instead-of-tuple-errors.stderr25
-rw-r--r--src/test/ui/suggestions/args-instead-of-tuple.fixed4
-rw-r--r--src/test/ui/suggestions/args-instead-of-tuple.rs4
-rw-r--r--src/test/ui/suggestions/args-instead-of-tuple.stderr12
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