@@ -31,6 +31,18 @@ pub enum RecoverComma {
31
31
No ,
32
32
}
33
33
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
+
34
46
impl < ' a > Parser < ' a > {
35
47
/// Parses a pattern.
36
48
///
@@ -55,9 +67,26 @@ impl<'a> Parser<'a> {
55
67
gate_or : GateOr ,
56
68
rc : RecoverComma ,
57
69
) -> 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
+ //
58
84
// 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
+ } ;
61
90
62
91
// Parse the first pattern (`p_0`).
63
92
let first_pat = self . parse_pat_no_top_alt ( expected) ?;
@@ -77,16 +106,24 @@ impl<'a> Parser<'a> {
77
106
// If there was a leading vert, treat this as an or-pattern. This improves
78
107
// diagnostics.
79
108
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 ) ) ;
81
110
}
82
111
83
- return Ok ( first_pat) ;
112
+ return Ok ( ( first_pat, trailing_vert ) ) ;
84
113
}
85
114
86
115
// Parse the patterns `p_1 | ... | p_n` where `n > 0`.
87
116
let lo = leading_vert_span. unwrap_or ( first_pat. span ) ;
88
117
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
+ }
90
127
let pat = self . parse_pat_no_top_alt ( expected) . map_err ( |mut err| {
91
128
err. span_label ( lo, WHILE_PARSING_OR_MSG ) ;
92
129
err
@@ -101,15 +138,63 @@ impl<'a> Parser<'a> {
101
138
self . sess . gated_spans . gate ( sym:: or_patterns, or_pattern_span) ;
102
139
}
103
140
104
- Ok ( self . mk_pat ( or_pattern_span, PatKind :: Or ( pats) ) )
141
+ Ok ( ( self . mk_pat ( or_pattern_span, PatKind :: Or ( pats) ) , trailing_vert ) )
105
142
}
106
143
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 ) > {
113
198
// In order to get good UX, we first recover in the case of a leading vert for an illegal
114
199
// top-level or-pat. Normally, this means recovering both `|` and `||`, but in this case,
115
200
// a leading `||` probably doesn't indicate an or-pattern attempt, so we handle that
@@ -128,53 +213,28 @@ impl<'a> Parser<'a> {
128
213
self . bump ( ) ;
129
214
}
130
215
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
+ )
161
222
}
162
223
163
224
/// Eat the or-pattern `|` separator.
164
225
/// 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 {
166
227
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
178
238
}
179
239
}
180
240
@@ -190,14 +250,14 @@ impl<'a> Parser<'a> {
190
250
matches ! (
191
251
& token. uninterpolate( ) . kind,
192
252
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 | }`.
201
261
)
202
262
} ) ;
203
263
match ( is_end_ahead, & self . token . kind ) {
0 commit comments