diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 1bcfdf0faf66a..c2db2c82fa1c4 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -832,10 +832,15 @@ impl Visitor<'tcx> for EmbargoVisitor<'tcx> { } fn visit_macro_def(&mut self, md: &'tcx hir::MacroDef<'tcx>) { + // Non-opaque macros cannot make other items more accessible than they already are. if attr::find_transparency(&self.tcx.sess, &md.attrs, md.ast.macro_rules).0 != Transparency::Opaque { - self.update(md.hir_id, Some(AccessLevel::Public)); + // `#[macro_export]`-ed `macro_rules!` are `Public` since they + // ignore their containing path to always appear at the crate root. + if md.ast.macro_rules { + self.update(md.hir_id, Some(AccessLevel::Public)); + } return; } diff --git a/library/std/src/prelude/v1.rs b/library/std/src/prelude/v1.rs index 0fbd6b62f18ff..26302d0ecf2b1 100644 --- a/library/std/src/prelude/v1.rs +++ b/library/std/src/prelude/v1.rs @@ -41,17 +41,17 @@ pub use crate::result::Result::{self, Err, Ok}; pub use core::prelude::v1::{ asm, assert, cfg, column, compile_error, concat, concat_idents, env, file, format_args, format_args_nl, global_asm, include, include_bytes, include_str, line, llvm_asm, log_syntax, - module_path, option_env, stringify, trace_macros, + module_path, option_env, stringify, trace_macros, Clone, Copy, Debug, Default, Eq, Hash, Ord, + PartialEq, PartialOrd, }; -// FIXME: Attribute and derive macros are not documented because for them rustdoc generates +// FIXME: Attribute and internal derive macros are not documented because for them rustdoc generates // dead links which fail link checker testing. #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] #[allow(deprecated)] #[doc(hidden)] pub use core::prelude::v1::{ - bench, global_allocator, test, test_case, Clone, Copy, Debug, Default, Eq, Hash, Ord, - PartialEq, PartialOrd, RustcDecodable, RustcEncodable, + bench, global_allocator, test, test_case, RustcDecodable, RustcEncodable, }; #[unstable( diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index ed972cc16e954..ee45f74666a54 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -169,7 +169,17 @@ crate fn record_extern_fqn(cx: &DocContext<'_>, did: DefId, kind: clean::TypeKin if !s.is_empty() { Some(s) } else { None } }); let fqn = if let clean::TypeKind::Macro = kind { - vec![crate_name, relative.last().expect("relative was empty")] + // Check to see if it is a macro 2.0 or built-in macro + if matches!( + cx.enter_resolver(|r| r.cstore().load_macro_untracked(did, cx.sess())), + LoadedMacro::MacroDef(def, _) + if matches!(&def.kind, ast::ItemKind::MacroDef(ast_def) + if !ast_def.macro_rules) + ) { + once(crate_name).chain(relative).collect() + } else { + vec![crate_name, relative.last().expect("relative was empty")] + } } else { once(crate_name).chain(relative).collect() }; diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index 3c0aeaad43e5d..1fedd26a1ef2f 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -61,20 +61,60 @@ 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 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; + // Note: this is not the same as `.parent_module()`. Indeed, the latter looks + // for the closest module _ancestor_, which is not necessarily a direct parent + // (since a direct parent isn't necessarily a module, c.f. #77828). + let macro_parent_def_id = { + use rustc_middle::ty::DefIdTree; + tcx.parent(tcx.hir().local_def_id(def.hir_id).to_def_id()).unwrap() + }; + let macro_parent_path = tcx.def_path(macro_parent_def_id); + // 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. + let mut cur_mod = &mut top_level_module; + for path_segment in macro_parent_path.data { + // Path segments may refer to a module (in which case they belong to the type + // namespace), which is _necessary_ for the macro to be accessible outside it + // (no "associated macros" as of yet). Else we bail with an outer `continue`. + let path_segment_ty_ns = match path_segment.data { + rustc_hir::definitions::DefPathData::TypeNs(symbol) => symbol, + _ => continue 'exported_macros, + }; + // Descend into the child module that matches this path segment (if any). + match cur_mod.mods.iter_mut().find(|child| child.name == Some(path_segment_ty_ns)) { + Some(child_mod) => cur_mod = &mut *child_mod, + None => continue 'exported_macros, + } + } + let cur_mod_def_id = tcx.hir().local_def_id(cur_mod.id).to_def_id(); + assert_eq!(cur_mod_def_id, macro_parent_def_id); + cur_mod.macros.push((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/auxiliary/macro_pub_in_module.rs b/src/test/rustdoc/auxiliary/macro_pub_in_module.rs new file mode 100644 index 0000000000000..137b12386007c --- /dev/null +++ b/src/test/rustdoc/auxiliary/macro_pub_in_module.rs @@ -0,0 +1,13 @@ +// edition:2018 + +#![feature(decl_macro)] +#![crate_name = "external_crate"] + +pub mod some_module { + /* == Make sure the logic is not affected by a re-export == */ + mod private { + pub macro external_macro() {} + } + + pub use private::external_macro; +} 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..4fd85d6899401 --- /dev/null +++ b/src/test/rustdoc/macro_pub_in_module.rs @@ -0,0 +1,82 @@ +// aux-build:macro_pub_in_module.rs +// edition:2018 +// build-aux-docs + +//! See issue #74355 +#![feature(decl_macro, no_core, rustc_attrs)] +#![crate_name = "krate"] +#![no_core] + + // @has external_crate/some_module/macro.external_macro.html + // @!has external_crate/macro.external_macro.html +extern crate external_crate; + +pub mod inner { + // @has krate/inner/macro.raw_const.html + // @!has krate/macro.raw_const.html + pub macro raw_const() {} + + // @has krate/inner/macro.test.html + // @!has krate/macro.test.html + #[rustc_builtin_macro] + pub macro test($item:item) {} + + // @has krate/inner/macro.Clone.html + // @!has krate/macro.Clone.html + #[rustc_builtin_macro] + pub macro Clone($item:item) {} + + // Make sure the logic is not affected by re-exports. + mod unrenamed { + // @!has krate/macro.unrenamed.html + #[rustc_macro_transparency = "semitransparent"] + pub macro unrenamed() {} + } + // @has krate/inner/macro.unrenamed.html + pub use unrenamed::unrenamed; + + mod private { + // @!has krate/macro.m.html + pub macro m() {} + } + // @has krate/inner/macro.renamed.html + // @!has krate/macro.renamed.html + pub use private::m as renamed; + + mod private2 { + // @!has krate/macro.m2.html + pub macro m2() {} + } + use private2 as renamed_mod; + // @has krate/inner/macro.m2.html + pub use renamed_mod::m2; + + // @has krate/inner/macro.external_macro.html + // @!has krate/macro.external_macro.html + pub use ::external_crate::some_module::external_macro; +} + +// Namespaces: Make sure the logic does not mix up a function name with a module nameā€¦ +fn both_fn_and_mod() { + // @!has krate/macro.in_both_fn_and_mod.html + pub macro in_both_fn_and_mod() {} +} +pub mod both_fn_and_mod { + // @!has krate/both_fn_and_mod/macro.in_both_fn_and_mod.html +} + +const __: () = { + // @!has krate/macro.in_both_const_and_mod.html + pub macro in_both_const_and_mod() {} +}; +pub mod __ { + // @!has krate/__/macro.in_both_const_and_mod.html +} + +enum Enum { + Crazy = { + // @!has krate/macro.this_is_getting_weird.html; + pub macro this_is_getting_weird() {} + 42 + }, +}