From e742de2569f6222b26671171c1f32fcd02b80f67 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Sun, 11 Aug 2019 15:24:37 +0200 Subject: [PATCH] parser: split into pat.rs --- src/libsyntax/parse/parser.rs | 641 +----------------------------- src/libsyntax/parse/parser/pat.rs | 634 +++++++++++++++++++++++++++++ 2 files changed, 642 insertions(+), 633 deletions(-) create mode 100644 src/libsyntax/parse/parser/pat.rs diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 260381b575c..f7dced76b64 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -3,6 +3,8 @@ mod expr; use expr::LhsExpr; +mod pat; + use crate::ast::{AngleBracketedArgs, ParenthesizedArgs, AttrStyle, BareFnTy}; use crate::ast::{GenericBound, TraitBoundModifier}; use crate::ast::Unsafety; @@ -16,13 +18,10 @@ use crate::ast::{FnDecl, FnHeader}; use crate::ast::{ForeignItem, ForeignItemKind, FunctionRetTy}; use crate::ast::{GenericParam, GenericParamKind}; use crate::ast::GenericArg; -use crate::ast::{Ident, ImplItem, IsAsync, IsAuto, Item, ItemKind}; -use crate::ast::{Lifetime}; -use crate::ast::Local; -use crate::ast::MacStmtStyle; -use crate::ast::{Mac, Mac_, MacDelimiter}; +use crate::ast::{Ident, ImplItem, IsAsync, IsAuto, Item, ItemKind, Local, Lifetime}; +use crate::ast::{MacStmtStyle, Mac, Mac_, MacDelimiter}; use crate::ast::{MutTy, Mutability}; -use crate::ast::{Pat, PatKind, PathSegment}; +use crate::ast::{PathSegment}; use crate::ast::{PolyTraitRef, QSelf}; use crate::ast::{Stmt, StmtKind}; use crate::ast::{VariantData, StructField}; @@ -32,7 +31,6 @@ use crate::ast::{TraitItem, TraitRef, TraitObjectSyntax}; use crate::ast::{Ty, TyKind, AssocTyConstraint, AssocTyConstraintKind, GenericBounds}; use crate::ast::{Visibility, VisibilityKind, WhereClause, CrateSugar}; use crate::ast::{UseTree, UseTreeKind}; -use crate::ast::{RangeEnd, RangeSyntax}; use crate::{ast, attr}; use crate::ext::base::DummyResult; use crate::ext::hygiene::SyntaxContext; @@ -116,6 +114,7 @@ crate enum BlockMode { } /// As maybe_whole_expr, but for things other than expressions +#[macro_export] macro_rules! maybe_whole { ($p:expr, $constructor:ident, |$x:ident| $e:expr) => { if let token::Interpolated(nt) = &$p.token.kind { @@ -1491,12 +1490,8 @@ impl<'a> Parser<'a> { match ty { Ok(ty) => { let ident = Ident::new(kw::Invalid, self.prev_span); - let pat = P(Pat { - id: ast::DUMMY_NODE_ID, - node: PatKind::Ident( - BindingMode::ByValue(Mutability::Immutable), ident, None), - span: ty.span, - }); + let bm = BindingMode::ByValue(Mutability::Immutable); + let pat = self.mk_pat_ident(ty.span, bm, ident); (pat, ty) } Err(mut err) => { @@ -1924,626 +1919,6 @@ impl<'a> Parser<'a> { } } - /// Parses patterns, separated by '|' s. - fn parse_pats(&mut self) -> PResult<'a, Vec>> { - // Allow a '|' before the pats (RFC 1925 + RFC 2530) - self.eat(&token::BinOp(token::Or)); - - let mut pats = Vec::new(); - loop { - pats.push(self.parse_top_level_pat()?); - - if self.token == token::OrOr { - self.struct_span_err(self.token.span, "unexpected token `||` after pattern") - .span_suggestion( - self.token.span, - "use a single `|` to specify multiple patterns", - "|".to_owned(), - Applicability::MachineApplicable - ) - .emit(); - self.bump(); - } else if self.eat(&token::BinOp(token::Or)) { - // This is a No-op. Continue the loop to parse the next - // pattern. - } else { - return Ok(pats); - } - }; - } - - fn parse_pat_field( - &mut self, - lo: Span, - attrs: Vec - ) -> PResult<'a, source_map::Spanned> { - // Check if a colon exists one ahead. This means we're parsing a fieldname. - let hi; - let (subpat, fieldname, is_shorthand) = if self.look_ahead(1, |t| t == &token::Colon) { - // Parsing a pattern of the form "fieldname: pat" - let fieldname = self.parse_field_name()?; - self.bump(); - let pat = self.parse_pat(None)?; - hi = pat.span; - (pat, fieldname, false) - } else { - // Parsing a pattern of the form "(box) (ref) (mut) fieldname" - let is_box = self.eat_keyword(kw::Box); - let boxed_span = self.token.span; - let is_ref = self.eat_keyword(kw::Ref); - let is_mut = self.eat_keyword(kw::Mut); - let fieldname = self.parse_ident()?; - hi = self.prev_span; - - let bind_type = match (is_ref, is_mut) { - (true, true) => BindingMode::ByRef(Mutability::Mutable), - (true, false) => BindingMode::ByRef(Mutability::Immutable), - (false, true) => BindingMode::ByValue(Mutability::Mutable), - (false, false) => BindingMode::ByValue(Mutability::Immutable), - }; - let fieldpat = P(Pat { - id: ast::DUMMY_NODE_ID, - node: PatKind::Ident(bind_type, fieldname, None), - span: boxed_span.to(hi), - }); - - let subpat = if is_box { - P(Pat { - id: ast::DUMMY_NODE_ID, - node: PatKind::Box(fieldpat), - span: lo.to(hi), - }) - } else { - fieldpat - }; - (subpat, fieldname, true) - }; - - Ok(source_map::Spanned { - span: lo.to(hi), - node: ast::FieldPat { - ident: fieldname, - pat: subpat, - is_shorthand, - attrs: attrs.into(), - } - }) - } - - /// Parses the fields of a struct-like pattern. - fn parse_pat_fields(&mut self) -> PResult<'a, (Vec>, bool)> { - let mut fields = Vec::new(); - let mut etc = false; - let mut ate_comma = true; - let mut delayed_err: Option> = None; - let mut etc_span = None; - - while self.token != token::CloseDelim(token::Brace) { - let attrs = match self.parse_outer_attributes() { - Ok(attrs) => attrs, - Err(err) => { - if let Some(mut delayed) = delayed_err { - delayed.emit(); - } - return Err(err); - }, - }; - let lo = self.token.span; - - // check that a comma comes after every field - if !ate_comma { - let err = self.struct_span_err(self.prev_span, "expected `,`"); - if let Some(mut delayed) = delayed_err { - delayed.emit(); - } - return Err(err); - } - ate_comma = false; - - if self.check(&token::DotDot) || self.token == token::DotDotDot { - etc = true; - let mut etc_sp = self.token.span; - - if self.token == token::DotDotDot { // Issue #46718 - // Accept `...` as if it were `..` to avoid further errors - self.struct_span_err(self.token.span, "expected field pattern, found `...`") - .span_suggestion( - self.token.span, - "to omit remaining fields, use one fewer `.`", - "..".to_owned(), - Applicability::MachineApplicable - ) - .emit(); - } - self.bump(); // `..` || `...` - - if self.token == token::CloseDelim(token::Brace) { - etc_span = Some(etc_sp); - break; - } - let token_str = self.this_token_descr(); - let mut err = self.fatal(&format!("expected `}}`, found {}", token_str)); - - err.span_label(self.token.span, "expected `}`"); - let mut comma_sp = None; - if self.token == token::Comma { // Issue #49257 - let nw_span = self.sess.source_map().span_until_non_whitespace(self.token.span); - etc_sp = etc_sp.to(nw_span); - err.span_label(etc_sp, - "`..` must be at the end and cannot have a trailing comma"); - comma_sp = Some(self.token.span); - self.bump(); - ate_comma = true; - } - - etc_span = Some(etc_sp.until(self.token.span)); - if self.token == token::CloseDelim(token::Brace) { - // If the struct looks otherwise well formed, recover and continue. - if let Some(sp) = comma_sp { - err.span_suggestion_short( - sp, - "remove this comma", - String::new(), - Applicability::MachineApplicable, - ); - } - err.emit(); - break; - } else if self.token.is_ident() && ate_comma { - // Accept fields coming after `..,`. - // This way we avoid "pattern missing fields" errors afterwards. - // We delay this error until the end in order to have a span for a - // suggested fix. - if let Some(mut delayed_err) = delayed_err { - delayed_err.emit(); - return Err(err); - } else { - delayed_err = Some(err); - } - } else { - if let Some(mut err) = delayed_err { - err.emit(); - } - return Err(err); - } - } - - fields.push(match self.parse_pat_field(lo, attrs) { - Ok(field) => field, - Err(err) => { - if let Some(mut delayed_err) = delayed_err { - delayed_err.emit(); - } - return Err(err); - } - }); - ate_comma = self.eat(&token::Comma); - } - - if let Some(mut err) = delayed_err { - if let Some(etc_span) = etc_span { - err.multipart_suggestion( - "move the `..` to the end of the field list", - vec![ - (etc_span, String::new()), - (self.token.span, format!("{}.. }}", if ate_comma { "" } else { ", " })), - ], - Applicability::MachineApplicable, - ); - } - err.emit(); - } - return Ok((fields, etc)); - } - - fn parse_pat_range_end(&mut self) -> PResult<'a, P> { - if self.token.is_path_start() { - let lo = self.token.span; - let (qself, path) = if self.eat_lt() { - // Parse a qualified path - let (qself, path) = self.parse_qpath(PathStyle::Expr)?; - (Some(qself), path) - } else { - // Parse an unqualified path - (None, self.parse_path(PathStyle::Expr)?) - }; - let hi = self.prev_span; - Ok(self.mk_expr(lo.to(hi), ExprKind::Path(qself, path), ThinVec::new())) - } else { - self.parse_literal_maybe_minus() - } - } - - /// Is the current token suitable as the start of a range patterns end? - fn is_pat_range_end_start(&self) -> bool { - self.token.is_path_start() // e.g. `MY_CONST`; - || self.token == token::Dot // e.g. `.5` for recovery; - || self.token.can_begin_literal_or_bool() // e.g. `42`. - || self.token.is_whole_expr() - } - - // Helper function to decide whether to parse as ident binding - // or to try to do something more complex like range patterns. - fn parse_as_ident(&mut self) -> bool { - self.look_ahead(1, |t| match t.kind { - token::OpenDelim(token::Paren) | token::OpenDelim(token::Brace) | - token::DotDotDot | token::DotDotEq | token::DotDot | - token::ModSep | token::Not => false, - _ => true, - }) - } - - /// Parse and throw away a parentesized comma separated - /// sequence of patterns until `)` is reached. - fn skip_pat_list(&mut self) -> PResult<'a, ()> { - while !self.check(&token::CloseDelim(token::Paren)) { - self.parse_pat(None)?; - if !self.eat(&token::Comma) { - return Ok(()) - } - } - Ok(()) - } - - /// A wrapper around `parse_pat` with some special error handling for the - /// "top-level" patterns in a match arm, `for` loop, `let`, &c. (in contrast - /// to subpatterns within such). - fn parse_top_level_pat(&mut self) -> PResult<'a, P> { - let pat = self.parse_pat(None)?; - if self.token == token::Comma { - // An unexpected comma after a top-level pattern is a clue that the - // user (perhaps more accustomed to some other language) forgot the - // parentheses in what should have been a tuple pattern; return a - // suggestion-enhanced error here rather than choking on the comma - // later. - let comma_span = self.token.span; - self.bump(); - if let Err(mut err) = self.skip_pat_list() { - // We didn't expect this to work anyway; we just wanted - // to advance to the end of the comma-sequence so we know - // the span to suggest parenthesizing - err.cancel(); - } - let seq_span = pat.span.to(self.prev_span); - let mut err = self.struct_span_err(comma_span, - "unexpected `,` in pattern"); - if let Ok(seq_snippet) = self.span_to_snippet(seq_span) { - err.span_suggestion( - seq_span, - "try adding parentheses to match on a tuple..", - format!("({})", seq_snippet), - Applicability::MachineApplicable - ).span_suggestion( - seq_span, - "..or a vertical bar to match on multiple alternatives", - format!("{}", seq_snippet.replace(",", " |")), - Applicability::MachineApplicable - ); - } - return Err(err); - } - Ok(pat) - } - - /// Parses a pattern. - pub fn parse_pat(&mut self, expected: Option<&'static str>) -> PResult<'a, P> { - self.parse_pat_with_range_pat(true, expected) - } - - /// Parse a range-to pattern, e.g. `..X` and `..=X` for recovery. - fn parse_pat_range_to(&mut self, re: RangeEnd, form: &str) -> PResult<'a, PatKind> { - let lo = self.prev_span; - let end = self.parse_pat_range_end()?; - let range_span = lo.to(end.span); - let begin = self.mk_expr(range_span, ExprKind::Err, ThinVec::new()); - - self.diagnostic() - .struct_span_err(range_span, &format!("`{}X` range patterns are not supported", form)) - .span_suggestion( - range_span, - "try using the minimum value for the type", - format!("MIN{}{}", form, pprust::expr_to_string(&end)), - Applicability::HasPlaceholders, - ) - .emit(); - - Ok(PatKind::Range(begin, end, respan(lo, re))) - } - - /// Parse the end of a `X..Y`, `X..=Y`, or `X...Y` range pattern or recover - /// if that end is missing treating it as `X..`, `X..=`, or `X...` respectively. - fn parse_pat_range_end_opt(&mut self, begin: &Expr, form: &str) -> PResult<'a, P> { - if self.is_pat_range_end_start() { - // Parsing e.g. `X..=Y`. - self.parse_pat_range_end() - } else { - // Parsing e.g. `X..`. - let range_span = begin.span.to(self.prev_span); - - self.diagnostic() - .struct_span_err( - range_span, - &format!("`X{}` range patterns are not supported", form), - ) - .span_suggestion( - range_span, - "try using the maximum value for the type", - format!("{}{}MAX", pprust::expr_to_string(&begin), form), - Applicability::HasPlaceholders, - ) - .emit(); - - Ok(self.mk_expr(range_span, ExprKind::Err, ThinVec::new())) - } - } - - /// Parses a pattern, with a setting whether modern range patterns (e.g., `a..=b`, `a..b` are - /// allowed). - fn parse_pat_with_range_pat( - &mut self, - allow_range_pat: bool, - expected: Option<&'static str>, - ) -> PResult<'a, P> { - maybe_recover_from_interpolated_ty_qpath!(self, true); - maybe_whole!(self, NtPat, |x| x); - - let lo = self.token.span; - let pat; - match self.token.kind { - token::BinOp(token::And) | token::AndAnd => { - // Parse &pat / &mut pat - self.expect_and()?; - let mutbl = self.parse_mutability(); - if let token::Lifetime(name) = self.token.kind { - let mut err = self.fatal(&format!("unexpected lifetime `{}` in pattern", name)); - err.span_label(self.token.span, "unexpected lifetime"); - return Err(err); - } - let subpat = self.parse_pat_with_range_pat(false, expected)?; - pat = PatKind::Ref(subpat, mutbl); - } - token::OpenDelim(token::Paren) => { - // Parse a tuple or parenthesis pattern. - let (fields, trailing_comma) = self.parse_paren_comma_seq(|p| p.parse_pat(None))?; - - // Here, `(pat,)` is a tuple pattern. - // For backward compatibility, `(..)` is a tuple pattern as well. - pat = if fields.len() == 1 && !(trailing_comma || fields[0].is_rest()) { - PatKind::Paren(fields.into_iter().nth(0).unwrap()) - } else { - PatKind::Tuple(fields) - }; - } - token::OpenDelim(token::Bracket) => { - // Parse `[pat, pat,...]` as a slice pattern. - let (slice, _) = self.parse_delim_comma_seq(token::Bracket, |p| p.parse_pat(None))?; - pat = PatKind::Slice(slice); - } - token::DotDot => { - self.bump(); - pat = if self.is_pat_range_end_start() { - // Parse `..42` for recovery. - self.parse_pat_range_to(RangeEnd::Excluded, "..")? - } else { - // A rest pattern `..`. - PatKind::Rest - }; - } - token::DotDotEq => { - // Parse `..=42` for recovery. - self.bump(); - pat = self.parse_pat_range_to(RangeEnd::Included(RangeSyntax::DotDotEq), "..=")?; - } - token::DotDotDot => { - // Parse `...42` for recovery. - self.bump(); - pat = self.parse_pat_range_to(RangeEnd::Included(RangeSyntax::DotDotDot), "...")?; - } - // At this point, token != &, &&, (, [ - _ => if self.eat_keyword(kw::Underscore) { - // Parse _ - pat = PatKind::Wild; - } else if self.eat_keyword(kw::Mut) { - // Parse mut ident @ pat / mut ref ident @ pat - let mutref_span = self.prev_span.to(self.token.span); - let binding_mode = if self.eat_keyword(kw::Ref) { - self.diagnostic() - .struct_span_err(mutref_span, "the order of `mut` and `ref` is incorrect") - .span_suggestion( - mutref_span, - "try switching the order", - "ref mut".into(), - Applicability::MachineApplicable - ).emit(); - BindingMode::ByRef(Mutability::Mutable) - } else { - BindingMode::ByValue(Mutability::Mutable) - }; - pat = self.parse_pat_ident(binding_mode)?; - } else if self.eat_keyword(kw::Ref) { - // Parse ref ident @ pat / ref mut ident @ pat - let mutbl = self.parse_mutability(); - pat = self.parse_pat_ident(BindingMode::ByRef(mutbl))?; - } else if self.eat_keyword(kw::Box) { - // Parse box pat - let subpat = self.parse_pat_with_range_pat(false, None)?; - pat = PatKind::Box(subpat); - } else if self.token.is_ident() && !self.token.is_reserved_ident() && - self.parse_as_ident() { - // Parse ident @ pat - // This can give false positives and parse nullary enums, - // they are dealt with later in resolve - let binding_mode = BindingMode::ByValue(Mutability::Immutable); - pat = self.parse_pat_ident(binding_mode)?; - } else if self.token.is_path_start() { - // Parse pattern starting with a path - let (qself, path) = if self.eat_lt() { - // Parse a qualified path - let (qself, path) = self.parse_qpath(PathStyle::Expr)?; - (Some(qself), path) - } else { - // Parse an unqualified path - (None, self.parse_path(PathStyle::Expr)?) - }; - match self.token.kind { - token::Not if qself.is_none() => { - // Parse macro invocation - self.bump(); - let (delim, tts) = self.expect_delimited_token_tree()?; - let mac = respan(lo.to(self.prev_span), Mac_ { - path, - tts, - delim, - prior_type_ascription: self.last_type_ascription, - }); - pat = PatKind::Mac(mac); - } - token::DotDotDot | token::DotDotEq | token::DotDot => { - let (end_kind, form) = match self.token.kind { - token::DotDot => (RangeEnd::Excluded, ".."), - token::DotDotDot => (RangeEnd::Included(RangeSyntax::DotDotDot), "..."), - token::DotDotEq => (RangeEnd::Included(RangeSyntax::DotDotEq), "..="), - _ => panic!("can only parse `..`/`...`/`..=` for ranges \ - (checked above)"), - }; - let op_span = self.token.span; - // Parse range - let span = lo.to(self.prev_span); - let begin = self.mk_expr(span, ExprKind::Path(qself, path), ThinVec::new()); - self.bump(); - let end = self.parse_pat_range_end_opt(&begin, form)?; - pat = PatKind::Range(begin, end, respan(op_span, end_kind)); - } - token::OpenDelim(token::Brace) => { - if qself.is_some() { - let msg = "unexpected `{` after qualified path"; - let mut err = self.fatal(msg); - err.span_label(self.token.span, msg); - return Err(err); - } - // Parse struct pattern - self.bump(); - let (fields, etc) = self.parse_pat_fields().unwrap_or_else(|mut e| { - e.emit(); - self.recover_stmt(); - (vec![], true) - }); - self.bump(); - pat = PatKind::Struct(path, fields, etc); - } - token::OpenDelim(token::Paren) => { - if qself.is_some() { - let msg = "unexpected `(` after qualified path"; - let mut err = self.fatal(msg); - err.span_label(self.token.span, msg); - return Err(err); - } - // Parse tuple struct or enum pattern - let (fields, _) = self.parse_paren_comma_seq(|p| p.parse_pat(None))?; - pat = PatKind::TupleStruct(path, fields) - } - _ => pat = PatKind::Path(qself, path), - } - } else { - // Try to parse everything else as literal with optional minus - match self.parse_literal_maybe_minus() { - Ok(begin) => { - let op_span = self.token.span; - if self.check(&token::DotDot) || self.check(&token::DotDotEq) || - self.check(&token::DotDotDot) { - let (end_kind, form) = if self.eat(&token::DotDotDot) { - (RangeEnd::Included(RangeSyntax::DotDotDot), "...") - } else if self.eat(&token::DotDotEq) { - (RangeEnd::Included(RangeSyntax::DotDotEq), "..=") - } else if self.eat(&token::DotDot) { - (RangeEnd::Excluded, "..") - } else { - panic!("impossible case: we already matched \ - on a range-operator token") - }; - let end = self.parse_pat_range_end_opt(&begin, form)?; - pat = PatKind::Range(begin, end, respan(op_span, end_kind)) - } else { - pat = PatKind::Lit(begin); - } - } - Err(mut err) => { - self.cancel(&mut err); - let expected = expected.unwrap_or("pattern"); - let msg = format!( - "expected {}, found {}", - expected, - self.this_token_descr(), - ); - let mut err = self.fatal(&msg); - err.span_label(self.token.span, format!("expected {}", expected)); - let sp = self.sess.source_map().start_point(self.token.span); - if let Some(sp) = self.sess.ambiguous_block_expr_parse.borrow().get(&sp) { - self.sess.expr_parentheses_needed(&mut err, *sp, None); - } - return Err(err); - } - } - } - } - - let pat = P(Pat { node: pat, span: lo.to(self.prev_span), id: ast::DUMMY_NODE_ID }); - let pat = self.maybe_recover_from_bad_qpath(pat, true)?; - - if !allow_range_pat { - match pat.node { - PatKind::Range( - _, _, Spanned { node: RangeEnd::Included(RangeSyntax::DotDotDot), .. } - ) => {}, - PatKind::Range(..) => { - let mut err = self.struct_span_err( - pat.span, - "the range pattern here has ambiguous interpretation", - ); - err.span_suggestion( - pat.span, - "add parentheses to clarify the precedence", - format!("({})", pprust::pat_to_string(&pat)), - // "ambiguous interpretation" implies that we have to be guessing - Applicability::MaybeIncorrect - ); - return Err(err); - } - _ => {} - } - } - - Ok(pat) - } - - /// Parses `ident` or `ident @ pat`. - /// used by the copy foo and ref foo patterns to give a good - /// error message when parsing mistakes like `ref foo(a, b)`. - fn parse_pat_ident(&mut self, - binding_mode: ast::BindingMode) - -> PResult<'a, PatKind> { - let ident = self.parse_ident()?; - let sub = if self.eat(&token::At) { - Some(self.parse_pat(Some("binding pattern"))?) - } else { - None - }; - - // just to be friendly, if they write something like - // ref Some(i) - // we end up here with ( as the current token. This shortly - // leads to a parse error. Note that if there is no explicit - // binding mode then we do not end up here, because the lookahead - // will direct us over to parse_enum_variant() - if self.token == token::OpenDelim(token::Paren) { - return Err(self.span_fatal( - self.prev_span, - "expected identifier, found enum pattern")) - } - - Ok(PatKind::Ident(binding_mode, ident, sub)) - } - /// Parses a local variable declaration. fn parse_local(&mut self, attrs: ThinVec) -> PResult<'a, P> { let lo = self.prev_span; diff --git a/src/libsyntax/parse/parser/pat.rs b/src/libsyntax/parse/parser/pat.rs new file mode 100644 index 00000000000..5cc428a4df1 --- /dev/null +++ b/src/libsyntax/parse/parser/pat.rs @@ -0,0 +1,634 @@ +use super::{Parser, PResult, PathStyle}; + +use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole}; +use crate::ptr::P; +use crate::ast::{self, Attribute, Pat, PatKind, FieldPat, RangeEnd, RangeSyntax, Mac_}; +use crate::ast::{BindingMode, Ident, Mutability, Expr, ExprKind}; +use crate::parse::token::{self}; +use crate::print::pprust; +use crate::source_map::{respan, Span, Spanned}; +use crate::symbol::kw; +use crate::ThinVec; + +use errors::{Applicability, DiagnosticBuilder}; + +impl<'a> Parser<'a> { + /// Parses a pattern. + pub fn parse_pat(&mut self, expected: Option<&'static str>) -> PResult<'a, P> { + self.parse_pat_with_range_pat(true, expected) + } + + /// Parses patterns, separated by '|' s. + pub(super) fn parse_pats(&mut self) -> PResult<'a, Vec>> { + // Allow a '|' before the pats (RFC 1925 + RFC 2530) + self.eat(&token::BinOp(token::Or)); + + let mut pats = Vec::new(); + loop { + pats.push(self.parse_top_level_pat()?); + + if self.token == token::OrOr { + self.struct_span_err(self.token.span, "unexpected token `||` after pattern") + .span_suggestion( + self.token.span, + "use a single `|` to specify multiple patterns", + "|".to_owned(), + Applicability::MachineApplicable + ) + .emit(); + self.bump(); + } else if self.eat(&token::BinOp(token::Or)) { + // This is a No-op. Continue the loop to parse the next + // pattern. + } else { + return Ok(pats); + } + }; + } + + /// A wrapper around `parse_pat` with some special error handling for the + /// "top-level" patterns in a match arm, `for` loop, `let`, &c. (in contrast + /// to subpatterns within such). + pub(super) fn parse_top_level_pat(&mut self) -> PResult<'a, P> { + let pat = self.parse_pat(None)?; + if self.token == token::Comma { + // An unexpected comma after a top-level pattern is a clue that the + // user (perhaps more accustomed to some other language) forgot the + // parentheses in what should have been a tuple pattern; return a + // suggestion-enhanced error here rather than choking on the comma + // later. + let comma_span = self.token.span; + self.bump(); + if let Err(mut err) = self.skip_pat_list() { + // We didn't expect this to work anyway; we just wanted + // to advance to the end of the comma-sequence so we know + // the span to suggest parenthesizing + err.cancel(); + } + let seq_span = pat.span.to(self.prev_span); + let mut err = self.struct_span_err(comma_span, "unexpected `,` in pattern"); + if let Ok(seq_snippet) = self.span_to_snippet(seq_span) { + err.span_suggestion( + seq_span, + "try adding parentheses to match on a tuple..", + format!("({})", seq_snippet), + Applicability::MachineApplicable + ).span_suggestion( + seq_span, + "..or a vertical bar to match on multiple alternatives", + format!("{}", seq_snippet.replace(",", " |")), + Applicability::MachineApplicable + ); + } + return Err(err); + } + Ok(pat) + } + + /// Parse and throw away a parentesized comma separated + /// sequence of patterns until `)` is reached. + fn skip_pat_list(&mut self) -> PResult<'a, ()> { + while !self.check(&token::CloseDelim(token::Paren)) { + self.parse_pat(None)?; + if !self.eat(&token::Comma) { + return Ok(()) + } + } + Ok(()) + } + + /// Parses a pattern, with a setting whether modern range patterns (e.g., `a..=b`, `a..b` are + /// allowed). + fn parse_pat_with_range_pat( + &mut self, + allow_range_pat: bool, + expected: Option<&'static str>, + ) -> PResult<'a, P> { + maybe_recover_from_interpolated_ty_qpath!(self, true); + maybe_whole!(self, NtPat, |x| x); + + let lo = self.token.span; + let pat; + match self.token.kind { + token::BinOp(token::And) | token::AndAnd => { + // Parse &pat / &mut pat + self.expect_and()?; + let mutbl = self.parse_mutability(); + if let token::Lifetime(name) = self.token.kind { + let mut err = self.fatal(&format!("unexpected lifetime `{}` in pattern", name)); + err.span_label(self.token.span, "unexpected lifetime"); + return Err(err); + } + let subpat = self.parse_pat_with_range_pat(false, expected)?; + pat = PatKind::Ref(subpat, mutbl); + } + token::OpenDelim(token::Paren) => { + // Parse a tuple or parenthesis pattern. + let (fields, trailing_comma) = self.parse_paren_comma_seq(|p| p.parse_pat(None))?; + + // Here, `(pat,)` is a tuple pattern. + // For backward compatibility, `(..)` is a tuple pattern as well. + pat = if fields.len() == 1 && !(trailing_comma || fields[0].is_rest()) { + PatKind::Paren(fields.into_iter().nth(0).unwrap()) + } else { + PatKind::Tuple(fields) + }; + } + token::OpenDelim(token::Bracket) => { + // Parse `[pat, pat,...]` as a slice pattern. + let (slice, _) = self.parse_delim_comma_seq(token::Bracket, |p| p.parse_pat(None))?; + pat = PatKind::Slice(slice); + } + token::DotDot => { + self.bump(); + pat = if self.is_pat_range_end_start() { + // Parse `..42` for recovery. + self.parse_pat_range_to(RangeEnd::Excluded, "..")? + } else { + // A rest pattern `..`. + PatKind::Rest + }; + } + token::DotDotEq => { + // Parse `..=42` for recovery. + self.bump(); + pat = self.parse_pat_range_to(RangeEnd::Included(RangeSyntax::DotDotEq), "..=")?; + } + token::DotDotDot => { + // Parse `...42` for recovery. + self.bump(); + pat = self.parse_pat_range_to(RangeEnd::Included(RangeSyntax::DotDotDot), "...")?; + } + // At this point, token != &, &&, (, [ + _ => if self.eat_keyword(kw::Underscore) { + // Parse _ + pat = PatKind::Wild; + } else if self.eat_keyword(kw::Mut) { + // Parse mut ident @ pat / mut ref ident @ pat + let mutref_span = self.prev_span.to(self.token.span); + let binding_mode = if self.eat_keyword(kw::Ref) { + self.diagnostic() + .struct_span_err(mutref_span, "the order of `mut` and `ref` is incorrect") + .span_suggestion( + mutref_span, + "try switching the order", + "ref mut".into(), + Applicability::MachineApplicable + ).emit(); + BindingMode::ByRef(Mutability::Mutable) + } else { + BindingMode::ByValue(Mutability::Mutable) + }; + pat = self.parse_pat_ident(binding_mode)?; + } else if self.eat_keyword(kw::Ref) { + // Parse ref ident @ pat / ref mut ident @ pat + let mutbl = self.parse_mutability(); + pat = self.parse_pat_ident(BindingMode::ByRef(mutbl))?; + } else if self.eat_keyword(kw::Box) { + // Parse box pat + let subpat = self.parse_pat_with_range_pat(false, None)?; + pat = PatKind::Box(subpat); + } else if self.token.is_ident() && !self.token.is_reserved_ident() && + self.parse_as_ident() { + // Parse ident @ pat + // This can give false positives and parse nullary enums, + // they are dealt with later in resolve + let binding_mode = BindingMode::ByValue(Mutability::Immutable); + pat = self.parse_pat_ident(binding_mode)?; + } else if self.token.is_path_start() { + // Parse pattern starting with a path + let (qself, path) = if self.eat_lt() { + // Parse a qualified path + let (qself, path) = self.parse_qpath(PathStyle::Expr)?; + (Some(qself), path) + } else { + // Parse an unqualified path + (None, self.parse_path(PathStyle::Expr)?) + }; + match self.token.kind { + token::Not if qself.is_none() => { + // Parse macro invocation + self.bump(); + let (delim, tts) = self.expect_delimited_token_tree()?; + let mac = respan(lo.to(self.prev_span), Mac_ { + path, + tts, + delim, + prior_type_ascription: self.last_type_ascription, + }); + pat = PatKind::Mac(mac); + } + token::DotDotDot | token::DotDotEq | token::DotDot => { + let (end_kind, form) = match self.token.kind { + token::DotDot => (RangeEnd::Excluded, ".."), + token::DotDotDot => (RangeEnd::Included(RangeSyntax::DotDotDot), "..."), + token::DotDotEq => (RangeEnd::Included(RangeSyntax::DotDotEq), "..="), + _ => panic!("can only parse `..`/`...`/`..=` for ranges \ + (checked above)"), + }; + let op_span = self.token.span; + // Parse range + let span = lo.to(self.prev_span); + let begin = self.mk_expr(span, ExprKind::Path(qself, path), ThinVec::new()); + self.bump(); + let end = self.parse_pat_range_end_opt(&begin, form)?; + pat = PatKind::Range(begin, end, respan(op_span, end_kind)); + } + token::OpenDelim(token::Brace) => { + if qself.is_some() { + let msg = "unexpected `{` after qualified path"; + let mut err = self.fatal(msg); + err.span_label(self.token.span, msg); + return Err(err); + } + // Parse struct pattern + self.bump(); + let (fields, etc) = self.parse_pat_fields().unwrap_or_else(|mut e| { + e.emit(); + self.recover_stmt(); + (vec![], true) + }); + self.bump(); + pat = PatKind::Struct(path, fields, etc); + } + token::OpenDelim(token::Paren) => { + if qself.is_some() { + let msg = "unexpected `(` after qualified path"; + let mut err = self.fatal(msg); + err.span_label(self.token.span, msg); + return Err(err); + } + // Parse tuple struct or enum pattern + let (fields, _) = self.parse_paren_comma_seq(|p| p.parse_pat(None))?; + pat = PatKind::TupleStruct(path, fields) + } + _ => pat = PatKind::Path(qself, path), + } + } else { + // Try to parse everything else as literal with optional minus + match self.parse_literal_maybe_minus() { + Ok(begin) => { + let op_span = self.token.span; + if self.check(&token::DotDot) || self.check(&token::DotDotEq) || + self.check(&token::DotDotDot) { + let (end_kind, form) = if self.eat(&token::DotDotDot) { + (RangeEnd::Included(RangeSyntax::DotDotDot), "...") + } else if self.eat(&token::DotDotEq) { + (RangeEnd::Included(RangeSyntax::DotDotEq), "..=") + } else if self.eat(&token::DotDot) { + (RangeEnd::Excluded, "..") + } else { + panic!("impossible case: we already matched \ + on a range-operator token") + }; + let end = self.parse_pat_range_end_opt(&begin, form)?; + pat = PatKind::Range(begin, end, respan(op_span, end_kind)) + } else { + pat = PatKind::Lit(begin); + } + } + Err(mut err) => { + self.cancel(&mut err); + let expected = expected.unwrap_or("pattern"); + let msg = format!( + "expected {}, found {}", + expected, + self.this_token_descr(), + ); + let mut err = self.fatal(&msg); + err.span_label(self.token.span, format!("expected {}", expected)); + let sp = self.sess.source_map().start_point(self.token.span); + if let Some(sp) = self.sess.ambiguous_block_expr_parse.borrow().get(&sp) { + self.sess.expr_parentheses_needed(&mut err, *sp, None); + } + return Err(err); + } + } + } + } + + let pat = self.mk_pat(lo.to(self.prev_span), pat); + let pat = self.maybe_recover_from_bad_qpath(pat, true)?; + + if !allow_range_pat { + match pat.node { + PatKind::Range( + _, _, Spanned { node: RangeEnd::Included(RangeSyntax::DotDotDot), .. } + ) => {}, + PatKind::Range(..) => { + let mut err = self.struct_span_err( + pat.span, + "the range pattern here has ambiguous interpretation", + ); + err.span_suggestion( + pat.span, + "add parentheses to clarify the precedence", + format!("({})", pprust::pat_to_string(&pat)), + // "ambiguous interpretation" implies that we have to be guessing + Applicability::MaybeIncorrect + ); + return Err(err); + } + _ => {} + } + } + + Ok(pat) + } + + // Helper function to decide whether to parse as ident binding + // or to try to do something more complex like range patterns. + fn parse_as_ident(&mut self) -> bool { + self.look_ahead(1, |t| match t.kind { + token::OpenDelim(token::Paren) | token::OpenDelim(token::Brace) | + token::DotDotDot | token::DotDotEq | token::DotDot | + token::ModSep | token::Not => false, + _ => true, + }) + } + + /// Is the current token suitable as the start of a range patterns end? + fn is_pat_range_end_start(&self) -> bool { + self.token.is_path_start() // e.g. `MY_CONST`; + || self.token == token::Dot // e.g. `.5` for recovery; + || self.token.can_begin_literal_or_bool() // e.g. `42`. + || self.token.is_whole_expr() + } + + /// Parse a range-to pattern, e.g. `..X` and `..=X` for recovery. + fn parse_pat_range_to(&mut self, re: RangeEnd, form: &str) -> PResult<'a, PatKind> { + let lo = self.prev_span; + let end = self.parse_pat_range_end()?; + let range_span = lo.to(end.span); + let begin = self.mk_expr(range_span, ExprKind::Err, ThinVec::new()); + + self.diagnostic() + .struct_span_err(range_span, &format!("`{}X` range patterns are not supported", form)) + .span_suggestion( + range_span, + "try using the minimum value for the type", + format!("MIN{}{}", form, pprust::expr_to_string(&end)), + Applicability::HasPlaceholders, + ) + .emit(); + + Ok(PatKind::Range(begin, end, respan(lo, re))) + } + + /// Parse the end of a `X..Y`, `X..=Y`, or `X...Y` range pattern or recover + /// if that end is missing treating it as `X..`, `X..=`, or `X...` respectively. + fn parse_pat_range_end_opt(&mut self, begin: &Expr, form: &str) -> PResult<'a, P> { + if self.is_pat_range_end_start() { + // Parsing e.g. `X..=Y`. + self.parse_pat_range_end() + } else { + // Parsing e.g. `X..`. + let range_span = begin.span.to(self.prev_span); + + self.diagnostic() + .struct_span_err( + range_span, + &format!("`X{}` range patterns are not supported", form), + ) + .span_suggestion( + range_span, + "try using the maximum value for the type", + format!("{}{}MAX", pprust::expr_to_string(&begin), form), + Applicability::HasPlaceholders, + ) + .emit(); + + Ok(self.mk_expr(range_span, ExprKind::Err, ThinVec::new())) + } + } + + fn parse_pat_range_end(&mut self) -> PResult<'a, P> { + if self.token.is_path_start() { + let lo = self.token.span; + let (qself, path) = if self.eat_lt() { + // Parse a qualified path + let (qself, path) = self.parse_qpath(PathStyle::Expr)?; + (Some(qself), path) + } else { + // Parse an unqualified path + (None, self.parse_path(PathStyle::Expr)?) + }; + let hi = self.prev_span; + Ok(self.mk_expr(lo.to(hi), ExprKind::Path(qself, path), ThinVec::new())) + } else { + self.parse_literal_maybe_minus() + } + } + + /// Parses `ident` or `ident @ pat`. + /// used by the copy foo and ref foo patterns to give a good + /// error message when parsing mistakes like `ref foo(a, b)`. + fn parse_pat_ident(&mut self, + binding_mode: ast::BindingMode) + -> PResult<'a, PatKind> { + let ident = self.parse_ident()?; + let sub = if self.eat(&token::At) { + Some(self.parse_pat(Some("binding pattern"))?) + } else { + None + }; + + // just to be friendly, if they write something like + // ref Some(i) + // we end up here with ( as the current token. This shortly + // leads to a parse error. Note that if there is no explicit + // binding mode then we do not end up here, because the lookahead + // will direct us over to parse_enum_variant() + if self.token == token::OpenDelim(token::Paren) { + return Err(self.span_fatal( + self.prev_span, + "expected identifier, found enum pattern")) + } + + Ok(PatKind::Ident(binding_mode, ident, sub)) + } + + /// Parses the fields of a struct-like pattern. + fn parse_pat_fields(&mut self) -> PResult<'a, (Vec>, bool)> { + let mut fields = Vec::new(); + let mut etc = false; + let mut ate_comma = true; + let mut delayed_err: Option> = None; + let mut etc_span = None; + + while self.token != token::CloseDelim(token::Brace) { + let attrs = match self.parse_outer_attributes() { + Ok(attrs) => attrs, + Err(err) => { + if let Some(mut delayed) = delayed_err { + delayed.emit(); + } + return Err(err); + }, + }; + let lo = self.token.span; + + // check that a comma comes after every field + if !ate_comma { + let err = self.struct_span_err(self.prev_span, "expected `,`"); + if let Some(mut delayed) = delayed_err { + delayed.emit(); + } + return Err(err); + } + ate_comma = false; + + if self.check(&token::DotDot) || self.token == token::DotDotDot { + etc = true; + let mut etc_sp = self.token.span; + + if self.token == token::DotDotDot { // Issue #46718 + // Accept `...` as if it were `..` to avoid further errors + self.struct_span_err(self.token.span, "expected field pattern, found `...`") + .span_suggestion( + self.token.span, + "to omit remaining fields, use one fewer `.`", + "..".to_owned(), + Applicability::MachineApplicable + ) + .emit(); + } + self.bump(); // `..` || `...` + + if self.token == token::CloseDelim(token::Brace) { + etc_span = Some(etc_sp); + break; + } + let token_str = self.this_token_descr(); + let mut err = self.fatal(&format!("expected `}}`, found {}", token_str)); + + err.span_label(self.token.span, "expected `}`"); + let mut comma_sp = None; + if self.token == token::Comma { // Issue #49257 + let nw_span = self.sess.source_map().span_until_non_whitespace(self.token.span); + etc_sp = etc_sp.to(nw_span); + err.span_label(etc_sp, + "`..` must be at the end and cannot have a trailing comma"); + comma_sp = Some(self.token.span); + self.bump(); + ate_comma = true; + } + + etc_span = Some(etc_sp.until(self.token.span)); + if self.token == token::CloseDelim(token::Brace) { + // If the struct looks otherwise well formed, recover and continue. + if let Some(sp) = comma_sp { + err.span_suggestion_short( + sp, + "remove this comma", + String::new(), + Applicability::MachineApplicable, + ); + } + err.emit(); + break; + } else if self.token.is_ident() && ate_comma { + // Accept fields coming after `..,`. + // This way we avoid "pattern missing fields" errors afterwards. + // We delay this error until the end in order to have a span for a + // suggested fix. + if let Some(mut delayed_err) = delayed_err { + delayed_err.emit(); + return Err(err); + } else { + delayed_err = Some(err); + } + } else { + if let Some(mut err) = delayed_err { + err.emit(); + } + return Err(err); + } + } + + fields.push(match self.parse_pat_field(lo, attrs) { + Ok(field) => field, + Err(err) => { + if let Some(mut delayed_err) = delayed_err { + delayed_err.emit(); + } + return Err(err); + } + }); + ate_comma = self.eat(&token::Comma); + } + + if let Some(mut err) = delayed_err { + if let Some(etc_span) = etc_span { + err.multipart_suggestion( + "move the `..` to the end of the field list", + vec![ + (etc_span, String::new()), + (self.token.span, format!("{}.. }}", if ate_comma { "" } else { ", " })), + ], + Applicability::MachineApplicable, + ); + } + err.emit(); + } + return Ok((fields, etc)); + } + + fn parse_pat_field( + &mut self, + lo: Span, + attrs: Vec + ) -> PResult<'a, Spanned> { + // Check if a colon exists one ahead. This means we're parsing a fieldname. + let hi; + let (subpat, fieldname, is_shorthand) = if self.look_ahead(1, |t| t == &token::Colon) { + // Parsing a pattern of the form "fieldname: pat" + let fieldname = self.parse_field_name()?; + self.bump(); + let pat = self.parse_pat(None)?; + hi = pat.span; + (pat, fieldname, false) + } else { + // Parsing a pattern of the form "(box) (ref) (mut) fieldname" + let is_box = self.eat_keyword(kw::Box); + let boxed_span = self.token.span; + let is_ref = self.eat_keyword(kw::Ref); + let is_mut = self.eat_keyword(kw::Mut); + let fieldname = self.parse_ident()?; + hi = self.prev_span; + + let bind_type = match (is_ref, is_mut) { + (true, true) => BindingMode::ByRef(Mutability::Mutable), + (true, false) => BindingMode::ByRef(Mutability::Immutable), + (false, true) => BindingMode::ByValue(Mutability::Mutable), + (false, false) => BindingMode::ByValue(Mutability::Immutable), + }; + + let fieldpat = self.mk_pat_ident(boxed_span.to(hi), bind_type, fieldname); + let subpat = if is_box { + self.mk_pat(lo.to(hi), PatKind::Box(fieldpat)) + } else { + fieldpat + }; + (subpat, fieldname, true) + }; + + Ok(Spanned { + span: lo.to(hi), + node: FieldPat { + ident: fieldname, + pat: subpat, + is_shorthand, + attrs: attrs.into(), + } + }) + } + + pub(super) fn mk_pat_ident(&self, span: Span, bm: BindingMode, ident: Ident) -> P { + self.mk_pat(span, PatKind::Ident(bm, ident, None)) + } + + fn mk_pat(&self, span: Span, node: PatKind) -> P { + P(Pat { node, span, id: ast::DUMMY_NODE_ID }) + } +}