Require passing an AttrWrapper to collect_tokens_trailing_token

This is a pure refactoring split out from #80689.
It represents the most invasive part of that PR, requiring changes in
every caller of `parse_outer_attributes`

In order to eagerly expand `#[cfg]` attributes while preserving the
original `TokenStream`, we need to know the range of tokens that
corresponds to every attribute target. This is accomplished by making
`parse_outer_attributes` return an opaque `AttrWrapper` struct. An
`AttrWrapper` must be converted to a plain `AttrVec` by passing it to
`collect_tokens_trailing_token`. This makes it difficult to accidentally
construct an AST node with attributes without calling `collect_tokens_trailing_token`,
since AST nodes store an `AttrVec`, not an `AttrWrapper`.

As a result, we now call `collect_tokens_trailing_token` for attribute
targets which only support inert attributes, such as generic arguments
and struct fields. Currently, the constructed `LazyTokenStream` is
simply discarded. Future PRs will record the token range corresponding
to the attribute target, allowing those tokens to be removed from an
enclosing `collect_tokens_trailing_token` call if necessary.
This commit is contained in:
Aaron Hill 2021-01-22 13:28:08 -05:00
parent 7e0241c637
commit 0b411f56e1
No known key found for this signature in database
GPG key ID: B4087E510E98B164
9 changed files with 621 additions and 407 deletions

View file

@ -3,8 +3,10 @@ use super::diagnostics::{AttemptLocalParseRecovery, Error};
use super::expr::LhsExpr;
use super::pat::{GateOr, RecoverComma};
use super::path::PathStyle;
use super::{BlockMode, ForceCollect, Parser, Restrictions, SemiColonMode, TrailingToken};
use crate::{maybe_collect_tokens, maybe_whole};
use super::{
AttrWrapper, BlockMode, ForceCollect, Parser, Restrictions, SemiColonMode, TrailingToken,
};
use crate::maybe_whole;
use rustc_ast as ast;
use rustc_ast::attr::HasAttrs;
@ -38,30 +40,47 @@ impl<'a> Parser<'a> {
capture_semi: bool,
force_collect: ForceCollect,
) -> PResult<'a, Option<Stmt>> {
let mut attrs = self.parse_outer_attributes()?;
let attrs = self.parse_outer_attributes()?;
let lo = self.token.span;
maybe_whole!(self, NtStmt, |stmt| {
let mut stmt = stmt;
stmt.visit_attrs(|stmt_attrs| {
mem::swap(stmt_attrs, &mut attrs);
stmt_attrs.extend(attrs);
});
Some(stmt)
});
// Don't use `maybe_whole` so that we have precise control
// over when we bump the parser
if let token::Interpolated(nt) = &self.token.kind {
if let token::NtStmt(stmt) = &**nt {
let mut stmt = stmt.clone();
return self.collect_tokens_trailing_token(
attrs,
force_collect,
|this, mut attrs| {
stmt.visit_attrs(|stmt_attrs| {
mem::swap(stmt_attrs, &mut attrs);
stmt_attrs.extend(attrs);
});
// Make sure we capture the token::Interpolated
this.bump();
Ok((Some(stmt), TrailingToken::None))
},
);
}
}
Ok(Some(if self.token.is_keyword(kw::Let) {
self.parse_local_mk(lo, attrs.into(), capture_semi, force_collect)?
self.parse_local_mk(lo, attrs, capture_semi, force_collect)?
} else if self.is_kw_followed_by_ident(kw::Mut) {
self.recover_stmt_local(lo, attrs.into(), "missing keyword", "let mut")?
self.recover_stmt_local(
lo,
attrs.take_for_recovery().into(),
"missing keyword",
"let mut",
)?
} else if self.is_kw_followed_by_ident(kw::Auto) {
self.bump(); // `auto`
let msg = "write `let` instead of `auto` to introduce a new variable";
self.recover_stmt_local(lo, attrs.into(), msg, "let")?
self.recover_stmt_local(lo, attrs.take_for_recovery().into(), msg, "let")?
} else if self.is_kw_followed_by_ident(sym::var) {
self.bump(); // `var`
let msg = "write `let` instead of `var` to introduce a new variable";
self.recover_stmt_local(lo, attrs.into(), msg, "let")?
self.recover_stmt_local(lo, attrs.take_for_recovery().into(), msg, "let")?
} else if self.check_path() && !self.token.is_qpath_start() && !self.is_path_start_item() {
// We have avoided contextual keywords like `union`, items with `crate` visibility,
// or `auto trait` items. We aim to parse an arbitrary path `a::b` but not something
@ -75,14 +94,14 @@ impl<'a> Parser<'a> {
self.mk_stmt(lo.to(item.span), StmtKind::Item(P(item)))
} else if self.eat(&token::Semi) {
// Do not attempt to parse an expression if we're done here.
self.error_outer_attrs(&attrs);
self.error_outer_attrs(&attrs.take_for_recovery());
self.mk_stmt(lo, StmtKind::Empty)
} else if self.token != token::CloseDelim(token::Brace) {
// Remainder are line-expr stmts.
let e = self.parse_expr_res(Restrictions::STMT_EXPR, Some(attrs.into()))?;
self.mk_stmt(lo.to(e.span), StmtKind::Expr(e))
} else {
self.error_outer_attrs(&attrs);
self.error_outer_attrs(&attrs.take_for_recovery());
return Ok(None);
}))
}
@ -90,10 +109,10 @@ impl<'a> Parser<'a> {
fn parse_stmt_path_start(
&mut self,
lo: Span,
attrs: Vec<Attribute>,
attrs: AttrWrapper,
force_collect: ForceCollect,
) -> PResult<'a, Stmt> {
maybe_collect_tokens!(self, force_collect, &attrs, |this: &mut Parser<'a>| {
self.collect_tokens_trailing_token(attrs, force_collect, |this, attrs| {
let path = this.parse_path(PathStyle::Expr)?;
if this.eat(&token::Not) {
@ -142,7 +161,7 @@ impl<'a> Parser<'a> {
// Since none of the above applied, this is an expression statement macro.
let e = self.mk_expr(lo.to(hi), ExprKind::MacCall(mac), AttrVec::new());
let e = self.maybe_recover_from_bad_qpath(e, true)?;
let e = self.parse_dot_or_call_expr_with(e, lo, attrs)?;
let e = self.parse_dot_or_call_expr_with(e, lo, attrs.into())?;
let e = self.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(e))?;
StmtKind::Expr(e)
};
@ -178,11 +197,11 @@ impl<'a> Parser<'a> {
fn parse_local_mk(
&mut self,
lo: Span,
attrs: AttrVec,
attrs: AttrWrapper,
capture_semi: bool,
force_collect: ForceCollect,
) -> PResult<'a, Stmt> {
maybe_collect_tokens!(self, force_collect, &attrs, |this: &mut Parser<'a>| {
self.collect_tokens_trailing_token(attrs, force_collect, |this, attrs| {
this.expect_keyword(kw::Let)?;
let local = this.parse_local(attrs.into())?;
let trailing = if capture_semi && this.token.kind == token::Semi {