diff options
Diffstat (limited to 'compiler/rustc_parse/src/parser/expr.rs')
| -rw-r--r-- | compiler/rustc_parse/src/parser/expr.rs | 392 |
1 files changed, 232 insertions, 160 deletions
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index cfd7ad48222..404ba903613 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1,6 +1,8 @@ use super::pat::{GateOr, RecoverComma, PARAM_EXPECTED}; use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign}; -use super::{BlockMode, Parser, PathStyle, Restrictions, TokenType}; +use super::{ + AttrWrapper, BlockMode, ForceCollect, Parser, PathStyle, Restrictions, TokenType, TrailingToken, +}; use super::{SemiColonMode, SeqSep, TokenExpectType}; use crate::maybe_recover_from_interpolated_ty_qpath; @@ -62,16 +64,16 @@ macro_rules! maybe_whole_expr { #[derive(Debug)] pub(super) enum LhsExpr { NotYetParsed, - AttributesParsed(AttrVec), + AttributesParsed(AttrWrapper), AlreadyParsed(P<Expr>), } -impl From<Option<AttrVec>> for LhsExpr { +impl From<Option<AttrWrapper>> for LhsExpr { /// Converts `Some(attrs)` into `LhsExpr::AttributesParsed(attrs)` /// and `None` into `LhsExpr::NotYetParsed`. /// /// This conversion does not allocate. - fn from(o: Option<AttrVec>) -> Self { + fn from(o: Option<AttrWrapper>) -> Self { if let Some(attrs) = o { LhsExpr::AttributesParsed(attrs) } else { LhsExpr::NotYetParsed } } } @@ -123,7 +125,7 @@ impl<'a> Parser<'a> { pub(super) fn parse_expr_res( &mut self, r: Restrictions, - already_parsed_attrs: Option<AttrVec>, + already_parsed_attrs: Option<AttrWrapper>, ) -> PResult<'a, P<Expr>> { self.with_res(r, |this| this.parse_assoc_expr(already_parsed_attrs)) } @@ -133,7 +135,10 @@ impl<'a> Parser<'a> { /// This parses an expression accounting for associativity and precedence of the operators in /// the expression. #[inline] - fn parse_assoc_expr(&mut self, already_parsed_attrs: Option<AttrVec>) -> PResult<'a, P<Expr>> { + fn parse_assoc_expr( + &mut self, + already_parsed_attrs: Option<AttrWrapper>, + ) -> PResult<'a, P<Expr>> { self.parse_assoc_expr_with(0, already_parsed_attrs.into()) } @@ -439,7 +444,7 @@ impl<'a> Parser<'a> { } /// Parses prefix-forms of range notation: `..expr`, `..`, `..=expr`. - fn parse_prefix_range_expr(&mut self, attrs: Option<AttrVec>) -> PResult<'a, P<Expr>> { + fn parse_prefix_range_expr(&mut self, attrs: Option<AttrWrapper>) -> PResult<'a, P<Expr>> { // Check for deprecated `...` syntax. if self.token == token::DotDotDot { self.err_dotdotdot_syntax(self.token.span); @@ -457,44 +462,68 @@ impl<'a> Parser<'a> { }; let op = AssocOp::from_token(&self.token); let attrs = self.parse_or_use_outer_attributes(attrs)?; - let lo = self.token.span; - self.bump(); - let (span, opt_end) = if self.is_at_start_of_range_notation_rhs() { - // RHS must be parsed with more associativity than the dots. - self.parse_assoc_expr_with(op.unwrap().precedence() + 1, LhsExpr::NotYetParsed) - .map(|x| (lo.to(x.span), Some(x)))? - } else { - (lo, None) - }; - Ok(self.mk_expr(span, self.mk_range(None, opt_end, limits)?, attrs)) + // RESOLVED: It looks like we only haev non-empty attributes here when + // this is used as a statement: + // `#[my_attr] 25..;` + // We should still investigate `parse_or_use_outer_attributes`, since we haven't + // yet eaten the '..' + // + // FIXME - does this code ever haev attributes? `let a = #[attr] ..` doesn't even parse + // // We try to aprse attributes *before* bumping the token, so this can only + // ever succeeed if the `attrs` parameter is `Some` + self.collect_tokens_for_expr(attrs, |this, attrs| { + let lo = this.token.span; + this.bump(); + let (span, opt_end) = if this.is_at_start_of_range_notation_rhs() { + // RHS must be parsed with more associativity than the dots. + this.parse_assoc_expr_with(op.unwrap().precedence() + 1, LhsExpr::NotYetParsed) + .map(|x| (lo.to(x.span), Some(x)))? + } else { + (lo, None) + }; + Ok(this.mk_expr(span, this.mk_range(None, opt_end, limits)?, attrs.into())) + }) } /// Parses a prefix-unary-operator expr. - fn parse_prefix_expr(&mut self, attrs: Option<AttrVec>) -> PResult<'a, P<Expr>> { + fn parse_prefix_expr(&mut self, attrs: Option<AttrWrapper>) -> PResult<'a, P<Expr>> { let attrs = self.parse_or_use_outer_attributes(attrs)?; - // FIXME: Use super::attr::maybe_needs_tokens(&attrs) once we come up - // with a good way of passing `force_tokens` through from `parse_nonterminal`. - // Checking !attrs.is_empty() is correct, but will cause us to unnecessarily - // capture tokens in some circumstances. - let needs_tokens = !attrs.is_empty(); - let do_parse = |this: &mut Parser<'a>| { - let lo = this.token.span; - // Note: when adding new unary operators, don't forget to adjust TokenKind::can_begin_expr() - let (hi, ex) = match this.token.uninterpolate().kind { - token::Not => this.parse_unary_expr(lo, UnOp::Not), // `!expr` - token::Tilde => this.recover_tilde_expr(lo), // `~expr` - token::BinOp(token::Minus) => this.parse_unary_expr(lo, UnOp::Neg), // `-expr` - token::BinOp(token::Star) => this.parse_unary_expr(lo, UnOp::Deref), // `*expr` - token::BinOp(token::And) | token::AndAnd => this.parse_borrow_expr(lo), - token::Ident(..) if this.token.is_keyword(kw::Box) => this.parse_box_expr(lo), - token::Ident(..) if this.is_mistaken_not_ident_negation() => { - this.recover_not_expr(lo) - } - _ => return this.parse_dot_or_call_expr(Some(attrs)), - }?; - Ok(this.mk_expr(lo.to(hi), ex, attrs)) - }; - if needs_tokens { self.collect_tokens(do_parse) } else { do_parse(self) } + let lo = self.token.span; + + macro_rules! make_it { + ($this:ident, $attrs:expr, |this, _| $body:expr) => { + $this.collect_tokens_for_expr($attrs, |$this, attrs| { + let (hi, ex) = $body?; + Ok($this.mk_expr(lo.to(hi), ex, attrs.into())) + }) + }; + } + + let this = self; + + // Note: when adding new unary operators, don't forget to adjust TokenKind::can_begin_expr() + match this.token.uninterpolate().kind { + token::Not => make_it!(this, attrs, |this, _| this.parse_unary_expr(lo, UnOp::Not)), // `!expr` + token::Tilde => make_it!(this, attrs, |this, _| this.recover_tilde_expr(lo)), // `~expr` + token::BinOp(token::Minus) => { + make_it!(this, attrs, |this, _| this.parse_unary_expr(lo, UnOp::Neg)) + } // `-expr` + token::BinOp(token::Star) => { + make_it!(this, attrs, |this, _| this.parse_unary_expr(lo, UnOp::Deref)) + } // `*expr` + token::BinOp(token::And) | token::AndAnd => { + make_it!(this, attrs, |this, _| this.parse_borrow_expr(lo)) + } + token::Ident(..) if this.token.is_keyword(kw::Box) => { + make_it!(this, attrs, |this, _| this.parse_box_expr(lo)) + } + token::Ident(..) if this.is_mistaken_not_ident_negation() => { + // FIXME - what is our polciy for handling tokens during recovery? + // Should we ever invoke a proc-macro with these tokens? + make_it!(this, attrs, |this, _| this.recover_not_expr(lo)) + } + _ => return this.parse_dot_or_call_expr(Some(attrs.into())), + } } fn parse_prefix_expr_common(&mut self, lo: Span) -> PResult<'a, (Span, P<Expr>)> { @@ -805,18 +834,20 @@ impl<'a> Parser<'a> { } /// Parses `a.b` or `a(13)` or `a[4]` or just `a`. - fn parse_dot_or_call_expr(&mut self, attrs: Option<AttrVec>) -> PResult<'a, P<Expr>> { + fn parse_dot_or_call_expr(&mut self, attrs: Option<AttrWrapper>) -> PResult<'a, P<Expr>> { let attrs = self.parse_or_use_outer_attributes(attrs)?; - let base = self.parse_bottom_expr(); - let (span, base) = self.interpolated_or_expr_span(base)?; - self.parse_dot_or_call_expr_with(base, span, attrs) + self.collect_tokens_for_expr(attrs, |this, attrs| { + let base = this.parse_bottom_expr(); + let (span, base) = this.interpolated_or_expr_span(base)?; + this.parse_dot_or_call_expr_with(base, span, attrs) + }) } pub(super) fn parse_dot_or_call_expr_with( &mut self, e0: P<Expr>, lo: Span, - mut attrs: AttrVec, + mut attrs: Vec<ast::Attribute>, ) -> PResult<'a, P<Expr>> { // Stitch the list of outer attributes onto the return value. // A little bit ugly, but the best way given the current code @@ -824,7 +855,7 @@ impl<'a> Parser<'a> { self.parse_dot_or_call_expr_with_(e0, lo).map(|expr| { expr.map(|mut expr| { attrs.extend::<Vec<_>>(expr.attrs.into()); - expr.attrs = attrs; + expr.attrs = attrs.into(); expr }) }) @@ -1703,19 +1734,25 @@ impl<'a> Parser<'a> { fn parse_fn_block_param(&mut self) -> PResult<'a, Param> { let lo = self.token.span; let attrs = self.parse_outer_attributes()?; - let pat = self.parse_pat(PARAM_EXPECTED)?; - let ty = if self.eat(&token::Colon) { - self.parse_ty()? - } else { - self.mk_ty(self.prev_token.span, TyKind::Infer) - }; - Ok(Param { - attrs: attrs.into(), - ty, - pat, - span: lo.to(self.token.span), - id: DUMMY_NODE_ID, - is_placeholder: false, + self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| { + let pat = this.parse_pat(PARAM_EXPECTED)?; + let ty = if this.eat(&token::Colon) { + this.parse_ty()? + } else { + this.mk_ty(this.prev_token.span, TyKind::Infer) + }; + + Ok(( + Param { + attrs: attrs.into(), + ty, + pat, + span: lo.to(this.token.span), + id: DUMMY_NODE_ID, + is_placeholder: false, + }, + TrailingToken::MaybeComma, + )) }) } @@ -1731,7 +1768,7 @@ impl<'a> Parser<'a> { let thn = if self.eat_keyword(kw::Else) || !cond.returns() { self.error_missing_if_cond(lo, cond.span) } else { - let attrs = self.parse_outer_attributes()?; // For recovery. + let attrs = self.parse_outer_attributes()?.take_for_recovery(); // For recovery. let not_block = self.token != token::OpenDelim(token::Brace); let block = self.parse_block().map_err(|mut err| { if not_block { @@ -1788,7 +1825,7 @@ impl<'a> Parser<'a> { /// Parses an `else { ... }` expression (`else` token already eaten). fn parse_else_expr(&mut self) -> PResult<'a, P<Expr>> { let ctx_span = self.prev_token.span; // `else` - let attrs = self.parse_outer_attributes()?; // For recovery. + let attrs = self.parse_outer_attributes()?.take_for_recovery(); // For recovery. let expr = if self.eat_keyword(kw::If) { self.parse_if_expr(AttrVec::new())? } else { @@ -1947,85 +1984,91 @@ impl<'a> Parser<'a> { pub(super) fn parse_arm(&mut self) -> PResult<'a, Arm> { let attrs = self.parse_outer_attributes()?; - let lo = self.token.span; - let pat = self.parse_top_pat(GateOr::No, RecoverComma::Yes)?; - let guard = if self.eat_keyword(kw::If) { - let if_span = self.prev_token.span; - let cond = self.parse_expr()?; - if let ExprKind::Let(..) = cond.kind { - // Remove the last feature gating of a `let` expression since it's stable. - self.sess.gated_spans.ungate_last(sym::let_chains, cond.span); - let span = if_span.to(cond.span); - self.sess.gated_spans.gate(sym::if_let_guard, span); - } - Some(cond) - } else { - None - }; - let arrow_span = self.token.span; - self.expect(&token::FatArrow)?; - let arm_start_span = self.token.span; + self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| { + let lo = this.token.span; + let pat = this.parse_top_pat(GateOr::No, RecoverComma::Yes)?; + let guard = if this.eat_keyword(kw::If) { + let if_span = this.prev_token.span; + let cond = this.parse_expr()?; + if let ExprKind::Let(..) = cond.kind { + // Remove the last feature gating of a `let` expression since it's stable. + this.sess.gated_spans.ungate_last(sym::let_chains, cond.span); + let span = if_span.to(cond.span); + this.sess.gated_spans.gate(sym::if_let_guard, span); + } + Some(cond) + } else { + None + }; + let arrow_span = this.token.span; + this.expect(&token::FatArrow)?; + let arm_start_span = this.token.span; - let expr = self.parse_expr_res(Restrictions::STMT_EXPR, None).map_err(|mut err| { - err.span_label(arrow_span, "while parsing the `match` arm starting here"); - err - })?; + let expr = this.parse_expr_res(Restrictions::STMT_EXPR, None).map_err(|mut err| { + err.span_label(arrow_span, "while parsing the `match` arm starting here"); + err + })?; - let require_comma = classify::expr_requires_semi_to_be_stmt(&expr) - && self.token != token::CloseDelim(token::Brace); - - let hi = self.prev_token.span; - - if require_comma { - let sm = self.sess.source_map(); - self.expect_one_of(&[token::Comma], &[token::CloseDelim(token::Brace)]).map_err( - |mut err| { - match (sm.span_to_lines(expr.span), sm.span_to_lines(arm_start_span)) { - (Ok(ref expr_lines), Ok(ref arm_start_lines)) - if arm_start_lines.lines[0].end_col == expr_lines.lines[0].end_col - && expr_lines.lines.len() == 2 - && self.token == token::FatArrow => - { - // We check whether there's any trailing code in the parse span, - // if there isn't, we very likely have the following: - // - // X | &Y => "y" - // | -- - missing comma - // | | - // | arrow_span - // X | &X => "x" - // | - ^^ self.token.span - // | | - // | parsed until here as `"y" & X` - err.span_suggestion_short( - arm_start_span.shrink_to_hi(), - "missing a comma here to end this `match` arm", - ",".to_owned(), - Applicability::MachineApplicable, - ); - } - _ => { - err.span_label( - arrow_span, - "while parsing the `match` arm starting here", - ); + let require_comma = classify::expr_requires_semi_to_be_stmt(&expr) + && this.token != token::CloseDelim(token::Brace); + + let hi = this.prev_token.span; + + if require_comma { + let sm = this.sess.source_map(); + this.expect_one_of(&[token::Comma], &[token::CloseDelim(token::Brace)]).map_err( + |mut err| { + match (sm.span_to_lines(expr.span), sm.span_to_lines(arm_start_span)) { + (Ok(ref expr_lines), Ok(ref arm_start_lines)) + if arm_start_lines.lines[0].end_col + == expr_lines.lines[0].end_col + && expr_lines.lines.len() == 2 + && this.token == token::FatArrow => + { + // We check whether there's any trailing code in the parse span, + // if there isn't, we very likely have the following: + // + // X | &Y => "y" + // | -- - missing comma + // | | + // | arrow_span + // X | &X => "x" + // | - ^^ self.token.span + // | | + // | parsed until here as `"y" & X` + err.span_suggestion_short( + arm_start_span.shrink_to_hi(), + "missing a comma here to end this `match` arm", + ",".to_owned(), + Applicability::MachineApplicable, + ); + } + _ => { + err.span_label( + arrow_span, + "while parsing the `match` arm starting here", + ); + } } - } - err - }, - )?; - } else { - self.eat(&token::Comma); - } + err + }, + )?; + } else { + this.eat(&token::Comma); + } - Ok(ast::Arm { - attrs, - pat, - guard, - body: expr, - span: lo.to(hi), - id: DUMMY_NODE_ID, - is_placeholder: false, + Ok(( + ast::Arm { + attrs, + pat, + guard, + body: expr, + span: lo.to(hi), + id: DUMMY_NODE_ID, + is_placeholder: false, + }, + TrailingToken::None, + )) }) } @@ -2274,30 +2317,36 @@ impl<'a> Parser<'a> { /// Parses `ident (COLON expr)?`. fn parse_field(&mut self) -> PResult<'a, Field> { - let attrs = self.parse_outer_attributes()?.into(); - let lo = self.token.span; + let attrs = self.parse_outer_attributes()?; + self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| { + let lo = this.token.span; - // Check if a colon exists one ahead. This means we're parsing a fieldname. - let is_shorthand = !self.look_ahead(1, |t| t == &token::Colon || t == &token::Eq); - let (ident, expr) = if is_shorthand { - // Mimic `x: x` for the `x` field shorthand. - let ident = self.parse_ident_common(false)?; - let path = ast::Path::from_ident(ident); - (ident, self.mk_expr(ident.span, ExprKind::Path(None, path), AttrVec::new())) - } else { - let ident = self.parse_field_name()?; - self.error_on_eq_field_init(ident); - self.bump(); // `:` - (ident, self.parse_expr()?) - }; - Ok(ast::Field { - ident, - span: lo.to(expr.span), - expr, - is_shorthand, - attrs, - id: DUMMY_NODE_ID, - is_placeholder: false, + // Check if a colon exists one ahead. This means we're parsing a fieldname. + let is_shorthand = !this.look_ahead(1, |t| t == &token::Colon || t == &token::Eq); + let (ident, expr) = if is_shorthand { + // Mimic `x: x` for the `x` field shorthand. + let ident = this.parse_ident_common(false)?; + let path = ast::Path::from_ident(ident); + (ident, this.mk_expr(ident.span, ExprKind::Path(None, path), AttrVec::new())) + } else { + let ident = this.parse_field_name()?; + this.error_on_eq_field_init(ident); + this.bump(); // `:` + (ident, this.parse_expr()?) + }; + + Ok(( + ast::Field { + ident, + span: lo.to(expr.span), + expr, + is_shorthand, + attrs: attrs.into(), + id: DUMMY_NODE_ID, + is_placeholder: false, + }, + TrailingToken::MaybeComma, + )) }) } @@ -2405,4 +2454,27 @@ impl<'a> Parser<'a> { .map_or(lhs_span, |a| a.span) .to(rhs_span) } + + fn collect_tokens_for_expr( + &mut self, + attrs: AttrWrapper, + f: impl FnOnce(&mut Self, Vec<ast::Attribute>) -> PResult<'a, P<Expr>>, + ) -> PResult<'a, P<Expr>> { + // FIXME - come up with a nice way to properly forward `ForceCollect`from + // the nonterminal parsing code. TThis approach iscorrect, but will cause + // us to unnecessarily capture tokens for exprs that have only builtin + // attributes. Revisit this before #![feature(stmt_expr_attributes)] is stabilized + let force_collect = if attrs.is_empty() { ForceCollect::No } else { ForceCollect::Yes }; + self.collect_tokens_trailing_token(attrs, force_collect, |this, attrs| { + let res = f(this, attrs)?; + let trailing = if this.restrictions.contains(Restrictions::STMT_EXPR) + && this.token.kind == token::Semi + { + TrailingToken::Semi + } else { + TrailingToken::None + }; + Ok((res, trailing)) + }) + } } |
