From b7838ba017b52affbbf3d9105468697176fd310b Mon Sep 17 00:00:00 2001 From: Frank Steffahn Date: Sat, 21 Mar 2026 21:46:26 +0100 Subject: [PATCH] Improve description of `ident` fragment matcher MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I am initially coming to work on this due to the line > an IDENTIFIER_OR_KEYWORD except _, RAW_IDENTIFIER, or $crate being unfortunately very ambiguous, since this can be read as “_, RAW_IDENTIFIER, or $crate” all belonging to the “except” listing. My best fix for that is to add parentheses around the “except _”. This seems to read more nicely than alternatives I have tried, for instance > either an IDENTIFIER_OR_KEYWORD except _, or a RAW_IDENTIFIER, or "$crate" (which still seems ambiguous) And the exception for “_” seems minor enough (previously it had been missing completely for a while) that parentheses also seem sensible semantically, IMHO. The meaning of accepting `$crate` was confusing to me though; and apparently a known issue, so this closes rust-lang/reference#588. To achieve this, I have written a new section to the reference description of `$crate`, and worded the concept as `$crate` being "initially replaced". The verb "replace" is for consistency with the wording of other metavariables, which are being "replaced" during expansion; the qualifier "initially" is added to emphasize the important difference of this process from *macro expansion*. And also where I'm linking to it, the phrasing "replaced $crate" can easily be misunderstood for people to think that "$crate" has already been replaced by the name of the crate it's referring to, even though that's not at all how this process works! --- src/macros-by-example.md | 47 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/src/macros-by-example.md b/src/macros-by-example.md index 392f3d9ed8..bc34672981 100644 --- a/src/macros-by-example.md +++ b/src/macros-by-example.md @@ -109,7 +109,7 @@ Valid fragment specifiers are: * `block`: a [BlockExpression] * `expr`: an [Expression] * `expr_2021`: an [Expression] except [UnderscoreExpression] and [ConstBlockExpression] (see [macro.decl.meta.edition2024]) - * `ident`: an [IDENTIFIER_OR_KEYWORD] except `_`, [RAW_IDENTIFIER], or [`$crate`] + * `ident`: an [IDENTIFIER_OR_KEYWORD] (except `_`), a [RAW_IDENTIFIER], or an [initially replaced `$crate`] * `item`: an [Item] * `lifetime`: a [LIFETIME_TOKEN] * `literal`: matches `-`?[LiteralExpression] @@ -664,6 +664,48 @@ pub mod inner { } ``` +r[macro.decl.hygiene.crate.replaced] +The raw syntax of `$crate` consists of two tokens (`$` followed by `crate`). When used as a metavariable within a macro definition, this syntax is initially replaced with a single token (also called "`$crate`"), which can be used an identifier. + +```rust +macro_rules! print_tokens { + ($($token:tt)*) => { + $( + println!("* token {:?}", stringify!($token)); + )* + } +} + +// first, calling print_tokens!($crate) directly: +println!("raw syntax:"); +print_tokens!($crate); +// +--- OUTPUT -------+ +// | raw syntax: | +// | * token "$" | +// | * token "crate" | +// +------------------+ + +println!(); // ================================== + +// next, calling print_tokens!($crate) +// from within a macro definition's transcriber: +macro_rules! print_dollar_crate { + () => { + print_tokens!($crate); + } +} + +println!("replaced token:"); +print_dollar_crate!(); +// +--- OUTPUT -------+ +// | replaced token: | +// | * token "$crate" | +// +------------------+ +``` + +The semantic meaning of this token, referring to the crate defining this macro, only comes into effect *later* in compilation, after all macro-expansion is completed. +It makes use of the hygiene information that is attached to the "`$crate`" token during the initial replacement. + r[macro.decl.hygiene.vis] Additionally, even though `$crate` allows a macro to refer to items within its own crate when expanding, its use has no effect on visibility. An item or macro referred to must still be visible from the invocation site. In the following example, any attempt to invoke `call_foo!()` from outside its crate will fail because `foo()` is not public. @@ -676,6 +718,8 @@ macro_rules! call_foo { fn foo() {} ``` +However, the crate being referred to does *not* itself need to be visible from the invocation site as a directly declared dependency. A main purpose of `$crate` is to offer a way of reliably naming crates (and their public items) in macro-generated code, even if the only exists as a transitive dependency (i.e. "dependency of a dependency") from the invocation site. + > [!NOTE] > Prior to Rust 1.30, `$crate` and [`local_inner_macros`][macro.decl.scope.macro_export.local_inner_macros] were unsupported. They were added alongside [path-based imports of macros][macro.decl.scope.macro_export], to ensure that helper macros did not need to be manually imported by users of a macro-exporting crate. Crates written for earlier versions of Rust that use helper macros need to be modified to use `$crate` or `local_inner_macros` to work well with path-based imports. @@ -726,6 +770,7 @@ For more detail, see the [formal specification]. [Repetitions]: #repetitions [`macro_export`]: #the-macro_export-attribute [`$crate`]: macro.decl.hygiene.crate +[initially replaced `$crate`]: macro.decl.hygiene.crate.replaced [`extern crate self`]: items.extern-crate.self [`macro_use` prelude]: names/preludes.md#macro_use-prelude [block labels]: expr.loop.block-labels