about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDaniel Henry-Mantilla <daniel.henry.mantilla@gmail.com>2020-10-12 19:50:12 +0200
committerDaniel Henry-Mantilla <daniel.henry.mantilla@gmail.com>2021-01-06 15:13:38 +0100
commit0bee80210d1e155b5502e51b693b6c478ed29efd (patch)
tree0fd4aba99622724360d13f4c74cf93455cafcfaf
parent8fec6c7bb9f2a549b6f424b3dd1c2c257286bc6d (diff)
downloadrust-0bee80210d1e155b5502e51b693b6c478ed29efd.tar.gz
rust-0bee80210d1e155b5502e51b693b6c478ed29efd.zip
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
-rw-r--r--src/librustdoc/visit_ast.rs56
-rw-r--r--src/test/rustdoc/macro_pub_in_module.rs9
2 files changed, 60 insertions, 5 deletions
diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs
index 3c0aeaad43e..8508af080be 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: FnOnce() -> 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 00000000000..2dfc6b41706
--- /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() {}
+}