From 0bee80210d1e155b5502e51b693b6c478ed29efd Mon Sep 17 00:00:00 2001 From: Daniel Henry-Mantilla Date: Mon, 12 Oct 2020 19:50:12 +0200 Subject: [PATCH] Rustdoc: Fix macros 2.0 and built-in derives being shown at the wrong path. Fixes #74355 The issue with the built-in derives may be related to: https://github.com/rust-lang/rust/issues/55482#issuecomment-434035721 --- src/librustdoc/visit_ast.rs | 56 ++++++++++++++++++++++--- src/test/rustdoc/macro_pub_in_module.rs | 9 ++++ 2 files changed, 60 insertions(+), 5 deletions(-) create mode 100644 src/test/rustdoc/macro_pub_in_module.rs diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index 3c0aeaad43e5d..8508af080bebf 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -61,20 +61,66 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { } crate fn visit(mut self, krate: &'tcx hir::Crate<'_>) -> Module<'tcx> { - let mut module = self.visit_mod_contents( + let mut top_level_module = self.visit_mod_contents( krate.item.span, &Spanned { span: rustc_span::DUMMY_SP, node: hir::VisibilityKind::Public }, hir::CRATE_HIR_ID, &krate.item.module, None, ); - // 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; + 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 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. + krate.exported_macros.iter().for_each(|def| { + macro_rules! try_some {($($body:tt)*) => ({ + fn fn_once R> (f: F) -> F { f } + fn_once(|| Some({ $($body)* }))() + })} + // In the case of dummy items, some of the following operations may fail. We propagate + // that within a `?`-capturing block, so as to fallback to the basic behavior. + let containing_module_of_def = try_some! { + // The `def` of a macro in `exported_macros` should correspond to either: + // - a `#[macro-export] macro_rules!` macro, + // - a built-in `derive` macro such as the ones in `::core`, + // - a `pub macro`. + // Only the last two need to be fixed, thus: + if def.ast.macro_rules { + return None; + } + let macro_parent_module = self.cx.tcx.def_path({ + use rustc_middle::ty::DefIdTree; + self.cx + .tcx + /* Because of #77828 we cannot do the simpler: + .parent_module(def.hir_id).to_def_id() + // and instead have to do: */ + .parent(self.cx.tcx.hir().local_def_id(def.hir_id).to_def_id())? + }); + let mut cur_mod = &mut top_level_module; + for path_segment in macro_parent_module.data { + let path_segment = path_segment.to_string(); + cur_mod = cur_mod.mods.iter_mut().find(|module| { + matches!( + module.name, Some(symbol) + if symbol.with(|mod_name| mod_name == path_segment) + ) + })?; + } + cur_mod + }; + if let Some(module) = containing_module_of_def { + &mut module.macros + } else { + &mut top_level_module.macros + } + .push(self.visit_local_macro(def, None)); + }); self.cx.renderinfo.get_mut().exact_paths = self.exact_paths; - module + top_level_module } fn visit_mod_contents( diff --git a/src/test/rustdoc/macro_pub_in_module.rs b/src/test/rustdoc/macro_pub_in_module.rs new file mode 100644 index 0000000000000..2dfc6b4170668 --- /dev/null +++ b/src/test/rustdoc/macro_pub_in_module.rs @@ -0,0 +1,9 @@ +//! See issue #74355 +#![crate_name = "krate"] +#![feature(decl_macro)] + +// @has krate/some_module/macro.my_macro.html +pub mod some_module { + // + pub macro my_macro() {} +}