Auto merge of #77862 - danielhenrymantilla:rustdoc/fix-macros_2_0-paths, r=jyn514,petrochenkov

Rustdoc: Fix macros 2.0 and built-in derives being shown at the wrong path

Fixes #74355

  - ~~waiting on author + draft PR since my code ought to be cleaned up _w.r.t._ the way I avoid the `.unwrap()`s:~~

      - ~~dummy items may avoid the first `?`,~~

      - ~~but within the module traversal some tests did fail (hence the second `?`), meaning the crate did not possess the exact path of the containing module (`extern` / `impl` blocks maybe? I'll look into that).~~

r? `@jyn514`
This commit is contained in:
bors 2021-01-10 05:15:01 +00:00
commit 7a193921a0
6 changed files with 163 additions and 13 deletions

View file

@ -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;
}

View file

@ -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(

View file

@ -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()
};

View file

@ -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(

View file

@ -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;
}

View file

@ -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
},
}