Detect tuple struct incorrectly used as struct pat
This commit is contained in:
parent
346aec9b02
commit
5daedea3db
10 changed files with 137 additions and 49 deletions
|
@ -3996,6 +3996,7 @@ dependencies = [
|
|||
"rustc_data_structures",
|
||||
"rustc_errors",
|
||||
"rustc_hir",
|
||||
"rustc_hir_pretty",
|
||||
"rustc_index",
|
||||
"rustc_infer",
|
||||
"rustc_middle",
|
||||
|
|
|
@ -450,6 +450,7 @@ E0765: include_str!("./error_codes/E0765.md"),
|
|||
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
|
||||
|
|
39
src/librustc_error_codes/error_codes/E0769.md
Normal file
39
src/librustc_error_codes/error_codes/E0769.md
Normal file
|
@ -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_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"] }
|
||||
|
|
|
@ -1082,20 +1082,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
.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 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
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 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
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 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
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 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
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 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
ignore unwanted fields.",
|
||||
);
|
||||
}
|
||||
diag.emit();
|
||||
diag
|
||||
}
|
||||
|
||||
fn check_pat_box(
|
||||
|
|
|
@ -2,8 +2,7 @@ struct S(usize, usize, usize, usize);
|
|||
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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`.
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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`.
|
||||
|
|
|
@ -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
|
||||
|
|
||||
|
|
Loading…
Add table
Reference in a new issue