Rollup merge of #74173 - estebank:struct-pat-as-enum, r=petrochenkov
Detect tuple struct incorrectly used as struct pat Subpart of #74005. r? @petrochenkov
This commit is contained in:
commit
b9a0f5803e
|
@ -3996,6 +3996,7 @@ dependencies = [
|
||||||
"rustc_data_structures",
|
"rustc_data_structures",
|
||||||
"rustc_errors",
|
"rustc_errors",
|
||||||
"rustc_hir",
|
"rustc_hir",
|
||||||
|
"rustc_hir_pretty",
|
||||||
"rustc_index",
|
"rustc_index",
|
||||||
"rustc_infer",
|
"rustc_infer",
|
||||||
"rustc_middle",
|
"rustc_middle",
|
||||||
|
|
|
@ -451,6 +451,7 @@ E0765: include_str!("./error_codes/E0765.md"),
|
||||||
E0766: include_str!("./error_codes/E0766.md"),
|
E0766: include_str!("./error_codes/E0766.md"),
|
||||||
E0767: include_str!("./error_codes/E0767.md"),
|
E0767: include_str!("./error_codes/E0767.md"),
|
||||||
E0768: include_str!("./error_codes/E0768.md"),
|
E0768: include_str!("./error_codes/E0768.md"),
|
||||||
|
E0769: include_str!("./error_codes/E0769.md"),
|
||||||
;
|
;
|
||||||
// E0006, // merged with E0005
|
// E0006, // merged with E0005
|
||||||
// E0008, // cannot bind by-move into a pattern guard
|
// E0008, // cannot bind by-move into a pattern guard
|
||||||
|
|
|
@ -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),
|
||||||
|
}
|
||||||
|
```
|
|
@ -18,6 +18,7 @@ rustc_attr = { path = "../librustc_attr" }
|
||||||
rustc_data_structures = { path = "../librustc_data_structures" }
|
rustc_data_structures = { path = "../librustc_data_structures" }
|
||||||
rustc_errors = { path = "../librustc_errors" }
|
rustc_errors = { path = "../librustc_errors" }
|
||||||
rustc_hir = { path = "../librustc_hir" }
|
rustc_hir = { path = "../librustc_hir" }
|
||||||
|
rustc_hir_pretty = { path = "../librustc_hir_pretty" }
|
||||||
rustc_target = { path = "../librustc_target" }
|
rustc_target = { path = "../librustc_target" }
|
||||||
rustc_session = { path = "../librustc_session" }
|
rustc_session = { path = "../librustc_session" }
|
||||||
smallvec = { version = "1.0", features = ["union", "may_dangle"] }
|
smallvec = { version = "1.0", features = ["union", "may_dangle"] }
|
||||||
|
|
|
@ -1082,20 +1082,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
.filter(|ident| !used_fields.contains_key(&ident))
|
.filter(|ident| !used_fields.contains_key(&ident))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
if !inexistent_fields.is_empty() && !variant.recovered {
|
let inexistent_fields_err = if !inexistent_fields.is_empty() && !variant.recovered {
|
||||||
self.error_inexistent_fields(
|
Some(self.error_inexistent_fields(
|
||||||
adt.variant_descr(),
|
adt.variant_descr(),
|
||||||
&inexistent_fields,
|
&inexistent_fields,
|
||||||
&mut unmentioned_fields,
|
&mut unmentioned_fields,
|
||||||
variant,
|
variant,
|
||||||
);
|
))
|
||||||
}
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
// Require `..` if struct has non_exhaustive attribute.
|
// Require `..` if struct has non_exhaustive attribute.
|
||||||
if variant.is_field_list_non_exhaustive() && !adt.did.is_local() && !etc {
|
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());
|
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.
|
// Report an error if incorrect number of the fields were specified.
|
||||||
if adt.is_union() {
|
if adt.is_union() {
|
||||||
if fields.len() != 1 {
|
if fields.len() != 1 {
|
||||||
|
@ -1107,7 +1110,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
tcx.sess.struct_span_err(pat.span, "`..` cannot be used in union patterns").emit();
|
tcx.sess.struct_span_err(pat.span, "`..` cannot be used in union patterns").emit();
|
||||||
}
|
}
|
||||||
} else if !etc && !unmentioned_fields.is_empty() {
|
} 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
|
no_field_errors
|
||||||
}
|
}
|
||||||
|
@ -1154,7 +1175,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
inexistent_fields: &[Ident],
|
inexistent_fields: &[Ident],
|
||||||
unmentioned_fields: &mut Vec<Ident>,
|
unmentioned_fields: &mut Vec<Ident>,
|
||||||
variant: &ty::VariantDef,
|
variant: &ty::VariantDef,
|
||||||
) {
|
) -> DiagnosticBuilder<'tcx> {
|
||||||
let tcx = self.tcx;
|
let tcx = self.tcx;
|
||||||
let (field_names, t, plural) = if inexistent_fields.len() == 1 {
|
let (field_names, t, plural) = if inexistent_fields.len() == 1 {
|
||||||
(format!("a field named `{}`", inexistent_fields[0]), "this", "")
|
(format!("a field named `{}`", inexistent_fields[0]), "this", "")
|
||||||
|
@ -1221,15 +1242,62 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
it explicitly.",
|
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(
|
fn error_unmentioned_fields(
|
||||||
&self,
|
&self,
|
||||||
span: Span,
|
span: Span,
|
||||||
unmentioned_fields: &[Ident],
|
unmentioned_fields: &[Ident],
|
||||||
variant: &ty::VariantDef,
|
) -> DiagnosticBuilder<'tcx> {
|
||||||
) {
|
|
||||||
let field_names = if unmentioned_fields.len() == 1 {
|
let field_names = if unmentioned_fields.len() == 1 {
|
||||||
format!("field `{}`", unmentioned_fields[0])
|
format!("field `{}`", unmentioned_fields[0])
|
||||||
} else {
|
} else {
|
||||||
|
@ -1248,9 +1316,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
field_names
|
field_names
|
||||||
);
|
);
|
||||||
diag.span_label(span, format!("missing {}", 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()) {
|
if self.tcx.sess.teach(&diag.get_code().unwrap()) {
|
||||||
diag.note(
|
diag.note(
|
||||||
"This error indicates that a pattern for a struct fails to specify a \
|
"This error indicates that a pattern for a struct fails to specify a \
|
||||||
|
@ -1259,7 +1324,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
ignore unwanted fields.",
|
ignore unwanted fields.",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
diag.emit();
|
diag
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_pat_box(
|
fn check_pat_box(
|
||||||
|
|
|
@ -2,8 +2,7 @@ struct S(usize, usize, usize, usize);
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
if let S { a, b, c, d } = S(1, 2, 3, 4) {
|
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 tuple variant `S` written as struct variant
|
||||||
//~| ERROR pattern does not mention fields `0`, `1`, `2`, `3` [E0027]
|
|
||||||
println!("hi");
|
println!("hi");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,9 @@
|
||||||
error[E0026]: struct `S` does not have fields named `a`, `b`, `c`, `d`
|
error[E0769]: tuple variant `S` written as struct variant
|
||||||
--> $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`
|
|
||||||
--> $DIR/missing-fields-in-struct-pattern.rs:4:12
|
--> $DIR/missing-fields-in-struct-pattern.rs:4:12
|
||||||
|
|
|
|
||||||
LL | if let S { a, b, c, d } = S(1, 2, 3, 4) {
|
LL | if let S { a, b, c, d } = S(1, 2, 3, 4) {
|
||||||
| ^^^^^^^^^^^^^^^^ missing fields `0`, `1`, `2`, `3`
|
| ^^^^^^^^^^^^^^^^ help: use the tuple variant pattern syntax instead: `S(a, b, c, d)`
|
||||||
|
|
|
||||||
= note: trying to match a tuple variant with a struct variant pattern
|
|
||||||
|
|
||||||
error: aborting due to 2 previous errors
|
error: aborting due to previous error
|
||||||
|
|
||||||
Some errors have detailed explanations: E0026, E0027.
|
For more information about this error, try `rustc --explain E0769`.
|
||||||
For more information about an error, try `rustc --explain E0026`.
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ enum X {
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
match X::Y(0) {
|
match X::Y(0) {
|
||||||
X::Y { number } => {} //~ ERROR does not have a field named `number`
|
X::Y { number } => {}
|
||||||
//~^ ERROR pattern does not mention field `0`
|
//~^ ERROR tuple variant `X::Y` written as struct variant
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,9 @@
|
||||||
error[E0026]: variant `X::Y` does not have a field named `number`
|
error[E0769]: tuple variant `X::Y` written as struct variant
|
||||||
--> $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`
|
|
||||||
--> $DIR/issue-41314.rs:7:9
|
--> $DIR/issue-41314.rs:7:9
|
||||||
|
|
|
|
||||||
LL | X::Y { number } => {}
|
LL | X::Y { number } => {}
|
||||||
| ^^^^^^^^^^^^^^^ missing field `0`
|
| ^^^^^^^^^^^^^^^ help: use the tuple variant pattern syntax instead: `X::Y(number)`
|
||||||
|
|
|
||||||
= note: trying to match a tuple variant with a struct variant pattern
|
|
||||||
|
|
||||||
error: aborting due to 2 previous errors
|
error: aborting due to previous error
|
||||||
|
|
||||||
Some errors have detailed explanations: E0026, E0027.
|
For more information about this error, try `rustc --explain E0769`.
|
||||||
For more information about an error, try `rustc --explain E0026`.
|
|
||||||
|
|
|
@ -48,18 +48,18 @@ error: union patterns should have exactly one field
|
||||||
LL | let U { a, b } = u;
|
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
|
error: union patterns should have exactly one field
|
||||||
--> $DIR/union-fields-2.rs:18:9
|
--> $DIR/union-fields-2.rs:18:9
|
||||||
|
|
|
|
||||||
LL | let U { a, b, c } = u;
|
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
|
error: union patterns should have exactly one field
|
||||||
--> $DIR/union-fields-2.rs:20:9
|
--> $DIR/union-fields-2.rs:20:9
|
||||||
|
|
|
|
||||||
|
|
Loading…
Reference in New Issue