diff --git a/compiler/rustc_parse/src/parser/cfg_select.rs b/compiler/rustc_parse/src/parser/cfg_select.rs index f5e82b96b41eb..cd82e56eb7c6b 100644 --- a/compiler/rustc_parse/src/parser/cfg_select.rs +++ b/compiler/rustc_parse/src/parser/cfg_select.rs @@ -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 diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 1c99c2c2dae1e..eec1caa1a3dd8 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -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`. if snapshot.token == token::EqEq { err.span_suggestion_verbose( @@ -2697,13 +2694,10 @@ impl<'a> Parser<'a> { &mut self, mut snapshot: SnapshotParser<'a>, ) -> Option> { - 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) } diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 34044e72ab92b..5afb292580a01 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -54,9 +54,7 @@ impl<'a> Parser<'a> { #[inline] pub fn parse_expr(&mut self) -> PResult<'a, Box> { 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. @@ -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)) @@ -90,9 +89,8 @@ impl<'a> Parser<'a> { &mut self, restrictions: Restrictions, ) -> PResult<'a, Box> { - 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) => @@ -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> { + 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, 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, + ) -> PResult<'a, Box> { + 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, attrs: AttrWrapper, @@ -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, starts_stmt: bool, @@ -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); @@ -419,11 +434,9 @@ impl<'a> Parser<'a> { ) -> PResult<'a, Box> { 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 @@ -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)) }) } @@ -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) } // Recover from `++x`: token::Plus if this.look_ahead(1, |t| *t == token::Plus) => { @@ -570,7 +580,6 @@ impl<'a> Parser<'a> { } fn parse_expr_prefix_common(&mut self, lo: Span) -> PResult<'a, (Span, Box)> { - self.bump(); let attrs = self.parse_outer_attributes()?; let expr = if self.token.is_range_separator() { self.parse_expr_prefix_range(attrs) @@ -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))) } @@ -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)); @@ -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)); } @@ -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)?, } } @@ -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() @@ -2811,9 +2814,8 @@ impl<'a> Parser<'a> { &mut self, let_chains_policy: LetChainsPolicy, ) -> PResult<'a, Box> { - 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); @@ -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))) } @@ -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 @@ -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)) } @@ -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> { 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) } @@ -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 })?; @@ -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 { diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index e2671a24177f1..c67f220e84bd9 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -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)) }) } diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 74b2194cc97fa..026fe8acb35cd 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -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. diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index 066d4402d5fde..026c45cb4a804 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -892,15 +892,13 @@ impl<'a> Parser<'a> { /// wrapped in braces. pub(super) fn parse_unambiguous_unbraced_const_arg(&mut self) -> PResult<'a, Box> { 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, @@ -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, diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 6339532af530c..7c3752cfff187 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -182,7 +182,7 @@ impl<'a> Parser<'a> { AttrWrapper::empty(), force_collect, |this, _empty_attrs| { - let (expr, _) = this.parse_expr_res(restrictions, attrs)?; + let (expr, _) = this.parse_expr_res_after_attrs(restrictions, attrs)?; Ok((expr, Trailing::No, UsePreAttrPos::Yes)) }, )?; @@ -235,7 +235,7 @@ impl<'a> Parser<'a> { // Perform this outside of the `collect_tokens` closure, since our // outer attributes do not apply to this part of the expression. let (expr, _) = self.with_res(Restrictions::STMT_EXPR, |this| { - this.parse_expr_assoc_rest_with(Bound::Unbounded, true, expr) + this.parse_expr_assoc_rest(Bound::Unbounded, true, expr) })?; Ok(self.mk_stmt(lo.to(self.prev_token.span), StmtKind::Expr(expr))) } else { @@ -270,7 +270,7 @@ impl<'a> Parser<'a> { let e = self.mk_expr(lo.to(hi), ExprKind::MacCall(mac)); let e = self.maybe_recover_from_bad_qpath(e)?; let e = self.parse_expr_dot_or_call_with(attrs, e, lo)?; - let (e, _) = self.parse_expr_assoc_rest_with(Bound::Unbounded, false, e)?; + let (e, _) = self.parse_expr_assoc_rest(Bound::Unbounded, false, e)?; StmtKind::Expr(e) }; Ok(self.mk_stmt(lo.to(hi), kind)) diff --git a/tests/ui/parser/attribute/missing-cases.rs b/tests/ui/parser/attribute/missing-cases.rs new file mode 100644 index 0000000000000..db3e677451363 --- /dev/null +++ b/tests/ui/parser/attribute/missing-cases.rs @@ -0,0 +1,21 @@ +// Some examples of attribute parsing previously missing from the test suite + +#![feature(rustc_attrs, stmt_expr_attributes)] + +fn foo() {} +//~^ ERROR defaults for generic parameters are not allowed here + +fn main() { + match #[rustc_dummy] 10 { _ => {} } + let _ = - #[rustc_dummy] 10; + let _ = || #[rustc_dummy] 10; + if #[rustc_dummy] true {} + if let _ = #[rustc_dummy] 10 {} + for _ in #[rustc_dummy] (0..10) {} + for (_ in #[rustc_dummy] (0..10)) {} + //~^ ERROR unexpected parentheses surrounding `for` loop head + foo::<#[rustc_dummy] 10>(); + //~^ ERROR attributes cannot be applied to generic arguments + cfg_select! { _ => #[rustc_dummy] 10 } + //~^ ERROR expected expression, found `#` +} diff --git a/tests/ui/parser/attribute/missing-cases.stderr b/tests/ui/parser/attribute/missing-cases.stderr new file mode 100644 index 0000000000000..97f011226ed5d --- /dev/null +++ b/tests/ui/parser/attribute/missing-cases.stderr @@ -0,0 +1,32 @@ +error: unexpected parentheses surrounding `for` loop head + --> $DIR/missing-cases.rs:15:9 + | +LL | for (_ in #[rustc_dummy] (0..10)) {} + | ^ ^ + | +help: remove parentheses in `for` loop + | +LL - for (_ in #[rustc_dummy] (0..10)) {} +LL + for _ in #[rustc_dummy] (0..10) {} + | + +error: attributes cannot be applied to generic arguments + --> $DIR/missing-cases.rs:17:11 + | +LL | foo::<#[rustc_dummy] 10>(); + | ^^^^^^^^^^^^^^ attributes are not allowed here + +error: expected expression, found `#` + --> $DIR/missing-cases.rs:19:24 + | +LL | cfg_select! { _ => #[rustc_dummy] 10 } + | ^ expected expression + +error: defaults for generic parameters are not allowed here + --> $DIR/missing-cases.rs:5:8 + | +LL | fn foo() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors +