]> git.lizzy.rs Git - rust.git/commitdiff
Detect tuple struct incorrectly used as struct pat
authorEsteban Küber <esteban@kuber.com.ar>
Thu, 9 Jul 2020 02:39:26 +0000 (19:39 -0700)
committerEsteban Küber <esteban@kuber.com.ar>
Sun, 12 Jul 2020 17:34:48 +0000 (10:34 -0700)
Cargo.lock
src/librustc_error_codes/error_codes.rs
src/librustc_error_codes/error_codes/E0769.md [new file with mode: 0644]
src/librustc_typeck/Cargo.toml
src/librustc_typeck/check/pat.rs
src/test/ui/missing/missing-fields-in-struct-pattern.rs
src/test/ui/missing/missing-fields-in-struct-pattern.stderr
src/test/ui/type/type-check/issue-41314.rs
src/test/ui/type/type-check/issue-41314.stderr
src/test/ui/union/union-fields-2.stderr

index 2096a3dfff9eadee975257ef4cf44e98153a3d29..61a0b5caebec655794a743010b3e2f017b6d88b4 100644 (file)
@@ -3996,6 +3996,7 @@ dependencies = [
  "rustc_data_structures",
  "rustc_errors",
  "rustc_hir",
+ "rustc_hir_pretty",
  "rustc_index",
  "rustc_infer",
  "rustc_middle",
index f687221d78e039a6f3f524faf1ea2cd54a240d6b..2e1b897216bb29f9478a2ce1d9270f0b1a9ce474 100644 (file)
 E0766: include_str!("./error_codes/E0766.md"),
 E0767: include_str!("./error_codes/E0767.md"),
 E0768: include_str!("./error_codes/E0768.md"),
+E0769: include_str!("./error_codes/E0769.md"),
 ;
 //  E0006, // merged with E0005
 //  E0008, // cannot bind by-move into a pattern guard
diff --git a/src/librustc_error_codes/error_codes/E0769.md b/src/librustc_error_codes/error_codes/E0769.md
new file mode 100644 (file)
index 0000000..d1995be
--- /dev/null
@@ -0,0 +1,39 @@
+A tuple struct or tuple variant was used in a pattern as if it were a
+struct or struct variant.
+
+Erroneous code example:
+
+```compile_fail,E0769
+enum E {
+    A(i32),
+}
+let e = E::A(42);
+match e {
+    E::A { number } => println!("{}", x),
+}
+```
+
+To fix this error, you can use the tuple pattern:
+
+```
+# enum E {
+#     A(i32),
+# }
+# let e = E::A(42);
+match e {
+    E::A(number) => println!("{}", number),
+}
+```
+
+Alternatively, you can also use the struct pattern by using the correct
+field names and binding them to new identifiers:
+
+```
+# enum E {
+#     A(i32),
+# }
+# let e = E::A(42);
+match e {
+    E::A { 0: number } => println!("{}", number),
+}
+```
index 9329069c48dd1faaea58d771af81926ff15fe56a..93b503c976be4c8706ecee600cf78d08cfefe1f1 100644 (file)
@@ -18,6 +18,7 @@ rustc_attr = { path = "../librustc_attr" }
 rustc_data_structures = { path = "../librustc_data_structures" }
 rustc_errors = { path = "../librustc_errors" }
 rustc_hir = { path = "../librustc_hir" }
+rustc_hir_pretty = { path = "../librustc_hir_pretty" }
 rustc_target = { path = "../librustc_target" }
 rustc_session = { path = "../librustc_session" }
 smallvec = { version = "1.0", features = ["union", "may_dangle"] }
index ea47ae68ce7d36c63220b15c2140602a7765cc14..a654fc3dfc2dffa8d47c6adc9d81dfb36b3e4a4e 100644 (file)
@@ -1082,20 +1082,23 @@ fn check_struct_pat_fields(
             .filter(|ident| !used_fields.contains_key(&ident))
             .collect::<Vec<_>>();
 
-        if !inexistent_fields.is_empty() && !variant.recovered {
-            self.error_inexistent_fields(
+        let inexistent_fields_err = if !inexistent_fields.is_empty() && !variant.recovered {
+            Some(self.error_inexistent_fields(
                 adt.variant_descr(),
                 &inexistent_fields,
                 &mut unmentioned_fields,
                 variant,
-            );
-        }
+            ))
+        } else {
+            None
+        };
 
         // Require `..` if struct has non_exhaustive attribute.
         if variant.is_field_list_non_exhaustive() && !adt.did.is_local() && !etc {
             self.error_foreign_non_exhaustive_spat(pat, adt.variant_descr(), fields.is_empty());
         }
 
+        let mut unmentioned_err = None;
         // Report an error if incorrect number of the fields were specified.
         if adt.is_union() {
             if fields.len() != 1 {
@@ -1107,7 +1110,25 @@ fn check_struct_pat_fields(
                 tcx.sess.struct_span_err(pat.span, "`..` cannot be used in union patterns").emit();
             }
         } else if !etc && !unmentioned_fields.is_empty() {
-            self.error_unmentioned_fields(pat.span, &unmentioned_fields, variant);
+            unmentioned_err = Some(self.error_unmentioned_fields(pat.span, &unmentioned_fields));
+        }
+        match (inexistent_fields_err, unmentioned_err) {
+            (Some(mut i), Some(mut u)) => {
+                if let Some(mut e) = self.error_tuple_variant_as_struct_pat(pat, fields, variant) {
+                    // We don't want to show the inexistent fields error when this was
+                    // `Foo { a, b }` when it should have been `Foo(a, b)`.
+                    i.delay_as_bug();
+                    u.delay_as_bug();
+                    e.emit();
+                } else {
+                    i.emit();
+                    u.emit();
+                }
+            }
+            (None, Some(mut err)) | (Some(mut err), None) => {
+                err.emit();
+            }
+            (None, None) => {}
         }
         no_field_errors
     }
@@ -1154,7 +1175,7 @@ fn error_inexistent_fields(
         inexistent_fields: &[Ident],
         unmentioned_fields: &mut Vec<Ident>,
         variant: &ty::VariantDef,
-    ) {
+    ) -> DiagnosticBuilder<'tcx> {
         let tcx = self.tcx;
         let (field_names, t, plural) = if inexistent_fields.len() == 1 {
             (format!("a field named `{}`", inexistent_fields[0]), "this", "")
@@ -1221,15 +1242,62 @@ fn error_inexistent_fields(
                     it explicitly.",
             );
         }
-        err.emit();
+        err
+    }
+
+    fn error_tuple_variant_as_struct_pat(
+        &self,
+        pat: &Pat<'_>,
+        fields: &'tcx [hir::FieldPat<'tcx>],
+        variant: &ty::VariantDef,
+    ) -> Option<DiagnosticBuilder<'tcx>> {
+        if let (CtorKind::Fn, PatKind::Struct(qpath, ..)) = (variant.ctor_kind, &pat.kind) {
+            let path = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| {
+                s.print_qpath(qpath, false)
+            });
+            let mut err = struct_span_err!(
+                self.tcx.sess,
+                pat.span,
+                E0769,
+                "tuple variant `{}` written as struct variant",
+                path
+            );
+            let (sugg, appl) = if fields.len() == variant.fields.len() {
+                (
+                    fields
+                        .iter()
+                        .map(|f| match self.tcx.sess.source_map().span_to_snippet(f.pat.span) {
+                            Ok(f) => f,
+                            Err(_) => rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| {
+                                s.print_pat(f.pat)
+                            }),
+                        })
+                        .collect::<Vec<String>>()
+                        .join(", "),
+                    Applicability::MachineApplicable,
+                )
+            } else {
+                (
+                    variant.fields.iter().map(|_| "_").collect::<Vec<&str>>().join(", "),
+                    Applicability::MaybeIncorrect,
+                )
+            };
+            err.span_suggestion(
+                pat.span,
+                "use the tuple variant pattern syntax instead",
+                format!("{}({})", path, sugg),
+                appl,
+            );
+            return Some(err);
+        }
+        None
     }
 
     fn error_unmentioned_fields(
         &self,
         span: Span,
         unmentioned_fields: &[Ident],
-        variant: &ty::VariantDef,
-    ) {
+    ) -> DiagnosticBuilder<'tcx> {
         let field_names = if unmentioned_fields.len() == 1 {
             format!("field `{}`", unmentioned_fields[0])
         } else {
@@ -1248,9 +1316,6 @@ fn error_unmentioned_fields(
             field_names
         );
         diag.span_label(span, format!("missing {}", field_names));
-        if variant.ctor_kind == CtorKind::Fn {
-            diag.note("trying to match a tuple variant with a struct variant pattern");
-        }
         if self.tcx.sess.teach(&diag.get_code().unwrap()) {
             diag.note(
                 "This error indicates that a pattern for a struct fails to specify a \
@@ -1259,7 +1324,7 @@ fn error_unmentioned_fields(
                     ignore unwanted fields.",
             );
         }
-        diag.emit();
+        diag
     }
 
     fn check_pat_box(
index 24b6b55db6692fe286045cc3290ec9523c63e566..40304a674a63365b6e6321fd16128427b0ce0326 100644 (file)
@@ -2,8 +2,7 @@
 
 fn main() {
     if let S { a, b, c, d } = S(1, 2, 3, 4) {
-    //~^ ERROR struct `S` does not have fields named `a`, `b`, `c`, `d` [E0026]
-    //~| ERROR pattern does not mention fields `0`, `1`, `2`, `3` [E0027]
+    //~^ ERROR tuple variant `S` written as struct variant
         println!("hi");
     }
 }
index f7037468996f43c296692a92fb98af24d3e81376..6583524aad18f9ba401e3a9e9310549adaf2a9ad 100644 (file)
@@ -1,18 +1,9 @@
-error[E0026]: struct `S` does not have fields named `a`, `b`, `c`, `d`
-  --> $DIR/missing-fields-in-struct-pattern.rs:4:16
-   |
-LL |     if let S { a, b, c, d } = S(1, 2, 3, 4) {
-   |                ^  ^  ^  ^ struct `S` does not have these fields
-
-error[E0027]: pattern does not mention fields `0`, `1`, `2`, `3`
+error[E0769]: tuple variant `S` written as struct variant
   --> $DIR/missing-fields-in-struct-pattern.rs:4:12
    |
 LL |     if let S { a, b, c, d } = S(1, 2, 3, 4) {
-   |            ^^^^^^^^^^^^^^^^ missing fields `0`, `1`, `2`, `3`
-   |
-   = note: trying to match a tuple variant with a struct variant pattern
+   |            ^^^^^^^^^^^^^^^^ help: use the tuple variant pattern syntax instead: `S(a, b, c, d)`
 
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
-Some errors have detailed explanations: E0026, E0027.
-For more information about an error, try `rustc --explain E0026`.
+For more information about this error, try `rustc --explain E0769`.
index 856d4ff6334bc785c0a506bc6420316d8a8fa923..cbd39f5f9e6ed5c842acc199414cfb3063475edd 100644 (file)
@@ -4,7 +4,7 @@ enum X {
 
 fn main() {
     match X::Y(0) {
-        X::Y { number } => {} //~ ERROR does not have a field named `number`
-        //~^ ERROR pattern does not mention field `0`
+        X::Y { number } => {}
+        //~^ ERROR tuple variant `X::Y` written as struct variant
     }
 }
index c2bba98d10a837e6f80c9d1d6e4596acc3ad62be..bd4d2071c2059ac2c9208a87442863114340b098 100644 (file)
@@ -1,18 +1,9 @@
-error[E0026]: variant `X::Y` does not have a field named `number`
-  --> $DIR/issue-41314.rs:7:16
-   |
-LL |         X::Y { number } => {}
-   |                ^^^^^^ variant `X::Y` does not have this field
-
-error[E0027]: pattern does not mention field `0`
+error[E0769]: tuple variant `X::Y` written as struct variant
   --> $DIR/issue-41314.rs:7:9
    |
 LL |         X::Y { number } => {}
-   |         ^^^^^^^^^^^^^^^ missing field `0`
-   |
-   = note: trying to match a tuple variant with a struct variant pattern
+   |         ^^^^^^^^^^^^^^^ help: use the tuple variant pattern syntax instead: `X::Y(number)`
 
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
-Some errors have detailed explanations: E0026, E0027.
-For more information about an error, try `rustc --explain E0026`.
+For more information about this error, try `rustc --explain E0769`.
index 68cb66d89d2184627e38187f0d082a585c069994..48654347285d3be45c7a97d9f4641788e03ef972 100644 (file)
@@ -48,18 +48,18 @@ error: union patterns should have exactly one field
 LL |     let U { a, b } = u;
    |         ^^^^^^^^^^
 
-error[E0026]: union `U` does not have a field named `c`
-  --> $DIR/union-fields-2.rs:18:19
-   |
-LL |     let U { a, b, c } = u;
-   |                   ^ union `U` does not have this field
-
 error: union patterns should have exactly one field
   --> $DIR/union-fields-2.rs:18:9
    |
 LL |     let U { a, b, c } = u;
    |         ^^^^^^^^^^^^^
 
+error[E0026]: union `U` does not have a field named `c`
+  --> $DIR/union-fields-2.rs:18:19
+   |
+LL |     let U { a, b, c } = u;
+   |                   ^ union `U` does not have this field
+
 error: union patterns should have exactly one field
   --> $DIR/union-fields-2.rs:20:9
    |