Skip to content

broken link in respanned macro-generated reference to macro #157304

@ijackson

Description

@ijackson

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:

  1. In your browser, visit the local path printed by the rustdoc rune
  2. Click on the entry for the macro derive_deftly_template_Earlier
  3. Click on the word "Reference" (which is the start of the body of the doc comment)
  4. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-bugCategory: This is a bug.T-rustdocRelevant to the rustdoc team, which will review and decide on the PR/issue.needs-triageThis issue may need triage. Remove it if it has been sufficiently triaged.

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions