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:
parent
7e0241c637
commit
0b411f56e1
9 changed files with 621 additions and 407 deletions
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue