Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions compiler/rustc_expand/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
28 changes: 26 additions & 2 deletions compiler/rustc_expand/src/mbe/quoted.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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)
Expand Down
28 changes: 28 additions & 0 deletions compiler/rustc_lint_defs/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
71 changes: 71 additions & 0 deletions tests/ui/macros/dollar-crate-in-matcher.rs
Original file line number Diff line number Diff line change
@@ -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() {}
100 changes: 100 additions & 0 deletions tests/ui/macros/dollar-crate-in-matcher.stderr
Original file line number Diff line number Diff line change
@@ -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 <https://github.com/rust-lang/rust/issues/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 <https://github.com/rust-lang/rust/issues/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 <https://github.com/rust-lang/rust/issues/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 <https://github.com/rust-lang/rust/issues/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 <https://github.com/rust-lang/rust/issues/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 <https://github.com/rust-lang/rust/issues/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

Loading