Audit AllowConstBlockItems

This commit is contained in:
Pavel Grigorenko 2025-11-22 00:24:04 +03:00
parent 61ac56209d
commit 5eb01938c9
17 changed files with 103 additions and 44 deletions

View file

@ -3898,6 +3898,7 @@ impl ConstItemRhs {
#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
pub struct ConstBlockItem {
// FIXME(const_block_items): current invariant is body.kind == InlineConst
pub body: Box<Expr>,
}

View file

@ -9,7 +9,7 @@ use rustc_expand::base::{Annotatable, ExtCtxt};
use rustc_expand::config::StripUnconfigured;
use rustc_expand::configure;
use rustc_feature::Features;
use rustc_parse::parser::{ConstBlockItemsAllowed, ForceCollect, Parser};
use rustc_parse::parser::{AllowConstBlockItems, ForceCollect, Parser};
use rustc_session::Session;
use rustc_span::{Span, sym};
use smallvec::SmallVec;
@ -111,7 +111,7 @@ impl CfgEval<'_> {
match annotatable {
Annotatable::Item(_) => {
let item =
parser.parse_item(ForceCollect::Yes, ConstBlockItemsAllowed::No)?.unwrap();
parser.parse_item(ForceCollect::Yes, AllowConstBlockItems::Yes)?.unwrap();
Annotatable::Item(self.flat_map_item(item).pop().unwrap())
}
Annotatable::AssocItem(_, ctxt) => {

View file

@ -13,7 +13,7 @@ use rustc_expand::base::{
};
use rustc_expand::module::DirOwnership;
use rustc_parse::lexer::StripTokens;
use rustc_parse::parser::{ConstBlockItemsAllowed, ForceCollect};
use rustc_parse::parser::{AllowConstBlockItems, ForceCollect};
use rustc_parse::{new_parser_from_file, unwrap_or_emit_fatal, utf8_error};
use rustc_session::lint::builtin::INCOMPLETE_INCLUDE;
use rustc_session::parse::ParseSess;
@ -168,7 +168,7 @@ pub(crate) fn expand_include<'cx>(
));
let mut ret = SmallVec::new();
loop {
match p.parse_item(ForceCollect::No, ConstBlockItemsAllowed::No) {
match p.parse_item(ForceCollect::No, AllowConstBlockItems::Yes) {
Err(err) => {
err.emit();
break;

View file

@ -22,7 +22,7 @@ use rustc_hir::limit::Limit;
use rustc_hir::{Stability, find_attr};
use rustc_lint_defs::RegisteredTools;
use rustc_parse::MACRO_ARGUMENTS;
use rustc_parse::parser::{ConstBlockItemsAllowed, ForceCollect, Parser};
use rustc_parse::parser::{AllowConstBlockItems, ForceCollect, Parser};
use rustc_session::Session;
use rustc_session::parse::ParseSess;
use rustc_span::def_id::{CrateNum, DefId, LocalDefId};
@ -1472,7 +1472,7 @@ pub(crate) fn stream_pretty_printing_compatibility_hack(
let mut parser = Parser::new(psess, stream.clone(), None);
// No need to collect tokens for this simple check.
parser
.parse_item(ForceCollect::No, ConstBlockItemsAllowed::No)
.parse_item(ForceCollect::No, AllowConstBlockItems::No)
.expect("failed to reparse item")
.expect("an actual item")
}

View file

@ -25,7 +25,7 @@ use rustc_hir::Target;
use rustc_hir::def::MacroKinds;
use rustc_hir::limit::Limit;
use rustc_parse::parser::{
AttemptLocalParseRecovery, CommaRecoveryMode, ConstBlockItemsAllowed, ForceCollect, Parser,
AllowConstBlockItems, AttemptLocalParseRecovery, CommaRecoveryMode, ForceCollect, Parser,
RecoverColon, RecoverComma, token_descr,
};
use rustc_session::Session;
@ -1099,7 +1099,7 @@ pub fn parse_ast_fragment<'a>(
Ok(match kind {
AstFragmentKind::Items => {
let mut items = SmallVec::new();
while let Some(item) = this.parse_item(ForceCollect::No, ConstBlockItemsAllowed::No)? {
while let Some(item) = this.parse_item(ForceCollect::No, AllowConstBlockItems::Yes)? {
items.push(item);
}
AstFragment::Items(items)

View file

@ -1,7 +1,7 @@
use rustc_ast::tokenstream::TokenStream;
use rustc_errors::ErrorGuaranteed;
use rustc_middle::ty::{self, TyCtxt};
use rustc_parse::parser::{ConstBlockItemsAllowed, ForceCollect, Parser};
use rustc_parse::parser::{AllowConstBlockItems, ForceCollect, Parser};
use rustc_session::Session;
use rustc_session::config::ProcMacroExecutionStrategy;
use rustc_span::profiling::SpannedEventArgRecorder;
@ -160,7 +160,10 @@ impl MultiItemModifier for DeriveProcMacro {
let mut items = vec![];
loop {
match parser.parse_item(ForceCollect::No, ConstBlockItemsAllowed::No) {
match parser.parse_item(
ForceCollect::No,
if is_stmt { AllowConstBlockItems::No } else { AllowConstBlockItems::Yes },
) {
Ok(None) => break,
Ok(Some(item)) => {
if is_stmt {

View file

@ -9,7 +9,7 @@ use thin_vec::ThinVec;
use tracing::debug;
use super::{
AttrWrapper, Capturing, ConstBlockItemsAllowed, FnParseMode, ForceCollect, Parser, PathStyle,
AllowConstBlockItems, AttrWrapper, Capturing, FnParseMode, ForceCollect, Parser, PathStyle,
Trailing, UsePreAttrPos,
};
use crate::parser::FnContext;
@ -204,7 +204,7 @@ impl<'a> Parser<'a> {
false,
FnParseMode { req_name: |_, _| true, context: FnContext::Free, req_body: true },
ForceCollect::No,
ConstBlockItemsAllowed::No,
AllowConstBlockItems::Yes,
) {
Ok(Some(item)) => {
// FIXME(#100717)

View file

@ -22,8 +22,8 @@ use tracing::debug;
use super::diagnostics::{ConsumeClosingDelim, dummy_arg};
use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign};
use super::{
AttrWrapper, ConstBlockItemsAllowed, ExpKeywordPair, ExpTokenPair, FollowedByType,
ForceCollect, Parser, PathStyle, Recovered, Trailing, UsePreAttrPos,
AllowConstBlockItems, AttrWrapper, ExpKeywordPair, ExpTokenPair, FollowedByType, ForceCollect,
Parser, PathStyle, Recovered, Trailing, UsePreAttrPos,
};
use crate::errors::{self, FnPointerCannotBeAsync, FnPointerCannotBeConst, MacroExpandsToAdtField};
use crate::{exp, fluent_generated as fluent};
@ -69,7 +69,7 @@ impl<'a> Parser<'a> {
// `parse_item` consumes the appropriate semicolons so any leftover is an error.
loop {
while self.maybe_consume_incorrect_semicolon(items.last().map(|x| &**x)) {} // Eat all bad semicolons
let Some(item) = self.parse_item(ForceCollect::No, ConstBlockItemsAllowed::Yes)? else {
let Some(item) = self.parse_item(ForceCollect::No, AllowConstBlockItems::Yes)? else {
break;
};
items.push(item);
@ -126,11 +126,11 @@ impl<'a> Parser<'a> {
pub fn parse_item(
&mut self,
force_collect: ForceCollect,
const_block_items_allowed: ConstBlockItemsAllowed,
allow_const_block_items: AllowConstBlockItems,
) -> PResult<'a, Option<Box<Item>>> {
let fn_parse_mode =
FnParseMode { req_name: |_, _| true, context: FnContext::Free, req_body: true };
self.parse_item_(fn_parse_mode, force_collect, const_block_items_allowed)
self.parse_item_(fn_parse_mode, force_collect, allow_const_block_items)
.map(|i| i.map(Box::new))
}
@ -138,7 +138,7 @@ impl<'a> Parser<'a> {
&mut self,
fn_parse_mode: FnParseMode,
force_collect: ForceCollect,
const_block_items_allowed: ConstBlockItemsAllowed,
const_block_items_allowed: AllowConstBlockItems,
) -> PResult<'a, Option<Item>> {
self.recover_vcs_conflict_marker();
let attrs = self.parse_outer_attributes()?;
@ -160,10 +160,10 @@ impl<'a> Parser<'a> {
attrs_allowed: bool,
fn_parse_mode: FnParseMode,
force_collect: ForceCollect,
const_block_items_allowed: ConstBlockItemsAllowed,
allow_const_block_items: AllowConstBlockItems,
) -> PResult<'a, Option<Item>> {
if let Some(item) = self.eat_metavar_seq(MetaVarKind::Item, |this| {
this.parse_item(ForceCollect::Yes, const_block_items_allowed)
this.parse_item(ForceCollect::Yes, allow_const_block_items)
}) {
let mut item = item.expect("an actual item");
attrs.prepend_to_nt_inner(&mut item.attrs);
@ -177,7 +177,7 @@ impl<'a> Parser<'a> {
let kind = this.parse_item_kind(
&mut attrs,
mac_allowed,
const_block_items_allowed,
allow_const_block_items,
lo,
&vis,
&mut def,
@ -224,7 +224,7 @@ impl<'a> Parser<'a> {
&mut self,
attrs: &mut AttrVec,
macros_allowed: bool,
const_block_items_allowed: ConstBlockItemsAllowed,
allow_const_block_items: AllowConstBlockItems,
lo: Span,
vis: &Visibility,
def: &mut Defaultness,
@ -273,12 +273,16 @@ impl<'a> Parser<'a> {
} else if self.check_impl_frontmatter(0) {
// IMPL ITEM
self.parse_item_impl(attrs, def_(), false)?
} else if let ConstBlockItemsAllowed::Yes = const_block_items_allowed
} else if let AllowConstBlockItems::Yes | AllowConstBlockItems::DoesNotMatter =
allow_const_block_items
&& self.token.is_keyword(kw::Const)
&& self.look_ahead(1, |t| *t == token::OpenBrace || t.is_metavar_block())
{
// CONST BLOCK ITEM
self.psess.gated_spans.gate(sym::const_block_items, self.token.span);
if let AllowConstBlockItems::DoesNotMatter = allow_const_block_items {
debug!("Parsing a const block item that does not matter: {:?}", self.token.span);
};
ItemKind::ConstBlock(ConstBlockItem { body: self.parse_expr()? })
} else if let Const::Yes(const_span) = self.parse_constness(case) {
// CONST ITEM
@ -339,7 +343,7 @@ impl<'a> Parser<'a> {
return self.parse_item_kind(
attrs,
macros_allowed,
const_block_items_allowed,
allow_const_block_items,
lo,
vis,
def,
@ -1092,8 +1096,13 @@ impl<'a> Parser<'a> {
fn_parse_mode: FnParseMode,
force_collect: ForceCollect,
) -> PResult<'a, Option<Option<Box<AssocItem>>>> {
Ok(self.parse_item_(fn_parse_mode, force_collect, ConstBlockItemsAllowed::No)?.map(
|Item { attrs, id, span, vis, kind, tokens }| {
Ok(self
.parse_item_(
fn_parse_mode,
force_collect,
AllowConstBlockItems::DoesNotMatter, // due to `AssocItemKind::try_from` below
)?
.map(|Item { attrs, id, span, vis, kind, tokens }| {
let kind = match AssocItemKind::try_from(kind) {
Ok(kind) => kind,
Err(kind) => match kind {
@ -1120,8 +1129,7 @@ impl<'a> Parser<'a> {
},
};
Some(Box::new(Item { attrs, id, span, vis, kind, tokens }))
},
))
}))
}
/// Parses a `type` alias with the following grammar:
@ -1344,8 +1352,13 @@ impl<'a> Parser<'a> {
context: FnContext::Free,
req_body: false,
};
Ok(self.parse_item_(fn_parse_mode, force_collect, ConstBlockItemsAllowed::No)?.map(
|Item { attrs, id, span, vis, kind, tokens }| {
Ok(self
.parse_item_(
fn_parse_mode,
force_collect,
AllowConstBlockItems::DoesNotMatter, // due to `ForeignItemKind::try_from` below
)?
.map(|Item { attrs, id, span, vis, kind, tokens }| {
let kind = match ForeignItemKind::try_from(kind) {
Ok(kind) => kind,
Err(kind) => match kind {
@ -1372,8 +1385,7 @@ impl<'a> Parser<'a> {
},
};
Some(Box::new(Item { attrs, id, span, vis, kind, tokens }))
},
))
}))
}
fn error_bad_item_kind<T>(&self, span: Span, kind: &ItemKind, ctx: &'static str) -> Option<T> {
@ -2418,7 +2430,10 @@ impl<'a> Parser<'a> {
{
let kw_token = self.token;
let kw_str = pprust::token_to_string(&kw_token);
let item = self.parse_item(ForceCollect::No, ConstBlockItemsAllowed::No)?;
let item = self.parse_item(
ForceCollect::No,
AllowConstBlockItems::DoesNotMatter, // self.token != kw::Const
)?;
let mut item = item.unwrap().span;
if self.token == token::Comma {
item = item.to(self.token.span);

View file

@ -146,10 +146,11 @@ pub enum ForceCollect {
}
/// Whether to accept `const { ... }` as a shorthand for `const _: () = const { ... }`.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum ConstBlockItemsAllowed {
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum AllowConstBlockItems {
Yes,
No,
DoesNotMatter,
}
/// If the next tokens are ill-formed `$ty::` recover them as `<$ty>::`.

View file

@ -7,7 +7,7 @@ use rustc_span::{Ident, kw};
use crate::errors::UnexpectedNonterminal;
use crate::parser::pat::{CommaRecoveryMode, RecoverColon, RecoverComma};
use crate::parser::{
ConstBlockItemsAllowed, FollowedByType, ForceCollect, ParseNtResult, Parser, PathStyle,
AllowConstBlockItems, FollowedByType, ForceCollect, ParseNtResult, Parser, PathStyle,
};
impl<'a> Parser<'a> {
@ -121,7 +121,7 @@ impl<'a> Parser<'a> {
// Note that TT is treated differently to all the others.
NonterminalKind::TT => Ok(ParseNtResult::Tt(self.parse_token_tree())),
NonterminalKind::Item => match self
.parse_item(ForceCollect::Yes, ConstBlockItemsAllowed::Yes)?
.parse_item(ForceCollect::Yes, AllowConstBlockItems::Yes)?
{
Some(item) => Ok(ParseNtResult::Item(item)),
None => Err(self.dcx().create_err(UnexpectedNonterminal::Item(self.token.span))),

View file

@ -20,7 +20,7 @@ use super::diagnostics::AttemptLocalParseRecovery;
use super::pat::{PatternLocation, RecoverComma};
use super::path::PathStyle;
use super::{
AttrWrapper, BlockMode, ConstBlockItemsAllowed, FnContext, FnParseMode, ForceCollect, Parser,
AllowConstBlockItems, AttrWrapper, BlockMode, FnContext, FnParseMode, ForceCollect, Parser,
Restrictions, SemiColonMode, Trailing, UsePreAttrPos,
};
use crate::errors::{self, MalformedLoopLabel};
@ -156,7 +156,7 @@ impl<'a> Parser<'a> {
true,
FnParseMode { req_name: |_, _| true, context: FnContext::Free, req_body: true },
force_collect,
ConstBlockItemsAllowed::No,
AllowConstBlockItems::No,
)? {
self.mk_stmt(lo.to(item.span), StmtKind::Item(Box::new(item)))
} else if self.eat(exp!(Semi)) {

View file

@ -541,7 +541,7 @@ fn parse_source(
let not_crate_attrs = &[sym::forbid, sym::allow, sym::warn, sym::deny, sym::expect];
let parsed = parser.parse_item(
rustc_parse::parser::ForceCollect::No,
rustc_parse::parser::ConstBlockItemsAllowed::No,
rustc_parse::parser::AllowConstBlockItems::No,
);
let result = match parsed {

View file

@ -3,7 +3,7 @@ use std::panic::{AssertUnwindSafe, catch_unwind};
use rustc_ast::ast;
use rustc_ast::token::TokenKind;
use rustc_parse::exp;
use rustc_parse::parser::ForceCollect;
use rustc_parse::parser::{AllowConstBlockItems, ForceCollect};
use rustc_span::symbol::kw;
use crate::parse::macros::build_stream_parser;
@ -61,7 +61,7 @@ fn parse_cfg_if_inner<'a>(
}
while parser.token != TokenKind::CloseBrace && parser.token.kind != TokenKind::Eof {
let item = match parser.parse_item(ForceCollect::No) {
let item = match parser.parse_item(ForceCollect::No, AllowConstBlockItems::Yes) {
Ok(Some(item_ptr)) => *item_ptr,
Ok(None) => continue,
Err(err) => {

View file

@ -2,7 +2,7 @@ use rustc_ast::ast;
use rustc_ast::token::{Delimiter, NonterminalKind, NtExprKind::*, NtPatKind::*, TokenKind};
use rustc_ast::tokenstream::TokenStream;
use rustc_parse::MACRO_ARGUMENTS;
use rustc_parse::parser::{ForceCollect, Parser, Recovery};
use rustc_parse::parser::{AllowConstBlockItems, ForceCollect, Parser, Recovery};
use rustc_session::parse::ParseSess;
use rustc_span::symbol;
@ -67,7 +67,7 @@ fn parse_macro_arg<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option<MacroArg> {
parse_macro_arg!(
Item,
NonterminalKind::Item,
|parser: &mut Parser<'b>| parser.parse_item(ForceCollect::No),
|parser: &mut Parser<'b>| parser.parse_item(ForceCollect::No, AllowConstBlockItems::Yes),
|x: Option<Box<ast::Item>>| x
);

View file

@ -0,0 +1,12 @@
//@ check-pass
#![feature(const_block_items)]
macro_rules! foo {
($item:item) => {
$item
};
}
foo!(const {});
fn main() {}

View file

@ -0,0 +1,14 @@
//@ check-fail
#![feature(const_block_items)]
macro_rules! foo {
($item:item) => {
$item
//~^ ERROR: expected expression, found ``
};
}
fn main() {
foo!(const {});
}

View file

@ -0,0 +1,13 @@
error: expected expression, found ``
--> $DIR/macro-stmt.rs:7:9
|
LL | $item
| ^^^^^ expected expression
...
LL | foo!(const {});
| -------------- in this macro invocation
|
= note: this error originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to 1 previous error