diff --git a/book.toml b/book.toml index d02ccde184..e4dced2e64 100644 --- a/book.toml +++ b/book.toml @@ -33,6 +33,7 @@ use-boolean-and = true "/items/modules.html#prelude-items" = "../names/preludes.html" "/items/traits.html#object-safety" = "traits.html#dyn-compatibility" "/lifetime-elision.html#static-lifetime-elision" = "lifetime-elision.html#const-and-static-elision" +"/macros-by-example.html#path-based-scope" = "macros-by-example.html#the-macro_export-attribute" "/procedural-macros.html#derive-macros" = "procedural-macros.html#the-proc_macro_derive-attribute" "/runtime.html#the-panic_handler-attribute" = "panic.html#the-panic_handler-attribute" "/unsafe-blocks.html" = "unsafe-keyword.html" diff --git a/src/attributes.md b/src/attributes.md index 53ce86a443..a2a927bef0 100644 --- a/src/attributes.md +++ b/src/attributes.md @@ -359,7 +359,7 @@ The following is an index of all built-in attributes. [`link_ordinal`]: items/external-blocks.md#the-link_ordinal-attribute [`link_section`]: abi.md#the-link_section-attribute [`link`]: items/external-blocks.md#the-link-attribute -[`macro_export`]: macros-by-example.md#path-based-scope +[`macro_export`]: macros-by-example.md#the-macro_export-attribute [`macro_use`]: macros-by-example.md#the-macro_use-attribute [`must_use`]: attributes/diagnostics.md#the-must_use-attribute [`naked`]: attributes/codegen.md#the-naked-attribute diff --git a/src/macros-by-example.md b/src/macros-by-example.md index b1c585da7a..33844e18c8 100644 --- a/src/macros-by-example.md +++ b/src/macros-by-example.md @@ -365,37 +365,145 @@ lazy_static!{} ``` r[macro.decl.scope.macro_use.export] -Macros to be imported with `#[macro_use]` must be exported with -`#[macro_export]`, which is described below. +Macros to be imported with `macro_use` must be exported with +[`macro_export`][macro.decl.scope.macro_export]. -r[macro.decl.scope.path] -### Path-based scope + +r[macro.decl.scope.macro_export] +### The `macro_export` attribute -r[macro.decl.scope.path.intro] -By default, a macro has no path-based scope. However, if it has the -`#[macro_export]` attribute, then it is declared in the crate root scope and can -be referred to normally as such: +r[macro.decl.scope.macro_export.intro] +The *`macro_export` [attribute][attributes]* exports the macro from the crate and makes it available in the root of the crate for path-based resolution. -```rust -self::m!(); -m!(); // OK: Path-based lookup finds m in the current module. +> [!EXAMPLE] +> ```rust +> self::m!(); +> // ^^^^ OK: Path-based lookup finds `m` in the current module. +> m!(); // As above. +> +> mod inner { +> super::m!(); +> crate::m!(); +> } +> +> mod mac { +> #[macro_export] +> macro_rules! m { +> () => {}; +> } +> } +> ``` -mod inner { - super::m!(); - crate::m!(); -} +r[macro.decl.scope.macro_export.syntax] +The `macro_export` attribute uses the [MetaWord] and [MetaListIdents] syntaxes. With the [MetaListIdents] syntax, it accepts a single [`local_inner_macros`][macro.decl.scope.macro_export.local_inner_macros] value. -mod mac { - #[macro_export] - macro_rules! m { - () => {}; - } -} -``` +r[macro.decl.scope.macro_export.allowed-positions] +The `macro_export` attribute may be applied to `macro_rules` definitions. + +> [!NOTE] +> `rustc` ignores use in other positions but lints against it. This may become an error in the future. + +r[macro.decl.scope.macro_export.duplicates] +Only the first use of `macro_export` on a macro has effect. + +> [!NOTE] +> `rustc` lints against any use following the first. -r[macro.decl.scope.path.export] -Macros labeled with `#[macro_export]` are always `pub` and can be referred to -by other crates, either by path or by `#[macro_use]` as described above. +r[macro.decl.scope.macro_export.path-based] +By default, macros only have [textual scope][macro.decl.scope.textual] and cannot be resolved by path. When the `macro_export` attribute is used, the macro is made available in the crate root and can be referred to by its path. + +> [!EXAMPLE] +> Without `macro_export`, macros only have textual scope, so path-based resolution of the macro fails. +> +> ```rust,compile_fail,E0433 +> macro_rules! m { +> () => {}; +> } +> self::m!(); // ERROR +> crate::m!(); // ERROR +> # fn main() {} +> ``` +> +> With `macro_export`, path-based resolution works. +> +> ```rust +> #[macro_export] +> macro_rules! m { +> () => {}; +> } +> self::m!(); // OK +> crate::m!(); // OK +> # fn main() {} +> ``` + +r[macro.decl.scope.macro_export.export] +The `macro_export` attribute causes a macro to be exported from the crate root so that it can be referred to in other crates by path. + +> [!EXAMPLE] +> Given the following in a `log` crate: +> +> ```rust +> #[macro_export] +> macro_rules! warn { +> ($message:expr) => { eprintln!("WARN: {}", $message) }; +> } +> ``` +> +> From another crate, you can refer to the macro by path: +> +> +> ```rust,ignore +> fn main() { +> log::warn!("example warning"); +> } +> ``` + +r[macro.decl.scope.macro_export.macro_use] +`macro_export` allows the use of [`macro_use`][macro.decl.scope.macro_use] on an `extern crate` to import the macro into the [`macro_use` prelude]. + +> [!EXAMPLE] +> Given the following in a `log` crate: +> +> ```rust +> #[macro_export] +> macro_rules! warn { +> ($message:expr) => { eprintln!("WARN: {}", $message) }; +> } +> ``` +> +> Using `macro_use` in a dependent crate allows you to use the macro from the prelude: +> +> +> ```rust,ignore +> #[macro_use] +> extern crate log; +> +> pub mod util { +> pub fn do_thing() { +> // Resolved via macro prelude. +> warn!("example warning"); +> } +> } +> ``` + +r[macro.decl.scope.macro_export.local_inner_macros] +Adding `local_inner_macros` to the `macro_export` attribute causes all single-segment macro invocations in the macro definition to have an implicit `$crate::` prefix. + +> [!NOTE] +> This is intended primarily as a tool to migrate code written before [`$crate`] was added to the language to work with Rust 2018's path-based imports of macros. Its use is discouraged in new code. + +> [!EXAMPLE] +> ```rust +> #[macro_export(local_inner_macros)] +> macro_rules! helped { +> () => { helper!() } // Automatically converted to $crate::helper!(). +> } +> +> #[macro_export] +> macro_rules! helper { +> () => { () } +> } +> ``` r[macro.decl.hygiene] ## Hygiene @@ -495,32 +603,8 @@ macro_rules! call_foo { fn foo() {} ``` -> **Version differences**: Prior to Rust 1.30, `$crate` and -> `local_inner_macros` (below) were unsupported. They were added alongside -> path-based imports of macros (described above), 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. - -r[macro.decl.hygiene.local_inner_macros] -When a macro is exported, the `#[macro_export]` attribute can have the -`local_inner_macros` keyword added to automatically prefix all contained macro -invocations with `$crate::`. This is intended primarily as a tool to migrate -code written before `$crate` was added to the language to work with Rust 2018's -path-based imports of macros. Its use is discouraged in new code. - -```rust -#[macro_export(local_inner_macros)] -macro_rules! helped { - () => { helper!() } // Automatically converted to $crate::helper!(). -} - -#[macro_export] -macro_rules! helper { - () => { () } -} -``` +> [!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. r[macro.decl.follow-set] ## Follow-set ambiguity restrictions diff --git a/src/names.md b/src/names.md index b9af0e5359..a714728b9a 100644 --- a/src/names.md +++ b/src/names.md @@ -137,7 +137,7 @@ to with certain [path qualifiers] or aliases. [`for`]: expressions/loop-expr.md#iterator-loops [`if let`]: expressions/if-expr.md#if-let-patterns [`let` statement]: statements.md#let-statements -[`macro_export` attribute]: macros-by-example.md#path-based-scope +[`macro_export` attribute]: macros-by-example.md#the-macro_export-attribute [`macro_rules` declarations]: macros-by-example.md [`macro_use` attribute]: macros-by-example.md#the-macro_use-attribute [`match`]: expressions/match-expr.md diff --git a/src/names/scopes.md b/src/names/scopes.md index 0a7240512f..382aa29241 100644 --- a/src/names/scopes.md +++ b/src/names/scopes.md @@ -343,7 +343,7 @@ impl ImplExample { [`if let`]: ../expressions/if-expr.md#if-let-patterns [`while let`]: ../expressions/loop-expr.md#while-let-patterns [`let` statement]: ../statements.md#let-statements -[`macro_export`]: ../macros-by-example.md#path-based-scope +[`macro_export`]: ../macros-by-example.md#the-macro_export-attribute [`macro_use` prelude]: preludes.md#macro_use-prelude [`macro_use`]: ../macros-by-example.md#the-macro_use-attribute [`match` arms]: ../expressions/match-expr.md