]>
Commit | Line | Data |
---|---|---|
6a06907d | 1 | use super::{ForceCollect, Parser, PathStyle, TrailingToken}; |
416331ca | 2 | use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole}; |
29967ef6 | 3 | use rustc_ast::mut_visit::{noop_visit_pat, MutVisitor}; |
74b04a01 XL |
4 | use rustc_ast::ptr::P; |
5 | use rustc_ast::token; | |
6a06907d | 6 | use rustc_ast::{self as ast, AttrVec, Attribute, MacCall, Pat, PatField, PatKind, RangeEnd}; |
3dfed10e | 7 | use rustc_ast::{BindingMode, Expr, ExprKind, Mutability, Path, QSelf, RangeSyntax}; |
74b04a01 | 8 | use rustc_ast_pretty::pprust; |
dfeec247 XL |
9 | use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, PResult}; |
10 | use rustc_span::source_map::{respan, Span, Spanned}; | |
f9f354fc | 11 | use rustc_span::symbol::{kw, sym, Ident}; |
416331ca | 12 | |
e1599b0c XL |
13 | type Expected = Option<&'static str>; |
14 | ||
15 | /// `Expected` for function and lambda parameter patterns. | |
16 | pub(super) const PARAM_EXPECTED: Expected = Some("parameter name"); | |
17 | ||
e74abb32 XL |
18 | const WHILE_PARSING_OR_MSG: &str = "while parsing this or-pattern starting here"; |
19 | ||
e1599b0c | 20 | /// Whether or not an or-pattern should be gated when occurring in the current context. |
fc512014 | 21 | #[derive(PartialEq, Clone, Copy)] |
6a06907d | 22 | pub enum GateOr { |
dfeec247 XL |
23 | Yes, |
24 | No, | |
25 | } | |
e1599b0c XL |
26 | |
27 | /// Whether or not to recover a `,` when parsing or-patterns. | |
28 | #[derive(PartialEq, Copy, Clone)] | |
6a06907d | 29 | pub enum RecoverComma { |
dfeec247 XL |
30 | Yes, |
31 | No, | |
32 | } | |
e1599b0c | 33 | |
6a06907d XL |
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 | ||
416331ca XL |
46 | impl<'a> Parser<'a> { |
47 | /// Parses a pattern. | |
e1599b0c XL |
48 | /// |
49 | /// Corresponds to `pat<no_top_alt>` in RFC 2535 and does not admit or-patterns | |
50 | /// at the top level. Used when parsing the parameters of lambda expressions, | |
51 | /// functions, function pointers, and `pat` macro fragments. | |
6a06907d | 52 | pub fn parse_pat_no_top_alt(&mut self, expected: Expected) -> PResult<'a, P<Pat>> { |
416331ca XL |
53 | self.parse_pat_with_range_pat(true, expected) |
54 | } | |
55 | ||
6a06907d XL |
56 | /// Parses a pattern. |
57 | /// | |
e1599b0c | 58 | /// Corresponds to `top_pat` in RFC 2535 and allows or-pattern at the top level. |
6a06907d XL |
59 | /// Used for parsing patterns in all cases when `pat<no_top_alt>` is not used. |
60 | /// | |
61 | /// Note that after the FCP in <https://github.com/rust-lang/rust/issues/81415>, | |
62 | /// a leading vert is allowed in nested or-patterns, too. This allows us to | |
63 | /// simplify the grammar somewhat. | |
64 | pub fn parse_pat_allow_top_alt( | |
fc512014 | 65 | &mut self, |
6a06907d | 66 | expected: Expected, |
fc512014 XL |
67 | gate_or: GateOr, |
68 | rc: RecoverComma, | |
69 | ) -> PResult<'a, P<Pat>> { | |
6a06907d | 70 | self.parse_pat_allow_top_alt_inner(expected, gate_or, rc).map(|(pat, _)| pat) |
416331ca XL |
71 | } |
72 | ||
6a06907d XL |
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( | |
e1599b0c XL |
76 | &mut self, |
77 | expected: Expected, | |
78 | gate_or: GateOr, | |
79 | rc: RecoverComma, | |
6a06907d XL |
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 | // | |
84 | // Allow a '|' before the pats (RFCs 1925, 2530, and 2535). | |
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 | }; | |
90 | ||
e74abb32 | 91 | // Parse the first pattern (`p_0`). |
6a06907d | 92 | let first_pat = self.parse_pat_no_top_alt(expected)?; |
fc512014 | 93 | self.maybe_recover_unexpected_comma(first_pat.span, rc, gate_or)?; |
e1599b0c XL |
94 | |
95 | // If the next token is not a `|`, | |
96 | // this is not an or-pattern and we should exit here. | |
97 | if !self.check(&token::BinOp(token::Or)) && self.token != token::OrOr { | |
6a06907d XL |
98 | // If we parsed a leading `|` which should be gated, |
99 | // then we should really gate the leading `|`. | |
100 | // This complicated procedure is done purely for diagnostics UX. | |
101 | if let Some(leading_vert_span) = leading_vert_span { | |
102 | if gate_or == GateOr::Yes && self.sess.gated_spans.is_ungated(sym::or_patterns) { | |
103 | self.sess.gated_spans.gate(sym::or_patterns, leading_vert_span); | |
104 | } | |
105 | ||
106 | // If there was a leading vert, treat this as an or-pattern. This improves | |
107 | // diagnostics. | |
108 | let span = leading_vert_span.to(self.prev_token.span); | |
109 | return Ok((self.mk_pat(span, PatKind::Or(vec![first_pat])), trailing_vert)); | |
110 | } | |
111 | ||
112 | return Ok((first_pat, trailing_vert)); | |
e1599b0c XL |
113 | } |
114 | ||
e74abb32 | 115 | // Parse the patterns `p_1 | ... | p_n` where `n > 0`. |
6a06907d | 116 | let lo = leading_vert_span.unwrap_or(first_pat.span); |
e1599b0c | 117 | let mut pats = vec![first_pat]; |
6a06907d XL |
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 | } | |
127 | let pat = self.parse_pat_no_top_alt(expected).map_err(|mut err| { | |
e74abb32 | 128 | err.span_label(lo, WHILE_PARSING_OR_MSG); |
e1599b0c XL |
129 | err |
130 | })?; | |
fc512014 | 131 | self.maybe_recover_unexpected_comma(pat.span, rc, gate_or)?; |
e1599b0c XL |
132 | pats.push(pat); |
133 | } | |
74b04a01 | 134 | let or_pattern_span = lo.to(self.prev_token.span); |
e1599b0c XL |
135 | |
136 | // Feature gate the or-pattern if instructed: | |
137 | if gate_or == GateOr::Yes { | |
60c5eb7d | 138 | self.sess.gated_spans.gate(sym::or_patterns, or_pattern_span); |
e1599b0c XL |
139 | } |
140 | ||
6a06907d XL |
141 | Ok((self.mk_pat(or_pattern_span, PatKind::Or(pats)), trailing_vert)) |
142 | } | |
143 | ||
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)> { | |
198 | // In order to get good UX, we first recover in the case of a leading vert for an illegal | |
199 | // top-level or-pat. Normally, this means recovering both `|` and `||`, but in this case, | |
200 | // a leading `||` probably doesn't indicate an or-pattern attempt, so we handle that | |
201 | // separately. | |
202 | if let token::OrOr = self.token.kind { | |
203 | let span = self.token.span; | |
204 | let mut err = self.struct_span_err(span, "unexpected `||` before function parameter"); | |
205 | err.span_suggestion( | |
206 | span, | |
207 | "remove the `||`", | |
208 | String::new(), | |
209 | Applicability::MachineApplicable, | |
210 | ); | |
211 | err.note("alternatives in or-patterns are separated with `|`, not `||`"); | |
212 | err.emit(); | |
213 | self.bump(); | |
214 | } | |
215 | ||
216 | self.parse_pat_before_ty( | |
217 | PARAM_EXPECTED, | |
218 | GateOr::No, | |
219 | RecoverComma::No, | |
220 | "function parameters", | |
221 | ) | |
e1599b0c XL |
222 | } |
223 | ||
224 | /// Eat the or-pattern `|` separator. | |
225 | /// If instead a `||` token is encountered, recover and pretend we parsed `|`. | |
6a06907d | 226 | fn eat_or_separator(&mut self, lo: Option<Span>) -> EatOrResult { |
e74abb32 | 227 | if self.recover_trailing_vert(lo) { |
6a06907d XL |
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 | |
e1599b0c XL |
238 | } |
239 | } | |
240 | ||
e74abb32 XL |
241 | /// Recover if `|` or `||` is the current token and we have one of the |
242 | /// tokens `=>`, `if`, `=`, `:`, `;`, `,`, `]`, `)`, or `}` ahead of us. | |
243 | /// | |
244 | /// These tokens all indicate that we reached the end of the or-pattern | |
245 | /// list and can now reliably say that the `|` was an illegal trailing vert. | |
246 | /// Note that there are more tokens such as `@` for which we know that the `|` | |
247 | /// is an illegal parse. However, the user's intent is less clear in that case. | |
248 | fn recover_trailing_vert(&mut self, lo: Option<Span>) -> bool { | |
29967ef6 XL |
249 | let is_end_ahead = self.look_ahead(1, |token| { |
250 | matches!( | |
251 | &token.uninterpolate().kind, | |
252 | token::FatArrow // e.g. `a | => 0,`. | |
6a06907d XL |
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 | }`. | |
29967ef6 | 261 | ) |
e74abb32 XL |
262 | }); |
263 | match (is_end_ahead, &self.token.kind) { | |
ba9703b0 | 264 | (true, token::BinOp(token::Or) | token::OrOr) => { |
e74abb32 XL |
265 | self.ban_illegal_vert(lo, "trailing", "not allowed in an or-pattern"); |
266 | self.bump(); | |
267 | true | |
268 | } | |
269 | _ => false, | |
270 | } | |
271 | } | |
272 | ||
e1599b0c | 273 | /// We have parsed `||` instead of `|`. Error and suggest `|` instead. |
e74abb32 | 274 | fn ban_unexpected_or_or(&mut self, lo: Option<Span>) { |
6a06907d | 275 | let mut err = self.struct_span_err(self.token.span, "unexpected token `||` in pattern"); |
e74abb32 XL |
276 | err.span_suggestion( |
277 | self.token.span, | |
278 | "use a single `|` to separate multiple alternative patterns", | |
279 | "|".to_owned(), | |
dfeec247 | 280 | Applicability::MachineApplicable, |
e74abb32 XL |
281 | ); |
282 | if let Some(lo) = lo { | |
283 | err.span_label(lo, WHILE_PARSING_OR_MSG); | |
284 | } | |
285 | err.emit(); | |
e1599b0c XL |
286 | } |
287 | ||
288 | /// Some special error handling for the "top-level" patterns in a match arm, | |
289 | /// `for` loop, `let`, &c. (in contrast to subpatterns within such). | |
fc512014 XL |
290 | fn maybe_recover_unexpected_comma( |
291 | &mut self, | |
292 | lo: Span, | |
293 | rc: RecoverComma, | |
294 | gate_or: GateOr, | |
295 | ) -> PResult<'a, ()> { | |
e1599b0c XL |
296 | if rc == RecoverComma::No || self.token != token::Comma { |
297 | return Ok(()); | |
298 | } | |
299 | ||
300 | // An unexpected comma after a top-level pattern is a clue that the | |
301 | // user (perhaps more accustomed to some other language) forgot the | |
302 | // parentheses in what should have been a tuple pattern; return a | |
303 | // suggestion-enhanced error here rather than choking on the comma later. | |
304 | let comma_span = self.token.span; | |
305 | self.bump(); | |
306 | if let Err(mut err) = self.skip_pat_list() { | |
307 | // We didn't expect this to work anyway; we just wanted to advance to the | |
308 | // end of the comma-sequence so we know the span to suggest parenthesizing. | |
309 | err.cancel(); | |
310 | } | |
74b04a01 | 311 | let seq_span = lo.to(self.prev_token.span); |
e1599b0c XL |
312 | let mut err = self.struct_span_err(comma_span, "unexpected `,` in pattern"); |
313 | if let Ok(seq_snippet) = self.span_to_snippet(seq_span) { | |
fc512014 XL |
314 | const MSG: &str = "try adding parentheses to match on a tuple..."; |
315 | ||
316 | let or_suggestion = | |
317 | gate_or == GateOr::No || !self.sess.gated_spans.is_ungated(sym::or_patterns); | |
e1599b0c XL |
318 | err.span_suggestion( |
319 | seq_span, | |
fc512014 | 320 | if or_suggestion { MSG } else { MSG.trim_end_matches('.') }, |
e1599b0c | 321 | format!("({})", seq_snippet), |
dfeec247 | 322 | Applicability::MachineApplicable, |
e1599b0c | 323 | ); |
fc512014 XL |
324 | if or_suggestion { |
325 | err.span_suggestion( | |
326 | seq_span, | |
327 | "...or a vertical bar to match on multiple alternatives", | |
328 | seq_snippet.replace(",", " |"), | |
329 | Applicability::MachineApplicable, | |
330 | ); | |
331 | } | |
e1599b0c XL |
332 | } |
333 | Err(err) | |
334 | } | |
335 | ||
5869c6ff | 336 | /// Parse and throw away a parenthesized comma separated |
416331ca XL |
337 | /// sequence of patterns until `)` is reached. |
338 | fn skip_pat_list(&mut self) -> PResult<'a, ()> { | |
339 | while !self.check(&token::CloseDelim(token::Paren)) { | |
6a06907d | 340 | self.parse_pat_no_top_alt(None)?; |
416331ca | 341 | if !self.eat(&token::Comma) { |
dfeec247 | 342 | return Ok(()); |
416331ca XL |
343 | } |
344 | } | |
345 | Ok(()) | |
346 | } | |
347 | ||
e74abb32 XL |
348 | /// A `|` or possibly `||` token shouldn't be here. Ban it. |
349 | fn ban_illegal_vert(&mut self, lo: Option<Span>, pos: &str, ctx: &str) { | |
350 | let span = self.token.span; | |
351 | let mut err = self.struct_span_err(span, &format!("a {} `|` is {}", pos, ctx)); | |
352 | err.span_suggestion( | |
353 | span, | |
354 | &format!("remove the `{}`", pprust::token_to_string(&self.token)), | |
355 | String::new(), | |
356 | Applicability::MachineApplicable, | |
357 | ); | |
358 | if let Some(lo) = lo { | |
359 | err.span_label(lo, WHILE_PARSING_OR_MSG); | |
360 | } | |
361 | if let token::OrOr = self.token.kind { | |
362 | err.note("alternatives in or-patterns are separated with `|`, not `||`"); | |
363 | } | |
364 | err.emit(); | |
365 | } | |
366 | ||
416331ca XL |
367 | /// Parses a pattern, with a setting whether modern range patterns (e.g., `a..=b`, `a..b` are |
368 | /// allowed). | |
369 | fn parse_pat_with_range_pat( | |
370 | &mut self, | |
371 | allow_range_pat: bool, | |
e1599b0c | 372 | expected: Expected, |
416331ca XL |
373 | ) -> PResult<'a, P<Pat>> { |
374 | maybe_recover_from_interpolated_ty_qpath!(self, true); | |
375 | maybe_whole!(self, NtPat, |x| x); | |
376 | ||
377 | let lo = self.token.span; | |
dfeec247 XL |
378 | |
379 | let pat = if self.check(&token::BinOp(token::And)) || self.token.kind == token::AndAnd { | |
380 | self.parse_pat_deref(expected)? | |
381 | } else if self.check(&token::OpenDelim(token::Paren)) { | |
382 | self.parse_pat_tuple_or_parens()? | |
383 | } else if self.check(&token::OpenDelim(token::Bracket)) { | |
384 | // Parse `[pat, pat,...]` as a slice pattern. | |
6a06907d XL |
385 | let (pats, _) = self.parse_delim_comma_seq(token::Bracket, |p| { |
386 | p.parse_pat_allow_top_alt(None, GateOr::Yes, RecoverComma::No) | |
387 | })?; | |
dfeec247 XL |
388 | PatKind::Slice(pats) |
389 | } else if self.check(&token::DotDot) && !self.is_pat_range_end_start(1) { | |
390 | // A rest pattern `..`. | |
391 | self.bump(); // `..` | |
392 | PatKind::Rest | |
ba9703b0 XL |
393 | } else if self.check(&token::DotDotDot) && !self.is_pat_range_end_start(1) { |
394 | self.recover_dotdotdot_rest_pat(lo) | |
dfeec247 XL |
395 | } else if let Some(form) = self.parse_range_end() { |
396 | self.parse_pat_range_to(form)? // `..=X`, `...X`, or `..X`. | |
397 | } else if self.eat_keyword(kw::Underscore) { | |
398 | // Parse _ | |
399 | PatKind::Wild | |
400 | } else if self.eat_keyword(kw::Mut) { | |
401 | self.parse_pat_ident_mut()? | |
402 | } else if self.eat_keyword(kw::Ref) { | |
403 | // Parse ref ident @ pat / ref mut ident @ pat | |
404 | let mutbl = self.parse_mutability(); | |
405 | self.parse_pat_ident(BindingMode::ByRef(mutbl))? | |
406 | } else if self.eat_keyword(kw::Box) { | |
407 | // Parse `box pat` | |
408 | let pat = self.parse_pat_with_range_pat(false, None)?; | |
74b04a01 | 409 | self.sess.gated_spans.gate(sym::box_patterns, lo.to(self.prev_token.span)); |
dfeec247 | 410 | PatKind::Box(pat) |
29967ef6 XL |
411 | } else if self.check_inline_const(0) { |
412 | // Parse `const pat` | |
413 | let const_expr = self.parse_const_block(lo.to(self.token.span))?; | |
414 | ||
415 | if let Some(re) = self.parse_range_end() { | |
416 | self.parse_pat_range_begin_with(const_expr, re)? | |
417 | } else { | |
418 | PatKind::Lit(const_expr) | |
419 | } | |
dfeec247 XL |
420 | } else if self.can_be_ident_pat() { |
421 | // Parse `ident @ pat` | |
422 | // This can give false positives and parse nullary enums, | |
423 | // they are dealt with later in resolve. | |
424 | self.parse_pat_ident(BindingMode::ByValue(Mutability::Not))? | |
425 | } else if self.is_start_of_pat_with_path() { | |
426 | // Parse pattern starting with a path | |
427 | let (qself, path) = if self.eat_lt() { | |
428 | // Parse a qualified path | |
429 | let (qself, path) = self.parse_qpath(PathStyle::Expr)?; | |
430 | (Some(qself), path) | |
416331ca | 431 | } else { |
dfeec247 XL |
432 | // Parse an unqualified path |
433 | (None, self.parse_path(PathStyle::Expr)?) | |
434 | }; | |
74b04a01 | 435 | let span = lo.to(self.prev_token.span); |
dfeec247 XL |
436 | |
437 | if qself.is_none() && self.check(&token::Not) { | |
438 | self.parse_pat_mac_invoc(path)? | |
439 | } else if let Some(form) = self.parse_range_end() { | |
440 | let begin = self.mk_expr(span, ExprKind::Path(qself, path), AttrVec::new()); | |
441 | self.parse_pat_range_begin_with(begin, form)? | |
442 | } else if self.check(&token::OpenDelim(token::Brace)) { | |
443 | self.parse_pat_struct(qself, path)? | |
444 | } else if self.check(&token::OpenDelim(token::Paren)) { | |
445 | self.parse_pat_tuple_struct(qself, path)? | |
446 | } else { | |
447 | PatKind::Path(qself, path) | |
448 | } | |
449 | } else { | |
450 | // Try to parse everything else as literal with optional minus | |
451 | match self.parse_literal_maybe_minus() { | |
452 | Ok(begin) => match self.parse_range_end() { | |
453 | Some(form) => self.parse_pat_range_begin_with(begin, form)?, | |
454 | None => PatKind::Lit(begin), | |
455 | }, | |
456 | Err(err) => return self.fatal_unexpected_non_pat(err, expected), | |
416331ca | 457 | } |
e1599b0c | 458 | }; |
416331ca | 459 | |
74b04a01 | 460 | let pat = self.mk_pat(lo.to(self.prev_token.span), pat); |
416331ca | 461 | let pat = self.maybe_recover_from_bad_qpath(pat, true)?; |
e74abb32 | 462 | let pat = self.recover_intersection_pat(pat)?; |
416331ca XL |
463 | |
464 | if !allow_range_pat { | |
dfeec247 | 465 | self.ban_pat_range_if_ambiguous(&pat) |
416331ca XL |
466 | } |
467 | ||
468 | Ok(pat) | |
469 | } | |
470 | ||
ba9703b0 XL |
471 | /// Recover from a typoed `...` pattern that was encountered |
472 | /// Ref: Issue #70388 | |
473 | fn recover_dotdotdot_rest_pat(&mut self, lo: Span) -> PatKind { | |
474 | // A typoed rest pattern `...`. | |
475 | self.bump(); // `...` | |
476 | ||
477 | // The user probably mistook `...` for a rest pattern `..`. | |
478 | self.struct_span_err(lo, "unexpected `...`") | |
479 | .span_label(lo, "not a valid pattern") | |
480 | .span_suggestion_short( | |
481 | lo, | |
482 | "for a rest pattern, use `..` instead of `...`", | |
483 | "..".to_owned(), | |
484 | Applicability::MachineApplicable, | |
485 | ) | |
486 | .emit(); | |
487 | PatKind::Rest | |
488 | } | |
489 | ||
e74abb32 XL |
490 | /// Try to recover the more general form `intersect ::= $pat_lhs @ $pat_rhs`. |
491 | /// | |
492 | /// Allowed binding patterns generated by `binding ::= ref? mut? $ident @ $pat_rhs` | |
493 | /// should already have been parsed by now at this point, | |
494 | /// if the next token is `@` then we can try to parse the more general form. | |
495 | /// | |
496 | /// Consult `parse_pat_ident` for the `binding` grammar. | |
497 | /// | |
498 | /// The notion of intersection patterns are found in | |
499 | /// e.g. [F#][and] where they are called AND-patterns. | |
500 | /// | |
501 | /// [and]: https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/pattern-matching | |
502 | fn recover_intersection_pat(&mut self, lhs: P<Pat>) -> PResult<'a, P<Pat>> { | |
503 | if self.token.kind != token::At { | |
504 | // Next token is not `@` so it's not going to be an intersection pattern. | |
505 | return Ok(lhs); | |
506 | } | |
507 | ||
508 | // At this point we attempt to parse `@ $pat_rhs` and emit an error. | |
509 | self.bump(); // `@` | |
6a06907d | 510 | let mut rhs = self.parse_pat_no_top_alt(None)?; |
e74abb32 XL |
511 | let sp = lhs.span.to(rhs.span); |
512 | ||
513 | if let PatKind::Ident(_, _, ref mut sub @ None) = rhs.kind { | |
514 | // The user inverted the order, so help them fix that. | |
515 | let mut applicability = Applicability::MachineApplicable; | |
dfeec247 | 516 | // FIXME(bindings_after_at): Remove this code when stabilizing the feature. |
e74abb32 XL |
517 | lhs.walk(&mut |p| match p.kind { |
518 | // `check_match` is unhappy if the subpattern has a binding anywhere. | |
519 | PatKind::Ident(..) => { | |
520 | applicability = Applicability::MaybeIncorrect; | |
521 | false // Short-circuit. | |
dfeec247 | 522 | } |
e74abb32 XL |
523 | _ => true, |
524 | }); | |
525 | ||
526 | let lhs_span = lhs.span; | |
527 | // Move the LHS into the RHS as a subpattern. | |
528 | // The RHS is now the full pattern. | |
529 | *sub = Some(lhs); | |
530 | ||
531 | self.struct_span_err(sp, "pattern on wrong side of `@`") | |
532 | .span_label(lhs_span, "pattern on the left, should be on the right") | |
533 | .span_label(rhs.span, "binding on the right, should be on the left") | |
534 | .span_suggestion(sp, "switch the order", pprust::pat_to_string(&rhs), applicability) | |
535 | .emit(); | |
536 | } else { | |
537 | // The special case above doesn't apply so we may have e.g. `A(x) @ B(y)`. | |
538 | rhs.kind = PatKind::Wild; | |
539 | self.struct_span_err(sp, "left-hand side of `@` must be a binding") | |
540 | .span_label(lhs.span, "interpreted as a pattern, not a binding") | |
541 | .span_label(rhs.span, "also a pattern") | |
542 | .note("bindings are `x`, `mut x`, `ref x`, and `ref mut x`") | |
543 | .emit(); | |
544 | } | |
545 | ||
546 | rhs.span = sp; | |
547 | Ok(rhs) | |
548 | } | |
549 | ||
e1599b0c | 550 | /// Ban a range pattern if it has an ambiguous interpretation. |
dfeec247 | 551 | fn ban_pat_range_if_ambiguous(&self, pat: &Pat) { |
e74abb32 | 552 | match pat.kind { |
e1599b0c | 553 | PatKind::Range( |
dfeec247 XL |
554 | .., |
555 | Spanned { node: RangeEnd::Included(RangeSyntax::DotDotDot), .. }, | |
556 | ) => return, | |
e1599b0c | 557 | PatKind::Range(..) => {} |
dfeec247 | 558 | _ => return, |
e1599b0c XL |
559 | } |
560 | ||
dfeec247 XL |
561 | self.struct_span_err(pat.span, "the range pattern here has ambiguous interpretation") |
562 | .span_suggestion( | |
563 | pat.span, | |
564 | "add parentheses to clarify the precedence", | |
565 | format!("({})", pprust::pat_to_string(&pat)), | |
566 | // "ambiguous interpretation" implies that we have to be guessing | |
567 | Applicability::MaybeIncorrect, | |
568 | ) | |
569 | .emit(); | |
e1599b0c XL |
570 | } |
571 | ||
572 | /// Parse `&pat` / `&mut pat`. | |
573 | fn parse_pat_deref(&mut self, expected: Expected) -> PResult<'a, PatKind> { | |
574 | self.expect_and()?; | |
60c5eb7d | 575 | self.recover_lifetime_in_deref_pat(); |
e1599b0c | 576 | let mutbl = self.parse_mutability(); |
60c5eb7d XL |
577 | let subpat = self.parse_pat_with_range_pat(false, expected)?; |
578 | Ok(PatKind::Ref(subpat, mutbl)) | |
579 | } | |
e1599b0c | 580 | |
60c5eb7d | 581 | fn recover_lifetime_in_deref_pat(&mut self) { |
e1599b0c | 582 | if let token::Lifetime(name) = self.token.kind { |
60c5eb7d XL |
583 | self.bump(); // `'a` |
584 | ||
74b04a01 | 585 | let span = self.prev_token.span; |
60c5eb7d XL |
586 | self.struct_span_err(span, &format!("unexpected lifetime `{}` in pattern", name)) |
587 | .span_suggestion( | |
588 | span, | |
589 | "remove the lifetime", | |
590 | String::new(), | |
591 | Applicability::MachineApplicable, | |
592 | ) | |
593 | .emit(); | |
e1599b0c | 594 | } |
e1599b0c XL |
595 | } |
596 | ||
597 | /// Parse a tuple or parenthesis pattern. | |
598 | fn parse_pat_tuple_or_parens(&mut self) -> PResult<'a, PatKind> { | |
6a06907d XL |
599 | let (fields, trailing_comma) = self.parse_paren_comma_seq(|p| { |
600 | p.parse_pat_allow_top_alt(None, GateOr::Yes, RecoverComma::No) | |
601 | })?; | |
e1599b0c XL |
602 | |
603 | // Here, `(pat,)` is a tuple pattern. | |
604 | // For backward compatibility, `(..)` is a tuple pattern as well. | |
605 | Ok(if fields.len() == 1 && !(trailing_comma || fields[0].is_rest()) { | |
74b04a01 | 606 | PatKind::Paren(fields.into_iter().next().unwrap()) |
e1599b0c XL |
607 | } else { |
608 | PatKind::Tuple(fields) | |
416331ca XL |
609 | }) |
610 | } | |
611 | ||
e1599b0c XL |
612 | /// Parse a mutable binding with the `mut` token already eaten. |
613 | fn parse_pat_ident_mut(&mut self) -> PResult<'a, PatKind> { | |
74b04a01 | 614 | let mut_span = self.prev_token.span; |
e1599b0c XL |
615 | |
616 | if self.eat_keyword(kw::Ref) { | |
dfeec247 | 617 | return self.recover_mut_ref_ident(mut_span); |
e1599b0c XL |
618 | } |
619 | ||
620 | self.recover_additional_muts(); | |
621 | ||
622 | // Make sure we don't allow e.g. `let mut $p;` where `$p:pat`. | |
623 | if let token::Interpolated(ref nt) = self.token.kind { | |
dfeec247 XL |
624 | if let token::NtPat(_) = **nt { |
625 | self.expected_ident_found().emit(); | |
626 | } | |
e1599b0c XL |
627 | } |
628 | ||
629 | // Parse the pattern we hope to be an identifier. | |
6a06907d | 630 | let mut pat = self.parse_pat_no_top_alt(Some("identifier"))?; |
e1599b0c | 631 | |
74b04a01 XL |
632 | // If we don't have `mut $ident (@ pat)?`, error. |
633 | if let PatKind::Ident(BindingMode::ByValue(m @ Mutability::Not), ..) = &mut pat.kind { | |
634 | // Don't recurse into the subpattern. | |
635 | // `mut` on the outer binding doesn't affect the inner bindings. | |
636 | *m = Mutability::Mut; | |
637 | } else { | |
638 | // Add `mut` to any binding in the parsed pattern. | |
639 | let changed_any_binding = Self::make_all_value_bindings_mutable(&mut pat); | |
640 | self.ban_mut_general_pat(mut_span, &pat, changed_any_binding); | |
e1599b0c XL |
641 | } |
642 | ||
74b04a01 | 643 | Ok(pat.into_inner().kind) |
e1599b0c XL |
644 | } |
645 | ||
646 | /// Recover on `mut ref? ident @ pat` and suggest | |
647 | /// that the order of `mut` and `ref` is incorrect. | |
648 | fn recover_mut_ref_ident(&mut self, lo: Span) -> PResult<'a, PatKind> { | |
74b04a01 | 649 | let mutref_span = lo.to(self.prev_token.span); |
e1599b0c XL |
650 | self.struct_span_err(mutref_span, "the order of `mut` and `ref` is incorrect") |
651 | .span_suggestion( | |
652 | mutref_span, | |
653 | "try switching the order", | |
654 | "ref mut".into(), | |
dfeec247 | 655 | Applicability::MachineApplicable, |
e1599b0c XL |
656 | ) |
657 | .emit(); | |
658 | ||
dfeec247 | 659 | self.parse_pat_ident(BindingMode::ByRef(Mutability::Mut)) |
e1599b0c XL |
660 | } |
661 | ||
662 | /// Turn all by-value immutable bindings in a pattern into mutable bindings. | |
663 | /// Returns `true` if any change was made. | |
664 | fn make_all_value_bindings_mutable(pat: &mut P<Pat>) -> bool { | |
665 | struct AddMut(bool); | |
666 | impl MutVisitor for AddMut { | |
e1599b0c | 667 | fn visit_pat(&mut self, pat: &mut P<Pat>) { |
74b04a01 | 668 | if let PatKind::Ident(BindingMode::ByValue(m @ Mutability::Not), ..) = &mut pat.kind |
e1599b0c | 669 | { |
e1599b0c | 670 | self.0 = true; |
74b04a01 | 671 | *m = Mutability::Mut; |
e1599b0c XL |
672 | } |
673 | noop_visit_pat(pat, self); | |
674 | } | |
675 | } | |
676 | ||
677 | let mut add_mut = AddMut(false); | |
678 | add_mut.visit_pat(pat); | |
679 | add_mut.0 | |
680 | } | |
681 | ||
682 | /// Error on `mut $pat` where `$pat` is not an ident. | |
683 | fn ban_mut_general_pat(&self, lo: Span, pat: &Pat, changed_any_binding: bool) { | |
684 | let span = lo.to(pat.span); | |
685 | let fix = pprust::pat_to_string(&pat); | |
686 | let (problem, suggestion) = if changed_any_binding { | |
687 | ("`mut` must be attached to each individual binding", "add `mut` to each binding") | |
688 | } else { | |
689 | ("`mut` must be followed by a named binding", "remove the `mut` prefix") | |
690 | }; | |
691 | self.struct_span_err(span, problem) | |
692 | .span_suggestion(span, suggestion, fix, Applicability::MachineApplicable) | |
693 | .note("`mut` may be followed by `variable` and `variable @ pattern`") | |
74b04a01 | 694 | .emit(); |
e1599b0c XL |
695 | } |
696 | ||
697 | /// Eat any extraneous `mut`s and error + recover if we ate any. | |
698 | fn recover_additional_muts(&mut self) { | |
699 | let lo = self.token.span; | |
700 | while self.eat_keyword(kw::Mut) {} | |
701 | if lo == self.token.span { | |
702 | return; | |
703 | } | |
704 | ||
74b04a01 | 705 | let span = lo.to(self.prev_token.span); |
e1599b0c XL |
706 | self.struct_span_err(span, "`mut` on a binding may not be repeated") |
707 | .span_suggestion( | |
708 | span, | |
709 | "remove the additional `mut`s", | |
710 | String::new(), | |
711 | Applicability::MachineApplicable, | |
712 | ) | |
713 | .emit(); | |
714 | } | |
715 | ||
716 | /// Parse macro invocation | |
60c5eb7d | 717 | fn parse_pat_mac_invoc(&mut self, path: Path) -> PResult<'a, PatKind> { |
e1599b0c | 718 | self.bump(); |
60c5eb7d | 719 | let args = self.parse_mac_args()?; |
ba9703b0 XL |
720 | let mac = MacCall { path, args, prior_type_ascription: self.last_type_ascription }; |
721 | Ok(PatKind::MacCall(mac)) | |
e1599b0c XL |
722 | } |
723 | ||
e1599b0c XL |
724 | fn fatal_unexpected_non_pat( |
725 | &mut self, | |
726 | mut err: DiagnosticBuilder<'a>, | |
727 | expected: Expected, | |
728 | ) -> PResult<'a, P<Pat>> { | |
729 | err.cancel(); | |
730 | ||
731 | let expected = expected.unwrap_or("pattern"); | |
dfeec247 | 732 | let msg = format!("expected {}, found {}", expected, super::token_descr(&self.token)); |
e1599b0c | 733 | |
dfeec247 | 734 | let mut err = self.struct_span_err(self.token.span, &msg); |
e1599b0c XL |
735 | err.span_label(self.token.span, format!("expected {}", expected)); |
736 | ||
737 | let sp = self.sess.source_map().start_point(self.token.span); | |
738 | if let Some(sp) = self.sess.ambiguous_block_expr_parse.borrow().get(&sp) { | |
739 | self.sess.expr_parentheses_needed(&mut err, *sp, None); | |
740 | } | |
741 | ||
742 | Err(err) | |
743 | } | |
744 | ||
dfeec247 XL |
745 | /// Parses the range pattern end form `".." | "..." | "..=" ;`. |
746 | fn parse_range_end(&mut self) -> Option<Spanned<RangeEnd>> { | |
747 | let re = if self.eat(&token::DotDotDot) { | |
748 | RangeEnd::Included(RangeSyntax::DotDotDot) | |
749 | } else if self.eat(&token::DotDotEq) { | |
750 | RangeEnd::Included(RangeSyntax::DotDotEq) | |
751 | } else if self.eat(&token::DotDot) { | |
74b04a01 | 752 | self.sess.gated_spans.gate(sym::exclusive_range_pattern, self.prev_token.span); |
dfeec247 XL |
753 | RangeEnd::Excluded |
754 | } else { | |
755 | return None; | |
756 | }; | |
74b04a01 | 757 | Some(respan(self.prev_token.span, re)) |
416331ca XL |
758 | } |
759 | ||
dfeec247 XL |
760 | /// Parse a range pattern `$begin $form $end?` where `$form = ".." | "..." | "..=" ;`. |
761 | /// `$begin $form` has already been parsed. | |
762 | fn parse_pat_range_begin_with( | |
763 | &mut self, | |
764 | begin: P<Expr>, | |
765 | re: Spanned<RangeEnd>, | |
766 | ) -> PResult<'a, PatKind> { | |
767 | let end = if self.is_pat_range_end_start(0) { | |
768 | // Parsing e.g. `X..=Y`. | |
769 | Some(self.parse_pat_range_end()?) | |
770 | } else { | |
771 | // Parsing e.g. `X..`. | |
772 | self.sess.gated_spans.gate(sym::half_open_range_patterns, begin.span.to(re.span)); | |
773 | if let RangeEnd::Included(_) = re.node { | |
774 | // FIXME(Centril): Consider semantic errors instead in `ast_validation`. | |
775 | // Possibly also do this for `X..=` in *expression* contexts. | |
776 | self.error_inclusive_range_with_no_end(re.span); | |
777 | } | |
778 | None | |
779 | }; | |
780 | Ok(PatKind::Range(Some(begin), end, re)) | |
781 | } | |
416331ca | 782 | |
dfeec247 XL |
783 | pub(super) fn error_inclusive_range_with_no_end(&self, span: Span) { |
784 | struct_span_err!(self.sess.span_diagnostic, span, E0586, "inclusive range with no end") | |
785 | .span_suggestion_short( | |
786 | span, | |
787 | "use `..` instead", | |
788 | "..".to_string(), | |
789 | Applicability::MachineApplicable, | |
416331ca | 790 | ) |
dfeec247 | 791 | .note("inclusive ranges must be bounded at the end (`..=b` or `a..=b`)") |
416331ca | 792 | .emit(); |
416331ca XL |
793 | } |
794 | ||
dfeec247 XL |
795 | /// Parse a range-to pattern, `..X` or `..=X` where `X` remains to be parsed. |
796 | /// | |
797 | /// The form `...X` is prohibited to reduce confusion with the potential | |
798 | /// expression syntax `...expr` for splatting in expressions. | |
799 | fn parse_pat_range_to(&mut self, mut re: Spanned<RangeEnd>) -> PResult<'a, PatKind> { | |
800 | let end = self.parse_pat_range_end()?; | |
74b04a01 | 801 | self.sess.gated_spans.gate(sym::half_open_range_patterns, re.span.to(self.prev_token.span)); |
dfeec247 XL |
802 | if let RangeEnd::Included(ref mut syn @ RangeSyntax::DotDotDot) = &mut re.node { |
803 | *syn = RangeSyntax::DotDotEq; | |
804 | self.struct_span_err(re.span, "range-to patterns with `...` are not allowed") | |
805 | .span_suggestion_short( | |
806 | re.span, | |
807 | "use `..=` instead", | |
808 | "..=".to_string(), | |
809 | Applicability::MachineApplicable, | |
416331ca XL |
810 | ) |
811 | .emit(); | |
416331ca | 812 | } |
dfeec247 XL |
813 | Ok(PatKind::Range(None, Some(end), re)) |
814 | } | |
815 | ||
816 | /// Is the token `dist` away from the current suitable as the start of a range patterns end? | |
817 | fn is_pat_range_end_start(&self, dist: usize) -> bool { | |
29967ef6 XL |
818 | self.check_inline_const(dist) |
819 | || self.look_ahead(dist, |t| { | |
820 | t.is_path_start() // e.g. `MY_CONST`; | |
dfeec247 | 821 | || t.kind == token::Dot // e.g. `.5` for recovery; |
74b04a01 | 822 | || t.can_begin_literal_maybe_minus() // e.g. `42`. |
dfeec247 | 823 | || t.is_whole_expr() |
29967ef6 | 824 | }) |
416331ca XL |
825 | } |
826 | ||
827 | fn parse_pat_range_end(&mut self) -> PResult<'a, P<Expr>> { | |
29967ef6 XL |
828 | if self.check_inline_const(0) { |
829 | self.parse_const_block(self.token.span) | |
830 | } else if self.check_path() { | |
416331ca XL |
831 | let lo = self.token.span; |
832 | let (qself, path) = if self.eat_lt() { | |
833 | // Parse a qualified path | |
834 | let (qself, path) = self.parse_qpath(PathStyle::Expr)?; | |
835 | (Some(qself), path) | |
836 | } else { | |
837 | // Parse an unqualified path | |
838 | (None, self.parse_path(PathStyle::Expr)?) | |
839 | }; | |
74b04a01 | 840 | let hi = self.prev_token.span; |
dfeec247 | 841 | Ok(self.mk_expr(lo.to(hi), ExprKind::Path(qself, path), AttrVec::new())) |
416331ca XL |
842 | } else { |
843 | self.parse_literal_maybe_minus() | |
844 | } | |
845 | } | |
846 | ||
e1599b0c XL |
847 | /// Is this the start of a pattern beginning with a path? |
848 | fn is_start_of_pat_with_path(&mut self) -> bool { | |
849 | self.check_path() | |
850 | // Just for recovery (see `can_be_ident`). | |
851 | || self.token.is_ident() && !self.token.is_bool_lit() && !self.token.is_keyword(kw::In) | |
852 | } | |
853 | ||
854 | /// Would `parse_pat_ident` be appropriate here? | |
855 | fn can_be_ident_pat(&mut self) -> bool { | |
856 | self.check_ident() | |
857 | && !self.token.is_bool_lit() // Avoid `true` or `false` as a binding as it is a literal. | |
858 | && !self.token.is_path_segment_keyword() // Avoid e.g. `Self` as it is a path. | |
859 | // Avoid `in`. Due to recovery in the list parser this messes with `for ( $pat in $expr )`. | |
860 | && !self.token.is_keyword(kw::In) | |
29967ef6 XL |
861 | // Try to do something more complex? |
862 | && self.look_ahead(1, |t| !matches!(t.kind, token::OpenDelim(token::Paren) // A tuple struct pattern. | |
e1599b0c XL |
863 | | token::OpenDelim(token::Brace) // A struct pattern. |
864 | | token::DotDotDot | token::DotDotEq | token::DotDot // A range pattern. | |
865 | | token::ModSep // A tuple / struct variant pattern. | |
29967ef6 | 866 | | token::Not)) // A macro expanding to a pattern. |
e1599b0c XL |
867 | } |
868 | ||
416331ca | 869 | /// Parses `ident` or `ident @ pat`. |
e1599b0c | 870 | /// Used by the copy foo and ref foo patterns to give a good |
416331ca | 871 | /// error message when parsing mistakes like `ref foo(a, b)`. |
e1599b0c | 872 | fn parse_pat_ident(&mut self, binding_mode: BindingMode) -> PResult<'a, PatKind> { |
416331ca XL |
873 | let ident = self.parse_ident()?; |
874 | let sub = if self.eat(&token::At) { | |
6a06907d | 875 | Some(self.parse_pat_no_top_alt(Some("binding pattern"))?) |
416331ca XL |
876 | } else { |
877 | None | |
878 | }; | |
879 | ||
e1599b0c XL |
880 | // Just to be friendly, if they write something like `ref Some(i)`, |
881 | // we end up here with `(` as the current token. | |
882 | // This shortly leads to a parse error. Note that if there is no explicit | |
416331ca | 883 | // binding mode then we do not end up here, because the lookahead |
e1599b0c | 884 | // will direct us over to `parse_enum_variant()`. |
416331ca | 885 | if self.token == token::OpenDelim(token::Paren) { |
74b04a01 XL |
886 | return Err(self |
887 | .struct_span_err(self.prev_token.span, "expected identifier, found enum pattern")); | |
416331ca XL |
888 | } |
889 | ||
890 | Ok(PatKind::Ident(binding_mode, ident, sub)) | |
891 | } | |
892 | ||
e1599b0c XL |
893 | /// Parse a struct ("record") pattern (e.g. `Foo { ... }` or `Foo::Bar { ... }`). |
894 | fn parse_pat_struct(&mut self, qself: Option<QSelf>, path: Path) -> PResult<'a, PatKind> { | |
895 | if qself.is_some() { | |
dfeec247 | 896 | return self.error_qpath_before_pat(&path, "{"); |
e1599b0c | 897 | } |
e1599b0c XL |
898 | self.bump(); |
899 | let (fields, etc) = self.parse_pat_fields().unwrap_or_else(|mut e| { | |
1b1a35ee | 900 | e.span_label(path.span, "while parsing the fields for this pattern"); |
e1599b0c XL |
901 | e.emit(); |
902 | self.recover_stmt(); | |
903 | (vec![], true) | |
904 | }); | |
905 | self.bump(); | |
906 | Ok(PatKind::Struct(path, fields, etc)) | |
907 | } | |
908 | ||
909 | /// Parse tuple struct or tuple variant pattern (e.g. `Foo(...)` or `Foo::Bar(...)`). | |
910 | fn parse_pat_tuple_struct(&mut self, qself: Option<QSelf>, path: Path) -> PResult<'a, PatKind> { | |
911 | if qself.is_some() { | |
dfeec247 | 912 | return self.error_qpath_before_pat(&path, "("); |
e1599b0c | 913 | } |
6a06907d XL |
914 | let (fields, _) = self.parse_paren_comma_seq(|p| { |
915 | p.parse_pat_allow_top_alt(None, GateOr::Yes, RecoverComma::No) | |
916 | })?; | |
e1599b0c XL |
917 | Ok(PatKind::TupleStruct(path, fields)) |
918 | } | |
919 | ||
dfeec247 XL |
920 | /// Error when there's a qualified path, e.g. `<Foo as Bar>::Baz` |
921 | /// as the path of e.g., a tuple or record struct pattern. | |
922 | fn error_qpath_before_pat(&mut self, path: &Path, token: &str) -> PResult<'a, PatKind> { | |
923 | let msg = &format!("unexpected `{}` after qualified path", token); | |
924 | let mut err = self.struct_span_err(self.token.span, msg); | |
925 | err.span_label(self.token.span, msg); | |
926 | err.span_label(path.span, "the qualified path"); | |
927 | Err(err) | |
928 | } | |
929 | ||
416331ca | 930 | /// Parses the fields of a struct-like pattern. |
6a06907d | 931 | fn parse_pat_fields(&mut self) -> PResult<'a, (Vec<PatField>, bool)> { |
416331ca XL |
932 | let mut fields = Vec::new(); |
933 | let mut etc = false; | |
934 | let mut ate_comma = true; | |
935 | let mut delayed_err: Option<DiagnosticBuilder<'a>> = None; | |
936 | let mut etc_span = None; | |
937 | ||
938 | while self.token != token::CloseDelim(token::Brace) { | |
939 | let attrs = match self.parse_outer_attributes() { | |
940 | Ok(attrs) => attrs, | |
941 | Err(err) => { | |
942 | if let Some(mut delayed) = delayed_err { | |
943 | delayed.emit(); | |
944 | } | |
945 | return Err(err); | |
dfeec247 | 946 | } |
416331ca XL |
947 | }; |
948 | let lo = self.token.span; | |
949 | ||
950 | // check that a comma comes after every field | |
951 | if !ate_comma { | |
1b1a35ee | 952 | let err = self.struct_span_err(self.token.span, "expected `,`"); |
416331ca XL |
953 | if let Some(mut delayed) = delayed_err { |
954 | delayed.emit(); | |
955 | } | |
956 | return Err(err); | |
957 | } | |
958 | ate_comma = false; | |
959 | ||
960 | if self.check(&token::DotDot) || self.token == token::DotDotDot { | |
961 | etc = true; | |
962 | let mut etc_sp = self.token.span; | |
963 | ||
e1599b0c | 964 | self.recover_one_fewer_dotdot(); |
dfeec247 | 965 | self.bump(); // `..` || `...` |
416331ca XL |
966 | |
967 | if self.token == token::CloseDelim(token::Brace) { | |
968 | etc_span = Some(etc_sp); | |
969 | break; | |
970 | } | |
dfeec247 XL |
971 | let token_str = super::token_descr(&self.token); |
972 | let msg = &format!("expected `}}`, found {}", token_str); | |
973 | let mut err = self.struct_span_err(self.token.span, msg); | |
416331ca XL |
974 | |
975 | err.span_label(self.token.span, "expected `}`"); | |
976 | let mut comma_sp = None; | |
dfeec247 XL |
977 | if self.token == token::Comma { |
978 | // Issue #49257 | |
416331ca XL |
979 | let nw_span = self.sess.source_map().span_until_non_whitespace(self.token.span); |
980 | etc_sp = etc_sp.to(nw_span); | |
dfeec247 XL |
981 | err.span_label( |
982 | etc_sp, | |
983 | "`..` must be at the end and cannot have a trailing comma", | |
984 | ); | |
416331ca XL |
985 | comma_sp = Some(self.token.span); |
986 | self.bump(); | |
987 | ate_comma = true; | |
988 | } | |
989 | ||
990 | etc_span = Some(etc_sp.until(self.token.span)); | |
991 | if self.token == token::CloseDelim(token::Brace) { | |
992 | // If the struct looks otherwise well formed, recover and continue. | |
993 | if let Some(sp) = comma_sp { | |
994 | err.span_suggestion_short( | |
995 | sp, | |
996 | "remove this comma", | |
997 | String::new(), | |
998 | Applicability::MachineApplicable, | |
999 | ); | |
1000 | } | |
1001 | err.emit(); | |
1002 | break; | |
1003 | } else if self.token.is_ident() && ate_comma { | |
1004 | // Accept fields coming after `..,`. | |
1005 | // This way we avoid "pattern missing fields" errors afterwards. | |
1006 | // We delay this error until the end in order to have a span for a | |
1007 | // suggested fix. | |
1008 | if let Some(mut delayed_err) = delayed_err { | |
1009 | delayed_err.emit(); | |
1010 | return Err(err); | |
1011 | } else { | |
1012 | delayed_err = Some(err); | |
1013 | } | |
1014 | } else { | |
1015 | if let Some(mut err) = delayed_err { | |
1016 | err.emit(); | |
1017 | } | |
1018 | return Err(err); | |
1019 | } | |
1020 | } | |
1021 | ||
6a06907d XL |
1022 | let field = |
1023 | self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| { | |
1024 | let field = match this.parse_pat_field(lo, attrs) { | |
1025 | Ok(field) => Ok(field), | |
1026 | Err(err) => { | |
1027 | if let Some(mut delayed_err) = delayed_err.take() { | |
1028 | delayed_err.emit(); | |
1029 | } | |
1030 | return Err(err); | |
1031 | } | |
1032 | }?; | |
1033 | ate_comma = this.eat(&token::Comma); | |
1034 | // We just ate a comma, so there's no need to use | |
1035 | // `TrailingToken::Comma` | |
1036 | Ok((field, TrailingToken::None)) | |
1037 | })?; | |
1038 | ||
1039 | fields.push(field) | |
416331ca XL |
1040 | } |
1041 | ||
1042 | if let Some(mut err) = delayed_err { | |
1043 | if let Some(etc_span) = etc_span { | |
1044 | err.multipart_suggestion( | |
1045 | "move the `..` to the end of the field list", | |
1046 | vec![ | |
1047 | (etc_span, String::new()), | |
1048 | (self.token.span, format!("{}.. }}", if ate_comma { "" } else { ", " })), | |
1049 | ], | |
1050 | Applicability::MachineApplicable, | |
1051 | ); | |
1052 | } | |
1053 | err.emit(); | |
1054 | } | |
ba9703b0 | 1055 | Ok((fields, etc)) |
416331ca XL |
1056 | } |
1057 | ||
e1599b0c XL |
1058 | /// Recover on `...` as if it were `..` to avoid further errors. |
1059 | /// See issue #46718. | |
1060 | fn recover_one_fewer_dotdot(&self) { | |
1061 | if self.token != token::DotDotDot { | |
1062 | return; | |
1063 | } | |
1064 | ||
1065 | self.struct_span_err(self.token.span, "expected field pattern, found `...`") | |
1066 | .span_suggestion( | |
1067 | self.token.span, | |
1068 | "to omit remaining fields, use one fewer `.`", | |
1069 | "..".to_owned(), | |
dfeec247 | 1070 | Applicability::MachineApplicable, |
e1599b0c XL |
1071 | ) |
1072 | .emit(); | |
1073 | } | |
1074 | ||
6a06907d | 1075 | fn parse_pat_field(&mut self, lo: Span, attrs: Vec<Attribute>) -> PResult<'a, PatField> { |
416331ca XL |
1076 | // Check if a colon exists one ahead. This means we're parsing a fieldname. |
1077 | let hi; | |
1078 | let (subpat, fieldname, is_shorthand) = if self.look_ahead(1, |t| t == &token::Colon) { | |
e1599b0c | 1079 | // Parsing a pattern of the form `fieldname: pat`. |
416331ca XL |
1080 | let fieldname = self.parse_field_name()?; |
1081 | self.bump(); | |
6a06907d | 1082 | let pat = self.parse_pat_allow_top_alt(None, GateOr::Yes, RecoverComma::No)?; |
416331ca XL |
1083 | hi = pat.span; |
1084 | (pat, fieldname, false) | |
1085 | } else { | |
e1599b0c | 1086 | // Parsing a pattern of the form `(box) (ref) (mut) fieldname`. |
416331ca XL |
1087 | let is_box = self.eat_keyword(kw::Box); |
1088 | let boxed_span = self.token.span; | |
1089 | let is_ref = self.eat_keyword(kw::Ref); | |
1090 | let is_mut = self.eat_keyword(kw::Mut); | |
6a06907d | 1091 | let fieldname = self.parse_field_name()?; |
74b04a01 | 1092 | hi = self.prev_token.span; |
416331ca XL |
1093 | |
1094 | let bind_type = match (is_ref, is_mut) { | |
dfeec247 XL |
1095 | (true, true) => BindingMode::ByRef(Mutability::Mut), |
1096 | (true, false) => BindingMode::ByRef(Mutability::Not), | |
1097 | (false, true) => BindingMode::ByValue(Mutability::Mut), | |
1098 | (false, false) => BindingMode::ByValue(Mutability::Not), | |
416331ca XL |
1099 | }; |
1100 | ||
1101 | let fieldpat = self.mk_pat_ident(boxed_span.to(hi), bind_type, fieldname); | |
dfeec247 XL |
1102 | let subpat = |
1103 | if is_box { self.mk_pat(lo.to(hi), PatKind::Box(fieldpat)) } else { fieldpat }; | |
416331ca XL |
1104 | (subpat, fieldname, true) |
1105 | }; | |
1106 | ||
6a06907d | 1107 | Ok(PatField { |
e1599b0c XL |
1108 | ident: fieldname, |
1109 | pat: subpat, | |
1110 | is_shorthand, | |
1111 | attrs: attrs.into(), | |
1112 | id: ast::DUMMY_NODE_ID, | |
416331ca | 1113 | span: lo.to(hi), |
e1599b0c | 1114 | is_placeholder: false, |
416331ca XL |
1115 | }) |
1116 | } | |
1117 | ||
1118 | pub(super) fn mk_pat_ident(&self, span: Span, bm: BindingMode, ident: Ident) -> P<Pat> { | |
1119 | self.mk_pat(span, PatKind::Ident(bm, ident, None)) | |
1120 | } | |
1121 | ||
e74abb32 | 1122 | fn mk_pat(&self, span: Span, kind: PatKind) -> P<Pat> { |
3dfed10e | 1123 | P(Pat { kind, span, id: ast::DUMMY_NODE_ID, tokens: None }) |
416331ca XL |
1124 | } |
1125 | } |