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