mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-27 18:57:42 +03:00
Syntactically reject tuple index shorthands in struct patterns to fix a correctness regression
This commit is contained in:
@@ -866,7 +866,7 @@ fn parse_borrow_modifiers(&mut self) -> (ast::BorrowKind, ast::Mutability) {
|
||||
// `raw [ const | mut ]`.
|
||||
let found_raw = self.eat_keyword(exp!(Raw));
|
||||
assert!(found_raw);
|
||||
let mutability = self.parse_const_or_mut().unwrap();
|
||||
let mutability = self.parse_mut_or_const().unwrap();
|
||||
(ast::BorrowKind::Raw, mutability)
|
||||
} else {
|
||||
match self.parse_pin_and_mut() {
|
||||
|
||||
@@ -1302,12 +1302,16 @@ fn parse_const_block(&mut self, span: Span, pat: bool) -> PResult<'a, Box<Expr>>
|
||||
Ok(self.mk_expr_with_attrs(span.to(blk_span), kind, attrs))
|
||||
}
|
||||
|
||||
/// Parses mutability (`mut` or nothing).
|
||||
/// Parse nothing or `mut`.
|
||||
fn parse_mutability(&mut self) -> Mutability {
|
||||
if self.eat_keyword(exp!(Mut)) { Mutability::Mut } else { Mutability::Not }
|
||||
}
|
||||
|
||||
/// Parses reference binding mode (`ref`, `ref mut`, `ref pin const`, `ref pin mut`, or nothing).
|
||||
/// Parse nothing or a by-reference mode.
|
||||
///
|
||||
/// ```ebnf
|
||||
/// ByRef = "ref" PinAndMut?
|
||||
/// ```
|
||||
fn parse_byref(&mut self) -> ByRef {
|
||||
if self.eat_keyword(exp!(Ref)) {
|
||||
let (pinnedness, mutability) = self.parse_pin_and_mut();
|
||||
@@ -1317,8 +1321,12 @@ fn parse_byref(&mut self) -> ByRef {
|
||||
}
|
||||
}
|
||||
|
||||
/// Possibly parses mutability (`const` or `mut`).
|
||||
fn parse_const_or_mut(&mut self) -> Option<Mutability> {
|
||||
/// Parse nothing or "explicit" mutability.
|
||||
///
|
||||
/// ```ebnf
|
||||
/// MutOrConst = "mut" | "const"
|
||||
/// ```
|
||||
fn parse_mut_or_const(&mut self) -> Option<Mutability> {
|
||||
if self.eat_keyword(exp!(Mut)) {
|
||||
Some(Mutability::Mut)
|
||||
} else if self.eat_keyword(exp!(Const)) {
|
||||
@@ -1328,6 +1336,11 @@ fn parse_const_or_mut(&mut self) -> Option<Mutability> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse a field name.
|
||||
///
|
||||
/// ```enbf
|
||||
/// FieldName = IntLit | Ident
|
||||
/// ```
|
||||
fn parse_field_name(&mut self) -> PResult<'a, Ident> {
|
||||
if let token::Literal(token::Lit { kind: token::Integer, symbol, suffix }) = self.token.kind
|
||||
{
|
||||
|
||||
@@ -1731,11 +1731,14 @@ fn recover_bad_dot_dot(&self) {
|
||||
self.dcx().emit_err(DotDotDotForRemainingFields { span: self.token.span, token_str });
|
||||
}
|
||||
|
||||
/// Parse a field in a struct pattern.
|
||||
///
|
||||
/// ```ebnf
|
||||
/// PatField = FieldName ":" Pat | "box"? "mut"? ByRef? Ident
|
||||
/// ```
|
||||
fn parse_pat_field(&mut self, lo: Span, attrs: AttrVec) -> PResult<'a, PatField> {
|
||||
// Check if a colon exists one ahead. This means we're parsing a fieldname.
|
||||
let hi;
|
||||
let (subpat, fieldname, is_shorthand) = if self.look_ahead(1, |t| t == &token::Colon) {
|
||||
// Parsing a pattern of the form `fieldname: pat`.
|
||||
let fieldname = self.parse_field_name()?;
|
||||
self.bump();
|
||||
let pat = self.parse_pat_allow_top_guard(
|
||||
@@ -1747,7 +1750,6 @@ fn parse_pat_field(&mut self, lo: Span, attrs: AttrVec) -> PResult<'a, PatField>
|
||||
hi = pat.span;
|
||||
(pat, fieldname, false)
|
||||
} else {
|
||||
// Parsing a pattern of the form `(box) (ref) (mut) fieldname`.
|
||||
let is_box = self.eat_keyword(exp!(Box));
|
||||
if is_box {
|
||||
self.psess.gated_spans.gate(sym::box_patterns, self.prev_token.span);
|
||||
@@ -1756,7 +1758,7 @@ fn parse_pat_field(&mut self, lo: Span, attrs: AttrVec) -> PResult<'a, PatField>
|
||||
let mutability = self.parse_mutability();
|
||||
let by_ref = self.parse_byref();
|
||||
|
||||
let fieldname = self.parse_field_name()?;
|
||||
let fieldname = self.parse_ident_common(false)?;
|
||||
hi = self.prev_token.span;
|
||||
let ann = BindingMode(by_ref, mutability);
|
||||
let fieldpat = self.mk_pat_ident(boxed_span.to(hi), ann, fieldname);
|
||||
|
||||
@@ -596,7 +596,7 @@ fn parse_remaining_bounds(
|
||||
/// Parses a raw pointer with a C-style typo
|
||||
fn parse_ty_c_style_pointer(&mut self) -> PResult<'a, TyKind> {
|
||||
let kw_span = self.token.span;
|
||||
let mutbl = self.parse_const_or_mut();
|
||||
let mutbl = self.parse_mut_or_const();
|
||||
|
||||
if let Some(mutbl) = mutbl
|
||||
&& self.eat(exp!(Star))
|
||||
@@ -630,7 +630,7 @@ fn parse_ty_c_style_pointer(&mut self) -> PResult<'a, TyKind> {
|
||||
|
||||
/// Parses a raw pointer type: `*[const | mut] $type`.
|
||||
fn parse_ty_ptr(&mut self) -> PResult<'a, TyKind> {
|
||||
let mutbl = self.parse_const_or_mut().unwrap_or_else(|| {
|
||||
let mutbl = self.parse_mut_or_const().unwrap_or_else(|| {
|
||||
let span = self.prev_token.span;
|
||||
self.dcx().emit_err(ExpectedMutOrConstInRawPointerType {
|
||||
span,
|
||||
@@ -774,14 +774,16 @@ fn parse_borrowed_pointee(&mut self) -> PResult<'a, TyKind> {
|
||||
})
|
||||
}
|
||||
|
||||
/// Parses `pin` and `mut` annotations on references, patterns, or borrow modifiers.
|
||||
/// Parse nothing, mutability or `pin` followed by "explicit" mutability.
|
||||
///
|
||||
/// It must be either `pin const`, `pin mut`, `mut`, or nothing (immutable).
|
||||
/// ```ebnf
|
||||
/// PinAndMut = "pin" MutOrConst | "mut"
|
||||
/// ```
|
||||
pub(crate) fn parse_pin_and_mut(&mut self) -> (Pinnedness, Mutability) {
|
||||
if self.token.is_ident_named(sym::pin) && self.look_ahead(1, Token::is_mutability) {
|
||||
self.psess.gated_spans.gate(sym::pin_ergonomics, self.token.span);
|
||||
assert!(self.eat_keyword(exp!(Pin)));
|
||||
let mutbl = self.parse_const_or_mut().unwrap();
|
||||
let mutbl = self.parse_mut_or_const().unwrap();
|
||||
(Pinnedness::Pinned, mutbl)
|
||||
} else {
|
||||
(Pinnedness::Not, self.parse_mutability())
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
// Check that tuple indices in struct exprs & pats don't have a shorthand.
|
||||
// If they did it would be possible to bind and reference numeric identifiers
|
||||
// which is undesirable.
|
||||
|
||||
struct Rgb(u8, u8, u8);
|
||||
|
||||
#[cfg(false)] // ensures that this is a *syntax* error, not just a semantic one!
|
||||
fn scope() {
|
||||
// FIXME: Better recover and also report a diagnostic for the other two fields.
|
||||
let Rgb { 0, 1, 2 };
|
||||
//~^ ERROR expected identifier, found `0`
|
||||
|
||||
let _ = Rgb { 0, 1, 2 };
|
||||
//~^ ERROR expected identifier, found `0`
|
||||
//~| ERROR expected identifier, found `1`
|
||||
//~| ERROR expected identifier, found `2`
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
+12
-4
@@ -1,5 +1,13 @@
|
||||
error: expected identifier, found `0`
|
||||
--> $DIR/struct-field-numeric-shorthand.rs:4:19
|
||||
--> $DIR/struct-expr-pat-tuple-index-shorthand.rs:10:15
|
||||
|
|
||||
LL | let Rgb { 0, 1, 2 };
|
||||
| --- ^ expected identifier
|
||||
| |
|
||||
| while parsing the fields for this pattern
|
||||
|
||||
error: expected identifier, found `0`
|
||||
--> $DIR/struct-expr-pat-tuple-index-shorthand.rs:13:19
|
||||
|
|
||||
LL | let _ = Rgb { 0, 1, 2 };
|
||||
| --- ^ expected identifier
|
||||
@@ -7,7 +15,7 @@ LL | let _ = Rgb { 0, 1, 2 };
|
||||
| while parsing this struct
|
||||
|
||||
error: expected identifier, found `1`
|
||||
--> $DIR/struct-field-numeric-shorthand.rs:4:22
|
||||
--> $DIR/struct-expr-pat-tuple-index-shorthand.rs:13:22
|
||||
|
|
||||
LL | let _ = Rgb { 0, 1, 2 };
|
||||
| --- ^ expected identifier
|
||||
@@ -15,12 +23,12 @@ LL | let _ = Rgb { 0, 1, 2 };
|
||||
| while parsing this struct
|
||||
|
||||
error: expected identifier, found `2`
|
||||
--> $DIR/struct-field-numeric-shorthand.rs:4:25
|
||||
--> $DIR/struct-expr-pat-tuple-index-shorthand.rs:13:25
|
||||
|
|
||||
LL | let _ = Rgb { 0, 1, 2 };
|
||||
| --- ^ expected identifier
|
||||
| |
|
||||
| while parsing this struct
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
struct Rgb(u8, u8, u8);
|
||||
|
||||
fn main() {
|
||||
let _ = Rgb { 0, 1, 2 };
|
||||
//~^ ERROR expected identifier, found `0`
|
||||
//~| ERROR expected identifier, found `1`
|
||||
//~| ERROR expected identifier, found `2`
|
||||
}
|
||||
@@ -2,7 +2,9 @@ error: expected identifier, found keyword `Self`
|
||||
--> $DIR/self-ctor-133272.rs:17:21
|
||||
|
|
||||
LL | let S { ref Self } = todo!();
|
||||
| ^^^^ expected identifier, found keyword
|
||||
| - ^^^^ expected identifier, found keyword
|
||||
| |
|
||||
| while parsing the fields for this pattern
|
||||
|
||||
error[E0422]: cannot find struct, variant or union type `S` in this scope
|
||||
--> $DIR/self-ctor-133272.rs:17:13
|
||||
|
||||
@@ -22,7 +22,6 @@ pub fn main() {
|
||||
Foo { Self } => (),
|
||||
//~^ ERROR expected identifier, found keyword `Self`
|
||||
//~| ERROR mismatched types
|
||||
//~| ERROR `Foo` does not have a field named `Self`
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,22 +39,24 @@ error: expected identifier, found keyword `Self`
|
||||
--> $DIR/self_type_keyword.rs:22:15
|
||||
|
|
||||
LL | Foo { Self } => (),
|
||||
| ^^^^ expected identifier, found keyword
|
||||
| --- ^^^^ expected identifier, found keyword
|
||||
| |
|
||||
| while parsing the fields for this pattern
|
||||
|
||||
error: expected identifier, found keyword `Self`
|
||||
--> $DIR/self_type_keyword.rs:30:26
|
||||
--> $DIR/self_type_keyword.rs:29:26
|
||||
|
|
||||
LL | extern crate core as Self;
|
||||
| ^^^^ expected identifier, found keyword
|
||||
|
||||
error: expected identifier, found keyword `Self`
|
||||
--> $DIR/self_type_keyword.rs:35:32
|
||||
--> $DIR/self_type_keyword.rs:34:32
|
||||
|
|
||||
LL | use std::option::Option as Self;
|
||||
| ^^^^ expected identifier, found keyword
|
||||
|
||||
error: expected identifier, found keyword `Self`
|
||||
--> $DIR/self_type_keyword.rs:40:11
|
||||
--> $DIR/self_type_keyword.rs:39:11
|
||||
|
|
||||
LL | trait Self {}
|
||||
| ^^^^ expected identifier, found keyword
|
||||
@@ -88,13 +90,7 @@ LL | match 15 {
|
||||
LL | Foo { Self } => (),
|
||||
| ^^^^^^^^^^^^ expected integer, found `Foo`
|
||||
|
||||
error[E0026]: struct `Foo` does not have a field named `Self`
|
||||
--> $DIR/self_type_keyword.rs:22:15
|
||||
|
|
||||
LL | Foo { Self } => (),
|
||||
| ^^^^ struct `Foo` does not have this field
|
||||
error: aborting due to 12 previous errors
|
||||
|
||||
error: aborting due to 13 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0026, E0308, E0392, E0531.
|
||||
For more information about an error, try `rustc --explain E0026`.
|
||||
Some errors have detailed explanations: E0308, E0392, E0531.
|
||||
For more information about an error, try `rustc --explain E0308`.
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
// On unmentioned tuple indices in struct patterns, don't suggest turning the pattern into a
|
||||
// tuple struct pattern and keep the struct pattern in the suggestion.
|
||||
// issue: <https://github.com/rust-lang/rust/issues/108284>
|
||||
|
||||
struct S(i32, f32);
|
||||
enum E { V(i32, f32) }
|
||||
|
||||
fn main() {
|
||||
let S { 0: _ } = S(1, 2.2); //~ ERROR: pattern does not mention field `1`
|
||||
let E::V { 0: _ } = E::V(1, 2.2); //~ ERROR: pattern does not mention field `1`
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
error[E0027]: pattern does not mention field `1`
|
||||
--> $DIR/struct-pat-unmentioned-tuple-indices.rs:9:9
|
||||
|
|
||||
LL | let S { 0: _ } = S(1, 2.2);
|
||||
| ^^^^^^^^^^ missing field `1`
|
||||
|
|
||||
help: include the missing field in the pattern
|
||||
|
|
||||
LL | let S { 0: _, 1: _ } = S(1, 2.2);
|
||||
| ++++++
|
||||
help: if you don't care about this missing field, you can explicitly ignore it
|
||||
|
|
||||
LL | let S { 0: _, 1: _ } = S(1, 2.2);
|
||||
| ++++++
|
||||
help: or always ignore missing fields here
|
||||
|
|
||||
LL | let S { 0: _, .. } = S(1, 2.2);
|
||||
| ++++
|
||||
|
||||
error[E0027]: pattern does not mention field `1`
|
||||
--> $DIR/struct-pat-unmentioned-tuple-indices.rs:10:9
|
||||
|
|
||||
LL | let E::V { 0: _ } = E::V(1, 2.2);
|
||||
| ^^^^^^^^^^^^^ missing field `1`
|
||||
|
|
||||
help: include the missing field in the pattern
|
||||
|
|
||||
LL | let E::V { 0: _, 1: _ } = E::V(1, 2.2);
|
||||
| ++++++
|
||||
help: if you don't care about this missing field, you can explicitly ignore it
|
||||
|
|
||||
LL | let E::V { 0: _, 1: _ } = E::V(1, 2.2);
|
||||
| ++++++
|
||||
help: or always ignore missing fields here
|
||||
|
|
||||
LL | let E::V { 0: _, .. } = E::V(1, 2.2);
|
||||
| ++++
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0027`.
|
||||
@@ -1,18 +0,0 @@
|
||||
struct S(i32, f32);
|
||||
enum E {
|
||||
S(i32, f32),
|
||||
}
|
||||
fn main() {
|
||||
let x = E::S(1, 2.2);
|
||||
match x {
|
||||
E::S { 0, 1 } => {}
|
||||
//~^ ERROR tuple variant `E::S` written as struct variant [E0769]
|
||||
}
|
||||
let y = S(1, 2.2);
|
||||
match y {
|
||||
S { } => {} //~ ERROR: tuple variant `S` written as struct variant [E0769]
|
||||
}
|
||||
|
||||
if let E::S { 0: a } = x { //~ ERROR: pattern does not mention field `1`
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
error[E0769]: tuple variant `E::S` written as struct variant
|
||||
--> $DIR/struct-tuple-field-names.rs:8:9
|
||||
|
|
||||
LL | E::S { 0, 1 } => {}
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
help: use the tuple variant pattern syntax instead
|
||||
|
|
||||
LL - E::S { 0, 1 } => {}
|
||||
LL + E::S(_, _) => {}
|
||||
|
|
||||
|
||||
error[E0769]: tuple variant `S` written as struct variant
|
||||
--> $DIR/struct-tuple-field-names.rs:13:9
|
||||
|
|
||||
LL | S { } => {}
|
||||
| ^^^^^
|
||||
|
|
||||
help: use the tuple variant pattern syntax instead
|
||||
|
|
||||
LL - S { } => {}
|
||||
LL + S(_, _) => {}
|
||||
|
|
||||
|
||||
error[E0027]: pattern does not mention field `1`
|
||||
--> $DIR/struct-tuple-field-names.rs:16:12
|
||||
|
|
||||
LL | if let E::S { 0: a } = x {
|
||||
| ^^^^^^^^^^^^^ missing field `1`
|
||||
|
|
||||
help: include the missing field in the pattern
|
||||
|
|
||||
LL | if let E::S { 0: a, 1: _ } = x {
|
||||
| ++++++
|
||||
help: if you don't care about this missing field, you can explicitly ignore it
|
||||
|
|
||||
LL | if let E::S { 0: a, 1: _ } = x {
|
||||
| ++++++
|
||||
help: or always ignore missing fields here
|
||||
|
|
||||
LL | if let E::S { 0: a, .. } = x {
|
||||
| ++++
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0027, E0769.
|
||||
For more information about an error, try `rustc --explain E0027`.
|
||||
@@ -0,0 +1,7 @@
|
||||
struct S(i32);
|
||||
enum E { V(i32) }
|
||||
|
||||
fn main() {
|
||||
let S {} = S(0); //~ ERROR tuple variant `S` written as struct variant
|
||||
let E::V {} = E::V(0); //~ ERROR tuple variant `E::V` written as struct variant
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
error[E0769]: tuple variant `S` written as struct variant
|
||||
--> $DIR/tuple-variant-written-as-empty-struct-variant.rs:5:9
|
||||
|
|
||||
LL | let S {} = S(0);
|
||||
| ^^^^
|
||||
|
|
||||
help: use the tuple variant pattern syntax instead
|
||||
|
|
||||
LL - let S {} = S(0);
|
||||
LL + let S(_) = S(0);
|
||||
|
|
||||
|
||||
error[E0769]: tuple variant `E::V` written as struct variant
|
||||
--> $DIR/tuple-variant-written-as-empty-struct-variant.rs:6:9
|
||||
|
|
||||
LL | let E::V {} = E::V(0);
|
||||
| ^^^^^^^
|
||||
|
|
||||
help: use the tuple variant pattern syntax instead
|
||||
|
|
||||
LL - let E::V {} = E::V(0);
|
||||
LL + let E::V(_) = E::V(0);
|
||||
|
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0769`.
|
||||
Reference in New Issue
Block a user