summaryrefslogtreecommitdiff
path: root/compiler/rustc_parse/src/parser/generics.rs
diff options
context:
space:
mode:
authorAaron Hill <aa1ronham@gmail.com>2021-01-22 13:28:08 -0500
committerAaron Hill <aa1ronham@gmail.com>2021-02-13 12:07:15 -0500
commit0b411f56e1a539e2388e9a983feb45f362923dc5 (patch)
treea630893ab6e51faada2847b807e38afb28949270 /compiler/rustc_parse/src/parser/generics.rs
parent7e0241c63755ea28045d512b742f50b307874419 (diff)
downloadrust-0b411f56e1a539e2388e9a983feb45f362923dc5.tar.gz
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.
Diffstat (limited to 'compiler/rustc_parse/src/parser/generics.rs')
-rw-r--r--compiler/rustc_parse/src/parser/generics.rs139
1 files changed, 80 insertions, 59 deletions
diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs
index 42a13376863..f175c5b50b3 100644
--- a/compiler/rustc_parse/src/parser/generics.rs
+++ b/compiler/rustc_parse/src/parser/generics.rs
@@ -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();
+ // 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 !this.eat(&token::Comma) {
+ done = true;
}
- }
- break;
- }
+ // We just ate the comma, so no need to use `TrailingToken`
+ Ok((param, TrailingToken::None))
+ })?;
- if !self.eat(&token::Comma) {
+ if let Some(param) = param {
+ params.push(param);
+ } else {
break;
}
}