about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2021-06-11 10:25:53 +0000
committerbors <bors@rust-lang.org>2021-06-11 10:25:53 +0000
commitdddebf94bccddaa7b8836380c1d90b34553d79d0 (patch)
tree791e02b5df88211ddfaa1131936691bf4818c5db
parent66ba81059e15b3466c71fe5b5bf2418702dd1fd1 (diff)
parent6206247335619ac83550fae2013ead4e66b454d1 (diff)
downloadrust-dddebf94bccddaa7b8836380c1d90b34553d79d0.tar.gz
rust-dddebf94bccddaa7b8836380c1d90b34553d79d0.zip
Auto merge of #86116 - FabianWolff:issue-86100, r=varkor
Suggest a trailing comma if a 1-tuple is expected and a parenthesized expression is found

This pull request fixes #86100. The following code:
```rust
fn main() {
    let t: (i32,) = (1);
}
```
currently produces:
```
warning: unnecessary parentheses around assigned value
 --> test.rs:2:21
  |
2 |     let t: (i32,) = (1);
  |                     ^^^ help: remove these parentheses
  |
  = note: `#[warn(unused_parens)]` on by default

error[E0308]: mismatched types
 --> test.rs:2:21
  |
2 |     let t: (i32,) = (1);
  |            ------   ^^^ expected tuple, found integer
  |            |
  |            expected due to this
  |
  = note: expected tuple `(i32,)`
              found type `{integer}`

error: aborting due to previous error; 1 warning emitted
```
With my changes, I get the same warning and the following error:
```
error[E0308]: mismatched types
 --> test.rs:2:21
  |
2 |     let t: (i32,) = (1);
  |            ------   ^^^ expected tuple, found integer
  |            |
  |            expected due to this
  |
  = note: expected tuple `(i32,)`
              found type `{integer}`
help: use a trailing comma to create a tuple with one element
  |
2 |     let t: (i32,) = (1,);
  |                     ^^^^
```
i.e. I have added a suggestion to add a trailing comma to create a 1-tuple. This suggestion is only issued if a 1-tuple is expected and the expression (`(1)` in the example above) is surrounded by parentheses and does not already have a tuple type. In this situation, I'd say that it is pretty likely that the user meant to create a tuple.
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/mod.rs29
-rw-r--r--src/test/ui/suggestions/issue-86100-tuple-paren-comma.rs25
-rw-r--r--src/test/ui/suggestions/issue-86100-tuple-paren-comma.stderr55
3 files changed, 108 insertions, 1 deletions
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index fb762d2deba..e3a79fe2653 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -64,6 +64,7 @@ use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_hir::lang_items::LangItem;
 use rustc_hir::{Item, ItemKind, Node};
+use rustc_middle::dep_graph::DepContext;
 use rustc_middle::ty::error::TypeError;
 use rustc_middle::ty::{
     self,
@@ -1965,7 +1966,33 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                 struct_span_err!(self.tcx.sess, span, E0580, "{}", failure_str)
             }
             FailureCode::Error0308(failure_str) => {
-                struct_span_err!(self.tcx.sess, span, E0308, "{}", failure_str)
+                let mut err = struct_span_err!(self.tcx.sess, span, E0308, "{}", failure_str);
+                if let ValuePairs::Types(ty::error::ExpectedFound { expected, found }) =
+                    trace.values
+                {
+                    // If a tuple of length one was expected and the found expression has
+                    // parentheses around it, perhaps the user meant to write `(expr,)` to
+                    // build a tuple (issue #86100)
+                    match (expected.kind(), found.kind()) {
+                        (ty::Tuple(_), ty::Tuple(_)) => {}
+                        (ty::Tuple(_), _) if expected.tuple_fields().count() == 1 => {
+                            if let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span) {
+                                if let Some(code) =
+                                    code.strip_prefix('(').and_then(|s| s.strip_suffix(')'))
+                                {
+                                    err.span_suggestion(
+                                        span,
+                                        "use a trailing comma to create a tuple with one element",
+                                        format!("({},)", code),
+                                        Applicability::MaybeIncorrect,
+                                    );
+                                }
+                            }
+                        }
+                        _ => {}
+                    }
+                }
+                err
             }
             FailureCode::Error0644(failure_str) => {
                 struct_span_err!(self.tcx.sess, span, E0644, "{}", failure_str)
diff --git a/src/test/ui/suggestions/issue-86100-tuple-paren-comma.rs b/src/test/ui/suggestions/issue-86100-tuple-paren-comma.rs
new file mode 100644
index 00000000000..fa9d1a88928
--- /dev/null
+++ b/src/test/ui/suggestions/issue-86100-tuple-paren-comma.rs
@@ -0,0 +1,25 @@
+// Tests that a suggestion is issued for type mismatch errors when a
+// 1-tuple is expected and a parenthesized expression of non-tuple
+// type is supplied.
+
+fn foo<T>(_t: (T,)) {}
+struct S { _s: (String,) }
+
+fn main() {
+    let _x: (i32,) = (5);
+    //~^ ERROR: mismatched types [E0308]
+    //~| HELP: use a trailing comma to create a tuple with one element
+
+    foo((Some(3)));
+    //~^ ERROR: mismatched types [E0308]
+    //~| HELP: use a trailing comma to create a tuple with one element
+
+    let _s = S { _s: ("abc".to_string()) };
+    //~^ ERROR: mismatched types [E0308]
+    //~| HELP: use a trailing comma to create a tuple with one element
+
+    // Do not issue the suggestion if the found type is already a tuple.
+    let t = (1, 2);
+    let _x: (i32,) = (t);
+    //~^ ERROR: mismatched types [E0308]
+}
diff --git a/src/test/ui/suggestions/issue-86100-tuple-paren-comma.stderr b/src/test/ui/suggestions/issue-86100-tuple-paren-comma.stderr
new file mode 100644
index 00000000000..575379690b4
--- /dev/null
+++ b/src/test/ui/suggestions/issue-86100-tuple-paren-comma.stderr
@@ -0,0 +1,55 @@
+error[E0308]: mismatched types
+  --> $DIR/issue-86100-tuple-paren-comma.rs:9:22
+   |
+LL |     let _x: (i32,) = (5);
+   |             ------   ^^^ expected tuple, found integer
+   |             |
+   |             expected due to this
+   |
+   = note: expected tuple `(i32,)`
+               found type `{integer}`
+help: use a trailing comma to create a tuple with one element
+   |
+LL |     let _x: (i32,) = (5,);
+   |                      ^^^^
+
+error[E0308]: mismatched types
+  --> $DIR/issue-86100-tuple-paren-comma.rs:13:9
+   |
+LL |     foo((Some(3)));
+   |         ^^^^^^^^^ expected tuple, found enum `Option`
+   |
+   = note: expected tuple `(_,)`
+               found enum `Option<{integer}>`
+help: use a trailing comma to create a tuple with one element
+   |
+LL |     foo((Some(3),));
+   |         ^^^^^^^^^^
+
+error[E0308]: mismatched types
+  --> $DIR/issue-86100-tuple-paren-comma.rs:17:22
+   |
+LL |     let _s = S { _s: ("abc".to_string()) };
+   |                      ^^^^^^^^^^^^^^^^^^^ expected tuple, found struct `String`
+   |
+   = note: expected tuple `(String,)`
+             found struct `String`
+help: use a trailing comma to create a tuple with one element
+   |
+LL |     let _s = S { _s: ("abc".to_string(),) };
+   |                      ^^^^^^^^^^^^^^^^^^^^
+
+error[E0308]: mismatched types
+  --> $DIR/issue-86100-tuple-paren-comma.rs:23:22
+   |
+LL |     let _x: (i32,) = (t);
+   |             ------   ^^^ expected a tuple with 1 element, found one with 2 elements
+   |             |
+   |             expected due to this
+   |
+   = note: expected tuple `(i32,)`
+              found tuple `({integer}, {integer})`
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0308`.