@@ -19,14 +19,14 @@ const WHILE_PARSING_OR_MSG: &str = "while parsing this or-pattern starting here"
19
19
20
20
/// Whether or not an or-pattern should be gated when occurring in the current context.
21
21
#[ derive( PartialEq , Clone , Copy ) ]
22
- pub ( super ) enum GateOr {
22
+ pub enum GateOr {
23
23
Yes ,
24
24
No ,
25
25
}
26
26
27
27
/// Whether or not to recover a `,` when parsing or-patterns.
28
28
#[ derive( PartialEq , Copy , Clone ) ]
29
- pub ( super ) enum RecoverComma {
29
+ pub enum RecoverComma {
30
30
Yes ,
31
31
No ,
32
32
}
@@ -37,80 +37,57 @@ impl<'a> Parser<'a> {
37
37
/// Corresponds to `pat<no_top_alt>` in RFC 2535 and does not admit or-patterns
38
38
/// at the top level. Used when parsing the parameters of lambda expressions,
39
39
/// functions, function pointers, and `pat` macro fragments.
40
- pub fn parse_pat ( & mut self , expected : Expected ) -> PResult < ' a , P < Pat > > {
40
+ pub fn parse_pat_no_top_alt ( & mut self , expected : Expected ) -> PResult < ' a , P < Pat > > {
41
41
self . parse_pat_with_range_pat ( true , expected)
42
42
}
43
43
44
- /// Entry point to the main pattern parser.
44
+ /// Parses a pattern.
45
+ ///
45
46
/// Corresponds to `top_pat` in RFC 2535 and allows or-pattern at the top level.
46
- pub ( super ) fn parse_top_pat (
47
+ /// Used for parsing patterns in all cases when `pat<no_top_alt>` is not used.
48
+ ///
49
+ /// Note that after the FCP in <https://github.com/rust-lang/rust/issues/81415>,
50
+ /// a leading vert is allowed in nested or-patterns, too. This allows us to
51
+ /// simplify the grammar somewhat.
52
+ pub fn parse_pat_allow_top_alt (
47
53
& mut self ,
54
+ expected : Expected ,
48
55
gate_or : GateOr ,
49
56
rc : RecoverComma ,
50
57
) -> PResult < ' a , P < Pat > > {
51
58
// Allow a '|' before the pats (RFCs 1925, 2530, and 2535).
52
- let gated_leading_vert = self . eat_or_separator ( None ) && gate_or == GateOr :: Yes ;
53
- let leading_vert_span = self . prev_token . span ;
54
-
55
- // Parse the possibly-or-pattern.
56
- let pat = self . parse_pat_with_or ( None , gate_or, rc) ?;
57
-
58
- // If we parsed a leading `|` which should be gated,
59
- // and no other gated or-pattern has been parsed thus far,
60
- // then we should really gate the leading `|`.
61
- // This complicated procedure is done purely for diagnostics UX.
62
- if gated_leading_vert && self . sess . gated_spans . is_ungated ( sym:: or_patterns) {
63
- self . sess . gated_spans . gate ( sym:: or_patterns, leading_vert_span) ;
64
- }
65
-
66
- Ok ( pat)
67
- }
68
-
69
- /// Parse the pattern for a function or function pointer parameter.
70
- /// Special recovery is provided for or-patterns and leading `|`.
71
- pub ( super ) fn parse_fn_param_pat ( & mut self ) -> PResult < ' a , P < Pat > > {
72
- self . recover_leading_vert ( None , "not allowed in a parameter pattern" ) ;
73
- let pat = self . parse_pat_with_or ( PARAM_EXPECTED , GateOr :: No , RecoverComma :: No ) ?;
74
-
75
- if let PatKind :: Or ( ..) = & pat. kind {
76
- self . ban_illegal_fn_param_or_pat ( & pat) ;
77
- }
78
-
79
- Ok ( pat)
80
- }
59
+ let leading_vert_span =
60
+ if self . eat_or_separator ( None ) { Some ( self . prev_token . span ) } else { None } ;
81
61
82
- /// Ban `A | B` immediately in a parameter pattern and suggest wrapping in parens.
83
- fn ban_illegal_fn_param_or_pat ( & self , pat : & Pat ) {
84
- let msg = "wrap the pattern in parenthesis" ;
85
- let fix = format ! ( "({})" , pprust:: pat_to_string( pat) ) ;
86
- self . struct_span_err ( pat. span , "an or-pattern parameter must be wrapped in parenthesis" )
87
- . span_suggestion ( pat. span , msg, fix, Applicability :: MachineApplicable )
88
- . emit ( ) ;
89
- }
90
-
91
- /// Parses a pattern, that may be a or-pattern (e.g. `Foo | Bar` in `Some(Foo | Bar)`).
92
- /// Corresponds to `pat<allow_top_alt>` in RFC 2535.
93
- fn parse_pat_with_or (
94
- & mut self ,
95
- expected : Expected ,
96
- gate_or : GateOr ,
97
- rc : RecoverComma ,
98
- ) -> PResult < ' a , P < Pat > > {
99
62
// Parse the first pattern (`p_0`).
100
- let first_pat = self . parse_pat ( expected) ?;
63
+ let first_pat = self . parse_pat_no_top_alt ( expected) ?;
101
64
self . maybe_recover_unexpected_comma ( first_pat. span , rc, gate_or) ?;
102
65
103
66
// If the next token is not a `|`,
104
67
// this is not an or-pattern and we should exit here.
105
68
if !self . check ( & token:: BinOp ( token:: Or ) ) && self . token != token:: OrOr {
69
+ // If we parsed a leading `|` which should be gated,
70
+ // then we should really gate the leading `|`.
71
+ // This complicated procedure is done purely for diagnostics UX.
72
+ if let Some ( leading_vert_span) = leading_vert_span {
73
+ if gate_or == GateOr :: Yes && self . sess . gated_spans . is_ungated ( sym:: or_patterns) {
74
+ self . sess . gated_spans . gate ( sym:: or_patterns, leading_vert_span) ;
75
+ }
76
+
77
+ // If there was a leading vert, treat this as an or-pattern. This improves
78
+ // diagnostics.
79
+ let span = leading_vert_span. to ( self . prev_token . span ) ;
80
+ return Ok ( self . mk_pat ( span, PatKind :: Or ( vec ! [ first_pat] ) ) ) ;
81
+ }
82
+
106
83
return Ok ( first_pat) ;
107
84
}
108
85
109
86
// Parse the patterns `p_1 | ... | p_n` where `n > 0`.
110
- let lo = first_pat. span ;
87
+ let lo = leading_vert_span . unwrap_or ( first_pat. span ) ;
111
88
let mut pats = vec ! [ first_pat] ;
112
89
while self . eat_or_separator ( Some ( lo) ) {
113
- let pat = self . parse_pat ( expected) . map_err ( |mut err| {
90
+ let pat = self . parse_pat_no_top_alt ( expected) . map_err ( |mut err| {
114
91
err. span_label ( lo, WHILE_PARSING_OR_MSG ) ;
115
92
err
116
93
} ) ?;
@@ -127,6 +104,62 @@ impl<'a> Parser<'a> {
127
104
Ok ( self . mk_pat ( or_pattern_span, PatKind :: Or ( pats) ) )
128
105
}
129
106
107
+ /// Parse the pattern for a function or function pointer parameter.
108
+ pub ( super ) fn parse_fn_param_pat ( & mut self ) -> PResult < ' a , P < Pat > > {
109
+ // We actually do _not_ allow top-level or-patterns in function params, but we use
110
+ // `parse_pat_allow_top_alt` anyway so that we can detect when a user tries to use it. This
111
+ // allows us to print a better error message.
112
+ //
113
+ // In order to get good UX, we first recover in the case of a leading vert for an illegal
114
+ // top-level or-pat. Normally, this means recovering both `|` and `||`, but in this case,
115
+ // a leading `||` probably doesn't indicate an or-pattern attempt, so we handle that
116
+ // separately.
117
+ if let token:: OrOr = self . token . kind {
118
+ let span = self . token . span ;
119
+ let mut err = self . struct_span_err ( span, "unexpected `||` before function parameter" ) ;
120
+ err. span_suggestion (
121
+ span,
122
+ "remove the `||`" ,
123
+ String :: new ( ) ,
124
+ Applicability :: MachineApplicable ,
125
+ ) ;
126
+ err. note ( "alternatives in or-patterns are separated with `|`, not `||`" ) ;
127
+ err. emit ( ) ;
128
+ self . bump ( ) ;
129
+ }
130
+
131
+ let pat = self . parse_pat_allow_top_alt ( PARAM_EXPECTED , GateOr :: No , RecoverComma :: No ) ?;
132
+
133
+ if let PatKind :: Or ( ..) = & pat. kind {
134
+ self . ban_illegal_fn_param_or_pat ( & pat) ;
135
+ }
136
+
137
+ Ok ( pat)
138
+ }
139
+
140
+ /// Ban `A | B` immediately in a parameter pattern and suggest wrapping in parens.
141
+ fn ban_illegal_fn_param_or_pat ( & self , pat : & Pat ) {
142
+ // If all we have a leading vert, then print a special message. This is the case if
143
+ // `parse_pat_allow_top_alt` returns an or-pattern with one variant.
144
+ let ( msg, fix) = match & pat. kind {
145
+ PatKind :: Or ( pats) if pats. len ( ) == 1 => {
146
+ let msg = "remove the leading `|`" ;
147
+ let fix = pprust:: pat_to_string ( pat) ;
148
+ ( msg, fix)
149
+ }
150
+
151
+ _ => {
152
+ let msg = "wrap the pattern in parentheses" ;
153
+ let fix = format ! ( "({})" , pprust:: pat_to_string( pat) ) ;
154
+ ( msg, fix)
155
+ }
156
+ } ;
157
+
158
+ self . struct_span_err ( pat. span , "an or-pattern parameter must be wrapped in parentheses" )
159
+ . span_suggestion ( pat. span , msg, fix, Applicability :: MachineApplicable )
160
+ . emit ( ) ;
161
+ }
162
+
130
163
/// Eat the or-pattern `|` separator.
131
164
/// If instead a `||` token is encountered, recover and pretend we parsed `|`.
132
165
fn eat_or_separator ( & mut self , lo : Option < Span > ) -> bool {
@@ -179,7 +212,7 @@ impl<'a> Parser<'a> {
179
212
180
213
/// We have parsed `||` instead of `|`. Error and suggest `|` instead.
181
214
fn ban_unexpected_or_or ( & mut self , lo : Option < Span > ) {
182
- let mut err = self . struct_span_err ( self . token . span , "unexpected token `||` after pattern" ) ;
215
+ let mut err = self . struct_span_err ( self . token . span , "unexpected token `||` in pattern" ) ;
183
216
err. span_suggestion (
184
217
self . token . span ,
185
218
"use a single `|` to separate multiple alternative patterns" ,
@@ -244,30 +277,14 @@ impl<'a> Parser<'a> {
244
277
/// sequence of patterns until `)` is reached.
245
278
fn skip_pat_list ( & mut self ) -> PResult < ' a , ( ) > {
246
279
while !self . check ( & token:: CloseDelim ( token:: Paren ) ) {
247
- self . parse_pat ( None ) ?;
280
+ self . parse_pat_no_top_alt ( None ) ?;
248
281
if !self . eat ( & token:: Comma ) {
249
282
return Ok ( ( ) ) ;
250
283
}
251
284
}
252
285
Ok ( ( ) )
253
286
}
254
287
255
- /// Recursive possibly-or-pattern parser with recovery for an erroneous leading `|`.
256
- /// See `parse_pat_with_or` for details on parsing or-patterns.
257
- fn parse_pat_with_or_inner ( & mut self ) -> PResult < ' a , P < Pat > > {
258
- self . recover_leading_vert ( None , "only allowed in a top-level pattern" ) ;
259
- self . parse_pat_with_or ( None , GateOr :: Yes , RecoverComma :: No )
260
- }
261
-
262
- /// Recover if `|` or `||` is here.
263
- /// The user is thinking that a leading `|` is allowed in this position.
264
- fn recover_leading_vert ( & mut self , lo : Option < Span > , ctx : & str ) {
265
- if let token:: BinOp ( token:: Or ) | token:: OrOr = self . token . kind {
266
- self . ban_illegal_vert ( lo, "leading" , ctx) ;
267
- self . bump ( ) ;
268
- }
269
- }
270
-
271
288
/// A `|` or possibly `||` token shouldn't be here. Ban it.
272
289
fn ban_illegal_vert ( & mut self , lo : Option < Span > , pos : & str , ctx : & str ) {
273
290
let span = self . token . span ;
@@ -305,8 +322,9 @@ impl<'a> Parser<'a> {
305
322
self . parse_pat_tuple_or_parens ( ) ?
306
323
} else if self . check ( & token:: OpenDelim ( token:: Bracket ) ) {
307
324
// Parse `[pat, pat,...]` as a slice pattern.
308
- let ( pats, _) =
309
- self . parse_delim_comma_seq ( token:: Bracket , |p| p. parse_pat_with_or_inner ( ) ) ?;
325
+ let ( pats, _) = self . parse_delim_comma_seq ( token:: Bracket , |p| {
326
+ p. parse_pat_allow_top_alt ( None , GateOr :: Yes , RecoverComma :: No )
327
+ } ) ?;
310
328
PatKind :: Slice ( pats)
311
329
} else if self . check ( & token:: DotDot ) && !self . is_pat_range_end_start ( 1 ) {
312
330
// A rest pattern `..`.
@@ -429,7 +447,7 @@ impl<'a> Parser<'a> {
429
447
430
448
// At this point we attempt to parse `@ $pat_rhs` and emit an error.
431
449
self . bump ( ) ; // `@`
432
- let mut rhs = self . parse_pat ( None ) ?;
450
+ let mut rhs = self . parse_pat_no_top_alt ( None ) ?;
433
451
let sp = lhs. span . to ( rhs. span ) ;
434
452
435
453
if let PatKind :: Ident ( _, _, ref mut sub @ None ) = rhs. kind {
@@ -518,8 +536,9 @@ impl<'a> Parser<'a> {
518
536
519
537
/// Parse a tuple or parenthesis pattern.
520
538
fn parse_pat_tuple_or_parens ( & mut self ) -> PResult < ' a , PatKind > {
521
- let ( fields, trailing_comma) =
522
- self . parse_paren_comma_seq ( |p| p. parse_pat_with_or_inner ( ) ) ?;
539
+ let ( fields, trailing_comma) = self . parse_paren_comma_seq ( |p| {
540
+ p. parse_pat_allow_top_alt ( None , GateOr :: Yes , RecoverComma :: No )
541
+ } ) ?;
523
542
524
543
// Here, `(pat,)` is a tuple pattern.
525
544
// For backward compatibility, `(..)` is a tuple pattern as well.
@@ -548,7 +567,7 @@ impl<'a> Parser<'a> {
548
567
}
549
568
550
569
// Parse the pattern we hope to be an identifier.
551
- let mut pat = self . parse_pat ( Some ( "identifier" ) ) ?;
570
+ let mut pat = self . parse_pat_no_top_alt ( Some ( "identifier" ) ) ?;
552
571
553
572
// If we don't have `mut $ident (@ pat)?`, error.
554
573
if let PatKind :: Ident ( BindingMode :: ByValue ( m @ Mutability :: Not ) , ..) = & mut pat. kind {
@@ -793,7 +812,7 @@ impl<'a> Parser<'a> {
793
812
fn parse_pat_ident ( & mut self , binding_mode : BindingMode ) -> PResult < ' a , PatKind > {
794
813
let ident = self . parse_ident ( ) ?;
795
814
let sub = if self . eat ( & token:: At ) {
796
- Some ( self . parse_pat ( Some ( "binding pattern" ) ) ?)
815
+ Some ( self . parse_pat_no_top_alt ( Some ( "binding pattern" ) ) ?)
797
816
} else {
798
817
None
799
818
} ;
@@ -832,7 +851,9 @@ impl<'a> Parser<'a> {
832
851
if qself. is_some ( ) {
833
852
return self . error_qpath_before_pat ( & path, "(" ) ;
834
853
}
835
- let ( fields, _) = self . parse_paren_comma_seq ( |p| p. parse_pat_with_or_inner ( ) ) ?;
854
+ let ( fields, _) = self . parse_paren_comma_seq ( |p| {
855
+ p. parse_pat_allow_top_alt ( None , GateOr :: Yes , RecoverComma :: No )
856
+ } ) ?;
836
857
Ok ( PatKind :: TupleStruct ( path, fields) )
837
858
}
838
859
@@ -998,7 +1019,7 @@ impl<'a> Parser<'a> {
998
1019
// Parsing a pattern of the form `fieldname: pat`.
999
1020
let fieldname = self . parse_field_name ( ) ?;
1000
1021
self . bump ( ) ;
1001
- let pat = self . parse_pat_with_or_inner ( ) ?;
1022
+ let pat = self . parse_pat_allow_top_alt ( None , GateOr :: Yes , RecoverComma :: No ) ?;
1002
1023
hi = pat. span ;
1003
1024
( pat, fieldname, false )
1004
1025
} else {
0 commit comments