mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-26 13:01:27 +03:00
Rollup merge of #155698 - fmease:no-struct-pat-tuple-index-shorthand, r=mu001999
Syntactically reject tuple index shorthands in struct patterns to fix a correctness regression Split out of PR rust-lang/rust#154492. This fixes a correctness regression introduced in PR rust-lang/rust#81235 from 2021. Crater was run in my other PR and didn't report any real regressions (https://github.com/rust-lang/rust/pull/154492#issuecomment-4187544786); a rerun has been issued for a few spurious builds (https://github.com/rust-lang/rust/pull/154492#issuecomment-4237077272) but I'm certain it won't find anything either. This is a theoretical breaking change that doesn't need any T-lang input IMHO since it's such a minute, niche and crystal clear bug that's not worth bothering them with (such a decision is not unprecedented). I'm adding it to the compatibility section of the release notes as is customary. The Reference doesn't need updating since it didn't adopt this bug and thus accurately describes this part of the grammar as it used to be before 2021-02-23 and as it's meant to be. The majority of the diff is doc comment additions & necessary UI test restructurings.
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