@@ -31,6 +31,18 @@ pub enum RecoverComma {
3131 No ,
3232}
3333
34+ /// The result of `eat_or_separator`. We want to distinguish which case we are in to avoid
35+ /// emitting duplicate diagnostics.
36+ #[ derive( Debug , Clone , Copy ) ]
37+ enum EatOrResult {
38+ /// We recovered from a trailing vert.
39+ TrailingVert ,
40+ /// We ate an `|` (or `||` and recovered).
41+ AteOr ,
42+ /// We did not eat anything (i.e. the current token is not `|` or `||`).
43+ None ,
44+ }
45+
3446impl < ' a > Parser < ' a > {
3547 /// Parses a pattern.
3648 ///
@@ -55,9 +67,26 @@ impl<'a> Parser<'a> {
5567 gate_or : GateOr ,
5668 rc : RecoverComma ,
5769 ) -> PResult < ' a , P < Pat > > {
70+ self . parse_pat_allow_top_alt_inner ( expected, gate_or, rc) . map ( |( pat, _) | pat)
71+ }
72+
73+ /// Returns the pattern and a bool indicating whether we recovered from a trailing vert (true =
74+ /// recovered).
75+ fn parse_pat_allow_top_alt_inner (
76+ & mut self ,
77+ expected : Expected ,
78+ gate_or : GateOr ,
79+ rc : RecoverComma ,
80+ ) -> PResult < ' a , ( P < Pat > , bool ) > {
81+ // Keep track of whether we recovered from a trailing vert so that we can avoid duplicated
82+ // suggestions (which bothers rustfix).
83+ //
5884 // Allow a '|' before the pats (RFCs 1925, 2530, and 2535).
59- let leading_vert_span =
60- if self . eat_or_separator ( None ) { Some ( self . prev_token . span ) } else { None } ;
85+ let ( leading_vert_span, mut trailing_vert) = match self . eat_or_separator ( None ) {
86+ EatOrResult :: AteOr => ( Some ( self . prev_token . span ) , false ) ,
87+ EatOrResult :: TrailingVert => ( None , true ) ,
88+ EatOrResult :: None => ( None , false ) ,
89+ } ;
6190
6291 // Parse the first pattern (`p_0`).
6392 let first_pat = self . parse_pat_no_top_alt ( expected) ?;
@@ -77,16 +106,24 @@ impl<'a> Parser<'a> {
77106 // If there was a leading vert, treat this as an or-pattern. This improves
78107 // diagnostics.
79108 let span = leading_vert_span. to ( self . prev_token . span ) ;
80- return Ok ( self . mk_pat ( span, PatKind :: Or ( vec ! [ first_pat] ) ) ) ;
109+ return Ok ( ( self . mk_pat ( span, PatKind :: Or ( vec ! [ first_pat] ) ) , trailing_vert ) ) ;
81110 }
82111
83- return Ok ( first_pat) ;
112+ return Ok ( ( first_pat, trailing_vert ) ) ;
84113 }
85114
86115 // Parse the patterns `p_1 | ... | p_n` where `n > 0`.
87116 let lo = leading_vert_span. unwrap_or ( first_pat. span ) ;
88117 let mut pats = vec ! [ first_pat] ;
89- while self . eat_or_separator ( Some ( lo) ) {
118+ loop {
119+ match self . eat_or_separator ( Some ( lo) ) {
120+ EatOrResult :: AteOr => { }
121+ EatOrResult :: None => break ,
122+ EatOrResult :: TrailingVert => {
123+ trailing_vert = true ;
124+ break ;
125+ }
126+ }
90127 let pat = self . parse_pat_no_top_alt ( expected) . map_err ( |mut err| {
91128 err. span_label ( lo, WHILE_PARSING_OR_MSG ) ;
92129 err
@@ -101,15 +138,63 @@ impl<'a> Parser<'a> {
101138 self . sess . gated_spans . gate ( sym:: or_patterns, or_pattern_span) ;
102139 }
103140
104- Ok ( self . mk_pat ( or_pattern_span, PatKind :: Or ( pats) ) )
141+ Ok ( ( self . mk_pat ( or_pattern_span, PatKind :: Or ( pats) ) , trailing_vert ) )
105142 }
106143
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- //
144+ /// Parse a pattern and (maybe) a `Colon` in positions where a pattern may be followed by a
145+ /// type annotation (e.g. for `let` bindings or `fn` params).
146+ ///
147+ /// Generally, this corresponds to `pat_no_top_alt` followed by an optional `Colon`. It will
148+ /// eat the `Colon` token if one is present.
149+ ///
150+ /// The return value represents the parsed pattern and `true` if a `Colon` was parsed (`false`
151+ /// otherwise).
152+ pub ( super ) fn parse_pat_before_ty (
153+ & mut self ,
154+ expected : Expected ,
155+ gate_or : GateOr ,
156+ rc : RecoverComma ,
157+ syntax_loc : & str ,
158+ ) -> PResult < ' a , ( P < Pat > , bool ) > {
159+ // We use `parse_pat_allow_top_alt` regardless of whether we actually want top-level
160+ // or-patterns so that we can detect when a user tries to use it. This allows us to print a
161+ // better error message.
162+ let ( pat, trailing_vert) = self . parse_pat_allow_top_alt_inner ( expected, gate_or, rc) ?;
163+ let colon = self . eat ( & token:: Colon ) ;
164+
165+ if let PatKind :: Or ( pats) = & pat. kind {
166+ let msg = format ! ( "top-level or-patterns are not allowed in {}" , syntax_loc) ;
167+ let ( help, fix) = if pats. len ( ) == 1 {
168+ // If all we have is a leading vert, then print a special message. This is the case
169+ // if `parse_pat_allow_top_alt` returns an or-pattern with one variant.
170+ let msg = "remove the `|`" ;
171+ let fix = pprust:: pat_to_string ( & pat) ;
172+ ( msg, fix)
173+ } else {
174+ let msg = "wrap the pattern in parentheses" ;
175+ let fix = format ! ( "({})" , pprust:: pat_to_string( & pat) ) ;
176+ ( msg, fix)
177+ } ;
178+
179+ if trailing_vert {
180+ // We already emitted an error and suggestion to remove the trailing vert. Don't
181+ // emit again.
182+ self . sess . span_diagnostic . delay_span_bug ( pat. span , & msg) ;
183+ } else {
184+ self . struct_span_err ( pat. span , & msg)
185+ . span_suggestion ( pat. span , help, fix, Applicability :: MachineApplicable )
186+ . emit ( ) ;
187+ }
188+ }
189+
190+ Ok ( ( pat, colon) )
191+ }
192+
193+ /// Parse the pattern for a function or function pointer parameter, followed by a colon.
194+ ///
195+ /// The return value represents the parsed pattern and `true` if a `Colon` was parsed (`false`
196+ /// otherwise).
197+ pub ( super ) fn parse_fn_param_pat_colon ( & mut self ) -> PResult < ' a , ( P < Pat > , bool ) > {
113198 // In order to get good UX, we first recover in the case of a leading vert for an illegal
114199 // top-level or-pat. Normally, this means recovering both `|` and `||`, but in this case,
115200 // a leading `||` probably doesn't indicate an or-pattern attempt, so we handle that
@@ -128,53 +213,28 @@ impl<'a> Parser<'a> {
128213 self . bump ( ) ;
129214 }
130215
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 ( ) ;
216+ self . parse_pat_before_ty (
217+ PARAM_EXPECTED ,
218+ GateOr :: No ,
219+ RecoverComma :: No ,
220+ "function parameters" ,
221+ )
161222 }
162223
163224 /// Eat the or-pattern `|` separator.
164225 /// If instead a `||` token is encountered, recover and pretend we parsed `|`.
165- fn eat_or_separator ( & mut self , lo : Option < Span > ) -> bool {
226+ fn eat_or_separator ( & mut self , lo : Option < Span > ) -> EatOrResult {
166227 if self . recover_trailing_vert ( lo) {
167- return false ;
168- }
169-
170- match self . token . kind {
171- token:: OrOr => {
172- // Found `||`; Recover and pretend we parsed `|`.
173- self . ban_unexpected_or_or ( lo) ;
174- self . bump ( ) ;
175- true
176- }
177- _ => self . eat ( & token:: BinOp ( token:: Or ) ) ,
228+ EatOrResult :: TrailingVert
229+ } else if matches ! ( self . token. kind, token:: OrOr ) {
230+ // Found `||`; Recover and pretend we parsed `|`.
231+ self . ban_unexpected_or_or ( lo) ;
232+ self . bump ( ) ;
233+ EatOrResult :: AteOr
234+ } else if self . eat ( & token:: BinOp ( token:: Or ) ) {
235+ EatOrResult :: AteOr
236+ } else {
237+ EatOrResult :: None
178238 }
179239 }
180240
@@ -190,14 +250,14 @@ impl<'a> Parser<'a> {
190250 matches ! (
191251 & token. uninterpolate( ) . kind,
192252 token:: FatArrow // e.g. `a | => 0,`.
193- | token:: Ident ( kw:: If , false ) // e.g. `a | if expr`.
194- | token:: Eq // e.g. `let a | = 0`.
195- | token:: Semi // e.g. `let a |;`.
196- | token:: Colon // e.g. `let a | :`.
197- | token:: Comma // e.g. `let (a |,)`.
198- | token:: CloseDelim ( token:: Bracket ) // e.g. `let [a | ]`.
199- | token:: CloseDelim ( token:: Paren ) // e.g. `let (a | )`.
200- | token:: CloseDelim ( token:: Brace ) // e.g. `let A { f: a | }`.
253+ | token:: Ident ( kw:: If , false ) // e.g. `a | if expr`.
254+ | token:: Eq // e.g. `let a | = 0`.
255+ | token:: Semi // e.g. `let a |;`.
256+ | token:: Colon // e.g. `let a | :`.
257+ | token:: Comma // e.g. `let (a |,)`.
258+ | token:: CloseDelim ( token:: Bracket ) // e.g. `let [a | ]`.
259+ | token:: CloseDelim ( token:: Paren ) // e.g. `let (a | )`.
260+ | token:: CloseDelim ( token:: Brace ) // e.g. `let A { f: a | }`.
201261 )
202262 } ) ;
203263 match ( is_end_ahead, & self . token . kind ) {
0 commit comments