@@ -18,6 +18,8 @@ type Expected = Option<&'static str>;
18
18
/// `Expected` for function and lambda parameter patterns.
19
19
pub ( super ) const PARAM_EXPECTED : Expected = Some ( "parameter name" ) ;
20
20
21
+ const WHILE_PARSING_OR_MSG : & str = "while parsing this or-pattern starting here" ;
22
+
21
23
/// Whether or not an or-pattern should be gated when occurring in the current context.
22
24
#[ derive( PartialEq ) ]
23
25
pub enum GateOr { Yes , No }
@@ -40,7 +42,7 @@ impl<'a> Parser<'a> {
40
42
/// Corresponds to `top_pat` in RFC 2535 and allows or-pattern at the top level.
41
43
pub ( super ) fn parse_top_pat ( & mut self , gate_or : GateOr ) -> PResult < ' a , P < Pat > > {
42
44
// Allow a '|' before the pats (RFCs 1925, 2530, and 2535).
43
- let gated_leading_vert = self . eat_or_separator ( ) && gate_or == GateOr :: Yes ;
45
+ let gated_leading_vert = self . eat_or_separator ( None ) && gate_or == GateOr :: Yes ;
44
46
let leading_vert_span = self . prev_span ;
45
47
46
48
// Parse the possibly-or-pattern.
@@ -63,7 +65,7 @@ impl<'a> Parser<'a> {
63
65
/// Parse the pattern for a function or function pointer parameter.
64
66
/// Special recovery is provided for or-patterns and leading `|`.
65
67
pub ( super ) fn parse_fn_param_pat ( & mut self ) -> PResult < ' a , P < Pat > > {
66
- self . recover_leading_vert ( "not allowed in a parameter pattern" ) ;
68
+ self . recover_leading_vert ( None , "not allowed in a parameter pattern" ) ;
67
69
let pat = self . parse_pat_with_or ( PARAM_EXPECTED , GateOr :: No , RecoverComma :: No ) ?;
68
70
69
71
if let PatKind :: Or ( ..) = & pat. kind {
@@ -90,7 +92,7 @@ impl<'a> Parser<'a> {
90
92
gate_or : GateOr ,
91
93
rc : RecoverComma ,
92
94
) -> PResult < ' a , P < Pat > > {
93
- // Parse the first pattern.
95
+ // Parse the first pattern (`p_0`) .
94
96
let first_pat = self . parse_pat ( expected) ?;
95
97
self . maybe_recover_unexpected_comma ( first_pat. span , rc) ?;
96
98
@@ -100,11 +102,12 @@ impl<'a> Parser<'a> {
100
102
return Ok ( first_pat)
101
103
}
102
104
105
+ // Parse the patterns `p_1 | ... | p_n` where `n > 0`.
103
106
let lo = first_pat. span ;
104
107
let mut pats = vec ! [ first_pat] ;
105
- while self . eat_or_separator ( ) {
108
+ while self . eat_or_separator ( Some ( lo ) ) {
106
109
let pat = self . parse_pat ( expected) . map_err ( |mut err| {
107
- err. span_label ( lo, "while parsing this or-pattern starting here" ) ;
110
+ err. span_label ( lo, WHILE_PARSING_OR_MSG ) ;
108
111
err
109
112
} ) ?;
110
113
self . maybe_recover_unexpected_comma ( pat. span , rc) ?;
@@ -122,28 +125,65 @@ impl<'a> Parser<'a> {
122
125
123
126
/// Eat the or-pattern `|` separator.
124
127
/// If instead a `||` token is encountered, recover and pretend we parsed `|`.
125
- fn eat_or_separator ( & mut self ) -> bool {
128
+ fn eat_or_separator ( & mut self , lo : Option < Span > ) -> bool {
129
+ if self . recover_trailing_vert ( lo) {
130
+ return false ;
131
+ }
132
+
126
133
match self . token . kind {
127
134
token:: OrOr => {
128
135
// Found `||`; Recover and pretend we parsed `|`.
129
- self . ban_unexpected_or_or ( ) ;
136
+ self . ban_unexpected_or_or ( lo ) ;
130
137
self . bump ( ) ;
131
138
true
132
139
}
133
140
_ => self . eat ( & token:: BinOp ( token:: Or ) ) ,
134
141
}
135
142
}
136
143
144
+ /// Recover if `|` or `||` is the current token and we have one of the
145
+ /// tokens `=>`, `if`, `=`, `:`, `;`, `,`, `]`, `)`, or `}` ahead of us.
146
+ ///
147
+ /// These tokens all indicate that we reached the end of the or-pattern
148
+ /// list and can now reliably say that the `|` was an illegal trailing vert.
149
+ /// Note that there are more tokens such as `@` for which we know that the `|`
150
+ /// is an illegal parse. However, the user's intent is less clear in that case.
151
+ fn recover_trailing_vert ( & mut self , lo : Option < Span > ) -> bool {
152
+ let is_end_ahead = self . look_ahead ( 1 , |token| match & token. kind {
153
+ token:: FatArrow // e.g. `a | => 0,`.
154
+ | token:: Ident ( kw:: If , false ) // e.g. `a | if expr`.
155
+ | token:: Eq // e.g. `let a | = 0`.
156
+ | token:: Semi // e.g. `let a |;`.
157
+ | token:: Colon // e.g. `let a | :`.
158
+ | token:: Comma // e.g. `let (a |,)`.
159
+ | token:: CloseDelim ( token:: Bracket ) // e.g. `let [a | ]`.
160
+ | token:: CloseDelim ( token:: Paren ) // e.g. `let (a | )`.
161
+ | token:: CloseDelim ( token:: Brace ) => true , // e.g. `let A { f: a | }`.
162
+ _ => false ,
163
+ } ) ;
164
+ match ( is_end_ahead, & self . token . kind ) {
165
+ ( true , token:: BinOp ( token:: Or ) ) | ( true , token:: OrOr ) => {
166
+ self . ban_illegal_vert ( lo, "trailing" , "not allowed in an or-pattern" ) ;
167
+ self . bump ( ) ;
168
+ true
169
+ }
170
+ _ => false ,
171
+ }
172
+ }
173
+
137
174
/// We have parsed `||` instead of `|`. Error and suggest `|` instead.
138
- fn ban_unexpected_or_or ( & mut self ) {
139
- self . struct_span_err ( self . token . span , "unexpected token `||` after pattern" )
140
- . span_suggestion (
141
- self . token . span ,
142
- "use a single `|` to separate multiple alternative patterns" ,
143
- "|" . to_owned ( ) ,
144
- Applicability :: MachineApplicable
145
- )
146
- . emit ( ) ;
175
+ fn ban_unexpected_or_or ( & mut self , lo : Option < Span > ) {
176
+ let mut err = self . struct_span_err ( self . token . span , "unexpected token `||` after pattern" ) ;
177
+ err. span_suggestion (
178
+ self . token . span ,
179
+ "use a single `|` to separate multiple alternative patterns" ,
180
+ "|" . to_owned ( ) ,
181
+ Applicability :: MachineApplicable
182
+ ) ;
183
+ if let Some ( lo) = lo {
184
+ err. span_label ( lo, WHILE_PARSING_OR_MSG ) ;
185
+ }
186
+ err. emit ( ) ;
147
187
}
148
188
149
189
/// Some special error handling for the "top-level" patterns in a match arm,
@@ -198,25 +238,38 @@ impl<'a> Parser<'a> {
198
238
/// Recursive possibly-or-pattern parser with recovery for an erroneous leading `|`.
199
239
/// See `parse_pat_with_or` for details on parsing or-patterns.
200
240
fn parse_pat_with_or_inner ( & mut self ) -> PResult < ' a , P < Pat > > {
201
- self . recover_leading_vert ( "only allowed in a top-level pattern" ) ;
241
+ self . recover_leading_vert ( None , "only allowed in a top-level pattern" ) ;
202
242
self . parse_pat_with_or ( None , GateOr :: Yes , RecoverComma :: No )
203
243
}
204
244
205
245
/// Recover if `|` or `||` is here.
206
246
/// The user is thinking that a leading `|` is allowed in this position.
207
- fn recover_leading_vert ( & mut self , ctx : & str ) {
247
+ fn recover_leading_vert ( & mut self , lo : Option < Span > , ctx : & str ) {
208
248
if let token:: BinOp ( token:: Or ) | token:: OrOr = self . token . kind {
209
- let span = self . token . span ;
210
- let rm_msg = format ! ( "remove the `{}`" , pprust:: token_to_string( & self . token) ) ;
211
-
212
- self . struct_span_err ( span, & format ! ( "a leading `|` is {}" , ctx) )
213
- . span_suggestion ( span, & rm_msg, String :: new ( ) , Applicability :: MachineApplicable )
214
- . emit ( ) ;
215
-
249
+ self . ban_illegal_vert ( lo, "leading" , ctx) ;
216
250
self . bump ( ) ;
217
251
}
218
252
}
219
253
254
+ /// A `|` or possibly `||` token shouldn't be here. Ban it.
255
+ fn ban_illegal_vert ( & mut self , lo : Option < Span > , pos : & str , ctx : & str ) {
256
+ let span = self . token . span ;
257
+ let mut err = self . struct_span_err ( span, & format ! ( "a {} `|` is {}" , pos, ctx) ) ;
258
+ err. span_suggestion (
259
+ span,
260
+ & format ! ( "remove the `{}`" , pprust:: token_to_string( & self . token) ) ,
261
+ String :: new ( ) ,
262
+ Applicability :: MachineApplicable ,
263
+ ) ;
264
+ if let Some ( lo) = lo {
265
+ err. span_label ( lo, WHILE_PARSING_OR_MSG ) ;
266
+ }
267
+ if let token:: OrOr = self . token . kind {
268
+ err. note ( "alternatives in or-patterns are separated with `|`, not `||`" ) ;
269
+ }
270
+ err. emit ( ) ;
271
+ }
272
+
220
273
/// Parses a pattern, with a setting whether modern range patterns (e.g., `a..=b`, `a..b` are
221
274
/// allowed).
222
275
fn parse_pat_with_range_pat (
@@ -259,7 +312,7 @@ impl<'a> Parser<'a> {
259
312
self . bump ( ) ;
260
313
self . parse_pat_range_to ( RangeEnd :: Included ( RangeSyntax :: DotDotDot ) , "..." ) ?
261
314
}
262
- // At this point, token != &, &&, (, [
315
+ // At this point, token != `&`, `&&`, `(`, `[`, `..`, `..=`, or `...`.
263
316
_ => if self . eat_keyword ( kw:: Underscore ) {
264
317
// Parse _
265
318
PatKind :: Wild
0 commit comments