1
Fork 0

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

@ -1,4 +1,4 @@
use super::Parser;
use super::{ForceCollect, Parser, TrailingToken};
use rustc_ast::token;
use rustc_ast::{
@ -84,68 +84,89 @@ impl<'a> Parser<'a> {
/// a trailing comma and erroneous trailing attributes.
pub(super) fn parse_generic_params(&mut self) -> PResult<'a, Vec<ast::GenericParam>> {
let mut params = Vec::new();
loop {
let mut done = false;
while !done {
let attrs = self.parse_outer_attributes()?;
if self.check_lifetime() {
let lifetime = self.expect_lifetime();
// Parse lifetime parameter.
let bounds =
if self.eat(&token::Colon) { self.parse_lt_param_bounds() } else { Vec::new() };
params.push(ast::GenericParam {
ident: lifetime.ident,
id: lifetime.id,
attrs: attrs.into(),
bounds,
kind: ast::GenericParamKind::Lifetime,
is_placeholder: false,
});
} else if self.check_keyword(kw::Const) {
// Parse const parameter.
params.push(self.parse_const_param(attrs)?);
} else if self.check_ident() {
// Parse type parameter.
params.push(self.parse_ty_param(attrs)?);
} else if self.token.can_begin_type() {
// Trying to write an associated type bound? (#26271)
let snapshot = self.clone();
match self.parse_ty_where_predicate() {
Ok(where_predicate) => {
self.struct_span_err(
where_predicate.span(),
"bounds on associated types do not belong here",
)
.span_label(where_predicate.span(), "belongs in `where` clause")
.emit();
}
Err(mut err) => {
err.cancel();
*self = snapshot;
break;
}
}
} else {
// Check for trailing attributes and stop parsing.
if !attrs.is_empty() {
if !params.is_empty() {
self.struct_span_err(
attrs[0].span,
"trailing attribute after generic parameter",
)
.span_label(attrs[0].span, "attributes must go before parameters")
.emit();
let param =
self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| {
let param = if this.check_lifetime() {
let lifetime = this.expect_lifetime();
// Parse lifetime parameter.
let bounds = if this.eat(&token::Colon) {
this.parse_lt_param_bounds()
} else {
Vec::new()
};
Some(ast::GenericParam {
ident: lifetime.ident,
id: lifetime.id,
attrs: attrs.into(),
bounds,
kind: ast::GenericParamKind::Lifetime,
is_placeholder: false,
})
} else if this.check_keyword(kw::Const) {
// Parse const parameter.
Some(this.parse_const_param(attrs)?)
} else if this.check_ident() {
// Parse type parameter.
Some(this.parse_ty_param(attrs)?)
} else if this.token.can_begin_type() {
// Trying to write an associated type bound? (#26271)
let snapshot = this.clone();
match this.parse_ty_where_predicate() {
Ok(where_predicate) => {
this.struct_span_err(
where_predicate.span(),
"bounds on associated types do not belong here",
)
.span_label(where_predicate.span(), "belongs in `where` clause")
.emit();
// FIXME - try to continue parsing other generics?
return Ok((None, TrailingToken::None));
}
Err(mut err) => {
err.cancel();
// FIXME - maybe we should overwrite 'self' outside of `collect_tokens`?
*this = snapshot;
return Ok((None, TrailingToken::None));
}
}
} else {
self.struct_span_err(attrs[0].span, "attribute without generic parameters")
.span_label(
attrs[0].span,
"attributes are only permitted when preceding parameters",
)
.emit();
}
}
break;
}
// Check for trailing attributes and stop parsing.
if !attrs.is_empty() {
if !params.is_empty() {
this.struct_span_err(
attrs[0].span,
"trailing attribute after generic parameter",
)
.span_label(attrs[0].span, "attributes must go before parameters")
.emit();
} else {
this.struct_span_err(
attrs[0].span,
"attribute without generic parameters",
)
.span_label(
attrs[0].span,
"attributes are only permitted when preceding parameters",
)
.emit();
}
}
return Ok((None, TrailingToken::None));
};
if !self.eat(&token::Comma) {
if !this.eat(&token::Comma) {
done = true;
}
// We just ate the comma, so no need to use `TrailingToken`
Ok((param, TrailingToken::None))
})?;
if let Some(param) = param {
params.push(param);
} else {
break;
}
}