@@ -18,6 +18,8 @@ type Expected = Option<&'static str>;
1818/// `Expected` for function and lambda parameter patterns.
1919pub ( super ) const PARAM_EXPECTED : Expected = Some ( "parameter name" ) ;
2020
21+ const WHILE_PARSING_OR_MSG : & str = "while parsing this or-pattern starting here" ;
22+
2123/// Whether or not an or-pattern should be gated when occurring in the current context.
2224#[ derive( PartialEq ) ]
2325pub enum GateOr { Yes , No }
@@ -40,7 +42,7 @@ impl<'a> Parser<'a> {
4042 /// Corresponds to `top_pat` in RFC 2535 and allows or-pattern at the top level.
4143 pub ( super ) fn parse_top_pat ( & mut self , gate_or : GateOr ) -> PResult < ' a , P < Pat > > {
4244 // 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 ;
4446 let leading_vert_span = self . prev_span ;
4547
4648 // Parse the possibly-or-pattern.
@@ -63,7 +65,7 @@ impl<'a> Parser<'a> {
6365 /// Parse the pattern for a function or function pointer parameter.
6466 /// Special recovery is provided for or-patterns and leading `|`.
6567 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" ) ;
6769 let pat = self . parse_pat_with_or ( PARAM_EXPECTED , GateOr :: No , RecoverComma :: No ) ?;
6870
6971 if let PatKind :: Or ( ..) = & pat. kind {
@@ -90,7 +92,7 @@ impl<'a> Parser<'a> {
9092 gate_or : GateOr ,
9193 rc : RecoverComma ,
9294 ) -> PResult < ' a , P < Pat > > {
93- // Parse the first pattern.
95+ // Parse the first pattern (`p_0`) .
9496 let first_pat = self . parse_pat ( expected) ?;
9597 self . maybe_recover_unexpected_comma ( first_pat. span , rc) ?;
9698
@@ -100,11 +102,12 @@ impl<'a> Parser<'a> {
100102 return Ok ( first_pat)
101103 }
102104
105+ // Parse the patterns `p_1 | ... | p_n` where `n > 0`.
103106 let lo = first_pat. span ;
104107 let mut pats = vec ! [ first_pat] ;
105- while self . eat_or_separator ( ) {
108+ while self . eat_or_separator ( Some ( lo ) ) {
106109 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 ) ;
108111 err
109112 } ) ?;
110113 self . maybe_recover_unexpected_comma ( pat. span , rc) ?;
@@ -122,28 +125,65 @@ impl<'a> Parser<'a> {
122125
123126 /// Eat the or-pattern `|` separator.
124127 /// 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+
126133 match self . token . kind {
127134 token:: OrOr => {
128135 // Found `||`; Recover and pretend we parsed `|`.
129- self . ban_unexpected_or_or ( ) ;
136+ self . ban_unexpected_or_or ( lo ) ;
130137 self . bump ( ) ;
131138 true
132139 }
133140 _ => self . eat ( & token:: BinOp ( token:: Or ) ) ,
134141 }
135142 }
136143
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+
137174 /// 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 ( ) ;
147187 }
148188
149189 /// Some special error handling for the "top-level" patterns in a match arm,
@@ -198,25 +238,38 @@ impl<'a> Parser<'a> {
198238 /// Recursive possibly-or-pattern parser with recovery for an erroneous leading `|`.
199239 /// See `parse_pat_with_or` for details on parsing or-patterns.
200240 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" ) ;
202242 self . parse_pat_with_or ( None , GateOr :: Yes , RecoverComma :: No )
203243 }
204244
205245 /// Recover if `|` or `||` is here.
206246 /// 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 ) {
208248 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) ;
216250 self . bump ( ) ;
217251 }
218252 }
219253
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+
220273 /// Parses a pattern, with a setting whether modern range patterns (e.g., `a..=b`, `a..b` are
221274 /// allowed).
222275 fn parse_pat_with_range_pat (
@@ -259,7 +312,7 @@ impl<'a> Parser<'a> {
259312 self . bump ( ) ;
260313 self . parse_pat_range_to ( RangeEnd :: Included ( RangeSyntax :: DotDotDot ) , "..." ) ?
261314 }
262- // At this point, token != &, &&, (, [
315+ // At this point, token != `&`, `&&`, `(`, `[`, `..`, `..=`, or `...`.
263316 _ => if self . eat_keyword ( kw:: Underscore ) {
264317 // Parse _
265318 PatKind :: Wild
0 commit comments