Skip to content

Commit b7838ba

Browse files
committed
Improve description of ident fragment matcher
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 #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!
1 parent 7446bf9 commit b7838ba

1 file changed

Lines changed: 46 additions & 1 deletion

File tree

src/macros-by-example.md

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ Valid fragment specifiers are:
109109
* `block`: a [BlockExpression]
110110
* `expr`: an [Expression]
111111
* `expr_2021`: an [Expression] except [UnderscoreExpression] and [ConstBlockExpression] (see [macro.decl.meta.edition2024])
112-
* `ident`: an [IDENTIFIER_OR_KEYWORD] except `_`, [RAW_IDENTIFIER], or [`$crate`]
112+
* `ident`: an [IDENTIFIER_OR_KEYWORD] (except `_`), a [RAW_IDENTIFIER], or an [initially replaced `$crate`]
113113
* `item`: an [Item]
114114
* `lifetime`: a [LIFETIME_TOKEN]
115115
* `literal`: matches `-`<sup>?</sup>[LiteralExpression]
@@ -664,6 +664,48 @@ pub mod inner {
664664
}
665665
```
666666

667+
r[macro.decl.hygiene.crate.replaced]
668+
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&nbsp;"`$crate`"), which can be used an identifier.
669+
670+
```rust
671+
macro_rules! print_tokens {
672+
($($token:tt)*) => {
673+
$(
674+
println!("* token {:?}", stringify!($token));
675+
)*
676+
}
677+
}
678+
679+
// first, calling print_tokens!($crate) directly:
680+
println!("raw syntax:");
681+
print_tokens!($crate);
682+
// +--- OUTPUT -------+
683+
// | raw syntax: |
684+
// | * token "$" |
685+
// | * token "crate" |
686+
// +------------------+
687+
688+
println!(); // ==================================
689+
690+
// next, calling print_tokens!($crate)
691+
// from within a macro definition's transcriber:
692+
macro_rules! print_dollar_crate {
693+
() => {
694+
print_tokens!($crate);
695+
}
696+
}
697+
698+
println!("replaced token:");
699+
print_dollar_crate!();
700+
// +--- OUTPUT -------+
701+
// | replaced token: |
702+
// | * token "$crate" |
703+
// +------------------+
704+
```
705+
706+
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.
707+
It makes use of the hygiene information that is attached to the "`$crate`" token during the initial replacement.
708+
667709
r[macro.decl.hygiene.vis]
668710
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.
669711

@@ -676,6 +718,8 @@ macro_rules! call_foo {
676718
fn foo() {}
677719
```
678720

721+
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.
722+
679723
> [!NOTE]
680724
> 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.
681725
@@ -726,6 +770,7 @@ For more detail, see the [formal specification].
726770
[Repetitions]: #repetitions
727771
[`macro_export`]: #the-macro_export-attribute
728772
[`$crate`]: macro.decl.hygiene.crate
773+
[initially replaced `$crate`]: macro.decl.hygiene.crate.replaced
729774
[`extern crate self`]: items.extern-crate.self
730775
[`macro_use` prelude]: names/preludes.md#macro_use-prelude
731776
[block labels]: expr.loop.block-labels

0 commit comments

Comments
 (0)