diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 7f766e63e060..71932f02017c 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -975,7 +975,9 @@ pub fn is_by_value(self) -> bool { } } -/// A statement +/// A statement. No `attrs` or `tokens` fields because each `StmtKind` variant +/// contains an AST node with those fields. (Except for `StmtKind::Empty`, +/// which never has attrs or tokens) #[derive(Clone, Encodable, Decodable, Debug)] pub struct Stmt { pub id: NodeId, diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index f678d11213c7..efb9526eabcf 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -133,6 +133,8 @@ parse_dot_dot_dot_for_remaining_fields = expected field pattern, found `{$token_ parse_dot_dot_dot_range_to_pattern_not_allowed = range-to patterns with `...` are not allowed .suggestion = use `..=` instead +parse_dot_dot_range_attribute = attributes are not allowed on range expressions starting with `..` + parse_dotdotdot = unexpected token: `...` .suggest_exclusive_range = use `..` for an exclusive range .suggest_inclusive_range = or `..=` for an inclusive range diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 936fc9d6b779..46e157348530 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -2990,3 +2990,10 @@ pub(crate) struct ExprRArrowCall { #[suggestion(style = "short", applicability = "machine-applicable", code = ".")] pub span: Span, } + +#[derive(Diagnostic)] +#[diag(parse_dot_dot_range_attribute)] +pub(crate) struct DotDotRangeAttribute { + #[primary_span] + pub span: Span, +} diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index f5c931034fd2..b5480b6b7d21 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -15,7 +15,7 @@ /// for the attribute target. This allows us to perform cfg-expansion on /// a token stream before we invoke a derive proc-macro. /// -/// This wrapper prevents direct access to the underlying `ast::AttrVec>`. +/// This wrapper prevents direct access to the underlying `ast::AttrVec`. /// Parsing code can only get access to the underlying attributes /// by passing an `AttrWrapper` to `collect_tokens_trailing_tokens`. /// This makes it difficult to accidentally construct an AST node @@ -177,6 +177,10 @@ impl<'a> Parser<'a> { /// into a `LazyAttrTokenStream`, and returned along with the result /// of the callback. /// + /// The `attrs` passed in are in `AttrWrapper` form, which is opaque. The + /// `AttrVec` within is passed to `f`. See the comment on `AttrWrapper` for + /// details. + /// /// Note: If your callback consumes an opening delimiter /// (including the case where you call `collect_tokens` /// when the current token is an opening delimiter), diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index c1aac84bcaec..c2b91488a117 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -2499,7 +2499,8 @@ pub(super) fn handle_ambiguous_unbraced_const_arg( /// wrapped in braces. pub(super) fn handle_unambiguous_unbraced_const_arg(&mut self) -> PResult<'a, P> { let start = self.token.span; - let expr = self.parse_expr_res(Restrictions::CONST_EXPR, None).map_err(|mut err| { + 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", @@ -2621,7 +2622,10 @@ pub(super) fn recover_const_arg( if is_op_or_dot { self.bump(); } - match self.parse_expr_res(Restrictions::CONST_EXPR, None) { + match (|| { + let attrs = self.parse_outer_attributes()?; + self.parse_expr_res(Restrictions::CONST_EXPR, attrs) + })() { Ok(expr) => { // Find a mistake like `MyTrait`. if token::EqEq == snapshot.token.kind { @@ -2675,7 +2679,10 @@ pub(crate) fn recover_unbraced_const_arg_that_can_begin_ty( &mut self, mut snapshot: SnapshotParser<'a>, ) -> Option> { - match snapshot.parse_expr_res(Restrictions::CONST_EXPR, None) { + match (|| { + let attrs = self.parse_outer_attributes()?; + snapshot.parse_expr_res(Restrictions::CONST_EXPR, attrs) + })() { // 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 => { diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 5decfc142dac..09f706143fa7 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -70,28 +70,10 @@ macro_rules! maybe_whole_expr { #[derive(Debug)] pub(super) enum LhsExpr { - NotYetParsed, - AttributesParsed(AttrWrapper), - AlreadyParsed { expr: P, starts_statement: bool }, -} - -impl From> for LhsExpr { - /// Converts `Some(attrs)` into `LhsExpr::AttributesParsed(attrs)` - /// and `None` into `LhsExpr::NotYetParsed`. - /// - /// This conversion does not allocate. - fn from(o: Option) -> Self { - if let Some(attrs) = o { LhsExpr::AttributesParsed(attrs) } else { LhsExpr::NotYetParsed } - } -} - -impl From> for LhsExpr { - /// Converts the `expr: P` into `LhsExpr::AlreadyParsed { expr, starts_statement: false }`. - /// - /// This conversion does not allocate. - fn from(expr: P) -> Self { - LhsExpr::AlreadyParsed { expr, starts_statement: false } - } + // Already parsed just the outer attributes. + Unparsed { attrs: AttrWrapper }, + // Already parsed the expression. + Parsed { expr: P, starts_statement: bool }, } #[derive(Debug)] @@ -112,12 +94,16 @@ impl<'a> Parser<'a> { pub fn parse_expr(&mut self) -> PResult<'a, P> { self.current_closure.take(); - self.parse_expr_res(Restrictions::empty(), None) + let attrs = self.parse_outer_attributes()?; + self.parse_expr_res(Restrictions::empty(), attrs) } - /// Parses an expression, forcing tokens to be collected + /// Parses an expression, forcing tokens to be collected. pub fn parse_expr_force_collect(&mut self) -> PResult<'a, P> { - self.collect_tokens_no_attrs(|this| this.parse_expr()) + self.current_closure.take(); + + let attrs = self.parse_outer_attributes()?; + self.collect_tokens_no_attrs(|this| this.parse_expr_res(Restrictions::empty(), attrs)) } pub fn parse_expr_anon_const(&mut self) -> PResult<'a, AnonConst> { @@ -125,7 +111,8 @@ pub fn parse_expr_anon_const(&mut self) -> PResult<'a, AnonConst> { } fn parse_expr_catch_underscore(&mut self, restrictions: Restrictions) -> PResult<'a, P> { - match self.parse_expr_res(restrictions, None) { + let attrs = self.parse_outer_attributes()?; + match self.parse_expr_res(restrictions, attrs) { Ok(expr) => Ok(expr), Err(err) => match self.token.ident() { Some((Ident { name: kw::Underscore, .. }, IdentIsRaw::No)) @@ -152,21 +139,9 @@ fn parse_expr_paren_seq(&mut self) -> PResult<'a, ThinVec>> { pub(super) fn parse_expr_res( &mut self, r: Restrictions, - already_parsed_attrs: Option, + attrs: AttrWrapper, ) -> PResult<'a, P> { - self.with_res(r, |this| this.parse_expr_assoc(already_parsed_attrs)) - } - - /// Parses an associative expression. - /// - /// This parses an expression accounting for associativity and precedence of the operators in - /// the expression. - #[inline] - fn parse_expr_assoc( - &mut self, - already_parsed_attrs: Option, - ) -> PResult<'a, P> { - self.parse_expr_assoc_with(0, already_parsed_attrs.into()) + self.with_res(r, |this| this.parse_expr_assoc_with(0, LhsExpr::Unparsed { attrs })) } /// Parses an associative expression with operators of at least `min_prec` precedence. @@ -176,18 +151,17 @@ pub(super) fn parse_expr_assoc_with( lhs: LhsExpr, ) -> PResult<'a, P> { let mut starts_stmt = false; - let mut lhs = if let LhsExpr::AlreadyParsed { expr, starts_statement } = lhs { - starts_stmt = starts_statement; - expr - } else { - let attrs = match lhs { - LhsExpr::AttributesParsed(attrs) => Some(attrs), - _ => None, - }; - if self.token.is_range_separator() { - return self.parse_expr_prefix_range(attrs); - } else { - self.parse_expr_prefix(attrs)? + let mut lhs = match lhs { + LhsExpr::Parsed { expr, starts_statement } => { + starts_stmt = starts_statement; + expr + } + LhsExpr::Unparsed { attrs } => { + if self.token.is_range_separator() { + return self.parse_expr_prefix_range(attrs); + } else { + self.parse_expr_prefix(attrs)? + } } }; @@ -325,7 +299,8 @@ pub(super) fn parse_expr_assoc_with( Fixity::None => 1, }; let rhs = self.with_res(restrictions - Restrictions::STMT_EXPR, |this| { - this.parse_expr_assoc_with(prec + prec_adjustment, LhsExpr::NotYetParsed) + let attrs = this.parse_outer_attributes()?; + this.parse_expr_assoc_with(prec + prec_adjustment, LhsExpr::Unparsed { attrs }) })?; let span = self.mk_expr_sp(&lhs, lhs_span, rhs.span); @@ -498,8 +473,9 @@ fn parse_expr_range( ) -> PResult<'a, P> { let rhs = if self.is_at_start_of_range_notation_rhs() { let maybe_lt = self.token.clone(); + let attrs = self.parse_outer_attributes()?; Some( - self.parse_expr_assoc_with(prec + 1, LhsExpr::NotYetParsed) + self.parse_expr_assoc_with(prec + 1, LhsExpr::Unparsed { attrs }) .map_err(|err| self.maybe_err_dotdotlt_syntax(maybe_lt, err))?, ) } else { @@ -526,7 +502,12 @@ fn is_at_start_of_range_notation_rhs(&self) -> bool { } /// Parses prefix-forms of range notation: `..expr`, `..`, `..=expr`. - fn parse_expr_prefix_range(&mut self, attrs: Option) -> PResult<'a, P> { + fn parse_expr_prefix_range(&mut self, attrs: AttrWrapper) -> PResult<'a, P> { + if !attrs.is_empty() { + let err = errors::DotDotRangeAttribute { span: self.token.span }; + self.dcx().emit_err(err); + } + // Check for deprecated `...` syntax. if self.token == token::DotDotDot { self.err_dotdotdot_syntax(self.token.span); @@ -543,20 +524,20 @@ fn parse_expr_prefix_range(&mut self, attrs: Option) -> PResult<'a, _ => RangeLimits::Closed, }; let op = AssocOp::from_token(&self.token); - // FIXME: `parse_prefix_range_expr` is called when the current - // token is `DotDot`, `DotDotDot`, or `DotDotEq`. If we haven't already - // parsed attributes, then trying to parse them here will always fail. - // We should figure out how we want attributes on range expressions to work. - let attrs = self.parse_or_use_outer_attributes(attrs)?; + let attrs = self.parse_outer_attributes()?; self.collect_tokens_for_expr(attrs, |this, 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. - this.parse_expr_assoc_with(op.unwrap().precedence() + 1, LhsExpr::NotYetParsed) - .map(|x| (lo.to(x.span), Some(x))) - .map_err(|err| this.maybe_err_dotdotlt_syntax(maybe_lt, err))? + let attrs = this.parse_outer_attributes()?; + this.parse_expr_assoc_with( + op.unwrap().precedence() + 1, + LhsExpr::Unparsed { attrs }, + ) + .map(|x| (lo.to(x.span), Some(x))) + .map_err(|err| this.maybe_err_dotdotlt_syntax(maybe_lt, err))? } else { (lo, None) }; @@ -566,8 +547,7 @@ fn parse_expr_prefix_range(&mut self, attrs: Option) -> PResult<'a, } /// Parses a prefix-unary-operator expr. - fn parse_expr_prefix(&mut self, attrs: Option) -> PResult<'a, P> { - let attrs = self.parse_or_use_outer_attributes(attrs)?; + fn parse_expr_prefix(&mut self, attrs: AttrWrapper) -> PResult<'a, P> { let lo = self.token.span; macro_rules! make_it { @@ -616,7 +596,8 @@ macro_rules! make_it { this.dcx().emit_err(err); this.bump(); - this.parse_expr_prefix(None) + let attrs = this.parse_outer_attributes()?; + this.parse_expr_prefix(attrs) } // Recover from `++x`: token::BinOp(token::Plus) @@ -629,7 +610,7 @@ macro_rules! make_it { this.bump(); this.bump(); - let operand_expr = this.parse_expr_dot_or_call(Default::default())?; + let operand_expr = this.parse_expr_dot_or_call(attrs)?; this.recover_from_prefix_increment(operand_expr, pre_span, starts_stmt) } token::Ident(..) if this.token.is_keyword(kw::Box) => { @@ -638,13 +619,14 @@ macro_rules! make_it { token::Ident(..) if this.may_recover() && this.is_mistaken_not_ident_negation() => { make_it!(this, attrs, |this, _| this.recover_not_expr(lo)) } - _ => return this.parse_expr_dot_or_call(Some(attrs)), + _ => return this.parse_expr_dot_or_call(attrs), } } fn parse_expr_prefix_common(&mut self, lo: Span) -> PResult<'a, (Span, P)> { self.bump(); - let expr = self.parse_expr_prefix(None)?; + let attrs = self.parse_outer_attributes()?; + let expr = self.parse_expr_prefix(attrs)?; let span = self.interpolated_or_expr_span(&expr); Ok((lo.to(span), expr)) } @@ -894,10 +876,11 @@ fn parse_expr_borrow(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> { 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 attrs = self.parse_outer_attributes()?; let expr = if self.token.is_range_separator() { - self.parse_expr_prefix_range(None) + self.parse_expr_prefix_range(attrs) } else { - self.parse_expr_prefix(None) + self.parse_expr_prefix(attrs) }?; let hi = self.interpolated_or_expr_span(&expr); let span = lo.to(hi); @@ -927,8 +910,7 @@ fn parse_borrow_modifiers(&mut self, lo: Span) -> (ast::BorrowKind, ast::Mutabil } /// Parses `a.b` or `a(13)` or `a[4]` or just `a`. - fn parse_expr_dot_or_call(&mut self, attrs: Option) -> PResult<'a, P> { - let attrs = self.parse_or_use_outer_attributes(attrs)?; + fn parse_expr_dot_or_call(&mut self, attrs: AttrWrapper) -> PResult<'a, P> { self.collect_tokens_for_expr(attrs, |this, attrs| { let base = this.parse_expr_bottom()?; let span = this.interpolated_or_expr_span(&base); @@ -2365,7 +2347,8 @@ fn parse_expr_closure(&mut self) -> PResult<'a, P> { self.restrictions - Restrictions::STMT_EXPR - Restrictions::ALLOW_LET; let prev = self.prev_token.clone(); let token = self.token.clone(); - match self.parse_expr_res(restrictions, None) { + let attrs = self.parse_outer_attributes()?; + match self.parse_expr_res(restrictions, attrs) { Ok(expr) => expr, Err(err) => self.recover_closure_body(err, before, prev, token, lo, decl_hi)?, } @@ -2613,8 +2596,9 @@ fn parse_if_after_cond(&mut self, lo: Span, mut cond: P) -> PResult<'a, P< /// Parses the condition of a `if` or `while` expression. fn parse_expr_cond(&mut self) -> PResult<'a, P> { + let attrs = self.parse_outer_attributes()?; let mut cond = - self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL | Restrictions::ALLOW_LET, None)?; + self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL | Restrictions::ALLOW_LET, attrs)?; CondChecker::new(self).visit_expr(&mut cond); @@ -2661,7 +2645,11 @@ fn parse_expr_let(&mut self, restrictions: Restrictions) -> PResult<'a, P> } else { self.expect(&token::Eq)?; } - let expr = self.parse_expr_assoc_with(1 + prec_let_scrutinee_needs_par(), None.into())?; + let attrs = self.parse_outer_attributes()?; + let expr = self.parse_expr_assoc_with( + 1 + prec_let_scrutinee_needs_par(), + LhsExpr::Unparsed { attrs }, + )?; let span = lo.to(expr.span); Ok(self.mk_expr(span, ExprKind::Let(pat, expr, span, recovered))) } @@ -2794,7 +2782,8 @@ fn parse_for_head(&mut self) -> PResult<'a, (P, P)> { (Err(err), Some((start_span, left))) if self.eat_keyword(kw::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 expr = match self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None) { + let attrs = self.parse_outer_attributes()?; + let expr = match self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, attrs) { Ok(expr) => expr, Err(expr_err) => { // We don't know what followed the `in`, so cancel and bubble up the @@ -2828,7 +2817,8 @@ fn parse_for_head(&mut self) -> PResult<'a, (P, P)> { self.error_missing_in_for_loop(); } self.check_for_for_in_in_typo(self.prev_token.span); - let expr = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?; + let attrs = self.parse_outer_attributes()?; + let expr = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, attrs)?; Ok((pat, expr)) } @@ -2940,7 +2930,8 @@ pub(crate) fn eat_label(&mut self) -> Option