Suppose our main crate foo defines a trait Foo. Now we want to write a derive(Foo) proc macro (or any other kind of proc macro which emits code using Foo). We define that proc macro in the foo-macro crate. Our main crate foo wants to rexport the proc macro so that users only have to depend on foo.
The code for this example:
Details
Cargo.toml
[package]
name = "foo"
[dependencies]
foo-macro = { path = "foo-macro" }
src/lib.rs
pub use foo_macro::Foo;
trait Foo {}
foo-macro/Cargo.toml
[package]
name = "foo-macro"
foo-macro/src/lib.rs
#[proc_macro_derive(Foo)]
fn derive_foo(_: TokenStream) -> TokenStream {
// ...
quote! { impl foo::Foo for #name {} }
}
The question is now: how to document the custom derive in a way such that cargo test can test included example code?. I don't think it's currently possible.
If we document the function derive_foo in the proc macro crate, we have to make all examples ignore because they cannot be compiled, because compiling them would require using the foo crate. But this leads to a cyclic dependency (not even dev-dependencies works). foo-macros cannot depend on foo.
But we also can't document the reexport (that documentation is ignored/not shown). So it's not possible to write the documentation in the foo crate either. As a consequence, we don't have checked documentation tests -- which probably leads to stale and incorrect example code.
Now I can think of three workarounds:
- Add
#[cfg(not(rustdoc))] to the reexport, and add some other item with #[cfg(rustdoc)] and the actual documentation to your foo crate. That way, the reexport doesn't happen when rustdoc generates the documentation and instead renders the other dummy item with the correct documentation. For function like proc macros, it might be fine to have a macro_rules macro as dummy item, but for other kinds of proc macros, this... absolutely not nice.
- Add a feature to your main
foo crate (like no_macro_reexport). When that feature is activated, your main crate does not reexport macros and does not depend on the foo-macro crate. Now the foo-macro crate can depend on foo via dev-dependencies and activate that no_macro_reexport feature. That prevents the cyclic dependency. But now your main crate has a feature that should be implementation detail and you often have to build your main crate twice.
- Just don't bother and document your proc macros somewhere else (e.g. how
serde does it). I don't think that's a good solution at all: rustdoc should be able to also properly document proc macros.
So neither of these workarounds is particularly nice. I guess it's clear that there should be some kind of solution to this.
Potentially related:
Suppose our main crate
foodefines a traitFoo. Now we want to write aderive(Foo)proc macro (or any other kind of proc macro which emits code usingFoo). We define that proc macro in thefoo-macrocrate. Our main cratefoowants to rexport the proc macro so that users only have to depend onfoo.The code for this example:
Details
Cargo.tomlsrc/lib.rsfoo-macro/Cargo.tomlfoo-macro/src/lib.rsThe question is now: how to document the custom derive in a way such that
cargo testcan test included example code?. I don't think it's currently possible.If we document the function
derive_fooin the proc macro crate, we have to make all examplesignorebecause they cannot be compiled, because compiling them would require using thefoocrate. But this leads to a cyclic dependency (not evendev-dependenciesworks).foo-macroscannot depend onfoo.But we also can't document the reexport (that documentation is ignored/not shown). So it's not possible to write the documentation in the
foocrate either. As a consequence, we don't have checked documentation tests -- which probably leads to stale and incorrect example code.Now I can think of three workarounds:
#[cfg(not(rustdoc))]to the reexport, and add some other item with#[cfg(rustdoc)]and the actual documentation to yourfoocrate. That way, the reexport doesn't happen when rustdoc generates the documentation and instead renders the other dummy item with the correct documentation. For function like proc macros, it might be fine to have amacro_rulesmacro as dummy item, but for other kinds of proc macros, this... absolutely not nice.foocrate (likeno_macro_reexport). When that feature is activated, your main crate does not reexport macros and does not depend on thefoo-macrocrate. Now thefoo-macrocrate can depend onfooviadev-dependenciesand activate thatno_macro_reexportfeature. That prevents the cyclic dependency. But now your main crate has a feature that should be implementation detail and you often have to build your main crate twice.serdedoes it). I don't think that's a good solution at all: rustdoc should be able to also properly document proc macros.So neither of these workarounds is particularly nice. I guess it's clear that there should be some kind of solution to this.
Potentially related:
$crate) #54363