Skip to content

Commit 07d015e

Browse files
committed
Syntactically reject tuple index shorthands in struct patterns to fix a correctness regression
1 parent 827651f commit 07d015e

16 files changed

Lines changed: 160 additions & 106 deletions

compiler/rustc_parse/src/parser/expr.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -866,7 +866,7 @@ impl<'a> Parser<'a> {
866866
// `raw [ const | mut ]`.
867867
let found_raw = self.eat_keyword(exp!(Raw));
868868
assert!(found_raw);
869-
let mutability = self.parse_const_or_mut().unwrap();
869+
let mutability = self.parse_mut_or_const().unwrap();
870870
(ast::BorrowKind::Raw, mutability)
871871
} else {
872872
match self.parse_pin_and_mut() {

compiler/rustc_parse/src/parser/mod.rs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1302,12 +1302,16 @@ impl<'a> Parser<'a> {
13021302
Ok(self.mk_expr_with_attrs(span.to(blk_span), kind, attrs))
13031303
}
13041304

1305-
/// Parses mutability (`mut` or nothing).
1305+
/// Parse nothing or `mut`.
13061306
fn parse_mutability(&mut self) -> Mutability {
13071307
if self.eat_keyword(exp!(Mut)) { Mutability::Mut } else { Mutability::Not }
13081308
}
13091309

1310-
/// Parses reference binding mode (`ref`, `ref mut`, `ref pin const`, `ref pin mut`, or nothing).
1310+
/// Parse nothing or a by-reference mode.
1311+
///
1312+
/// ```ebnf
1313+
/// ByRef = "ref" PinAndMut?
1314+
/// ```
13111315
fn parse_byref(&mut self) -> ByRef {
13121316
if self.eat_keyword(exp!(Ref)) {
13131317
let (pinnedness, mutability) = self.parse_pin_and_mut();
@@ -1317,8 +1321,12 @@ impl<'a> Parser<'a> {
13171321
}
13181322
}
13191323

1320-
/// Possibly parses mutability (`const` or `mut`).
1321-
fn parse_const_or_mut(&mut self) -> Option<Mutability> {
1324+
/// Parse nothing or "explicit" mutability.
1325+
///
1326+
/// ```ebnf
1327+
/// MutOrConst = "mut" | "const"
1328+
/// ```
1329+
fn parse_mut_or_const(&mut self) -> Option<Mutability> {
13221330
if self.eat_keyword(exp!(Mut)) {
13231331
Some(Mutability::Mut)
13241332
} else if self.eat_keyword(exp!(Const)) {
@@ -1328,6 +1336,11 @@ impl<'a> Parser<'a> {
13281336
}
13291337
}
13301338

1339+
/// Parse a field name.
1340+
///
1341+
/// ```enbf
1342+
/// FieldName = IntLit | Ident
1343+
/// ```
13311344
fn parse_field_name(&mut self) -> PResult<'a, Ident> {
13321345
if let token::Literal(token::Lit { kind: token::Integer, symbol, suffix }) = self.token.kind
13331346
{

compiler/rustc_parse/src/parser/pat.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1731,11 +1731,14 @@ impl<'a> Parser<'a> {
17311731
self.dcx().emit_err(DotDotDotForRemainingFields { span: self.token.span, token_str });
17321732
}
17331733

1734+
/// Parse a field in a struct pattern.
1735+
///
1736+
/// ```ebnf
1737+
/// PatField = FieldName ":" Pat | "box"? "mut"? ByRef? Ident
1738+
/// ```
17341739
fn parse_pat_field(&mut self, lo: Span, attrs: AttrVec) -> PResult<'a, PatField> {
1735-
// Check if a colon exists one ahead. This means we're parsing a fieldname.
17361740
let hi;
17371741
let (subpat, fieldname, is_shorthand) = if self.look_ahead(1, |t| t == &token::Colon) {
1738-
// Parsing a pattern of the form `fieldname: pat`.
17391742
let fieldname = self.parse_field_name()?;
17401743
self.bump();
17411744
let pat = self.parse_pat_allow_top_guard(
@@ -1747,7 +1750,6 @@ impl<'a> Parser<'a> {
17471750
hi = pat.span;
17481751
(pat, fieldname, false)
17491752
} else {
1750-
// Parsing a pattern of the form `(box) (ref) (mut) fieldname`.
17511753
let is_box = self.eat_keyword(exp!(Box));
17521754
if is_box {
17531755
self.psess.gated_spans.gate(sym::box_patterns, self.prev_token.span);
@@ -1756,7 +1758,7 @@ impl<'a> Parser<'a> {
17561758
let mutability = self.parse_mutability();
17571759
let by_ref = self.parse_byref();
17581760

1759-
let fieldname = self.parse_field_name()?;
1761+
let fieldname = self.parse_ident_common(false)?;
17601762
hi = self.prev_token.span;
17611763
let ann = BindingMode(by_ref, mutability);
17621764
let fieldpat = self.mk_pat_ident(boxed_span.to(hi), ann, fieldname);

compiler/rustc_parse/src/parser/ty.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -596,7 +596,7 @@ impl<'a> Parser<'a> {
596596
/// Parses a raw pointer with a C-style typo
597597
fn parse_ty_c_style_pointer(&mut self) -> PResult<'a, TyKind> {
598598
let kw_span = self.token.span;
599-
let mutbl = self.parse_const_or_mut();
599+
let mutbl = self.parse_mut_or_const();
600600

601601
if let Some(mutbl) = mutbl
602602
&& self.eat(exp!(Star))
@@ -630,7 +630,7 @@ impl<'a> Parser<'a> {
630630

631631
/// Parses a raw pointer type: `*[const | mut] $type`.
632632
fn parse_ty_ptr(&mut self) -> PResult<'a, TyKind> {
633-
let mutbl = self.parse_const_or_mut().unwrap_or_else(|| {
633+
let mutbl = self.parse_mut_or_const().unwrap_or_else(|| {
634634
let span = self.prev_token.span;
635635
self.dcx().emit_err(ExpectedMutOrConstInRawPointerType {
636636
span,
@@ -774,14 +774,16 @@ impl<'a> Parser<'a> {
774774
})
775775
}
776776

777-
/// Parses `pin` and `mut` annotations on references, patterns, or borrow modifiers.
777+
/// Parse nothing, mutability or `pin` followed by "explicit" mutability.
778778
///
779-
/// It must be either `pin const`, `pin mut`, `mut`, or nothing (immutable).
779+
/// ```ebnf
780+
/// PinAndMut = "pin" MutOrConst | "mut"
781+
/// ```
780782
pub(crate) fn parse_pin_and_mut(&mut self) -> (Pinnedness, Mutability) {
781783
if self.token.is_ident_named(sym::pin) && self.look_ahead(1, Token::is_mutability) {
782784
self.psess.gated_spans.gate(sym::pin_ergonomics, self.token.span);
783785
assert!(self.eat_keyword(exp!(Pin)));
784-
let mutbl = self.parse_const_or_mut().unwrap();
786+
let mutbl = self.parse_mut_or_const().unwrap();
785787
(Pinnedness::Pinned, mutbl)
786788
} else {
787789
(Pinnedness::Not, self.parse_mutability())
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Check that tuple indices in struct exprs & pats don't have a shorthand.
2+
// If they did it would be possible to bind and reference numeric identifiers
3+
// which is undesirable.
4+
5+
struct Rgb(u8, u8, u8);
6+
7+
#[cfg(false)] // ensures that this is a *syntax* error, not just a semantic one!
8+
fn scope() {
9+
// FIXME: Better recover and also report a diagnostic for the other two fields.
10+
let Rgb { 0, 1, 2 };
11+
//~^ ERROR expected identifier, found `0`
12+
13+
let _ = Rgb { 0, 1, 2 };
14+
//~^ ERROR expected identifier, found `0`
15+
//~| ERROR expected identifier, found `1`
16+
//~| ERROR expected identifier, found `2`
17+
}
18+
19+
fn main() {}

tests/ui/parser/struct-field-numeric-shorthand.stderr renamed to tests/ui/parser/struct-expr-pat-tuple-index-shorthand.stderr

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,34 @@
11
error: expected identifier, found `0`
2-
--> $DIR/struct-field-numeric-shorthand.rs:4:19
2+
--> $DIR/struct-expr-pat-tuple-index-shorthand.rs:10:15
3+
|
4+
LL | let Rgb { 0, 1, 2 };
5+
| --- ^ expected identifier
6+
| |
7+
| while parsing the fields for this pattern
8+
9+
error: expected identifier, found `0`
10+
--> $DIR/struct-expr-pat-tuple-index-shorthand.rs:13:19
311
|
412
LL | let _ = Rgb { 0, 1, 2 };
513
| --- ^ expected identifier
614
| |
715
| while parsing this struct
816

917
error: expected identifier, found `1`
10-
--> $DIR/struct-field-numeric-shorthand.rs:4:22
18+
--> $DIR/struct-expr-pat-tuple-index-shorthand.rs:13:22
1119
|
1220
LL | let _ = Rgb { 0, 1, 2 };
1321
| --- ^ expected identifier
1422
| |
1523
| while parsing this struct
1624

1725
error: expected identifier, found `2`
18-
--> $DIR/struct-field-numeric-shorthand.rs:4:25
26+
--> $DIR/struct-expr-pat-tuple-index-shorthand.rs:13:25
1927
|
2028
LL | let _ = Rgb { 0, 1, 2 };
2129
| --- ^ expected identifier
2230
| |
2331
| while parsing this struct
2432

25-
error: aborting due to 3 previous errors
33+
error: aborting due to 4 previous errors
2634

tests/ui/parser/struct-field-numeric-shorthand.rs

Lines changed: 0 additions & 8 deletions
This file was deleted.

tests/ui/pattern/self-ctor-133272.stderr

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ error: expected identifier, found keyword `Self`
22
--> $DIR/self-ctor-133272.rs:17:21
33
|
44
LL | let S { ref Self } = todo!();
5-
| ^^^^ expected identifier, found keyword
5+
| - ^^^^ expected identifier, found keyword
6+
| |
7+
| while parsing the fields for this pattern
68

79
error[E0422]: cannot find struct, variant or union type `S` in this scope
810
--> $DIR/self-ctor-133272.rs:17:13

tests/ui/self/self_type_keyword.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ pub fn main() {
2222
Foo { Self } => (),
2323
//~^ ERROR expected identifier, found keyword `Self`
2424
//~| ERROR mismatched types
25-
//~| ERROR `Foo` does not have a field named `Self`
2625
}
2726
}
2827

tests/ui/self/self_type_keyword.stderr

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -39,22 +39,24 @@ error: expected identifier, found keyword `Self`
3939
--> $DIR/self_type_keyword.rs:22:15
4040
|
4141
LL | Foo { Self } => (),
42-
| ^^^^ expected identifier, found keyword
42+
| --- ^^^^ expected identifier, found keyword
43+
| |
44+
| while parsing the fields for this pattern
4345

4446
error: expected identifier, found keyword `Self`
45-
--> $DIR/self_type_keyword.rs:30:26
47+
--> $DIR/self_type_keyword.rs:29:26
4648
|
4749
LL | extern crate core as Self;
4850
| ^^^^ expected identifier, found keyword
4951

5052
error: expected identifier, found keyword `Self`
51-
--> $DIR/self_type_keyword.rs:35:32
53+
--> $DIR/self_type_keyword.rs:34:32
5254
|
5355
LL | use std::option::Option as Self;
5456
| ^^^^ expected identifier, found keyword
5557

5658
error: expected identifier, found keyword `Self`
57-
--> $DIR/self_type_keyword.rs:40:11
59+
--> $DIR/self_type_keyword.rs:39:11
5860
|
5961
LL | trait Self {}
6062
| ^^^^ expected identifier, found keyword
@@ -88,13 +90,7 @@ LL | match 15 {
8890
LL | Foo { Self } => (),
8991
| ^^^^^^^^^^^^ expected integer, found `Foo`
9092

91-
error[E0026]: struct `Foo` does not have a field named `Self`
92-
--> $DIR/self_type_keyword.rs:22:15
93-
|
94-
LL | Foo { Self } => (),
95-
| ^^^^ struct `Foo` does not have this field
96-
97-
error: aborting due to 13 previous errors
93+
error: aborting due to 12 previous errors
9894

99-
Some errors have detailed explanations: E0026, E0308, E0392, E0531.
100-
For more information about an error, try `rustc --explain E0026`.
95+
Some errors have detailed explanations: E0308, E0392, E0531.
96+
For more information about an error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)