From 7f9112d574a8d7ff48c42f2f65489d1f09488ca1 Mon Sep 17 00:00:00 2001 From: Nik Revenco Date: Fri, 10 Apr 2026 19:24:28 +0100 Subject: [PATCH] Add FCW to disallow `$crate` in macro matcher Co-authored-by: @CAD97 --- compiler/rustc_expand/src/errors.rs | 4 + compiler/rustc_expand/src/mbe/quoted.rs | 28 ++++- compiler/rustc_lint_defs/src/builtin.rs | 28 +++++ tests/ui/macros/dollar-crate-in-matcher.rs | 71 +++++++++++++ .../ui/macros/dollar-crate-in-matcher.stderr | 100 ++++++++++++++++++ 5 files changed, 229 insertions(+), 2 deletions(-) create mode 100644 tests/ui/macros/dollar-crate-in-matcher.rs create mode 100644 tests/ui/macros/dollar-crate-in-matcher.stderr diff --git a/compiler/rustc_expand/src/errors.rs b/compiler/rustc_expand/src/errors.rs index 6c5732f497f8a..09efa6cd67d2f 100644 --- a/compiler/rustc_expand/src/errors.rs +++ b/compiler/rustc_expand/src/errors.rs @@ -10,6 +10,10 @@ use rustc_span::{Ident, MacroRulesNormalizedIdent, Span, Symbol}; #[diag("`#[cfg_attr]` does not expand to any attributes")] pub(crate) struct CfgAttrNoAttributes; +#[derive(Diagnostic)] +#[diag("usage of `$crate` in matcher")] +pub(crate) struct DollarCrateInMatcher; + #[derive(Diagnostic)] #[diag( "attempted to repeat an expression containing no syntax variables matched as repeating at this depth" diff --git a/compiler/rustc_expand/src/mbe/quoted.rs b/compiler/rustc_expand/src/mbe/quoted.rs index eb874a27cece5..bfa8ff96de236 100644 --- a/compiler/rustc_expand/src/mbe/quoted.rs +++ b/compiler/rustc_expand/src/mbe/quoted.rs @@ -1,8 +1,9 @@ -use rustc_ast::token::{self, Delimiter, IdentIsRaw, NonterminalKind, Token}; +use rustc_ast::token::{self, Delimiter, NonterminalKind, Token}; use rustc_ast::tokenstream::TokenStreamIter; use rustc_ast::{NodeId, tokenstream}; use rustc_ast_pretty::pprust; use rustc_feature::Features; +use rustc_lint_defs::builtin::DOLLAR_CRATE_IN_MATCHER; use rustc_session::Session; use rustc_session::parse::feature_err; use rustc_span::edition::Edition; @@ -299,7 +300,30 @@ fn parse_tree<'a>( Some(tokenstream::TokenTree::Token(token, _)) if token.is_ident() => { let (ident, is_raw) = token.ident().unwrap(); let span = ident.span.with_lo(dollar_span.lo()); - if ident.name == kw::Crate && matches!(is_raw, IdentIsRaw::No) { + + // NOTE: `$crate` and `crate` cannot be raw identifiers, + // see `Symbol::is_path_segment_keyword()` + + if matches!(ident.name, kw::Crate | kw::DollarCrate) { + // FCW for `$crate` in matcher, and: + // + // $ $crate + // ^^^^^^ where this is a single identifier token, NOT + // '$' token followed by 'crate' token. + // + // (this can happen under special circumstances with + // macros-generating-macros, see the `dollar-crate-in-matcher.rs` test) + if part.is_pattern() { + sess.psess.buffer_lint( + DOLLAR_CRATE_IN_MATCHER, + ident.span, + node_id, + crate::errors::DollarCrateInMatcher, + ); + } + } + + if ident.name == kw::Crate { TokenTree::token(token::Ident(kw::DollarCrate, is_raw), span) } else { TokenTree::MetaVar(span, ident) diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 4aff294aeac61..490340d334d12 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -39,6 +39,7 @@ declare_lint_pass! { DEPRECATED_IN_FUTURE, DEPRECATED_SAFE_2024, DEPRECATED_WHERE_CLAUSE_LOCATION, + DOLLAR_CRATE_IN_MATCHER, DUPLICATE_FEATURES, DUPLICATE_MACRO_ATTRIBUTES, ELIDED_LIFETIMES_IN_ASSOCIATED_CONSTANT, @@ -156,6 +157,33 @@ declare_lint_pass! { ] } +declare_lint! { + /// The `dollar_crate_in_matcher` lint detects cases where `$crate` is used in the matcher. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(dollar_crate_in_matcher)] + /// + /// macro_rules! m { + /// ($crate) => {}; + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// `$crate` is inconsistent with the behavior of other keywords in matchers, namely + /// that other keywords work like any other identifier, and are currently not reserved in this position. + pub DOLLAR_CRATE_IN_MATCHER, + Warn, + "detects when `$crate` is used in matcher", + @future_incompatible = FutureIncompatibleInfo { + reason: fcw!(FutureReleaseError #155123), + }; +} + declare_lint! { /// The `forbidden_lint_groups` lint detects violations of /// `forbid` applied to a lint group. Due to a bug in the compiler, diff --git a/tests/ui/macros/dollar-crate-in-matcher.rs b/tests/ui/macros/dollar-crate-in-matcher.rs new file mode 100644 index 0000000000000..f7f5813da971d --- /dev/null +++ b/tests/ui/macros/dollar-crate-in-matcher.rs @@ -0,0 +1,71 @@ +#![deny(dollar_crate_in_matcher)] + +macro_rules! direct { + ($crate) => {}; + //~^ ERROR usage of `$crate` in matcher + //~| WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +} + +macro_rules! direct_with_fragment_specifier { + ($crate:tt) => {}; + //~^ ERROR usage of `$crate` in matcher + //~| WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +} + +macro_rules! indirect { + ($dollar:tt $krate:tt) => { + macro_rules! indirect_inner { + ($dollar $krate) => {} + } + }; +} + +indirect!($crate); +//~^ ERROR usage of `$crate` in matcher +//~| WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +macro_rules! indirect_with_fragment_specifier { + ($dollar:tt $krate:tt) => { + macro_rules! indirect_with_fragment_specifier_inner { + ($dollar $krate : tt) => {} + } + }; +} + +indirect_with_fragment_specifier!($crate); +//~^ ERROR usage of `$crate` in matcher +//~| WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +macro_rules! dollar_crate_metavariable { + ($dol:tt) => { + macro_rules! dollar_crate_metavariable_inner { + ($dol $crate) => {} + //~^ ERROR missing fragment specifier + //~| ERROR usage of `$crate` in matcher + //~| WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + } + } +} + +dollar_crate_metavariable!($); + +macro_rules! dollar_crate_metavariable_with_fragment_specifier { + ($dol:tt) => { + macro_rules! dollar_crate_metavariable_with_fragment_specifier_inner { + ($dol $crate : tt) => {} + //~^ ERROR usage of `$crate` in matcher + //~| WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + } + } +} + +dollar_crate_metavariable_with_fragment_specifier!($); + +macro_rules! escaped { + ($$crate) => {}; + //~^ ERROR unexpected token: $ +} + +escaped!($crate); + +fn main() {} diff --git a/tests/ui/macros/dollar-crate-in-matcher.stderr b/tests/ui/macros/dollar-crate-in-matcher.stderr new file mode 100644 index 0000000000000..b57c8f876e0a8 --- /dev/null +++ b/tests/ui/macros/dollar-crate-in-matcher.stderr @@ -0,0 +1,100 @@ +error: unexpected token: $ + --> $DIR/dollar-crate-in-matcher.rs:65:7 + | +LL | ($$crate) => {}; + | ^ + +note: `$$` and meta-variable expressions are not allowed inside macro parameter definitions + --> $DIR/dollar-crate-in-matcher.rs:65:7 + | +LL | ($$crate) => {}; + | ^ + +error: missing fragment specifier + --> $DIR/dollar-crate-in-matcher.rs:42:25 + | +LL | ($dol $crate) => {} + | _________________________^ +... | +LL | | dollar_crate_metavariable!($); + | |_--------------------------^-- + | | + | in this macro invocation + | + = note: fragment specifiers must be provided + = help: valid fragment specifiers are `ident`, `block`, `stmt`, `expr`, `pat`, `ty`, `lifetime`, `literal`, `path`, `meta`, `tt`, `item` and `vis`, along with `expr_2021` and `pat_param` for edition compatibility + = note: this error originates in the macro `dollar_crate_metavariable` (in Nightly builds, run with -Z macro-backtrace for more info) +help: try adding a specifier here + | +LL | dollar_crate_metavariable!(:spec$); + | +++++ + +error: usage of `$crate` in matcher + --> $DIR/dollar-crate-in-matcher.rs:4:7 + | +LL | ($crate) => {}; + | ^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #155123 +note: the lint level is defined here + --> $DIR/dollar-crate-in-matcher.rs:1:9 + | +LL | #![deny(dollar_crate_in_matcher)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: usage of `$crate` in matcher + --> $DIR/dollar-crate-in-matcher.rs:10:7 + | +LL | ($crate:tt) => {}; + | ^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #155123 + +error: usage of `$crate` in matcher + --> $DIR/dollar-crate-in-matcher.rs:23:12 + | +LL | indirect!($crate); + | ^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #155123 + +error: usage of `$crate` in matcher + --> $DIR/dollar-crate-in-matcher.rs:35:36 + | +LL | indirect_with_fragment_specifier!($crate); + | ^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #155123 + +error: usage of `$crate` in matcher + --> $DIR/dollar-crate-in-matcher.rs:42:19 + | +LL | ($dol $crate) => {} + | ^^^^^^ +... +LL | dollar_crate_metavariable!($); + | ----------------------------- in this macro invocation + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #155123 + = note: this error originates in the macro `dollar_crate_metavariable` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: usage of `$crate` in matcher + --> $DIR/dollar-crate-in-matcher.rs:55:19 + | +LL | ($dol $crate : tt) => {} + | ^^^^^^ +... +LL | dollar_crate_metavariable_with_fragment_specifier!($); + | ----------------------------------------------------- in this macro invocation + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #155123 + = note: this error originates in the macro `dollar_crate_metavariable_with_fragment_specifier` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 8 previous errors +