@@ -19,14 +19,14 @@ const WHILE_PARSING_OR_MSG: &str = "while parsing this or-pattern starting here"
1919
2020/// Whether or not an or-pattern should be gated when occurring in the current context.
2121#[ derive( PartialEq , Clone , Copy ) ]
22- pub ( super ) enum GateOr {
22+ pub enum GateOr {
2323 Yes ,
2424 No ,
2525}
2626
2727/// Whether or not to recover a `,` when parsing or-patterns.
2828#[ derive( PartialEq , Copy , Clone ) ]
29- pub ( super ) enum RecoverComma {
29+ pub enum RecoverComma {
3030 Yes ,
3131 No ,
3232}
@@ -37,80 +37,57 @@ impl<'a> Parser<'a> {
3737 /// Corresponds to `pat<no_top_alt>` in RFC 2535 and does not admit or-patterns
3838 /// at the top level. Used when parsing the parameters of lambda expressions,
3939 /// 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 > > {
4141 self . parse_pat_with_range_pat ( true , expected)
4242 }
4343
44- /// Entry point to the main pattern parser.
44+ /// Parses a pattern.
45+ ///
4546 /// 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 (
4753 & mut self ,
54+ expected : Expected ,
4855 gate_or : GateOr ,
4956 rc : RecoverComma ,
5057 ) -> PResult < ' a , P < Pat > > {
5158 // 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 } ;
8161
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 > > {
9962 // 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) ?;
10164 self . maybe_recover_unexpected_comma ( first_pat. span , rc, gate_or) ?;
10265
10366 // If the next token is not a `|`,
10467 // this is not an or-pattern and we should exit here.
10568 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+
10683 return Ok ( first_pat) ;
10784 }
10885
10986 // 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 ) ;
11188 let mut pats = vec ! [ first_pat] ;
11289 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| {
11491 err. span_label ( lo, WHILE_PARSING_OR_MSG ) ;
11592 err
11693 } ) ?;
@@ -127,6 +104,62 @@ impl<'a> Parser<'a> {
127104 Ok ( self . mk_pat ( or_pattern_span, PatKind :: Or ( pats) ) )
128105 }
129106
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+
130163 /// Eat the or-pattern `|` separator.
131164 /// If instead a `||` token is encountered, recover and pretend we parsed `|`.
132165 fn eat_or_separator ( & mut self , lo : Option < Span > ) -> bool {
@@ -179,7 +212,7 @@ impl<'a> Parser<'a> {
179212
180213 /// We have parsed `||` instead of `|`. Error and suggest `|` instead.
181214 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" ) ;
183216 err. span_suggestion (
184217 self . token . span ,
185218 "use a single `|` to separate multiple alternative patterns" ,
@@ -244,30 +277,14 @@ impl<'a> Parser<'a> {
244277 /// sequence of patterns until `)` is reached.
245278 fn skip_pat_list ( & mut self ) -> PResult < ' a , ( ) > {
246279 while !self . check ( & token:: CloseDelim ( token:: Paren ) ) {
247- self . parse_pat ( None ) ?;
280+ self . parse_pat_no_top_alt ( None ) ?;
248281 if !self . eat ( & token:: Comma ) {
249282 return Ok ( ( ) ) ;
250283 }
251284 }
252285 Ok ( ( ) )
253286 }
254287
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-
271288 /// A `|` or possibly `||` token shouldn't be here. Ban it.
272289 fn ban_illegal_vert ( & mut self , lo : Option < Span > , pos : & str , ctx : & str ) {
273290 let span = self . token . span ;
@@ -305,8 +322,9 @@ impl<'a> Parser<'a> {
305322 self . parse_pat_tuple_or_parens ( ) ?
306323 } else if self . check ( & token:: OpenDelim ( token:: Bracket ) ) {
307324 // 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+ } ) ?;
310328 PatKind :: Slice ( pats)
311329 } else if self . check ( & token:: DotDot ) && !self . is_pat_range_end_start ( 1 ) {
312330 // A rest pattern `..`.
@@ -429,7 +447,7 @@ impl<'a> Parser<'a> {
429447
430448 // At this point we attempt to parse `@ $pat_rhs` and emit an error.
431449 self . bump ( ) ; // `@`
432- let mut rhs = self . parse_pat ( None ) ?;
450+ let mut rhs = self . parse_pat_no_top_alt ( None ) ?;
433451 let sp = lhs. span . to ( rhs. span ) ;
434452
435453 if let PatKind :: Ident ( _, _, ref mut sub @ None ) = rhs. kind {
@@ -518,8 +536,9 @@ impl<'a> Parser<'a> {
518536
519537 /// Parse a tuple or parenthesis pattern.
520538 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+ } ) ?;
523542
524543 // Here, `(pat,)` is a tuple pattern.
525544 // For backward compatibility, `(..)` is a tuple pattern as well.
@@ -548,7 +567,7 @@ impl<'a> Parser<'a> {
548567 }
549568
550569 // 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" ) ) ?;
552571
553572 // If we don't have `mut $ident (@ pat)?`, error.
554573 if let PatKind :: Ident ( BindingMode :: ByValue ( m @ Mutability :: Not ) , ..) = & mut pat. kind {
@@ -793,7 +812,7 @@ impl<'a> Parser<'a> {
793812 fn parse_pat_ident ( & mut self , binding_mode : BindingMode ) -> PResult < ' a , PatKind > {
794813 let ident = self . parse_ident ( ) ?;
795814 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" ) ) ?)
797816 } else {
798817 None
799818 } ;
@@ -832,7 +851,9 @@ impl<'a> Parser<'a> {
832851 if qself. is_some ( ) {
833852 return self . error_qpath_before_pat ( & path, "(" ) ;
834853 }
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+ } ) ?;
836857 Ok ( PatKind :: TupleStruct ( path, fields) )
837858 }
838859
@@ -998,7 +1019,7 @@ impl<'a> Parser<'a> {
9981019 // Parsing a pattern of the form `fieldname: pat`.
9991020 let fieldname = self . parse_field_name ( ) ?;
10001021 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 ) ?;
10021023 hi = pat. span ;
10031024 ( pat, fieldname, false )
10041025 } else {
0 commit comments