Change:
} crate fn visit(mut self, krate: &'tcx hir::Crate<'_>) -> Module<'tcx> { <del> let mut module = self.visit_mod_contents( </del> <ins> let mut top_level_module = self.visit_mod_contents( </ins> krate.item.span, &Spanned { span: rustc_span::DUMMY_SP, node: hir::VisibilityKind::Public }, hir::CRATE_HIR_ID, &krate.item.module, None, ); <del> // Attach the crate's exported macros to the top-level module: module.macros.extend(krate.exported_macros.iter().map(|def| (def, None))); module.is_crate = true; </del> <ins> top_level_module.is_crate = true; // Attach the crate's exported macros to the top-level module. // In the case of macros 2.0 (`pub macro`), and for built-in `derive`s or attributes as // well (_e.g._, `Copy`), these are wrongly bundled in there too, so we need to fix that by // moving them back to their correct locations. 'exported_macros: for def in krate.exported_macros { // The `def` of a macro in `exported_macros` should correspond to either: //  - a `#[macro-export] macro_rules!` macro, //  - a built-in `derive` (or attribute) macro such as the ones in `::core`, //  - a `pub macro`. // Only the last two need to be fixed, thus: if def.ast.macro_rules { top_level_module.macros.push((def, None)); continue 'exported_macros; } let tcx = self.cx.tcx; /* Because of #77828 we cannot do the simpler: let macro_parent_module = tcx.def_path(tcx.parent_module(def.hir_id).to_def_id()); // and instead have to do: */ let macro_parent_module = tcx.def_path({ use rustc_middle::ty::DefIdTree; tcx.parent(tcx.hir().local_def_id(def.hir_id).to_def_id()).unwrap() }); // HACK: rustdoc has no way to lookup `doctree::Module`s by their HirId. Instead, // lookup the module by its name, by looking at each path segment one at a time. // Once #80415 is merged, this whole `for` loop research can be replaced by that. let mut cur_mod = &mut top_level_module; for path_segment in macro_parent_module.data { let path_segment_ty_ns = match path_segment.data { rustc_hir::definitions::DefPathData::TypeNs(symbol) => symbol, _ => { // If the path segment is not from the type namespace // (_e.g._, it can be from a value namespace in the case of `f::` in: // `fn f() { pub macro m() {} }` // then the item is not accessible, and should thus act as if it didn't // exist (unless "associated macros" (inside an `impl`) were a thing…). continue 'exported_macros; } }; // The obtained name in the type namespace may belong to something that is not // a `mod`ule (_e.g._, it could be an `enum` with a `pub macro` defined within // the block used for a discriminant. if let Some(child_mod) = cur_mod.mods.iter_mut().find(|module| module.name == Some(path_segment_ty_ns)) { cur_mod = child_mod; } else { // If the macro's parent def path is not exclusively made of module // components, then it is not accessible (c.f. previous `continue`). continue 'exported_macros; } } cur_mod.macros.push((def, None)); } </ins> self.cx.renderinfo.get_mut().exact_paths = self.exact_paths; <del> module </del> <ins> top_level_module </ins> } fn visit_mod_contents(

Discussion:
rustdoc is showing the macro at the wrong path in libcore: says but really it is (and the same goes for ). Looks like is not handled correctly? Cc
cc
When a macro is marked as reexported, you can use it directly from the "top", so in this regard, isn't it the expected behavior?
AFAIK that is not true for macros, just for macros.
Note that there is no here. This macro is exported via , like normal items (functions, structs, ...).
Code example: So I think there is definitely a rustdoc bug here. The path it shows does not work.
Oh I see. Indeed, it's a bug.
Not sure if helpful, but the way we got public exports to work in rustdoc in was by creating a separate crate (), and then re-exporting from from the main crate. This enabled us to render  in rustdoc. This issue will also affect the upcoming  PR.
how hard is it to fix this problem, and what does the rustdoc team's prioritization for this look like? We'd like to stabilize , and I am not sure if we want to stabilize this while its docs are rendered incorrectly.
Sorry but no idea. I don't have much time lately so maybe someone else from the will give it a try? Otherwise I'll try to fix it as soon as I have time.
Yeah so the problem here is that we have TypeKind::Macro which forces all macros to live at the root. Given a Def or a Res, how do I tell if a macro is MBE or a newstyle macro? (There's a possibility that our current code actually handles this correctly already and we need to remove the special case for TypeKind::Macro)
Okay so here are the two offending bits of code: The latter is more about linking to these items rather than rendering them. So the problem is that by the time stuff gets to rustdoc, we're looking at the HIR and the two kinds of macros are squashed together on . The newstyle macros are not a variant of at all, they disappear from the HIR tree at that point and only exist as an array on the crate. It would be nice if MBEs stayed on and newstyle macros were actually Items or at least stored separately on the module.

We could handle these as we see their containing module, but that seems prone to issues when there are reexports and private modules in the mix. I think having newstyle macros live on rather than is a mistake.
An option to do this purely in rustdoc would be to check the and insert it in the appropriate modules after the fact, however this is kinda icky and i don't think this will be the last tooling problem encountered by newstyle macros living at the crate root in HIR.
will probably know best how to interpret this information; somehow macro name resolution also has to work with this, after all.
I don't think name resolution actually operates on the hir that way. But yeah, worth letting him answer.
I think you can recover the module they came from using the on it.
You can tell whether it's a macro or a by looking at .