1
Fork 0

Overhaul token collection.

This commit does the following.

- Renames `collect_tokens_trailing_token` as `collect_tokens`, because
  (a) it's annoying long, and (b) the `_trailing_token` bit is less
  accurate now that its types have changed.

- In `collect_tokens`, adds a `Option<CollectPos>` argument and a
  `UsePreAttrPos` in the return type of `f`. These are used in
  `parse_expr_force_collect` (for vanilla expressions) and in
  `parse_stmt_without_recovery` (for two different cases of expression
  statements). Together these ensure are enough to fix all the problems
  with token collection and assoc expressions. The changes to the
  `stringify.rs` test demonstrate some of these.

- Adds a new test. The code in this test was causing an assertion
  failure prior to this commit, due to an invalid `NodeRange`.

The extra complexity is annoying, but necessary to fix the existing
problems.
This commit is contained in:
Nicholas Nethercote 2024-08-06 17:16:40 +10:00
parent fe460ac28b
commit 9d31f86f0d
12 changed files with 414 additions and 292 deletions

View file

@ -7,7 +7,7 @@ use rustc_span::symbol::{kw, Ident};
use rustc_span::Span;
use thin_vec::ThinVec;
use super::{ForceCollect, Parser, Trailing};
use super::{ForceCollect, Parser, Trailing, UsePreAttrPos};
use crate::errors::{
self, MultipleWhereClauses, UnexpectedDefaultValueForLifetimeInGenericParameters,
UnexpectedSelfInGenericParameters, WhereClauseBeforeTupleStructBody,
@ -169,94 +169,88 @@ impl<'a> Parser<'a> {
let mut done = false;
while !done {
let attrs = self.parse_outer_attributes()?;
let param =
self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| {
if this.eat_keyword_noexpect(kw::SelfUpper) {
// `Self` as a generic param is invalid. Here we emit the diagnostic and continue parsing
// as if `Self` never existed.
this.dcx().emit_err(UnexpectedSelfInGenericParameters {
span: this.prev_token.span,
});
let param = self.collect_tokens(None, attrs, ForceCollect::No, |this, attrs| {
if this.eat_keyword_noexpect(kw::SelfUpper) {
// `Self` as a generic param is invalid. Here we emit the diagnostic and continue parsing
// as if `Self` never existed.
this.dcx()
.emit_err(UnexpectedSelfInGenericParameters { span: this.prev_token.span });
// Eat a trailing comma, if it exists.
let _ = this.eat(&token::Comma);
}
// Eat a trailing comma, if it exists.
let _ = this.eat(&token::Comma);
}
let param = if this.check_lifetime() {
let lifetime = this.expect_lifetime();
// Parse lifetime parameter.
let (colon_span, bounds) = if this.eat(&token::Colon) {
(Some(this.prev_token.span), this.parse_lt_param_bounds())
} else {
(None, Vec::new())
};
if this.check_noexpect(&token::Eq)
&& this.look_ahead(1, |t| t.is_lifetime())
{
let lo = this.token.span;
// Parse `= 'lifetime`.
this.bump(); // `=`
this.bump(); // `'lifetime`
let span = lo.to(this.prev_token.span);
this.dcx().emit_err(
UnexpectedDefaultValueForLifetimeInGenericParameters { span },
);
}
Some(ast::GenericParam {
ident: lifetime.ident,
id: lifetime.id,
attrs,
bounds,
kind: ast::GenericParamKind::Lifetime,
is_placeholder: false,
colon_span,
})
} 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.create_snapshot_for_diagnostic();
match this.parse_ty_where_predicate() {
Ok(where_predicate) => {
this.dcx().emit_err(errors::BadAssocTypeBounds {
span: where_predicate.span(),
});
// FIXME - try to continue parsing other generics?
return Ok((None, Trailing::No));
}
Err(err) => {
err.cancel();
// FIXME - maybe we should overwrite 'self' outside of `collect_tokens`?
this.restore_snapshot(snapshot);
return Ok((None, Trailing::No));
}
}
let param = if this.check_lifetime() {
let lifetime = this.expect_lifetime();
// Parse lifetime parameter.
let (colon_span, bounds) = if this.eat(&token::Colon) {
(Some(this.prev_token.span), this.parse_lt_param_bounds())
} else {
// Check for trailing attributes and stop parsing.
if !attrs.is_empty() {
if !params.is_empty() {
this.dcx()
.emit_err(errors::AttrAfterGeneric { span: attrs[0].span });
} else {
this.dcx()
.emit_err(errors::AttrWithoutGenerics { span: attrs[0].span });
}
}
return Ok((None, Trailing::No));
(None, Vec::new())
};
if !this.eat(&token::Comma) {
done = true;
if this.check_noexpect(&token::Eq) && this.look_ahead(1, |t| t.is_lifetime()) {
let lo = this.token.span;
// Parse `= 'lifetime`.
this.bump(); // `=`
this.bump(); // `'lifetime`
let span = lo.to(this.prev_token.span);
this.dcx().emit_err(UnexpectedDefaultValueForLifetimeInGenericParameters {
span,
});
}
// We just ate the comma, so no need to capture the trailing token.
Ok((param, Trailing::No))
})?;
Some(ast::GenericParam {
ident: lifetime.ident,
id: lifetime.id,
attrs,
bounds,
kind: ast::GenericParamKind::Lifetime,
is_placeholder: false,
colon_span,
})
} 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.create_snapshot_for_diagnostic();
match this.parse_ty_where_predicate() {
Ok(where_predicate) => {
this.dcx().emit_err(errors::BadAssocTypeBounds {
span: where_predicate.span(),
});
// FIXME - try to continue parsing other generics?
}
Err(err) => {
err.cancel();
// FIXME - maybe we should overwrite 'self' outside of `collect_tokens`?
this.restore_snapshot(snapshot);
}
}
return Ok((None, Trailing::No, UsePreAttrPos::No));
} else {
// Check for trailing attributes and stop parsing.
if !attrs.is_empty() {
if !params.is_empty() {
this.dcx().emit_err(errors::AttrAfterGeneric { span: attrs[0].span });
} else {
this.dcx()
.emit_err(errors::AttrWithoutGenerics { span: attrs[0].span });
}
}
return Ok((None, Trailing::No, UsePreAttrPos::No));
};
if !this.eat(&token::Comma) {
done = true;
}
// We just ate the comma, so no need to capture the trailing token.
Ok((param, Trailing::No, UsePreAttrPos::No))
})?;
if let Some(param) = param {
params.push(param);