Code
https://www.chiark.greenend.org.uk/ucgi/~ian/git?p=rust-experiments.git;a=shortlog;h=refs/heads/rustdoc-link-2026-06
Reproduction Steps
git clone https://www.chiark.greenend.org.uk/ucgi/~ian/githttp/rust-experiments.git -b rustdoc-link-2026-06
cd rust-experiments
cargo doc --workspace --all-features --document-private-items
Expected Outcome
warning: unresolved link to `derive_deftly_template_Later`
Actual Output
No errors, but documentation has a broken link. To see the broken link:
- In your browser, visit the local path printed by the rustdoc rune
- Click on the entry for the macro
derive_deftly_template_Earlier
- Click on the word "Reference" (which is the start of the body of the doc comment)
- You're now visiting a file
.../rust-experiments/target/doc/foo/later which does not exist
Version
rustdoc 1.96.0-beta.5 (a5a9a5438 2026-05-01)
Additional Details
Ordering is relevant
AFAICT the link is not resolved because it's a forward reference to a macro_rules macro. Swapping the definition of later to before Earlier makes the link resolve.
Exporting is relevant
Adding #[macro_export] to the definition of later, or the export keyword to the derive-deflty call (which causes the proc-macro-generated proc_macro macro to have #[macro_export]), seems to make it work.
Either spans, or merely the involvement of a proc macro, seem to be relevant
I was not able to reproduce this without derive-deftly. The define_derive_deftly call in lib.rs expands to this:
#[doc = " Doc comment"] #[doc = ""] #[doc = " [Reference](later)"]
#[doc =
"\n\nThis is a `derive_deftly` template. Do not invoke it directly.\nTo use it, write: `#[derive(Deftly)] #[derive_deftly(Earlier)]`."]
macro_rules! derive_deftly_template_Earlier
{
[ actual macro body elided for clarity ]
}
You can get that output yourself with a rune like this:
RUSTFLAGS="--cfg derive_deftly_dprint" DERIVE_DEFTLY_DPRINT=1 cargo doc --workspace --all-features --document-private-items
But, when I replace the define_derive_deftly! call with a cut-and-paste of the whole expansion (as printed via the TokenStream's Display impl) the bug goes away: I get the warning as expected.
I experimented with replacing the define_derive_deftly call with a macro_rules macro whose expansion was a c&p of the derive-deftly output, but that also didn't repro the bug.
It may be relevant that for Reasons, derive-deftly respans its output using .resolved_at(Span::call_site) . For terminal TTs it then uses .set_span(). For groups it must use Group::new (via proc_macro2::Group::new). The precise code can be seen here https://gitlab.torproject.org/Diziet/rust-derive-deftly/-/blob/03493e38977328ce1cfd89bad38a868538479419/macros/utils.rs#L200
However, I don't think any of this should cause rustdoc to emit HTML containing broken links.
Code
https://www.chiark.greenend.org.uk/ucgi/~ian/git?p=rust-experiments.git;a=shortlog;h=refs/heads/rustdoc-link-2026-06
Reproduction Steps
Expected Outcome
Actual Output
No errors, but documentation has a broken link. To see the broken link:
derive_deftly_template_Earlier.../rust-experiments/target/doc/foo/laterwhich does not existVersion
Additional Details
Ordering is relevant
AFAICT the link is not resolved because it's a forward reference to a macro_rules macro. Swapping the definition of
laterto beforeEarliermakes the link resolve.Exporting is relevant
Adding
#[macro_export]to the definition oflater, or theexportkeyword to the derive-deflty call (which causes the proc-macro-generated proc_macro macro to have#[macro_export]), seems to make it work.Either spans, or merely the involvement of a proc macro, seem to be relevant
I was not able to reproduce this without derive-deftly. The
define_derive_deftlycall inlib.rsexpands to this:You can get that output yourself with a rune like this:
RUSTFLAGS="--cfg derive_deftly_dprint" DERIVE_DEFTLY_DPRINT=1 cargo doc --workspace --all-features --document-private-itemsBut, when I replace the
define_derive_deftly!call with a cut-and-paste of the whole expansion (as printed via theTokenStream'sDisplayimpl) the bug goes away: I get the warning as expected.I experimented with replacing the
define_derive_deftlycall with a macro_rules macro whose expansion was a c&p of the derive-deftly output, but that also didn't repro the bug.It may be relevant that for Reasons, derive-deftly respans its output using
.resolved_at(Span::call_site). For terminal TTs it then uses.set_span(). For groups it must useGroup::new(viaproc_macro2::Group::new). The precise code can be seen here https://gitlab.torproject.org/Diziet/rust-derive-deftly/-/blob/03493e38977328ce1cfd89bad38a868538479419/macros/utils.rs#L200However, I don't think any of this should cause rustdoc to emit HTML containing broken links.