Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions compiler/rustc_parse/src/parser/cfg_select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,16 @@ impl<'a> Parser<'a> {
}
}
}
let expr = self.collect_tokens(None, AttrWrapper::empty(), ForceCollect::Yes, |p, _| {
p.parse_expr_res(Restrictions::STMT_EXPR, AttrWrapper::empty())
.map(|(expr, _)| (expr, Trailing::No, UsePreAttrPos::No))
})?;
let attrs = AttrWrapper::empty(); // FIXME expressions with attributes can be supported here
let expr = self.collect_tokens(
None,
AttrWrapper::empty(),
ForceCollect::Yes,
|p, _empty_attrs| {
p.parse_expr_res_after_attrs(Restrictions::STMT_EXPR, attrs)
.map(|(expr, _)| (expr, Trailing::No, UsePreAttrPos::No))
},
)?;
if !classify::expr_is_complete(&expr)
&& self.token != token::CloseBrace
&& self.token != token::Eof
Expand Down
14 changes: 4 additions & 10 deletions compiler/rustc_parse/src/parser/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2641,11 +2641,8 @@ impl<'a> Parser<'a> {
if is_op_or_dot {
self.bump();
}
match (|| {
let attrs = self.parse_outer_attributes()?;
self.parse_expr_res(Restrictions::CONST_EXPR, attrs)
})() {
Ok((expr, _)) => {
match (|| self.parse_expr_res(Restrictions::CONST_EXPR))() {
Ok(expr) => {
// Find a mistake like `MyTrait<Assoc == S::Assoc>`.
if snapshot.token == token::EqEq {
err.span_suggestion_verbose(
Expand Down Expand Up @@ -2697,13 +2694,10 @@ impl<'a> Parser<'a> {
&mut self,
mut snapshot: SnapshotParser<'a>,
) -> Option<Box<ast::Expr>> {
match (|| {
let attrs = self.parse_outer_attributes()?;
snapshot.parse_expr_res(Restrictions::CONST_EXPR, attrs)
})() {
match (|| snapshot.parse_expr_res(Restrictions::CONST_EXPR))() {
// Since we don't know the exact reason why we failed to parse the type or the
// expression, employ a simple heuristic to weed out some pathological cases.
Ok((expr, _)) if let token::Comma | token::Gt = snapshot.token.kind => {
Ok(expr) if let token::Comma | token::Gt = snapshot.token.kind => {
self.restore_snapshot(snapshot);
Some(expr)
}
Expand Down
117 changes: 57 additions & 60 deletions compiler/rustc_parse/src/parser/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,7 @@ impl<'a> Parser<'a> {
#[inline]
pub fn parse_expr(&mut self) -> PResult<'a, Box<Expr>> {
self.current_closure.take();

let attrs = self.parse_outer_attributes()?;
self.parse_expr_res(Restrictions::empty(), attrs).map(|res| res.0)
self.parse_expr_res(Restrictions::empty())
}

/// Parses an expression, forcing tokens to be collected.
Expand All @@ -74,7 +72,8 @@ impl<'a> Parser<'a> {
AttrWrapper::empty(),
ForceCollect::Yes,
|this, _empty_attrs| {
let (expr, is_assoc) = this.parse_expr_res(Restrictions::empty(), attrs)?;
let (expr, is_assoc) =
this.parse_expr_res_after_attrs(Restrictions::empty(), attrs)?;
let use_pre_attr_pos =
if is_assoc { UsePreAttrPos::Yes } else { UsePreAttrPos::No };
Ok((expr, Trailing::No, use_pre_attr_pos))
Expand All @@ -90,9 +89,8 @@ impl<'a> Parser<'a> {
&mut self,
restrictions: Restrictions,
) -> PResult<'a, Box<Expr>> {
let attrs = self.parse_outer_attributes()?;
match self.parse_expr_res(restrictions, attrs) {
Ok((expr, _)) => Ok(expr),
match self.parse_expr_res(restrictions) {
Ok(expr) => Ok(expr),
Err(err) => match self.token.ident() {
Some((Ident { name: kw::Underscore, .. }, IdentIsRaw::No))
if self.may_recover() && self.look_ahead(1, |t| t == &token::Comma) =>
Expand All @@ -115,18 +113,36 @@ impl<'a> Parser<'a> {

/// Parses an expression, subject to the given restrictions.
#[inline]
pub(super) fn parse_expr_res(
pub(super) fn parse_expr_res(&mut self, r: Restrictions) -> PResult<'a, Box<Expr>> {
let attrs = self.parse_outer_attributes()?;
self.parse_expr_res_after_attrs(r, attrs).map(|(expr, _)| expr)
}

/// Same as `parse_expr_res`, but with attributes already pre-parsed.
/// The `bool` in the return value indicates if it was an assoc expr, i.e. with an operator
/// followed by a subexpression (e.g. `1 + 2`).
#[inline]
pub(super) fn parse_expr_res_after_attrs(
&mut self,
r: Restrictions,
attrs: AttrWrapper,
) -> PResult<'a, (Box<Expr>, bool)> {
self.with_res(r, |this| this.parse_expr_assoc_with(Bound::Unbounded, attrs))
self.with_res(r, |this| this.parse_expr_assoc_after_attrs(Bound::Unbounded, attrs))
}

/// Parses an associative expression with operators of at least `min_prec` precedence.
pub(super) fn parse_expr_assoc(
&mut self,
min_prec: Bound<ExprPrecedence>,
) -> PResult<'a, Box<Expr>> {
let attrs = self.parse_outer_attributes()?;
self.parse_expr_assoc_after_attrs(min_prec, attrs).map(|(expr, _)| expr)
}

/// Same as `parse_expr_assoc`, but with attributes already pre-parsed.
/// The `bool` in the return value indicates if it was an assoc expr, i.e. with an operator
/// followed by a subexpression (e.g. `1 + 2`).
pub(super) fn parse_expr_assoc_with(
pub(super) fn parse_expr_assoc_after_attrs(
&mut self,
min_prec: Bound<ExprPrecedence>,
attrs: AttrWrapper,
Expand All @@ -136,13 +152,13 @@ impl<'a> Parser<'a> {
} else {
self.parse_expr_prefix(attrs)?
};
self.parse_expr_assoc_rest_with(min_prec, false, lhs)
self.parse_expr_assoc_rest(min_prec, false, lhs)
}

/// Parses the rest of an associative expression (i.e. the part after the lhs) with operators
/// of at least `min_prec` precedence. The `bool` in the return value indicates if something
/// was actually parsed.
pub(super) fn parse_expr_assoc_rest_with(
pub(super) fn parse_expr_assoc_rest(
&mut self,
min_prec: Bound<ExprPrecedence>,
starts_stmt: bool,
Expand Down Expand Up @@ -279,9 +295,8 @@ impl<'a> Parser<'a> {
Fixity::Right => Bound::Included(prec),
Fixity::Left | Fixity::None => Bound::Excluded(prec),
};
let (rhs, _) = self.with_res(restrictions - Restrictions::STMT_EXPR, |this| {
let attrs = this.parse_outer_attributes()?;
this.parse_expr_assoc_with(min_prec, attrs)
let rhs = self.with_res(restrictions - Restrictions::STMT_EXPR, |this| {
this.parse_expr_assoc(min_prec)
})?;

let span = self.mk_expr_sp(&lhs, lhs_span, op_span, rhs.span);
Expand Down Expand Up @@ -419,11 +434,9 @@ impl<'a> Parser<'a> {
) -> PResult<'a, Box<Expr>> {
let rhs = if self.is_at_start_of_range_notation_rhs() {
let maybe_lt = self.token;
let attrs = self.parse_outer_attributes()?;
Some(
self.parse_expr_assoc_with(Bound::Excluded(prec), attrs)
.map_err(|err| self.maybe_err_dotdotlt_syntax(maybe_lt, err))?
.0,
self.parse_expr_assoc(Bound::Excluded(prec))
.map_err(|err| self.maybe_err_dotdotlt_syntax(maybe_lt, err))?,
)
} else {
None
Expand Down Expand Up @@ -469,22 +482,20 @@ impl<'a> Parser<'a> {
_ => RangeLimits::Closed,
};
let op = AssocOp::from_token(&self.token);
let attrs = self.parse_outer_attributes()?;
self.collect_tokens_for_expr(attrs, |this, attrs| {
self.collect_tokens_for_expr(AttrWrapper::empty(), |this, _empty_attrs| {
let lo = this.token.span;
let maybe_lt = this.look_ahead(1, |t| t.clone());
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.
let attrs = this.parse_outer_attributes()?;
this.parse_expr_assoc_with(Bound::Excluded(op.unwrap().precedence()), attrs)
.map(|(x, _)| (lo.to(x.span), Some(x)))
this.parse_expr_assoc(Bound::Excluded(op.unwrap().precedence()))
.map(|expr| (lo.to(expr.span), Some(expr)))
.map_err(|err| this.maybe_err_dotdotlt_syntax(maybe_lt, err))?
} else {
(lo, None)
};
let range = this.mk_range(None, opt_end, limits);
Ok(this.mk_expr_with_attrs(span, range, attrs))
Ok(this.mk_expr(span, range))
})
}

Expand Down Expand Up @@ -537,9 +548,8 @@ impl<'a> Parser<'a> {
}
this.dcx().emit_err(err);

this.bump();
let attrs = this.parse_outer_attributes()?;
this.parse_expr_prefix(attrs)
this.bump(); // `+`
Ok(this.parse_expr_prefix_common(lo)?.1)

@petrochenkov petrochenkov Jul 31, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The recovery for unary + here is made to do the same thing as parse_expr_unary.

View changes since the review

}
// Recover from `++x`:
token::Plus if this.look_ahead(1, |t| *t == token::Plus) => {
Expand Down Expand Up @@ -570,7 +580,6 @@ impl<'a> Parser<'a> {
}

fn parse_expr_prefix_common(&mut self, lo: Span) -> PResult<'a, (Span, Box<Expr>)> {
self.bump();
let attrs = self.parse_outer_attributes()?;
let expr = if self.token.is_range_separator() {
self.parse_expr_prefix_range(attrs)
Expand All @@ -582,6 +591,7 @@ impl<'a> Parser<'a> {
}

fn parse_expr_unary(&mut self, lo: Span, op: UnOp) -> PResult<'a, (Span, ExprKind)> {
self.bump(); // `op`
let (span, expr) = self.parse_expr_prefix_common(lo)?;
Ok((span, self.mk_unary(op, expr)))
}
Expand All @@ -596,6 +606,7 @@ impl<'a> Parser<'a> {
/// Parse `box expr` - this syntax has been removed, but we still parse this
/// for now to provide a more useful error
fn parse_expr_box(&mut self, box_kw: Span) -> PResult<'a, (Span, ExprKind)> {
self.bump(); // `box`
let (span, expr) = self.parse_expr_prefix_common(box_kw)?;
// Make a multipart suggestion instead of `span_to_snippet` in case source isn't available
let box_kw_and_lo = box_kw.until(self.interpolated_or_expr_span(&expr));
Expand Down Expand Up @@ -841,14 +852,7 @@ impl<'a> Parser<'a> {
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();
let attrs = self.parse_outer_attributes()?;
let expr = if self.token.is_range_separator() {
self.parse_expr_prefix_range(attrs)
} else {
self.parse_expr_prefix(attrs)
}?;
let hi = self.interpolated_or_expr_span(&expr);
let span = lo.to(hi);
let (span, expr) = self.parse_expr_prefix_common(lo)?;
if let Some(lt) = lifetime {
self.error_remove_borrow_lifetime(span, lt.ident.span.until(expr.span));
}
Expand Down Expand Up @@ -2502,9 +2506,8 @@ impl<'a> Parser<'a> {
self.restrictions - Restrictions::STMT_EXPR - Restrictions::ALLOW_LET;
let prev = self.prev_token;
let token = self.token;
let attrs = self.parse_outer_attributes()?;
match self.parse_expr_res(restrictions, attrs) {
Ok((expr, _)) => expr,
match self.parse_expr_res(restrictions) {
Ok(expr) => expr,
Err(err) => self.recover_closure_body(err, before, prev, token, lo, decl_hi)?,
}
}
Expand Down Expand Up @@ -2571,8 +2574,8 @@ impl<'a> Parser<'a> {
let restrictions =
self.restrictions - Restrictions::STMT_EXPR - Restrictions::ALLOW_LET;
let tok = self.token.clone();
match self.parse_expr_res(restrictions, AttrWrapper::empty()) {
Ok((expr, _)) => {
match self.parse_expr_res(restrictions) {
Ok(expr) => {
let descr = super::token_descr(&tok);
let mut diag = self
.dcx()
Expand Down Expand Up @@ -2811,9 +2814,8 @@ impl<'a> Parser<'a> {
&mut self,
let_chains_policy: LetChainsPolicy,
) -> PResult<'a, Box<Expr>> {
let attrs = self.parse_outer_attributes()?;
let (mut cond, _) =
self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL | Restrictions::ALLOW_LET, attrs)?;
let mut cond =
self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL | Restrictions::ALLOW_LET)?;

let mut checker = CondChecker::new(self, let_chains_policy);
checker.visit_expr(&mut cond);
Expand Down Expand Up @@ -2859,9 +2861,7 @@ impl<'a> Parser<'a> {
} else {
self.expect(exp!(Eq))?;
}
let attrs = self.parse_outer_attributes()?;
let (expr, _) =
self.parse_expr_assoc_with(Bound::Excluded(prec_let_scrutinee_needs_par()), attrs)?;
let expr = self.parse_expr_assoc(Bound::Excluded(prec_let_scrutinee_needs_par()))?;
let span = lo.to(expr.span);
Ok(self.mk_expr(span, ExprKind::Let(Box::new(pat), expr, span, recovered)))
}
Expand Down Expand Up @@ -3004,8 +3004,7 @@ impl<'a> Parser<'a> {
(Err(err), Some((start_span, left))) if self.eat_keyword(exp!(In)) => {
// We know for sure we have seen `for ($SOMETHING in`. In the happy path this would
// happen right before the return of this method.
let attrs = self.parse_outer_attributes()?;
let (expr, _) = match self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, attrs) {
let expr = match self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL) {
Ok(expr) => expr,
Err(expr_err) => {
// We don't know what followed the `in`, so cancel and bubble up the
Expand Down Expand Up @@ -3039,8 +3038,7 @@ impl<'a> Parser<'a> {
self.error_missing_in_for_loop();
}
self.check_for_for_in_in_typo(self.prev_token.span);
let attrs = self.parse_outer_attributes()?;
let (expr, _) = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, attrs)?;
let expr = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL)?;
Ok((pat, expr))
}

Expand Down Expand Up @@ -3212,8 +3210,7 @@ impl<'a> Parser<'a> {
/// Parses a `match ... { ... }` expression (`match` token already eaten).
fn parse_expr_match(&mut self) -> PResult<'a, Box<Expr>> {
let match_span = self.prev_token.span;
let attrs = self.parse_outer_attributes()?;
let (scrutinee, _) = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, attrs)?;
let scrutinee = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL)?;

self.parse_match_block(match_span, match_span, scrutinee, MatchKind::Prefix)
}
Expand Down Expand Up @@ -3413,9 +3410,8 @@ impl<'a> Parser<'a> {
let arrow_span = this.prev_token.span;
let arm_start_span = this.token.span;

let attrs = this.parse_outer_attributes()?;
let (expr, _) =
this.parse_expr_res(Restrictions::STMT_EXPR, attrs).map_err(|mut err| {
let expr =
this.parse_expr_res(Restrictions::STMT_EXPR).map_err(|mut err| {
err.span_label(arrow_span, "while parsing the `match` arm starting here");
err
})?;
Expand Down Expand Up @@ -3654,9 +3650,10 @@ impl<'a> Parser<'a> {
AttrWrapper::empty(),
force_collect,
|this, _empty_attrs| {
match this
.parse_expr_res(Restrictions::ALLOW_LET | Restrictions::IN_IF_GUARD, attrs)
{
match this.parse_expr_res_after_attrs(
Restrictions::ALLOW_LET | Restrictions::IN_IF_GUARD,
attrs,
) {
Ok((expr, _)) => Ok((expr, Trailing::No, UsePreAttrPos::No)),
Err(mut err) => {
if this.prev_token == token::OpenBrace {
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_parse/src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1662,7 +1662,7 @@ impl<'a> Parser<'a> {
) -> PResult<'a, R> {
// The only reason to call `collect_tokens_no_attrs` is if you want tokens, so use
// `ForceCollect::Yes`
self.collect_tokens(None, AttrWrapper::empty(), ForceCollect::Yes, |this, _attrs| {
self.collect_tokens(None, AttrWrapper::empty(), ForceCollect::Yes, |this, _empty_attrs| {
Ok((f(this)?, Trailing::No, UsePreAttrPos::No))
})
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_parse/src/parser/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,7 @@ impl<'a> Parser<'a> {
// Parse an associative expression such as `+ expr`, `% expr`, ...
// Assignments, ranges and `|` are disabled by [`Restrictions::IS_PAT`].
let Ok((expr, _)) = snapshot
.parse_expr_assoc_rest_with(Bound::Unbounded, false, expr)
.parse_expr_assoc_rest(Bound::Unbounded, false, expr)
.map_err(|err| err.cancel())
else {
// We got a trailing method/operator, but that wasn't an expression.
Expand Down
21 changes: 9 additions & 12 deletions compiler/rustc_parse/src/parser/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -892,15 +892,13 @@ impl<'a> Parser<'a> {
/// wrapped in braces.
pub(super) fn parse_unambiguous_unbraced_const_arg(&mut self) -> PResult<'a, Box<Expr>> {
let start = self.token.span;
let attrs = self.parse_outer_attributes()?;
let (expr, _) =
self.parse_expr_res(Restrictions::CONST_EXPR, attrs).map_err(|mut err| {
err.span_label(
start.shrink_to_lo(),
"while parsing a const generic argument starting here",
);
err
})?;
let expr = self.parse_expr_res(Restrictions::CONST_EXPR).map_err(|mut err| {
err.span_label(
start.shrink_to_lo(),
"while parsing a const generic argument starting here",
);
err
})?;
if !self.expr_is_valid_const_arg(&expr) {
return Err(self.dcx().create_err(ConstGenericWithoutBraces {
span: expr.span,
Expand Down Expand Up @@ -990,9 +988,8 @@ impl<'a> Parser<'a> {
// Fall back by trying to parse a const-expr expression. If we successfully do so,
// then we should report an error that it needs to be wrapped in braces.
let snapshot = self.create_snapshot_for_diagnostic();
let attrs = self.parse_outer_attributes()?;
match self.parse_expr_res(Restrictions::CONST_EXPR, attrs) {
Ok((expr, _)) => {
match self.parse_expr_res(Restrictions::CONST_EXPR) {
Ok(expr) => {
return Ok(Some(self.dummy_const_arg_needs_braces(
self.dcx().struct_span_err(expr.span, "invalid const generic expression"),
expr.span,
Expand Down
Loading
Loading