Parse generic const items
This commit is contained in:
parent
b6dd153fbc
commit
afd009a8d8
12 changed files with 220 additions and 34 deletions
|
@ -2947,6 +2947,7 @@ pub struct StaticItem {
|
||||||
#[derive(Clone, Encodable, Decodable, Debug)]
|
#[derive(Clone, Encodable, Decodable, Debug)]
|
||||||
pub struct ConstItem {
|
pub struct ConstItem {
|
||||||
pub defaultness: Defaultness,
|
pub defaultness: Defaultness,
|
||||||
|
pub generics: Generics,
|
||||||
pub ty: P<Ty>,
|
pub ty: P<Ty>,
|
||||||
pub expr: Option<P<Expr>>,
|
pub expr: Option<P<Expr>>,
|
||||||
}
|
}
|
||||||
|
@ -3058,6 +3059,7 @@ impl ItemKind {
|
||||||
match self {
|
match self {
|
||||||
Self::Fn(box Fn { generics, .. })
|
Self::Fn(box Fn { generics, .. })
|
||||||
| Self::TyAlias(box TyAlias { generics, .. })
|
| Self::TyAlias(box TyAlias { generics, .. })
|
||||||
|
| Self::Const(box ConstItem { generics, .. })
|
||||||
| Self::Enum(_, generics)
|
| Self::Enum(_, generics)
|
||||||
| Self::Struct(_, generics)
|
| Self::Struct(_, generics)
|
||||||
| Self::Union(_, generics)
|
| Self::Union(_, generics)
|
||||||
|
|
|
@ -1149,10 +1149,11 @@ pub fn noop_flat_map_assoc_item<T: MutVisitor>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_const_item<T: MutVisitor>(
|
fn visit_const_item<T: MutVisitor>(
|
||||||
ConstItem { defaultness, ty, expr }: &mut ConstItem,
|
ConstItem { defaultness, generics, ty, expr }: &mut ConstItem,
|
||||||
visitor: &mut T,
|
visitor: &mut T,
|
||||||
) {
|
) {
|
||||||
visit_defaultness(defaultness, visitor);
|
visit_defaultness(defaultness, visitor);
|
||||||
|
visitor.visit_generics(generics);
|
||||||
visitor.visit_ty(ty);
|
visitor.visit_ty(ty);
|
||||||
visit_opt(expr, |expr| visitor.visit_expr(expr));
|
visit_opt(expr, |expr| visitor.visit_expr(expr));
|
||||||
}
|
}
|
||||||
|
|
|
@ -308,8 +308,12 @@ pub fn walk_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a Item) {
|
||||||
match &item.kind {
|
match &item.kind {
|
||||||
ItemKind::ExternCrate(_) => {}
|
ItemKind::ExternCrate(_) => {}
|
||||||
ItemKind::Use(use_tree) => visitor.visit_use_tree(use_tree, item.id, false),
|
ItemKind::Use(use_tree) => visitor.visit_use_tree(use_tree, item.id, false),
|
||||||
ItemKind::Static(box StaticItem { ty, mutability: _, expr })
|
ItemKind::Static(box StaticItem { ty, mutability: _, expr }) => {
|
||||||
| ItemKind::Const(box ConstItem { ty, expr, .. }) => {
|
visitor.visit_ty(ty);
|
||||||
|
walk_list!(visitor, visit_expr, expr);
|
||||||
|
}
|
||||||
|
ItemKind::Const(box ConstItem { defaultness: _, generics, ty, expr }) => {
|
||||||
|
visitor.visit_generics(generics);
|
||||||
visitor.visit_ty(ty);
|
visitor.visit_ty(ty);
|
||||||
walk_list!(visitor, visit_expr, expr);
|
walk_list!(visitor, visit_expr, expr);
|
||||||
}
|
}
|
||||||
|
@ -677,7 +681,8 @@ pub fn walk_assoc_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a AssocItem,
|
||||||
visitor.visit_ident(ident);
|
visitor.visit_ident(ident);
|
||||||
walk_list!(visitor, visit_attribute, attrs);
|
walk_list!(visitor, visit_attribute, attrs);
|
||||||
match kind {
|
match kind {
|
||||||
AssocItemKind::Const(box ConstItem { ty, expr, .. }) => {
|
AssocItemKind::Const(box ConstItem { defaultness: _, generics, ty, expr }) => {
|
||||||
|
visitor.visit_generics(generics);
|
||||||
visitor.visit_ty(ty);
|
visitor.visit_ty(ty);
|
||||||
walk_list!(visitor, visit_expr, expr);
|
walk_list!(visitor, visit_expr, expr);
|
||||||
}
|
}
|
||||||
|
|
|
@ -569,6 +569,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) {
|
||||||
gate_all!(const_closures, "const closures are experimental");
|
gate_all!(const_closures, "const closures are experimental");
|
||||||
gate_all!(builtin_syntax, "`builtin #` syntax is unstable");
|
gate_all!(builtin_syntax, "`builtin #` syntax is unstable");
|
||||||
gate_all!(explicit_tail_calls, "`become` expression is experimental");
|
gate_all!(explicit_tail_calls, "`become` expression is experimental");
|
||||||
|
gate_all!(generic_const_items, "generic const items are experimental");
|
||||||
|
|
||||||
if !visitor.features.negative_bounds {
|
if !visitor.features.negative_bounds {
|
||||||
for &span in spans.get(&sym::negative_bounds).iter().copied().flatten() {
|
for &span in spans.get(&sym::negative_bounds).iter().copied().flatten() {
|
||||||
|
|
|
@ -30,10 +30,15 @@ impl<'a> State<'a> {
|
||||||
ast::ForeignItemKind::Fn(box ast::Fn { defaultness, sig, generics, body }) => {
|
ast::ForeignItemKind::Fn(box ast::Fn { defaultness, sig, generics, body }) => {
|
||||||
self.print_fn_full(sig, ident, generics, vis, *defaultness, body.as_deref(), attrs);
|
self.print_fn_full(sig, ident, generics, vis, *defaultness, body.as_deref(), attrs);
|
||||||
}
|
}
|
||||||
ast::ForeignItemKind::Static(ty, mutbl, body) => {
|
ast::ForeignItemKind::Static(ty, mutbl, body) => self.print_item_const(
|
||||||
let def = ast::Defaultness::Final;
|
ident,
|
||||||
self.print_item_const(ident, Some(*mutbl), ty, body.as_deref(), vis, def);
|
Some(*mutbl),
|
||||||
}
|
&ast::Generics::default(),
|
||||||
|
ty,
|
||||||
|
body.as_deref(),
|
||||||
|
vis,
|
||||||
|
ast::Defaultness::Final,
|
||||||
|
),
|
||||||
ast::ForeignItemKind::TyAlias(box ast::TyAlias {
|
ast::ForeignItemKind::TyAlias(box ast::TyAlias {
|
||||||
defaultness,
|
defaultness,
|
||||||
generics,
|
generics,
|
||||||
|
@ -67,6 +72,7 @@ impl<'a> State<'a> {
|
||||||
&mut self,
|
&mut self,
|
||||||
ident: Ident,
|
ident: Ident,
|
||||||
mutbl: Option<ast::Mutability>,
|
mutbl: Option<ast::Mutability>,
|
||||||
|
generics: &ast::Generics,
|
||||||
ty: &ast::Ty,
|
ty: &ast::Ty,
|
||||||
body: Option<&ast::Expr>,
|
body: Option<&ast::Expr>,
|
||||||
vis: &ast::Visibility,
|
vis: &ast::Visibility,
|
||||||
|
@ -82,6 +88,7 @@ impl<'a> State<'a> {
|
||||||
};
|
};
|
||||||
self.word_space(leading);
|
self.word_space(leading);
|
||||||
self.print_ident(ident);
|
self.print_ident(ident);
|
||||||
|
self.print_generic_params(&generics.params);
|
||||||
self.word_space(":");
|
self.word_space(":");
|
||||||
self.print_type(ty);
|
self.print_type(ty);
|
||||||
if body.is_some() {
|
if body.is_some() {
|
||||||
|
@ -92,6 +99,7 @@ impl<'a> State<'a> {
|
||||||
self.word_space("=");
|
self.word_space("=");
|
||||||
self.print_expr(body);
|
self.print_expr(body);
|
||||||
}
|
}
|
||||||
|
self.print_where_clause(&generics.where_clause);
|
||||||
self.word(";");
|
self.word(";");
|
||||||
self.end(); // end the outer cbox
|
self.end(); // end the outer cbox
|
||||||
}
|
}
|
||||||
|
@ -158,20 +166,21 @@ impl<'a> State<'a> {
|
||||||
self.word(";");
|
self.word(";");
|
||||||
}
|
}
|
||||||
ast::ItemKind::Static(box StaticItem { ty, mutability: mutbl, expr: body }) => {
|
ast::ItemKind::Static(box StaticItem { ty, mutability: mutbl, expr: body }) => {
|
||||||
let def = ast::Defaultness::Final;
|
|
||||||
self.print_item_const(
|
self.print_item_const(
|
||||||
item.ident,
|
item.ident,
|
||||||
Some(*mutbl),
|
Some(*mutbl),
|
||||||
|
&ast::Generics::default(),
|
||||||
ty,
|
ty,
|
||||||
body.as_deref(),
|
body.as_deref(),
|
||||||
&item.vis,
|
&item.vis,
|
||||||
def,
|
ast::Defaultness::Final,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
ast::ItemKind::Const(box ast::ConstItem { defaultness, ty, expr }) => {
|
ast::ItemKind::Const(box ast::ConstItem { defaultness, generics, ty, expr }) => {
|
||||||
self.print_item_const(
|
self.print_item_const(
|
||||||
item.ident,
|
item.ident,
|
||||||
None,
|
None,
|
||||||
|
generics,
|
||||||
ty,
|
ty,
|
||||||
expr.as_deref(),
|
expr.as_deref(),
|
||||||
&item.vis,
|
&item.vis,
|
||||||
|
@ -515,8 +524,16 @@ impl<'a> State<'a> {
|
||||||
ast::AssocItemKind::Fn(box ast::Fn { defaultness, sig, generics, body }) => {
|
ast::AssocItemKind::Fn(box ast::Fn { defaultness, sig, generics, body }) => {
|
||||||
self.print_fn_full(sig, ident, generics, vis, *defaultness, body.as_deref(), attrs);
|
self.print_fn_full(sig, ident, generics, vis, *defaultness, body.as_deref(), attrs);
|
||||||
}
|
}
|
||||||
ast::AssocItemKind::Const(box ast::ConstItem { defaultness, ty, expr }) => {
|
ast::AssocItemKind::Const(box ast::ConstItem { defaultness, generics, ty, expr }) => {
|
||||||
self.print_item_const(ident, None, ty, expr.as_deref(), vis, *defaultness);
|
self.print_item_const(
|
||||||
|
ident,
|
||||||
|
None,
|
||||||
|
generics,
|
||||||
|
ty,
|
||||||
|
expr.as_deref(),
|
||||||
|
vis,
|
||||||
|
*defaultness,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
ast::AssocItemKind::Type(box ast::TyAlias {
|
ast::AssocItemKind::Type(box ast::TyAlias {
|
||||||
defaultness,
|
defaultness,
|
||||||
|
|
|
@ -255,6 +255,7 @@ pub fn expand_test_or_bench(
|
||||||
ast::ItemKind::Const(
|
ast::ItemKind::Const(
|
||||||
ast::ConstItem {
|
ast::ConstItem {
|
||||||
defaultness: ast::Defaultness::Final,
|
defaultness: ast::Defaultness::Final,
|
||||||
|
generics: ast::Generics::default(),
|
||||||
ty: cx.ty(sp, ast::TyKind::Path(None, test_path("TestDescAndFn"))),
|
ty: cx.ty(sp, ast::TyKind::Path(None, test_path("TestDescAndFn"))),
|
||||||
// test::TestDescAndFn {
|
// test::TestDescAndFn {
|
||||||
expr: Some(
|
expr: Some(
|
||||||
|
|
|
@ -643,7 +643,16 @@ impl<'a> ExtCtxt<'a> {
|
||||||
span,
|
span,
|
||||||
name,
|
name,
|
||||||
AttrVec::new(),
|
AttrVec::new(),
|
||||||
ast::ItemKind::Const(ast::ConstItem { defaultness, ty, expr: Some(expr) }.into()),
|
ast::ItemKind::Const(
|
||||||
|
ast::ConstItem {
|
||||||
|
defaultness,
|
||||||
|
// FIXME(generic_const_items): Pass the generics as a parameter.
|
||||||
|
generics: ast::Generics::default(),
|
||||||
|
ty,
|
||||||
|
expr: Some(expr),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -424,6 +424,8 @@ declare_features! (
|
||||||
(incomplete, generic_associated_types_extended, "1.61.0", Some(95451), None),
|
(incomplete, generic_associated_types_extended, "1.61.0", Some(95451), None),
|
||||||
/// Allows non-trivial generic constants which have to have wfness manually propagated to callers
|
/// Allows non-trivial generic constants which have to have wfness manually propagated to callers
|
||||||
(incomplete, generic_const_exprs, "1.56.0", Some(76560), None),
|
(incomplete, generic_const_exprs, "1.56.0", Some(76560), None),
|
||||||
|
/// Allows generic parameters and where-clauses on free & associated const items.
|
||||||
|
(incomplete, generic_const_items, "CURRENT_RUSTC_VERSION", Some(113521), None),
|
||||||
/// Allows using `..=X` as a patterns in slices.
|
/// Allows using `..=X` as a patterns in slices.
|
||||||
(active, half_open_range_patterns_in_slices, "1.66.0", Some(67264), None),
|
(active, half_open_range_patterns_in_slices, "1.66.0", Some(67264), None),
|
||||||
/// Allows `if let` guard in match arms.
|
/// Allows `if let` guard in match arms.
|
||||||
|
|
|
@ -690,6 +690,8 @@ parse_single_colon_import_path = expected `::`, found `:`
|
||||||
parse_single_colon_struct_type = found single colon in a struct field type path
|
parse_single_colon_struct_type = found single colon in a struct field type path
|
||||||
.suggestion = write a path separator here
|
.suggestion = write a path separator here
|
||||||
|
|
||||||
|
parse_static_with_generics = static items may not have generic parameters
|
||||||
|
|
||||||
parse_struct_literal_body_without_path =
|
parse_struct_literal_body_without_path =
|
||||||
struct literal body without path
|
struct literal body without path
|
||||||
.suggestion = you might have forgotten to add the struct literal inside the block
|
.suggestion = you might have forgotten to add the struct literal inside the block
|
||||||
|
@ -847,6 +849,12 @@ parse_visibility_not_followed_by_item = visibility `{$vis}` is not followed by a
|
||||||
.label = the visibility
|
.label = the visibility
|
||||||
.help = you likely meant to define an item, e.g., `{$vis} fn foo() {"{}"}`
|
.help = you likely meant to define an item, e.g., `{$vis} fn foo() {"{}"}`
|
||||||
|
|
||||||
|
parse_where_clause_before_const_body = where clauses are not allowed before const item bodies
|
||||||
|
.label = unexpected where clause
|
||||||
|
.name_label = while parsing this const item
|
||||||
|
.body_label = the item body
|
||||||
|
.suggestion = move the body before the where clause
|
||||||
|
|
||||||
parse_where_clause_before_tuple_struct_body = where clauses are not allowed before tuple struct bodies
|
parse_where_clause_before_tuple_struct_body = where clauses are not allowed before tuple struct bodies
|
||||||
.label = unexpected where clause
|
.label = unexpected where clause
|
||||||
.name_label = while parsing this tuple struct
|
.name_label = while parsing this tuple struct
|
||||||
|
|
|
@ -2692,3 +2692,34 @@ pub(crate) struct ExpectedBuiltinIdent {
|
||||||
#[primary_span]
|
#[primary_span]
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Diagnostic)]
|
||||||
|
#[diag(parse_static_with_generics)]
|
||||||
|
pub(crate) struct StaticWithGenerics {
|
||||||
|
#[primary_span]
|
||||||
|
pub span: Span,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Diagnostic)]
|
||||||
|
#[diag(parse_where_clause_before_const_body)]
|
||||||
|
pub(crate) struct WhereClauseBeforeConstBody {
|
||||||
|
#[primary_span]
|
||||||
|
#[label]
|
||||||
|
pub span: Span,
|
||||||
|
#[label(parse_name_label)]
|
||||||
|
pub name: Span,
|
||||||
|
#[label(parse_body_label)]
|
||||||
|
pub body: Span,
|
||||||
|
#[subdiagnostic]
|
||||||
|
pub sugg: Option<WhereClauseBeforeConstBodySugg>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Subdiagnostic)]
|
||||||
|
#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")]
|
||||||
|
pub(crate) struct WhereClauseBeforeConstBodySugg {
|
||||||
|
#[suggestion_part(code = "= {snippet} ")]
|
||||||
|
pub left: Span,
|
||||||
|
pub snippet: String,
|
||||||
|
#[suggestion_part(code = "")]
|
||||||
|
pub right: Span,
|
||||||
|
}
|
||||||
|
|
|
@ -226,9 +226,9 @@ impl<'a> Parser<'a> {
|
||||||
} else if self.is_static_global() {
|
} else if self.is_static_global() {
|
||||||
// STATIC ITEM
|
// STATIC ITEM
|
||||||
self.bump(); // `static`
|
self.bump(); // `static`
|
||||||
let m = self.parse_mutability();
|
let mutability = self.parse_mutability();
|
||||||
let (ident, ty, expr) = self.parse_item_global(Some(m))?;
|
let (ident, item) = self.parse_static_item(mutability)?;
|
||||||
(ident, ItemKind::Static(Box::new(StaticItem { ty, mutability: m, expr })))
|
(ident, ItemKind::Static(Box::new(item)))
|
||||||
} else if let Const::Yes(const_span) = self.parse_constness(Case::Sensitive) {
|
} else if let Const::Yes(const_span) = self.parse_constness(Case::Sensitive) {
|
||||||
// CONST ITEM
|
// CONST ITEM
|
||||||
if self.token.is_keyword(kw::Impl) {
|
if self.token.is_keyword(kw::Impl) {
|
||||||
|
@ -236,8 +236,16 @@ impl<'a> Parser<'a> {
|
||||||
self.recover_const_impl(const_span, attrs, def_())?
|
self.recover_const_impl(const_span, attrs, def_())?
|
||||||
} else {
|
} else {
|
||||||
self.recover_const_mut(const_span);
|
self.recover_const_mut(const_span);
|
||||||
let (ident, ty, expr) = self.parse_item_global(None)?;
|
let (ident, generics, ty, expr) = self.parse_const_item()?;
|
||||||
(ident, ItemKind::Const(Box::new(ConstItem { defaultness: def_(), ty, expr })))
|
(
|
||||||
|
ident,
|
||||||
|
ItemKind::Const(Box::new(ConstItem {
|
||||||
|
defaultness: def_(),
|
||||||
|
generics,
|
||||||
|
ty,
|
||||||
|
expr,
|
||||||
|
})),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
} else if self.check_keyword(kw::Trait) || self.check_auto_or_unsafe_trait_item() {
|
} else if self.check_keyword(kw::Trait) || self.check_auto_or_unsafe_trait_item() {
|
||||||
// TRAIT ITEM
|
// TRAIT ITEM
|
||||||
|
@ -878,6 +886,7 @@ impl<'a> Parser<'a> {
|
||||||
self.sess.emit_err(errors::AssociatedStaticItemNotAllowed { span });
|
self.sess.emit_err(errors::AssociatedStaticItemNotAllowed { span });
|
||||||
AssocItemKind::Const(Box::new(ConstItem {
|
AssocItemKind::Const(Box::new(ConstItem {
|
||||||
defaultness: Defaultness::Final,
|
defaultness: Defaultness::Final,
|
||||||
|
generics: Generics::default(),
|
||||||
ty,
|
ty,
|
||||||
expr,
|
expr,
|
||||||
}))
|
}))
|
||||||
|
@ -892,7 +901,7 @@ impl<'a> Parser<'a> {
|
||||||
|
|
||||||
/// Parses a `type` alias with the following grammar:
|
/// Parses a `type` alias with the following grammar:
|
||||||
/// ```ebnf
|
/// ```ebnf
|
||||||
/// TypeAlias = "type" Ident Generics {":" GenericBounds}? {"=" Ty}? ";" ;
|
/// TypeAlias = "type" Ident Generics (":" GenericBounds)? WhereClause ("=" Ty)? WhereClause ";" ;
|
||||||
/// ```
|
/// ```
|
||||||
/// The `"type"` has already been eaten.
|
/// The `"type"` has already been eaten.
|
||||||
fn parse_type_alias(&mut self, defaultness: Defaultness) -> PResult<'a, ItemInfo> {
|
fn parse_type_alias(&mut self, defaultness: Defaultness) -> PResult<'a, ItemInfo> {
|
||||||
|
@ -1220,33 +1229,132 @@ impl<'a> Parser<'a> {
|
||||||
Ok(impl_info)
|
Ok(impl_info)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse `["const" | ("static" "mut"?)] $ident ":" $ty (= $expr)?` with
|
/// Parse a static item with the prefix `"static" "mut"?` already parsed and stored in `mutability`.
|
||||||
/// `["const" | ("static" "mut"?)]` already parsed and stored in `m`.
|
|
||||||
///
|
///
|
||||||
/// When `m` is `"const"`, `$ident` may also be `"_"`.
|
/// ```ebnf
|
||||||
fn parse_item_global(
|
/// Static = "static" "mut"? $ident ":" $ty (= $expr)? ";" ;
|
||||||
&mut self,
|
/// ```
|
||||||
m: Option<Mutability>,
|
fn parse_static_item(&mut self, mutability: Mutability) -> PResult<'a, (Ident, StaticItem)> {
|
||||||
) -> PResult<'a, (Ident, P<Ty>, Option<P<ast::Expr>>)> {
|
let ident = self.parse_ident()?;
|
||||||
let id = if m.is_none() { self.parse_ident_or_underscore() } else { self.parse_ident() }?;
|
|
||||||
|
|
||||||
// Parse the type of a `const` or `static mut?` item.
|
if self.token.kind == TokenKind::Lt && self.may_recover() {
|
||||||
// That is, the `":" $ty` fragment.
|
let generics = self.parse_generics()?;
|
||||||
|
self.sess.emit_err(errors::StaticWithGenerics { span: generics.span });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the type of a static item. That is, the `":" $ty` fragment.
|
||||||
|
// FIXME: This could maybe benefit from `.may_recover()`?
|
||||||
let ty = match (self.eat(&token::Colon), self.check(&token::Eq) | self.check(&token::Semi))
|
let ty = match (self.eat(&token::Colon), self.check(&token::Eq) | self.check(&token::Semi))
|
||||||
{
|
{
|
||||||
// If there wasn't a `:` or the colon was followed by a `=` or `;` recover a missing type.
|
|
||||||
(true, false) => self.parse_ty()?,
|
(true, false) => self.parse_ty()?,
|
||||||
(colon, _) => self.recover_missing_const_type(colon, m),
|
// If there wasn't a `:` or the colon was followed by a `=` or `;`, recover a missing type.
|
||||||
|
(colon, _) => self.recover_missing_global_item_type(colon, Some(mutability)),
|
||||||
};
|
};
|
||||||
|
|
||||||
let expr = if self.eat(&token::Eq) { Some(self.parse_expr()?) } else { None };
|
let expr = if self.eat(&token::Eq) { Some(self.parse_expr()?) } else { None };
|
||||||
|
|
||||||
self.expect_semi()?;
|
self.expect_semi()?;
|
||||||
Ok((id, ty, expr))
|
|
||||||
|
Ok((ident, StaticItem { ty, mutability, expr }))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parse a constant item with the prefix `"const"` already parsed.
|
||||||
|
///
|
||||||
|
/// ```ebnf
|
||||||
|
/// Const = "const" ($ident | "_") Generics ":" $ty (= $expr)? WhereClause ";" ;
|
||||||
|
/// ```
|
||||||
|
fn parse_const_item(&mut self) -> PResult<'a, (Ident, Generics, P<Ty>, Option<P<ast::Expr>>)> {
|
||||||
|
let ident = self.parse_ident_or_underscore()?;
|
||||||
|
|
||||||
|
let mut generics = self.parse_generics()?;
|
||||||
|
|
||||||
|
// Check the span for emptiness instead of the list of parameters in order to correctly
|
||||||
|
// recognize and subsequently flag empty parameter lists (`<>`) as unstable.
|
||||||
|
if !generics.span.is_empty() {
|
||||||
|
self.sess.gated_spans.gate(sym::generic_const_items, generics.span);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the type of a constant item. That is, the `":" $ty` fragment.
|
||||||
|
// FIXME: This could maybe benefit from `.may_recover()`?
|
||||||
|
let ty = match (
|
||||||
|
self.eat(&token::Colon),
|
||||||
|
self.check(&token::Eq) | self.check(&token::Semi) | self.check_keyword(kw::Where),
|
||||||
|
) {
|
||||||
|
(true, false) => self.parse_ty()?,
|
||||||
|
// If there wasn't a `:` or the colon was followed by a `=`, `;` or `where`, recover a missing type.
|
||||||
|
(colon, _) => self.recover_missing_global_item_type(colon, None),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Proactively parse a where-clause to be able to provide a good error message in case we
|
||||||
|
// encounter the item body following it.
|
||||||
|
let before_where_clause =
|
||||||
|
if self.may_recover() { self.parse_where_clause()? } else { WhereClause::default() };
|
||||||
|
|
||||||
|
let expr = if self.eat(&token::Eq) { Some(self.parse_expr()?) } else { None };
|
||||||
|
|
||||||
|
let after_where_clause = self.parse_where_clause()?;
|
||||||
|
|
||||||
|
// Provide a nice error message if the user placed a where-clause before the item body.
|
||||||
|
// Users may be tempted to write such code if they are still used to the deprecated
|
||||||
|
// where-clause location on type aliases and associated types. See also #89122.
|
||||||
|
if before_where_clause.has_where_token && let Some(expr) = &expr {
|
||||||
|
self.sess.emit_err(errors::WhereClauseBeforeConstBody {
|
||||||
|
span: before_where_clause.span,
|
||||||
|
name: ident.span,
|
||||||
|
body: expr.span,
|
||||||
|
sugg: if !after_where_clause.has_where_token {
|
||||||
|
self.sess.source_map().span_to_snippet(expr.span).ok().map(|body| {
|
||||||
|
errors::WhereClauseBeforeConstBodySugg {
|
||||||
|
left: before_where_clause.span.shrink_to_lo(),
|
||||||
|
snippet: body,
|
||||||
|
right: before_where_clause.span.shrink_to_hi().to(expr.span),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// FIXME(generic_const_items): Provide a structured suggestion to merge the first
|
||||||
|
// where-clause into the second one.
|
||||||
|
None
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge the predicates of both where-clauses since either one can be relevant.
|
||||||
|
// If we didn't parse a body (which is valid for associated consts in traits) and we were
|
||||||
|
// allowed to recover, `before_where_clause` contains the predicates, otherwise they are
|
||||||
|
// in `after_where_clause`. Further, both of them might contain predicates iff two
|
||||||
|
// where-clauses were provided which is syntactically ill-formed but we want to recover from
|
||||||
|
// it and treat them as one large where-clause.
|
||||||
|
let mut predicates = before_where_clause.predicates;
|
||||||
|
predicates.extend(after_where_clause.predicates);
|
||||||
|
let where_clause = WhereClause {
|
||||||
|
has_where_token: before_where_clause.has_where_token
|
||||||
|
|| after_where_clause.has_where_token,
|
||||||
|
predicates,
|
||||||
|
span: if after_where_clause.has_where_token {
|
||||||
|
after_where_clause.span
|
||||||
|
} else {
|
||||||
|
before_where_clause.span
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if where_clause.has_where_token {
|
||||||
|
self.sess.gated_spans.gate(sym::generic_const_items, where_clause.span);
|
||||||
|
}
|
||||||
|
|
||||||
|
generics.where_clause = where_clause;
|
||||||
|
|
||||||
|
self.expect_semi()?;
|
||||||
|
|
||||||
|
Ok((ident, generics, ty, expr))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// We were supposed to parse `":" $ty` but the `:` or the type was missing.
|
/// We were supposed to parse `":" $ty` but the `:` or the type was missing.
|
||||||
/// This means that the type is missing.
|
/// This means that the type is missing.
|
||||||
fn recover_missing_const_type(&mut self, colon_present: bool, m: Option<Mutability>) -> P<Ty> {
|
fn recover_missing_global_item_type(
|
||||||
|
&mut self,
|
||||||
|
colon_present: bool,
|
||||||
|
m: Option<Mutability>,
|
||||||
|
) -> P<Ty> {
|
||||||
// Construct the error and stash it away with the hope
|
// Construct the error and stash it away with the hope
|
||||||
// that typeck will later enrich the error with a type.
|
// that typeck will later enrich the error with a type.
|
||||||
let kind = match m {
|
let kind = match m {
|
||||||
|
|
|
@ -783,6 +783,7 @@ symbols! {
|
||||||
generic_associated_types,
|
generic_associated_types,
|
||||||
generic_associated_types_extended,
|
generic_associated_types_extended,
|
||||||
generic_const_exprs,
|
generic_const_exprs,
|
||||||
|
generic_const_items,
|
||||||
generic_param_attrs,
|
generic_param_attrs,
|
||||||
get_context,
|
get_context,
|
||||||
global_allocator,
|
global_allocator,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue