use super::{SemiColonMode, SeqSep, TokenExpectType};
use crate::maybe_recover_from_interpolated_ty_qpath;
-use rustc_ast::ast::{self, AttrStyle, AttrVec, CaptureBy, Field, Ident, Lit, DUMMY_NODE_ID};
-use rustc_ast::ast::{AnonConst, BinOp, BinOpKind, FnDecl, FnRetTy, Mac, Param, Ty, TyKind, UnOp};
+use rustc_ast::ast::{self, AttrStyle, AttrVec, CaptureBy, Field, Ident, Lit, UnOp, DUMMY_NODE_ID};
+use rustc_ast::ast::{AnonConst, BinOp, BinOpKind, FnDecl, FnRetTy, MacCall, Param, Ty, TyKind};
use rustc_ast::ast::{Arm, Async, BlockCheckMode, Expr, ExprKind, Label, Movability, RangeLimits};
use rustc_ast::ptr::P;
use rustc_ast::token::{self, Token, TokenKind};
/// Parse `& mut? <expr>` or `& raw [ const | mut ] <expr>`.
fn parse_borrow_expr(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> {
self.expect_and()?;
+ let has_lifetime = self.token.is_lifetime() && self.look_ahead(1, |t| t != &token::Colon);
+ let lifetime = has_lifetime.then(|| self.expect_lifetime()); // For recovery, see below.
let (borrow_kind, mutbl) = self.parse_borrow_modifiers(lo);
let expr = self.parse_prefix_expr(None);
- let (span, expr) = self.interpolated_or_expr_span(expr)?;
- Ok((lo.to(span), ExprKind::AddrOf(borrow_kind, mutbl, expr)))
+ let (hi, expr) = self.interpolated_or_expr_span(expr)?;
+ let span = lo.to(hi);
+ if let Some(lt) = lifetime {
+ self.error_remove_borrow_lifetime(span, lt.ident.span);
+ }
+ Ok((span, ExprKind::AddrOf(borrow_kind, mutbl, expr)))
+ }
+
+ fn error_remove_borrow_lifetime(&self, span: Span, lt_span: Span) {
+ self.struct_span_err(span, "borrow expressions cannot be annotated with lifetimes")
+ .span_label(lt_span, "annotated with lifetime here")
+ .span_suggestion(
+ lt_span,
+ "remove the lifetime annotation",
+ String::new(),
+ Applicability::MachineApplicable,
+ )
+ .emit();
}
/// Parse `mut?` or `raw [ const | mut ]`.
} else if self.eat_lt() {
let (qself, path) = self.parse_qpath(PathStyle::Expr)?;
Ok(self.mk_expr(lo.to(path.span), ExprKind::Path(Some(qself), path), attrs))
- } else if self.token.is_path_start() {
+ } else if self.check_path() {
self.parse_path_start_expr(attrs)
} else if self.check_keyword(kw::Move) || self.check_keyword(kw::Static) {
self.parse_closure_expr(attrs)
} else if self.eat_keyword(kw::If) {
self.parse_if_expr(attrs)
- } else if self.eat_keyword(kw::For) {
- self.parse_for_expr(None, self.prev_token.span, attrs)
+ } else if self.check_keyword(kw::For) {
+ if self.choose_generics_over_qpath(1) {
+ // NOTE(Centril, eddyb): DO NOT REMOVE! Beyond providing parser recovery,
+ // this is an insurance policy in case we allow qpaths in (tuple-)struct patterns.
+ // When `for <Foo as Bar>::Proj in $expr $block` is wanted,
+ // you can disambiguate in favor of a pattern with `(...)`.
+ self.recover_quantified_closure_expr(attrs)
+ } else {
+ assert!(self.eat_keyword(kw::For));
+ self.parse_for_expr(None, self.prev_token.span, attrs)
+ }
} else if self.eat_keyword(kw::While) {
self.parse_while_expr(None, self.prev_token.span, attrs)
} else if let Some(label) = self.eat_label() {
let expr = self.mk_expr(lo.to(self.prev_token.span), ExprKind::Lit(literal), attrs);
self.maybe_recover_from_bad_qpath(expr, true)
}
- None => return Err(self.expected_expression_found()),
+ None => self.try_macro_suggestion(),
}
}
// `!`, as an operator, is prefix, so we know this isn't that.
let (hi, kind) = if self.eat(&token::Not) {
// MACRO INVOCATION expression
- let mac = Mac {
+ let mac = MacCall {
path,
args: self.parse_mac_args()?,
prior_type_ascription: self.last_type_ascription,
};
- (self.prev_token.span, ExprKind::Mac(mac))
+ (self.prev_token.span, ExprKind::MacCall(mac))
} else if self.check(&token::OpenDelim(token::Brace)) {
if let Some(expr) = self.maybe_parse_struct_expr(lo, &path, &attrs) {
return expr;
self.maybe_recover_from_bad_qpath(expr, true)
}
+ /// Parse `'label: $expr`. The label is already parsed.
fn parse_labeled_expr(&mut self, label: Label, attrs: AttrVec) -> PResult<'a, P<Expr>> {
let lo = label.ident.span;
- self.expect(&token::Colon)?;
- if self.eat_keyword(kw::While) {
- return self.parse_while_expr(Some(label), lo, attrs);
- }
- if self.eat_keyword(kw::For) {
- return self.parse_for_expr(Some(label), lo, attrs);
- }
- if self.eat_keyword(kw::Loop) {
- return self.parse_loop_expr(Some(label), lo, attrs);
- }
- if self.token == token::OpenDelim(token::Brace) {
- return self.parse_block_expr(Some(label), lo, BlockCheckMode::Default, attrs);
+ let label = Some(label);
+ let ate_colon = self.eat(&token::Colon);
+ let expr = if self.eat_keyword(kw::While) {
+ self.parse_while_expr(label, lo, attrs)
+ } else if self.eat_keyword(kw::For) {
+ self.parse_for_expr(label, lo, attrs)
+ } else if self.eat_keyword(kw::Loop) {
+ self.parse_loop_expr(label, lo, attrs)
+ } else if self.check(&token::OpenDelim(token::Brace)) || self.token.is_whole_block() {
+ self.parse_block_expr(label, lo, BlockCheckMode::Default, attrs)
+ } else {
+ let msg = "expected `while`, `for`, `loop` or `{` after a label";
+ self.struct_span_err(self.token.span, msg).span_label(self.token.span, msg).emit();
+ // Continue as an expression in an effort to recover on `'label: non_block_expr`.
+ self.parse_expr()
+ }?;
+
+ if !ate_colon {
+ self.error_labeled_expr_must_be_followed_by_colon(lo, expr.span);
}
- let msg = "expected `while`, `for`, `loop` or `{` after a label";
- self.struct_span_err(self.token.span, msg).span_label(self.token.span, msg).emit();
- // Continue as an expression in an effort to recover on `'label: non_block_expr`.
- self.parse_expr()
+ Ok(expr)
+ }
+
+ fn error_labeled_expr_must_be_followed_by_colon(&self, lo: Span, span: Span) {
+ self.struct_span_err(span, "labeled expression must be followed by `:`")
+ .span_label(lo, "the label")
+ .span_suggestion_short(
+ lo.shrink_to_hi(),
+ "add `:` after the label",
+ ": ".to_string(),
+ Applicability::MachineApplicable,
+ )
+ .note("labels are used before loops and blocks, allowing e.g., `break 'label` to them")
+ .emit();
}
/// Recover on the syntax `do catch { ... }` suggesting `try { ... }` instead.
opt_label: Option<Label>,
lo: Span,
blk_mode: BlockCheckMode,
- outer_attrs: AttrVec,
+ mut attrs: AttrVec,
) -> PResult<'a, P<Expr>> {
if let Some(label) = opt_label {
self.sess.gated_spans.gate(sym::label_break_value, label.ident.span);
}
- self.expect(&token::OpenDelim(token::Brace))?;
-
- let mut attrs = outer_attrs;
- attrs.extend(self.parse_inner_attributes()?);
+ if self.token.is_whole_block() {
+ self.struct_span_err(self.token.span, "cannot use a `block` macro fragment here")
+ .span_label(lo.to(self.token.span), "the `block` fragment is within this context")
+ .emit();
+ }
- let blk = self.parse_block_tail(lo, blk_mode)?;
+ let (inner_attrs, blk) = self.parse_block_common(lo, blk_mode)?;
+ attrs.extend(inner_attrs);
Ok(self.mk_expr(blk.span, ExprKind::Block(blk, opt_label), attrs))
}
+ /// Recover on an explicitly quantified closure expression, e.g., `for<'a> |x: &'a u8| *x + 1`.
+ fn recover_quantified_closure_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> {
+ let lo = self.token.span;
+ let _ = self.parse_late_bound_lifetime_defs()?;
+ let span_for = lo.to(self.prev_token.span);
+ let closure = self.parse_closure_expr(attrs)?;
+
+ self.struct_span_err(span_for, "cannot introduce explicit parameters for a closure")
+ .span_label(closure.span, "the parameters are attached to this closure")
+ .span_suggestion(
+ span_for,
+ "remove the parameters",
+ String::new(),
+ Applicability::MachineApplicable,
+ )
+ .emit();
+
+ Ok(self.mk_expr_err(lo.to(closure.span)))
+ }
+
/// Parses a closure expression (e.g., `move |args| expr`).
fn parse_closure_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> {
let lo = self.token.span;
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 not_block = self.token != token::OpenDelim(token::Brace);
- self.parse_block().map_err(|mut err| {
+ let block = self.parse_block().map_err(|mut err| {
if not_block {
err.span_label(lo, "this `if` expression has a condition, but no block");
}
err
- })?
+ })?;
+ self.error_on_if_block_attrs(lo, false, block.span, &attrs);
+ block
};
let els = if self.eat_keyword(kw::Else) { Some(self.parse_else_expr()?) } else { None };
Ok(self.mk_expr(lo.to(self.prev_token.span), ExprKind::If(cond, thn, els), attrs))
/// Parses an `else { ... }` expression (`else` token already eaten).
fn parse_else_expr(&mut self) -> PResult<'a, P<Expr>> {
- if self.eat_keyword(kw::If) {
- self.parse_if_expr(AttrVec::new())
+ let ctx_span = self.prev_token.span; // `else`
+ let attrs = self.parse_outer_attributes()?; // For recovery.
+ let expr = if self.eat_keyword(kw::If) {
+ self.parse_if_expr(AttrVec::new())?
} else {
let blk = self.parse_block()?;
- Ok(self.mk_expr(blk.span, ExprKind::Block(blk, None), AttrVec::new()))
- }
+ self.mk_expr(blk.span, ExprKind::Block(blk, None), AttrVec::new())
+ };
+ self.error_on_if_block_attrs(ctx_span, true, expr.span, &attrs);
+ Ok(expr)
+ }
+
+ fn error_on_if_block_attrs(
+ &self,
+ ctx_span: Span,
+ is_ctx_else: bool,
+ branch_span: Span,
+ attrs: &[ast::Attribute],
+ ) {
+ let (span, last) = match attrs {
+ [] => return,
+ [x0 @ xn] | [x0, .., xn] => (x0.span.to(xn.span), xn.span),
+ };
+ let ctx = if is_ctx_else { "else" } else { "if" };
+ self.struct_span_err(last, "outer attributes are not allowed on `if` and `else` branches")
+ .span_label(branch_span, "the attributes are attached to this branch")
+ .span_label(ctx_span, format!("the branch belongs to this `{}`", ctx))
+ .span_suggestion(
+ span,
+ "remove the attributes",
+ String::new(),
+ Applicability::MachineApplicable,
+ )
+ .emit();
}
/// Parses `for <src_pat> in <src_expr> <src_loop_block>` (`for` token already eaten).
}
let hi = self.token.span;
self.bump();
- return Ok(self.mk_expr(lo.to(hi), ExprKind::Match(scrutinee, arms), attrs));
+ Ok(self.mk_expr(lo.to(hi), ExprKind::Match(scrutinee, arms), attrs))
}
pub(super) fn parse_arm(&mut self) -> PResult<'a, Arm> {
}
fn is_try_block(&self) -> bool {
- self.token.is_keyword(kw::Try) &&
- self.look_ahead(1, |t| *t == token::OpenDelim(token::Brace)) &&
- self.token.uninterpolated_span().rust_2018() &&
- // Prevent `while try {} {}`, `if try {} {} else {}`, etc.
- !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL)
+ self.token.is_keyword(kw::Try)
+ && self.look_ahead(1, |t| *t == token::OpenDelim(token::Brace))
+ && self.token.uninterpolated_span().rust_2018()
}
/// Parses an `async move? {...}` expression.