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