diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index 72f89737f9952..e44725e7f15ba 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -878,6 +878,8 @@ pub enum NonterminalKind { }, PatWithOr, Expr, + /// Matches an expression using the rules from edition 2021 and earlier. + Expr2021, Ty, Ident, Lifetime, @@ -907,6 +909,7 @@ impl NonterminalKind { }, sym::pat_param => NonterminalKind::PatParam { inferred: false }, sym::expr => NonterminalKind::Expr, + sym::expr_2021 if edition().at_least_rust_2021() => NonterminalKind::Expr2021, sym::ty => NonterminalKind::Ty, sym::ident => NonterminalKind::Ident, sym::lifetime => NonterminalKind::Lifetime, @@ -926,6 +929,7 @@ impl NonterminalKind { NonterminalKind::PatParam { inferred: false } => sym::pat_param, NonterminalKind::PatParam { inferred: true } | NonterminalKind::PatWithOr => sym::pat, NonterminalKind::Expr => sym::expr, + NonterminalKind::Expr2021 => sym::expr_2021, NonterminalKind::Ty => sym::ty, NonterminalKind::Ident => sym::ident, NonterminalKind::Lifetime => sym::lifetime, diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 470bde232d723..71daaacd0567f 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -1290,7 +1290,7 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow { // maintain IsInFollow::Yes } - NonterminalKind::Stmt | NonterminalKind::Expr => { + NonterminalKind::Stmt | NonterminalKind::Expr | NonterminalKind::Expr2021 => { const TOKENS: &[&str] = &["`=>`", "`,`", "`;`"]; match tok { TokenTree::Token(token) => match token.kind { diff --git a/compiler/rustc_expand/src/mbe/quoted.rs b/compiler/rustc_expand/src/mbe/quoted.rs index 06c1612ddbaea..e67955be3d6d2 100644 --- a/compiler/rustc_expand/src/mbe/quoted.rs +++ b/compiler/rustc_expand/src/mbe/quoted.rs @@ -16,6 +16,10 @@ use rustc_span::Span; const VALID_FRAGMENT_NAMES_MSG: &str = "valid fragment specifiers are \ `ident`, `block`, `stmt`, `expr`, `pat`, `ty`, `lifetime`, \ `literal`, `path`, `meta`, `tt`, `item` and `vis`"; +const VALID_FRAGMENT_NAMES_MSG_2021: &str = "valid fragment specifiers are \ + `ident`, `block`, `stmt`, `expr`, `expr_2021`, `pat`, \ + `ty`, `lifetime`, `literal`, `path`, `meta`, `tt`, \ + `item` and `vis`"; /// Takes a `tokenstream::TokenStream` and returns a `Vec`. Specifically, this /// takes a generic `TokenStream`, such as is used in the rest of the compiler, and returns a @@ -63,35 +67,60 @@ pub(super) fn parse( Some(tokenstream::TokenTree::Token(token, _)) => match token.ident() { Some((fragment, _)) => { let span = token.span.with_lo(start_sp.lo()); - + let edition = || { + // FIXME(#85708) - once we properly decode a foreign + // crate's `SyntaxContext::root`, then we can replace + // this with just `span.edition()`. A + // `SyntaxContext::root()` from the current crate will + // have the edition of the current crate, and a + // `SyntaxContext::root()` from a foreign crate will + // have the edition of that crate (which we manually + // retrieve via the `edition` parameter). + if !span.from_expansion() { + edition + } else { + span.edition() + } + }; let kind = - token::NonterminalKind::from_symbol(fragment.name, || { - // FIXME(#85708) - once we properly decode a foreign - // crate's `SyntaxContext::root`, then we can replace - // this with just `span.edition()`. A - // `SyntaxContext::root()` from the current crate will - // have the edition of the current crate, and a - // `SyntaxContext::root()` from a foreign crate will - // have the edition of that crate (which we manually - // retrieve via the `edition` parameter). - if !span.from_expansion() { - edition - } else { - span.edition() - } - }) - .unwrap_or_else( - || { + token::NonterminalKind::from_symbol(fragment.name, edition) + .unwrap_or_else(|| { + let help = match fragment.name { + sym::expr_2021 => { + format!( + "fragment specifier `expr_2021` \ + requires Rust 2021 or later\n\ + {VALID_FRAGMENT_NAMES_MSG}" + ) + } + _ if edition().at_least_rust_2021() + && features + .expr_fragment_specifier_2024 => + { + VALID_FRAGMENT_NAMES_MSG_2021.into() + } + _ => VALID_FRAGMENT_NAMES_MSG.into(), + }; sess.dcx().emit_err( errors::InvalidFragmentSpecifier { span, fragment, - help: VALID_FRAGMENT_NAMES_MSG.into(), + help, }, ); token::NonterminalKind::Ident - }, - ); + }); + if kind == token::NonterminalKind::Expr2021 + && !features.expr_fragment_specifier_2024 + { + rustc_session::parse::feature_err( + sess, + sym::expr_fragment_specifier_2024, + span, + "fragment specifier `expr_2021` is unstable", + ) + .emit(); + } result.push(TokenTree::MetaVarDecl(span, ident, Some(kind))); continue; } diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 60b386acf9106..c6fc9de119dc4 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -458,6 +458,8 @@ declare_features! ( (unstable, exhaustive_patterns, "1.13.0", Some(51085)), /// Allows explicit tail calls via `become` expression. (incomplete, explicit_tail_calls, "1.72.0", Some(112788)), + /// Uses 2024 rules for matching `expr` fragments in macros. Also enables `expr_2021` fragment. + (incomplete, expr_fragment_specifier_2024, "CURRENT_RUSTC_VERSION", Some(123742)), /// Allows using `efiapi`, `sysv64` and `win64` as calling convention /// for functions with varargs. (unstable, extended_varargs_abi_support, "1.65.0", Some(100189)), diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs index 73b17353ac90c..274157dee57bf 100644 --- a/compiler/rustc_parse/src/parser/nonterminal.rs +++ b/compiler/rustc_parse/src/parser/nonterminal.rs @@ -37,13 +37,19 @@ impl<'a> Parser<'a> { } match kind { - NonterminalKind::Expr => { + NonterminalKind::Expr2021 => { token.can_begin_expr() // This exception is here for backwards compatibility. && !token.is_keyword(kw::Let) // This exception is here for backwards compatibility. && !token.is_keyword(kw::Const) } + NonterminalKind::Expr => { + token.can_begin_expr() + // This exception is here for backwards compatibility. + && !token.is_keyword(kw::Let) + && (token.span.edition().at_least_rust_2024() || !token.is_keyword(kw::Const)) + } NonterminalKind::Ty => token.can_begin_type(), NonterminalKind::Ident => get_macro_ident(token).is_some(), NonterminalKind::Literal => token.can_begin_literal_maybe_minus(), @@ -145,7 +151,9 @@ impl<'a> Parser<'a> { })?) } - NonterminalKind::Expr => NtExpr(self.parse_expr_force_collect()?), + NonterminalKind::Expr | NonterminalKind::Expr2021 => { + NtExpr(self.parse_expr_force_collect()?) + } NonterminalKind::Literal => { // The `:literal` matcher does not support attributes NtLiteral(self.collect_tokens_no_attrs(|this| this.parse_literal_maybe_minus())?) diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index d41059e8c24a4..68b1b32baf2dc 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -782,6 +782,8 @@ symbols! { explicit_tail_calls, export_name, expr, + expr_2021, + expr_fragment_specifier_2024, extended_key_value_attributes, extended_varargs_abi_support, extern_absolute_paths, diff --git a/tests/ui/macros/expr_2021_inline_const.edi2021.stderr b/tests/ui/macros/expr_2021_inline_const.edi2021.stderr new file mode 100644 index 0000000000000..5e88096445473 --- /dev/null +++ b/tests/ui/macros/expr_2021_inline_const.edi2021.stderr @@ -0,0 +1,32 @@ +error: no rules expected the token `const` + --> $DIR/expr_2021_inline_const.rs:21:12 + | +LL | macro_rules! m2021 { + | ------------------ when calling this macro +... +LL | m2021!(const { 1 }); + | ^^^^^ no rules expected this token in macro call + | +note: while trying to match meta-variable `$e:expr_2021` + --> $DIR/expr_2021_inline_const.rs:10:6 + | +LL | ($e:expr_2021) => { + | ^^^^^^^^^^^^ + +error: no rules expected the token `const` + --> $DIR/expr_2021_inline_const.rs:22:12 + | +LL | macro_rules! m2024 { + | ------------------ when calling this macro +... +LL | m2024!(const { 1 }); + | ^^^^^ no rules expected this token in macro call + | +note: while trying to match meta-variable `$e:expr` + --> $DIR/expr_2021_inline_const.rs:16:6 + | +LL | ($e:expr) => { + | ^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/macros/expr_2021_inline_const.edi2024.stderr b/tests/ui/macros/expr_2021_inline_const.edi2024.stderr new file mode 100644 index 0000000000000..237ecb2cc192a --- /dev/null +++ b/tests/ui/macros/expr_2021_inline_const.edi2024.stderr @@ -0,0 +1,17 @@ +error: no rules expected the token `const` + --> $DIR/expr_2021_inline_const.rs:21:12 + | +LL | macro_rules! m2021 { + | ------------------ when calling this macro +... +LL | m2021!(const { 1 }); + | ^^^^^ no rules expected this token in macro call + | +note: while trying to match meta-variable `$e:expr_2021` + --> $DIR/expr_2021_inline_const.rs:10:6 + | +LL | ($e:expr_2021) => { + | ^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/macros/expr_2021_inline_const.rs b/tests/ui/macros/expr_2021_inline_const.rs new file mode 100644 index 0000000000000..ebc5ea3642108 --- /dev/null +++ b/tests/ui/macros/expr_2021_inline_const.rs @@ -0,0 +1,23 @@ +//@ revisions: edi2021 edi2024 +//@[edi2024]compile-flags: --edition=2024 -Z unstable-options +//@[edi2021]compile-flags: --edition=2021 + +// This test ensures that the inline const match only on edition 2024 +#![feature(expr_fragment_specifier_2024)] +#![allow(incomplete_features)] + +macro_rules! m2021 { + ($e:expr_2021) => { + $e + }; +} + +macro_rules! m2024 { + ($e:expr) => { + $e + }; +} +fn main() { + m2021!(const { 1 }); //~ ERROR: no rules expected the token `const` + m2024!(const { 1 }); //[edi2021]~ ERROR: no rules expected the token `const` +} diff --git a/tests/ui/macros/expr_2021_old_edition.rs b/tests/ui/macros/expr_2021_old_edition.rs new file mode 100644 index 0000000000000..a771126610686 --- /dev/null +++ b/tests/ui/macros/expr_2021_old_edition.rs @@ -0,0 +1,13 @@ +//@ compile-flags: --edition=2018 + +// This test ensures that expr_2021 is not allowed on pre-2021 editions + +macro_rules! m { + ($e:expr_2021) => { //~ ERROR: invalid fragment specifier `expr_2021` + $e + }; +} + +fn main() { + m!(()); //~ ERROR: no rules expected the token `(` +} diff --git a/tests/ui/macros/expr_2021_old_edition.stderr b/tests/ui/macros/expr_2021_old_edition.stderr new file mode 100644 index 0000000000000..bffa8a1ca1759 --- /dev/null +++ b/tests/ui/macros/expr_2021_old_edition.stderr @@ -0,0 +1,26 @@ +error: invalid fragment specifier `expr_2021` + --> $DIR/expr_2021_old_edition.rs:6:6 + | +LL | ($e:expr_2021) => { + | ^^^^^^^^^^^^ + | + = help: fragment specifier `expr_2021` requires Rust 2021 or later + valid fragment specifiers are `ident`, `block`, `stmt`, `expr`, `pat`, `ty`, `lifetime`, `literal`, `path`, `meta`, `tt`, `item` and `vis` + +error: no rules expected the token `(` + --> $DIR/expr_2021_old_edition.rs:12:8 + | +LL | macro_rules! m { + | -------------- when calling this macro +... +LL | m!(()); + | ^ no rules expected this token in macro call + | +note: while trying to match meta-variable `$e:ident` + --> $DIR/expr_2021_old_edition.rs:6:6 + | +LL | ($e:expr_2021) => { + | ^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/macros/feature-gate-expr_fragment_specifier_2024.rs b/tests/ui/macros/feature-gate-expr_fragment_specifier_2024.rs new file mode 100644 index 0000000000000..5a737b298214d --- /dev/null +++ b/tests/ui/macros/feature-gate-expr_fragment_specifier_2024.rs @@ -0,0 +1,11 @@ +//@ compile-flags: --edition=2024 -Z unstable-options + +macro_rules! m { + ($e:expr_2021) => { //~ ERROR: fragment specifier `expr_2021` is unstable + $e + }; +} + +fn main() { + m!(()); +} diff --git a/tests/ui/macros/feature-gate-expr_fragment_specifier_2024.stderr b/tests/ui/macros/feature-gate-expr_fragment_specifier_2024.stderr new file mode 100644 index 0000000000000..273a93877ce13 --- /dev/null +++ b/tests/ui/macros/feature-gate-expr_fragment_specifier_2024.stderr @@ -0,0 +1,13 @@ +error[E0658]: fragment specifier `expr_2021` is unstable + --> $DIR/feature-gate-expr_fragment_specifier_2024.rs:4:6 + | +LL | ($e:expr_2021) => { + | ^^^^^^^^^^^^ + | + = note: see issue #123742 for more information + = help: add `#![feature(expr_fragment_specifier_2024)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`.