Auto merge of #88019 - inquisitivecrystal:macro-def, r=cjgillot

Treat macros as HIR items

Macros have historically been treated differently from other items at the HIR level. This PR makes them just like normal items. There are a few special cases left over, which I've attempted to lay out below. By normalizing the treatment of macro items, this PR simplifies a fair bit of code and fixes some bugs at the same time. For more information, see #87406.

r? `@cjgillot`

## Backwards incompatibility

This is backwards incompatible in one small way. Due to a mistake, it was previously possible to apply stability attributes to an exported macro, even without enabling the `staged_api` feature. This never should have worked. After this PR, it will error, as it should. We could make it a warning instead, but that would require a special case for a feature that shouldn't ever have worked and is likely used by no or very few crates, so I'm not thrilled about the idea.

## Notes for reviewers
### Commit seperation

I'd recommend reviewing this PR commit by commit. The commit chunking wasn't perfect, but it's better than looking at the combined diff, which is quite overwhelming. The compiler and standard library build after each commit, although tests do not necessarily pass and tools do not necessarily build till the end of the series.

### Special cases
There are a few special cases that remain after this change. Here are the notable ones I remember:

1. Visibility works a bit differently for `macro_rules!` macros than other items, since they aren't generally marked with `pub` but instead with `#[macro_export]`.
2. Since `#[macro_export]` macros always have paths at the top level of the crate, some additional handling needs to be done on the reexport to top level.
### Performance impact

I don't know for sure, but theses changes may slightly hurt performance. They create more work for the compiler in a few places. For instance, some operations that were previously run only for exported macros are now run for all macros. A perf run is probably advisable. For all I know we'll see performance improvements instead. :)

## Issues resolved

This resolves #87406 (the tracking issue for this change). It also fixes several bugs:

Fixes #59306.
Fixes #73754.
Fixes #87257.
This commit is contained in:
bors 2021-08-28 10:45:28 +00:00
commit 05cccdc9b3
46 changed files with 338 additions and 396 deletions

View File

@ -4146,6 +4146,7 @@ dependencies = [
name = "rustc_privacy"
version = "0.0.0"
dependencies = [
"rustc_ast",
"rustc_attr",
"rustc_data_structures",
"rustc_errors",

View File

@ -170,7 +170,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
self.lower_item_id_use_tree(use_tree, i.id, &mut vec);
vec
}
ItemKind::MacroDef(..) => SmallVec::new(),
ItemKind::Fn(..) | ItemKind::Impl(box ImplKind { of_trait: None, .. }) => {
smallvec![i.id]
}
@ -212,28 +211,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
pub fn lower_item(&mut self, i: &Item) -> Option<hir::Item<'hir>> {
let mut ident = i.ident;
let mut vis = self.lower_visibility(&i.vis, None);
if let ItemKind::MacroDef(MacroDef { ref body, macro_rules }) = i.kind {
if !macro_rules || self.sess.contains_name(&i.attrs, sym::macro_export) {
let hir_id = self.lower_node_id(i.id);
self.lower_attrs(hir_id, &i.attrs);
let body = P(self.lower_mac_args(body));
self.insert_macro_def(hir::MacroDef {
ident,
vis,
def_id: hir_id.expect_owner(),
span: i.span,
ast: MacroDef { body, macro_rules },
});
} else {
for a in i.attrs.iter() {
let a = self.lower_attr(a);
self.non_exported_macro_attrs.push(a);
}
}
return None;
}
let hir_id = self.lower_node_id(i.id);
let attrs = self.lower_attrs(hir_id, &i.attrs);
let kind = self.lower_item_kind(i.span, i.id, hir_id, &mut ident, attrs, &mut vis, &i.kind);
@ -465,7 +442,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
self.lower_generics(generics, ImplTraitContext::disallowed()),
self.lower_param_bounds(bounds, ImplTraitContext::disallowed()),
),
ItemKind::MacroDef(..) | ItemKind::MacCall(..) => {
ItemKind::MacroDef(MacroDef { ref body, macro_rules }) => {
let body = P(self.lower_mac_args(body));
hir::ItemKind::Macro(ast::MacroDef { body, macro_rules })
}
ItemKind::MacCall(..) => {
panic!("`TyMac` should have been expanded by now")
}
}

View File

@ -103,7 +103,6 @@ struct LoweringContext<'a, 'hir: 'a> {
/// The items being lowered are collected here.
owners: IndexVec<LocalDefId, Option<hir::OwnerNode<'hir>>>,
bodies: BTreeMap<hir::BodyId, hir::Body<'hir>>,
non_exported_macro_attrs: Vec<ast::Attribute>,
trait_impls: BTreeMap<DefId, Vec<LocalDefId>>,
@ -330,7 +329,6 @@ pub fn lower_crate<'a, 'hir>(
trait_impls: BTreeMap::new(),
modules: BTreeMap::new(),
attrs: BTreeMap::default(),
non_exported_macro_attrs: Vec::new(),
catch_scopes: Vec::new(),
loop_scopes: Vec::new(),
is_in_loop_condition: false,
@ -551,7 +549,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
}
let krate = hir::Crate {
non_exported_macro_attrs: self.arena.alloc_from_iter(self.non_exported_macro_attrs),
owners: self.owners,
bodies: self.bodies,
body_ids,
@ -600,13 +597,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
id
}
fn insert_macro_def(&mut self, item: hir::MacroDef<'hir>) {
let def_id = item.def_id;
let item = self.arena.alloc(item);
self.owners.ensure_contains_elem(def_id, || None);
self.owners[def_id] = Some(hir::OwnerNode::MacroDef(item));
}
fn allocate_hir_id_counter(&mut self, owner: NodeId) -> hir::HirId {
// Set up the counter if needed.
self.item_local_id_counters.entry(owner).or_insert(0);

View File

@ -578,6 +578,33 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
}
}
fn print_mac_def(
&mut self,
macro_def: &ast::MacroDef,
ident: &Ident,
sp: &Span,
print_visibility: impl FnOnce(&mut Self),
) {
let (kw, has_bang) = if macro_def.macro_rules {
("macro_rules", true)
} else {
print_visibility(self);
("macro", false)
};
self.print_mac_common(
Some(MacHeader::Keyword(kw)),
has_bang,
Some(*ident),
macro_def.body.delim(),
&macro_def.body.inner_tokens(),
true,
*sp,
);
if macro_def.body.need_semicolon() {
self.word(";");
}
}
fn print_path(&mut self, path: &ast::Path, colons_before_params: bool, depth: usize) {
self.maybe_print_comment(path.span.lo());
@ -1305,24 +1332,9 @@ impl<'a> State<'a> {
}
}
ast::ItemKind::MacroDef(ref macro_def) => {
let (kw, has_bang) = if macro_def.macro_rules {
("macro_rules", true)
} else {
self.print_visibility(&item.vis);
("macro", false)
};
self.print_mac_common(
Some(MacHeader::Keyword(kw)),
has_bang,
Some(item.ident),
macro_def.body.delim(),
&macro_def.body.inner_tokens(),
true,
item.span,
);
if macro_def.body.need_semicolon() {
self.word(";");
}
self.print_mac_def(macro_def, &item.ident, &item.span, |state| {
state.print_visibility(&item.vis)
});
}
}
self.ann.post(self, AnnNode::Item(item))

View File

@ -35,7 +35,6 @@ macro_rules! arena_types {
[few] inline_asm: rustc_hir::InlineAsm<$tcx>,
[few] llvm_inline_asm: rustc_hir::LlvmInlineAsm<$tcx>,
[] local: rustc_hir::Local<$tcx>,
[few] macro_def: rustc_hir::MacroDef<$tcx>,
[few] mod_: rustc_hir::Mod<$tcx>,
[] param: rustc_hir::Param<$tcx>,
[] pat: rustc_hir::Pat<$tcx>,

View File

@ -670,9 +670,6 @@ pub struct ModuleItems {
/// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/hir.html
#[derive(Debug)]
pub struct Crate<'hir> {
// Attributes from non-exported macros, kept only for collecting the library feature list.
pub non_exported_macro_attrs: &'hir [Attribute],
pub owners: IndexVec<LocalDefId, Option<OwnerNode<'hir>>>,
pub bodies: BTreeMap<BodyId, Body<'hir>>,
pub trait_impls: BTreeMap<DefId, Vec<LocalDefId>>,
@ -743,7 +740,7 @@ impl Crate<'_> {
OwnerNode::ForeignItem(item) => visitor.visit_foreign_item(item),
OwnerNode::ImplItem(item) => visitor.visit_impl_item(item),
OwnerNode::TraitItem(item) => visitor.visit_trait_item(item),
OwnerNode::MacroDef(_) | OwnerNode::Crate(_) => {}
OwnerNode::Crate(_) => {}
}
}
}
@ -758,7 +755,7 @@ impl Crate<'_> {
Some(OwnerNode::ForeignItem(item)) => visitor.visit_foreign_item(item),
Some(OwnerNode::ImplItem(item)) => visitor.visit_impl_item(item),
Some(OwnerNode::TraitItem(item)) => visitor.visit_trait_item(item),
Some(OwnerNode::MacroDef(_)) | Some(OwnerNode::Crate(_)) | None => {}
Some(OwnerNode::Crate(_)) | None => {}
})
}
@ -768,32 +765,6 @@ impl Crate<'_> {
_ => None,
})
}
pub fn exported_macros<'hir>(&'hir self) -> impl Iterator<Item = &'hir MacroDef<'hir>> + 'hir {
self.owners.iter().filter_map(|owner| match owner {
Some(OwnerNode::MacroDef(macro_def)) => Some(*macro_def),
_ => None,
})
}
}
/// A macro definition, in this crate or imported from another.
///
/// Not parsed directly, but created on macro import or `macro_rules!` expansion.
#[derive(Debug)]
pub struct MacroDef<'hir> {
pub ident: Ident,
pub vis: Visibility<'hir>,
pub def_id: LocalDefId,
pub span: Span,
pub ast: ast::MacroDef,
}
impl MacroDef<'_> {
#[inline]
pub fn hir_id(&self) -> HirId {
HirId::make_owner(self.def_id)
}
}
/// A block of statements `{ .. }`, which may have a label (in this case the
@ -2602,7 +2573,7 @@ pub struct PolyTraitRef<'hir> {
pub type Visibility<'hir> = Spanned<VisibilityKind<'hir>>;
#[derive(Debug)]
#[derive(Copy, Clone, Debug)]
pub enum VisibilityKind<'hir> {
Public,
Crate(CrateSugar),
@ -2791,6 +2762,8 @@ pub enum ItemKind<'hir> {
Const(&'hir Ty<'hir>, BodyId),
/// A function declaration.
Fn(FnSig<'hir>, Generics<'hir>, BodyId),
/// A MBE macro definition (`macro_rules!` or `macro`).
Macro(ast::MacroDef),
/// A module.
Mod(Mod<'hir>),
/// An external module, e.g. `extern { .. }`.
@ -2856,6 +2829,7 @@ impl ItemKind<'_> {
ItemKind::Static(..) => "static item",
ItemKind::Const(..) => "constant item",
ItemKind::Fn(..) => "function",
ItemKind::Macro(..) => "macro",
ItemKind::Mod(..) => "module",
ItemKind::ForeignMod { .. } => "extern block",
ItemKind::GlobalAsm(..) => "global asm item",
@ -2996,7 +2970,6 @@ pub enum OwnerNode<'hir> {
ForeignItem(&'hir ForeignItem<'hir>),
TraitItem(&'hir TraitItem<'hir>),
ImplItem(&'hir ImplItem<'hir>),
MacroDef(&'hir MacroDef<'hir>),
Crate(&'hir Mod<'hir>),
}
@ -3006,8 +2979,7 @@ impl<'hir> OwnerNode<'hir> {
OwnerNode::Item(Item { ident, .. })
| OwnerNode::ForeignItem(ForeignItem { ident, .. })
| OwnerNode::ImplItem(ImplItem { ident, .. })
| OwnerNode::TraitItem(TraitItem { ident, .. })
| OwnerNode::MacroDef(MacroDef { ident, .. }) => Some(*ident),
| OwnerNode::TraitItem(TraitItem { ident, .. }) => Some(*ident),
OwnerNode::Crate(..) => None,
}
}
@ -3018,7 +2990,6 @@ impl<'hir> OwnerNode<'hir> {
| OwnerNode::ForeignItem(ForeignItem { span, .. })
| OwnerNode::ImplItem(ImplItem { span, .. })
| OwnerNode::TraitItem(TraitItem { span, .. })
| OwnerNode::MacroDef(MacroDef { span, .. })
| OwnerNode::Crate(Mod { inner: span, .. }) => *span,
}
}
@ -3062,8 +3033,7 @@ impl<'hir> OwnerNode<'hir> {
OwnerNode::Item(Item { def_id, .. })
| OwnerNode::TraitItem(TraitItem { def_id, .. })
| OwnerNode::ImplItem(ImplItem { def_id, .. })
| OwnerNode::ForeignItem(ForeignItem { def_id, .. })
| OwnerNode::MacroDef(MacroDef { def_id, .. }) => *def_id,
| OwnerNode::ForeignItem(ForeignItem { def_id, .. }) => *def_id,
OwnerNode::Crate(..) => crate::CRATE_HIR_ID.owner,
}
}
@ -3095,13 +3065,6 @@ impl<'hir> OwnerNode<'hir> {
_ => panic!(),
}
}
pub fn expect_macro_def(self) -> &'hir MacroDef<'hir> {
match self {
OwnerNode::MacroDef(n) => n,
_ => panic!(),
}
}
}
impl<'hir> Into<OwnerNode<'hir>> for &'hir Item<'hir> {
@ -3128,12 +3091,6 @@ impl<'hir> Into<OwnerNode<'hir>> for &'hir TraitItem<'hir> {
}
}
impl<'hir> Into<OwnerNode<'hir>> for &'hir MacroDef<'hir> {
fn into(self) -> OwnerNode<'hir> {
OwnerNode::MacroDef(self)
}
}
impl<'hir> Into<Node<'hir>> for OwnerNode<'hir> {
fn into(self) -> Node<'hir> {
match self {
@ -3141,7 +3098,6 @@ impl<'hir> Into<Node<'hir>> for OwnerNode<'hir> {
OwnerNode::ForeignItem(n) => Node::ForeignItem(n),
OwnerNode::ImplItem(n) => Node::ImplItem(n),
OwnerNode::TraitItem(n) => Node::TraitItem(n),
OwnerNode::MacroDef(n) => Node::MacroDef(n),
OwnerNode::Crate(n) => Node::Crate(n),
}
}
@ -3167,7 +3123,6 @@ pub enum Node<'hir> {
Arm(&'hir Arm<'hir>),
Block(&'hir Block<'hir>),
Local(&'hir Local<'hir>),
MacroDef(&'hir MacroDef<'hir>),
/// `Ctor` refers to the constructor of an enum variant or struct. Only tuple or unit variants
/// with synthesized constructors.
@ -3204,7 +3159,6 @@ impl<'hir> Node<'hir> {
| Node::ForeignItem(ForeignItem { ident, .. })
| Node::Field(FieldDef { ident, .. })
| Node::Variant(Variant { ident, .. })
| Node::MacroDef(MacroDef { ident, .. })
| Node::Item(Item { ident, .. })
| Node::PathSegment(PathSegment { ident, .. }) => Some(*ident),
Node::Lifetime(lt) => Some(lt.name.ident()),
@ -3265,8 +3219,7 @@ impl<'hir> Node<'hir> {
Node::Item(Item { def_id, .. })
| Node::TraitItem(TraitItem { def_id, .. })
| Node::ImplItem(ImplItem { def_id, .. })
| Node::ForeignItem(ForeignItem { def_id, .. })
| Node::MacroDef(MacroDef { def_id, .. }) => Some(HirId::make_owner(*def_id)),
| Node::ForeignItem(ForeignItem { def_id, .. }) => Some(HirId::make_owner(*def_id)),
Node::Field(FieldDef { hir_id, .. })
| Node::AnonConst(AnonConst { hir_id, .. })
| Node::Expr(Expr { hir_id, .. })
@ -3326,7 +3279,6 @@ impl<'hir> Node<'hir> {
Node::ForeignItem(i) => Some(OwnerNode::ForeignItem(i)),
Node::TraitItem(i) => Some(OwnerNode::TraitItem(i)),
Node::ImplItem(i) => Some(OwnerNode::ImplItem(i)),
Node::MacroDef(i) => Some(OwnerNode::MacroDef(i)),
Node::Crate(i) => Some(OwnerNode::Crate(i)),
_ => None,
}

View File

@ -466,9 +466,6 @@ pub trait Visitor<'v>: Sized {
walk_assoc_type_binding(self, type_binding)
}
fn visit_attribute(&mut self, _id: HirId, _attr: &'v Attribute) {}
fn visit_macro_def(&mut self, macro_def: &'v MacroDef<'v>) {
walk_macro_def(self, macro_def)
}
fn visit_vis(&mut self, vis: &'v Visibility<'v>) {
walk_vis(self, vis)
}
@ -484,7 +481,6 @@ pub trait Visitor<'v>: Sized {
pub fn walk_crate<'v, V: Visitor<'v>>(visitor: &mut V, krate: &'v Crate<'v>) {
let top_mod = krate.module();
visitor.visit_mod(top_mod, top_mod.inner, CRATE_HIR_ID);
walk_list!(visitor, visit_macro_def, krate.exported_macros());
for (&id, attrs) in krate.attrs.iter() {
for a in *attrs {
visitor.visit_attribute(id, a)
@ -492,11 +488,6 @@ pub fn walk_crate<'v, V: Visitor<'v>>(visitor: &mut V, krate: &'v Crate<'v>) {
}
}
pub fn walk_macro_def<'v, V: Visitor<'v>>(visitor: &mut V, macro_def: &'v MacroDef<'v>) {
visitor.visit_id(macro_def.hir_id());
visitor.visit_ident(macro_def.ident);
}
pub fn walk_mod<'v, V: Visitor<'v>>(visitor: &mut V, module: &'v Mod<'v>, mod_hir_id: HirId) {
visitor.visit_id(mod_hir_id);
for &item_id in module.item_ids {
@ -586,6 +577,9 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) {
item.span,
item.hir_id(),
),
ItemKind::Macro(_) => {
visitor.visit_id(item.hir_id());
}
ItemKind::Mod(ref module) => {
// `visit_mod()` takes care of visiting the `Item`'s `HirId`.
visitor.visit_mod(module, item.span, item.hir_id())

View File

@ -1,8 +1,8 @@
use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey};
use crate::hir::{
BodyId, Expr, ForeignItem, ForeignItemId, ImplItem, ImplItemId, Item, ItemId, MacroDef, Mod,
TraitItem, TraitItemId, Ty, VisibilityKind,
BodyId, Expr, ForeignItem, ForeignItemId, ImplItem, ImplItemId, Item, ItemId, Mod, TraitItem,
TraitItemId, Ty, VisibilityKind,
};
use crate::hir_id::{HirId, ItemLocalId};
use rustc_span::def_id::DefPathHash;
@ -190,16 +190,3 @@ impl<HirCtx: crate::HashStableContext> HashStable<HirCtx> for Item<'_> {
});
}
}
impl<HirCtx: crate::HashStableContext> HashStable<HirCtx> for MacroDef<'_> {
fn hash_stable(&self, hcx: &mut HirCtx, hasher: &mut StableHasher) {
let MacroDef { ident, def_id: _, ref ast, ref vis, span } = *self;
hcx.hash_hir_item_like(|hcx| {
ident.name.hash_stable(hcx, hasher);
ast.hash_stable(hcx, hasher);
vis.hash_stable(hcx, hasher);
span.hash_stable(hcx, hasher);
});
}
}

View File

@ -111,6 +111,7 @@ impl Target {
ItemKind::Static(..) => Target::Static,
ItemKind::Const(..) => Target::Const,
ItemKind::Fn(..) => Target::Fn,
ItemKind::Macro(..) => Target::MacroDef,
ItemKind::Mod(..) => Target::Mod,
ItemKind::ForeignMod { .. } => Target::ForeignMod,
ItemKind::GlobalAsm(..) => Target::GlobalAsm,

View File

@ -120,7 +120,6 @@ impl<'a> State<'a> {
// printing.
Node::Ctor(..) => panic!("cannot print isolated Ctor"),
Node::Local(a) => self.print_local_decl(&a),
Node::MacroDef(_) => panic!("cannot print MacroDef"),
Node::Crate(..) => panic!("cannot print Crate"),
}
}
@ -642,6 +641,11 @@ impl<'a> State<'a> {
self.end(); // need to close a box
self.ann.nested(self, Nested::Body(body));
}
hir::ItemKind::Macro(ref macro_def) => {
self.print_mac_def(macro_def, &item.ident, &item.span, |state| {
state.print_visibility(&item.vis)
});
}
hir::ItemKind::Mod(ref _mod) => {
self.head(visibility_qualified(&item.vis, "mod"));
self.print_ident(item.ident);

View File

@ -585,24 +585,6 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
fn check_crate(&mut self, cx: &LateContext<'_>, krate: &hir::Crate<'_>) {
self.check_missing_docs_attrs(cx, CRATE_DEF_ID, krate.module().inner, "the", "crate");
for macro_def in krate.exported_macros() {
// Non exported macros should be skipped, since `missing_docs` only
// applies to externally visible items.
if !cx.access_levels.is_exported(macro_def.def_id) {
continue;
}
let attrs = cx.tcx.hir().attrs(macro_def.hir_id());
let has_doc = attrs.iter().any(has_doc);
if !has_doc {
cx.struct_span_lint(
MISSING_DOCS,
cx.tcx.sess.source_map().guess_head_span(macro_def.span),
|lint| lint.build("missing documentation for macro").emit(),
);
}
}
}
fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) {
@ -636,6 +618,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
hir::ItemKind::TyAlias(..)
| hir::ItemKind::Fn(..)
| hir::ItemKind::Macro(..)
| hir::ItemKind::Mod(..)
| hir::ItemKind::Enum(..)
| hir::ItemKind::Struct(..)

View File

@ -453,10 +453,6 @@ fn late_lint_pass_crate<'tcx, T: LateLintPass<'tcx>>(tcx: TyCtxt<'tcx>, pass: T)
lint_callback!(cx, check_crate, krate);
hir_visit::walk_crate(cx, krate);
for attr in krate.non_exported_macro_attrs {
// This HIR ID is a lie, since the macro ID isn't available.
cx.visit_attribute(hir::CRATE_HIR_ID, attr);
}
lint_callback!(cx, check_crate_post, krate);
})

View File

@ -37,9 +37,6 @@ fn lint_levels(tcx: TyCtxt<'_>, (): ()) -> LintLevelMap {
let push = builder.levels.push(tcx.hir().attrs(hir::CRATE_HIR_ID), &store, true);
builder.levels.register_id(hir::CRATE_HIR_ID);
for macro_def in krate.exported_macros() {
builder.levels.register_id(macro_def.hir_id());
}
intravisit::walk_crate(&mut builder, krate);
builder.levels.pop(push);

View File

@ -1100,7 +1100,13 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
let vis = self.get_visibility(child_index);
let def_id = self.local_def_id(child_index);
let res = Res::Def(kind, def_id);
callback(Export { res, ident, vis, span });
// FIXME: Macros are currently encoded twice, once as items and once as
// reexports. We ignore the items here and only use the reexports.
if !matches!(kind, DefKind::Macro(..)) {
callback(Export { res, ident, vis, span });
}
// For non-re-export structs and variants add their constructors to children.
// Re-export lists automatically contain constructors when necessary.
match kind {

View File

@ -448,9 +448,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
}
krate.visit_all_item_likes(&mut self.as_deep_visitor());
for macro_def in krate.exported_macros() {
self.visit_macro_def(macro_def);
}
}
fn encode_def_path_table(&mut self) {
@ -1385,6 +1382,9 @@ impl EncodeContext<'a, 'tcx> {
EntryKind::Fn(self.lazy(data))
}
hir::ItemKind::Macro(ref macro_def) => {
EntryKind::MacroDef(self.lazy(macro_def.clone()))
}
hir::ItemKind::Mod(ref m) => {
return self.encode_info_for_mod(item.def_id, m);
}
@ -1539,13 +1539,6 @@ impl EncodeContext<'a, 'tcx> {
}
}
/// Serialize the text of exported macros
fn encode_info_for_macro_def(&mut self, macro_def: &hir::MacroDef<'_>) {
let def_id = macro_def.def_id.to_def_id();
record!(self.tables.kind[def_id] <- EntryKind::MacroDef(self.lazy(macro_def.ast.clone())));
self.encode_ident_span(def_id, macro_def.ident);
}
fn encode_info_for_generic_param(&mut self, def_id: DefId, kind: EntryKind, encode_type: bool) {
record!(self.tables.kind[def_id] <- kind);
if encode_type {
@ -1915,9 +1908,6 @@ impl Visitor<'tcx> for EncodeContext<'a, 'tcx> {
intravisit::walk_generics(self, generics);
self.encode_info_for_generics(generics);
}
fn visit_macro_def(&mut self, macro_def: &'tcx hir::MacroDef<'tcx>) {
self.encode_info_for_macro_def(macro_def);
}
}
impl EncodeContext<'a, 'tcx> {
@ -1972,6 +1962,7 @@ impl EncodeContext<'a, 'tcx> {
hir::ItemKind::Static(..)
| hir::ItemKind::Const(..)
| hir::ItemKind::Fn(..)
| hir::ItemKind::Macro(..)
| hir::ItemKind::Mod(..)
| hir::ItemKind::ForeignMod { .. }
| hir::ItemKind::GlobalAsm(..)

View File

@ -394,20 +394,6 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> {
}
}
fn visit_macro_def(&mut self, macro_def: &'hir MacroDef<'hir>) {
// Exported macros are visited directly from the crate root,
// so they do not have `parent_node` set.
// Find the correct enclosing module from their DefKey.
let def_key = self.definitions.def_key(macro_def.def_id);
let parent = def_key.parent.map_or(hir::CRATE_HIR_ID, |local_def_index| {
self.definitions.local_def_id_to_hir_id(LocalDefId { local_def_index })
});
self.insert_owner(macro_def.def_id, OwnerNode::MacroDef(macro_def));
self.with_parent(parent, |this| {
this.insert_nested(macro_def.def_id);
});
}
fn visit_variant(&mut self, v: &'hir Variant<'hir>, g: &'hir Generics<'hir>, item_id: HirId) {
self.insert(v.span, v.id, Node::Variant(v));
self.with_parent(v.id, |this| {

View File

@ -10,7 +10,6 @@ use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE};
use rustc_hir::definitions::{DefKey, DefPath, DefPathHash};
use rustc_hir::intravisit;
use rustc_hir::intravisit::Visitor;
use rustc_hir::itemlikevisit::ItemLikeVisitor;
use rustc_hir::*;
use rustc_index::vec::Idx;
@ -218,6 +217,7 @@ impl<'hir> Map<'hir> {
ItemKind::Static(..) => DefKind::Static,
ItemKind::Const(..) => DefKind::Const,
ItemKind::Fn(..) => DefKind::Fn,
ItemKind::Macro(..) => DefKind::Macro(MacroKind::Bang),
ItemKind::Mod(..) => DefKind::Mod,
ItemKind::OpaqueTy(..) => DefKind::OpaqueTy,
ItemKind::TyAlias(..) => DefKind::TyAlias,
@ -266,7 +266,6 @@ impl<'hir> Map<'hir> {
ExprKind::Closure(.., Some(_)) => DefKind::Generator,
_ => bug!("def_kind: unsupported node: {}", self.node_to_string(hir_id)),
},
Node::MacroDef(_) => DefKind::Macro(MacroKind::Bang),
Node::GenericParam(param) => match param.kind {
GenericParamKind::Lifetime { .. } => DefKind::LifetimeParam,
GenericParamKind::Type { .. } => DefKind::TyParam,
@ -543,15 +542,6 @@ impl<'hir> Map<'hir> {
}
}
pub fn visit_exported_macros_in_krate<V>(&self, visitor: &mut V)
where
V: Visitor<'hir>,
{
for macro_def in self.krate().exported_macros() {
visitor.visit_macro_def(macro_def);
}
}
/// Returns an iterator for the nodes in the ancestor tree of the `current_id`
/// until the crate root is reached. Prefer this over your own loop using `get_parent_node`.
pub fn parent_iter(&self, current_id: HirId) -> ParentHirIterator<'_, 'hir> {
@ -645,8 +635,6 @@ impl<'hir> Map<'hir> {
/// in a module, trait, or impl.
pub fn get_parent_item(&self, hir_id: HirId) -> HirId {
if let Some((hir_id, _node)) = self.parent_owner_iter(hir_id).next() {
// A MacroDef does not have children.
debug_assert!(!matches!(_node, OwnerNode::MacroDef(_)));
hir_id
} else {
CRATE_HIR_ID
@ -774,13 +762,6 @@ impl<'hir> Map<'hir> {
}
}
pub fn expect_macro_def(&self, id: HirId) -> &'hir MacroDef<'hir> {
match self.tcx.hir_owner(id.expect_owner()) {
Some(Owner { node: OwnerNode::MacroDef(macro_def) }) => macro_def,
_ => bug!("expected macro def, found {}", self.node_to_string(id)),
}
}
pub fn expect_expr(&self, id: HirId) -> &'hir Expr<'hir> {
match self.find(id) {
Some(Node::Expr(expr)) => expr,
@ -800,7 +781,6 @@ impl<'hir> Map<'hir> {
Node::GenericParam(param) => param.name.ident().name,
Node::Binding(&Pat { kind: PatKind::Binding(_, _, l, _), .. }) => l.name,
Node::Ctor(..) => self.name(self.get_parent_item(id)),
Node::MacroDef(md) => md.ident.name,
_ => return None,
})
}
@ -867,7 +847,6 @@ impl<'hir> Map<'hir> {
Node::Infer(i) => i.span,
Node::Visibility(v) => bug!("unexpected Visibility {:?}", v),
Node::Local(local) => local.span,
Node::MacroDef(macro_def) => macro_def.span,
Node::Crate(item) => item.inner,
};
Some(span)
@ -1013,7 +992,6 @@ pub(super) fn crate_hash(tcx: TyCtxt<'_>, crate_num: CrateNum) -> Svh {
source_file_names.hash_stable(&mut hcx, &mut stable_hasher);
tcx.sess.opts.dep_tracking_hash(true).hash_stable(&mut hcx, &mut stable_hasher);
tcx.sess.local_stable_crate_id().hash_stable(&mut hcx, &mut stable_hasher);
tcx.untracked_crate.non_exported_macro_attrs.hash_stable(&mut hcx, &mut stable_hasher);
let crate_hash: Fingerprint = stable_hasher.finish();
Svh::new(crate_hash.to_smaller_hash())
@ -1062,6 +1040,7 @@ fn hir_id_to_string(map: &Map<'_>, id: HirId) -> String {
ItemKind::Static(..) => "static",
ItemKind::Const(..) => "const",
ItemKind::Fn(..) => "fn",
ItemKind::Macro(..) => "macro",
ItemKind::Mod(..) => "mod",
ItemKind::ForeignMod { .. } => "foreign mod",
ItemKind::GlobalAsm(..) => "global asm",
@ -1118,7 +1097,6 @@ fn hir_id_to_string(map: &Map<'_>, id: HirId) -> String {
Some(Node::Lifetime(_)) => node_str("lifetime"),
Some(Node::GenericParam(ref param)) => format!("generic_param {:?}{}", param, id_str),
Some(Node::Visibility(ref vis)) => format!("visibility {:?}{}", vis, id_str),
Some(Node::MacroDef(_)) => format!("macro {}{}", path_str(), id_str),
Some(Node::Crate(..)) => String::from("root_crate"),
None => format!("unknown node{}", id_str),
}

View File

@ -1149,6 +1149,7 @@ impl ItemLikeVisitor<'v> for RootCollector<'_, 'v> {
match item.kind {
hir::ItemKind::ExternCrate(..)
| hir::ItemKind::Use(..)
| hir::ItemKind::Macro(..)
| hir::ItemKind::ForeignMod { .. }
| hir::ItemKind::TyAlias(..)
| hir::ItemKind::Trait(..)

View File

@ -1723,6 +1723,16 @@ impl Visitor<'tcx> for CheckAttrVisitor<'tcx> {
}
fn visit_item(&mut self, item: &'tcx Item<'tcx>) {
// Historically we've run more checks on non-exported than exported macros,
// so this lets us continue to run them while maintaining backwards compatibility.
// In the long run, the checks should be harmonized.
if let ItemKind::Macro(ref macro_def) = item.kind {
let def_id = item.def_id.to_def_id();
if macro_def.macro_rules && !self.tcx.has_attr(def_id, sym::macro_export) {
check_non_exported_macro_for_invalid_attrs(self.tcx, item);
}
}
let target = Target::from_item(item);
self.check_attributes(item.hir_id(), &item.span, target, Some(ItemLike::Item(item)));
intravisit::walk_item(self, item)
@ -1795,11 +1805,6 @@ impl Visitor<'tcx> for CheckAttrVisitor<'tcx> {
intravisit::walk_variant(self, variant, generics, item_id)
}
fn visit_macro_def(&mut self, macro_def: &'tcx hir::MacroDef<'tcx>) {
self.check_attributes(macro_def.hir_id(), &macro_def.span, Target::MacroDef, None);
intravisit::walk_macro_def(self, macro_def);
}
fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
self.check_attributes(param.hir_id, &param.span, Target::Param, None);
@ -1848,7 +1853,9 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
}
}
fn check_invalid_macro_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
fn check_non_exported_macro_for_invalid_attrs(tcx: TyCtxt<'_>, item: &Item<'_>) {
let attrs = tcx.hir().attrs(item.hir_id());
for attr in attrs {
if attr.has_name(sym::inline) {
struct_span_err!(
@ -1869,8 +1876,6 @@ fn check_mod_attrs(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
if module_def_id.is_top_level_module() {
check_attr_visitor.check_attributes(CRATE_HIR_ID, &DUMMY_SP, Target::Mod, None);
check_invalid_crate_level_attr(tcx, tcx.hir().krate_attrs());
tcx.hir().visit_exported_macros_in_krate(check_attr_visitor);
check_invalid_macro_level_attr(tcx, tcx.hir().krate().non_exported_macro_attrs);
}
}

View File

@ -107,10 +107,6 @@ fn diagnostic_items<'tcx>(tcx: TyCtxt<'tcx>, cnum: CrateNum) -> FxHashMap<Symbol
// Collect diagnostic items in this crate.
tcx.hir().krate().visit_all_item_likes(&mut collector);
for m in tcx.hir().krate().exported_macros() {
collector.observe_item(m.def_id);
}
collector.items
}

View File

@ -244,11 +244,6 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> {
fn visit_attribute(&mut self, _: hir::HirId, attr: &'v ast::Attribute) {
self.record("Attribute", Id::Attr(attr.id), attr);
}
fn visit_macro_def(&mut self, macro_def: &'v hir::MacroDef<'v>) {
self.record("MacroDef", Id::Node(macro_def.hir_id()), macro_def);
hir_visit::walk_macro_def(self, macro_def)
}
}
impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {

View File

@ -127,9 +127,7 @@ impl Visitor<'tcx> for LibFeatureCollector<'tcx> {
fn get_lib_features(tcx: TyCtxt<'_>, (): ()) -> LibFeatures {
let mut collector = LibFeatureCollector::new(tcx);
let krate = tcx.hir().krate();
for attr in krate.non_exported_macro_attrs {
collector.visit_attribute(rustc_hir::CRATE_HIR_ID, attr);
}
intravisit::walk_crate(&mut collector, krate);
collector.lib_features
}

View File

@ -263,6 +263,7 @@ impl<'tcx> ReachableContext<'tcx> {
| hir::ItemKind::Use(..)
| hir::ItemKind::OpaqueTy(..)
| hir::ItemKind::TyAlias(..)
| hir::ItemKind::Macro(..)
| hir::ItemKind::Mod(..)
| hir::ItemKind::ForeignMod { .. }
| hir::ItemKind::Impl { .. }
@ -309,8 +310,7 @@ impl<'tcx> ReachableContext<'tcx> {
| Node::Ctor(..)
| Node::Field(_)
| Node::Ty(_)
| Node::Crate(_)
| Node::MacroDef(_) => {}
| Node::Crate(_) => {}
_ => {
bug!(
"found unexpected node kind in worklist: {} ({:?})",

View File

@ -538,19 +538,6 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> {
);
}
fn visit_macro_def(&mut self, md: &'tcx hir::MacroDef<'tcx>) {
self.annotate(
md.def_id,
md.span,
None,
AnnotationKind::Required,
InheritDeprecation::Yes,
InheritConstStability::No,
InheritStability::No,
|_| {},
);
}
fn visit_generic_param(&mut self, p: &'tcx hir::GenericParam<'tcx>) {
let kind = match &p.kind {
// Allow stability attributes on default generic arguments.
@ -662,11 +649,6 @@ impl<'tcx> Visitor<'tcx> for MissingStabilityAnnotations<'tcx> {
self.check_missing_stability(i.def_id, i.span);
intravisit::walk_foreign_item(self, i);
}
fn visit_macro_def(&mut self, md: &'tcx hir::MacroDef<'tcx>) {
self.check_missing_stability(md.def_id, md.span);
}
// Note that we don't need to `check_missing_stability` for default generic parameters,
// as we assume that any default generic parameters without attributes are automatically
// stable (assuming they have not inherited instability from their parent).

View File

@ -5,6 +5,7 @@ edition = "2018"
[dependencies]
rustc_middle = { path = "../rustc_middle" }
rustc_ast = { path = "../rustc_ast" }
rustc_attr = { path = "../rustc_attr" }
rustc_errors = { path = "../rustc_errors" }
rustc_hir = { path = "../rustc_hir" }

View File

@ -6,6 +6,7 @@
#![feature(associated_type_defaults)]
#![recursion_limit = "256"]
use rustc_ast::MacroDef;
use rustc_attr as attr;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::struct_span_err;
@ -26,7 +27,7 @@ use rustc_middle::ty::subst::{InternalSubsts, Subst};
use rustc_middle::ty::{self, Const, GenericParamDefKind, TraitRef, Ty, TyCtxt, TypeFoldable};
use rustc_session::lint;
use rustc_span::hygiene::Transparency;
use rustc_span::symbol::{kw, Ident};
use rustc_span::symbol::{kw, sym, Ident};
use rustc_span::Span;
use rustc_trait_selection::traits::const_evaluatable::{self, AbstractConst};
@ -462,6 +463,43 @@ impl EmbargoVisitor<'tcx> {
}
}
// We have to make sure that the items that macros might reference
// are reachable, since they might be exported transitively.
fn update_reachability_from_macro(&mut self, local_def_id: LocalDefId, md: &MacroDef) {
// Non-opaque macros cannot make other items more accessible than they already are.
let hir_id = self.tcx.hir().local_def_id_to_hir_id(local_def_id);
let attrs = self.tcx.hir().attrs(hir_id);
if attr::find_transparency(&attrs, md.macro_rules).0 != Transparency::Opaque {
return;
}
let item_def_id = local_def_id.to_def_id();
let macro_module_def_id =
ty::DefIdTree::parent(self.tcx, item_def_id).unwrap().expect_local();
if self.tcx.hir().opt_def_kind(macro_module_def_id) != Some(DefKind::Mod) {
// The macro's parent doesn't correspond to a `mod`, return early (#63164, #65252).
return;
}
if self.get(local_def_id).is_none() {
return;
}
// Since we are starting from an externally visible module,
// all the parents in the loop below are also guaranteed to be modules.
let mut module_def_id = macro_module_def_id;
loop {
let changed_reachability =
self.update_macro_reachable(module_def_id, macro_module_def_id);
if changed_reachability || module_def_id == CRATE_DEF_ID {
break;
}
module_def_id =
ty::DefIdTree::parent(self.tcx, module_def_id.to_def_id()).unwrap().expect_local();
}
}
/// Updates the item as being reachable through a macro defined in the given
/// module. Returns `true` if the level has changed.
fn update_macro_reachable(
@ -511,16 +549,26 @@ impl EmbargoVisitor<'tcx> {
}
match def_kind {
// No type privacy, so can be directly marked as reachable.
DefKind::Const
| DefKind::Macro(_)
| DefKind::Static
| DefKind::TraitAlias
| DefKind::TyAlias => {
DefKind::Const | DefKind::Static | DefKind::TraitAlias | DefKind::TyAlias => {
if vis.is_accessible_from(module.to_def_id(), self.tcx) {
self.update(def_id, level);
}
}
// Hygine isn't really implemented for `macro_rules!` macros at the
// moment. Accordingly, marking them as reachable is unwise. `macro` macros
// have normal hygine, so we can treat them like other items without type
// privacy and mark them reachable.
DefKind::Macro(_) => {
let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id);
let item = self.tcx.hir().expect_item(hir_id);
if let hir::ItemKind::Macro(MacroDef { macro_rules: false, .. }) = item.kind {
if vis.is_accessible_from(module.to_def_id(), self.tcx) {
self.update(def_id, level);
}
}
}
// We can't use a module name as the final segment of a path, except
// in use statements. Since re-export checking doesn't consider
// hygiene these don't need to be marked reachable. The contents of
@ -644,6 +692,12 @@ impl Visitor<'tcx> for EmbargoVisitor<'tcx> {
hir::ItemKind::Impl { .. } => {
Option::<AccessLevel>::of_impl(item.def_id, self.tcx, &self.access_levels)
}
// Only exported `macro_rules!` items are public, but they always are.
hir::ItemKind::Macro(MacroDef { macro_rules: true, .. }) => {
let def_id = item.def_id.to_def_id();
let is_macro_export = self.tcx.has_attr(def_id, sym::macro_export);
if is_macro_export { Some(AccessLevel::Public) } else { None }
}
// Foreign modules inherit level from parents.
hir::ItemKind::ForeignMod { .. } => self.prev_level,
// Other `pub` items inherit levels from parents.
@ -652,6 +706,7 @@ impl Visitor<'tcx> for EmbargoVisitor<'tcx> {
| hir::ItemKind::ExternCrate(..)
| hir::ItemKind::GlobalAsm(..)
| hir::ItemKind::Fn(..)
| hir::ItemKind::Macro(..)
| hir::ItemKind::Mod(..)
| hir::ItemKind::Static(..)
| hir::ItemKind::Struct(..)
@ -708,6 +763,9 @@ impl Visitor<'tcx> for EmbargoVisitor<'tcx> {
}
}
}
hir::ItemKind::Macro(ref macro_def) => {
self.update_reachability_from_macro(item.def_id, macro_def);
}
hir::ItemKind::ForeignMod { items, .. } => {
for foreign_item in items {
if foreign_item.vis.node.is_pub() {
@ -715,6 +773,7 @@ impl Visitor<'tcx> for EmbargoVisitor<'tcx> {
}
}
}
hir::ItemKind::OpaqueTy(..)
| hir::ItemKind::Use(..)
| hir::ItemKind::Static(..)
@ -730,7 +789,7 @@ impl Visitor<'tcx> for EmbargoVisitor<'tcx> {
// Mark all items in interfaces of reachable items as reachable.
match item.kind {
// The interface is empty.
hir::ItemKind::ExternCrate(..) => {}
hir::ItemKind::Macro(..) | hir::ItemKind::ExternCrate(..) => {}
// All nested items are checked by `visit_item`.
hir::ItemKind::Mod(..) => {}
// Re-exports are handled in `visit_mod`. However, in order to avoid looping over
@ -885,45 +944,6 @@ impl Visitor<'tcx> for EmbargoVisitor<'tcx> {
intravisit::walk_mod(self, m, id);
}
fn visit_macro_def(&mut self, md: &'tcx hir::MacroDef<'tcx>) {
// Non-opaque macros cannot make other items more accessible than they already are.
let attrs = self.tcx.hir().attrs(md.hir_id());
if attr::find_transparency(&attrs, md.ast.macro_rules).0 != Transparency::Opaque {
// `#[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.def_id, Some(AccessLevel::Public));
}
return;
}
let macro_module_def_id =
ty::DefIdTree::parent(self.tcx, md.def_id.to_def_id()).unwrap().expect_local();
if self.tcx.hir().opt_def_kind(macro_module_def_id) != Some(DefKind::Mod) {
// The macro's parent doesn't correspond to a `mod`, return early (#63164, #65252).
return;
}
let level = if md.vis.node.is_pub() { self.get(macro_module_def_id) } else { None };
let new_level = self.update(md.def_id, level);
if new_level.is_none() {
return;
}
// Since we are starting from an externally visible module,
// all the parents in the loop below are also guaranteed to be modules.
let mut module_def_id = macro_module_def_id;
loop {
let changed_reachability =
self.update_macro_reachable(module_def_id, macro_module_def_id);
if changed_reachability || module_def_id == CRATE_DEF_ID {
break;
}
module_def_id =
ty::DefIdTree::parent(self.tcx, module_def_id.to_def_id()).unwrap().expect_local();
}
}
}
impl ReachEverythingInTheInterfaceVisitor<'_, 'tcx> {
@ -1981,7 +2001,7 @@ impl<'tcx> Visitor<'tcx> for PrivateItemsInPublicInterfacesVisitor<'tcx> {
// Checked in resolve.
hir::ItemKind::Use(..) => {}
// No subitems.
hir::ItemKind::GlobalAsm(..) => {}
hir::ItemKind::Macro(..) | hir::ItemKind::GlobalAsm(..) => {}
// Subitems of these items have inherited publicity.
hir::ItemKind::Const(..)
| hir::ItemKind::Static(..)

View File

@ -740,6 +740,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
hir::ItemKind::ExternCrate(_)
| hir::ItemKind::Use(..)
| hir::ItemKind::Macro(..)
| hir::ItemKind::Mod(..)
| hir::ItemKind::ForeignMod { .. }
| hir::ItemKind::GlobalAsm(..) => {

View File

@ -416,6 +416,14 @@ impl<'hir> Sig for hir::Item<'hir> {
Ok(sig)
}
hir::ItemKind::Macro(_) => {
let mut text = "macro".to_owned();
let name = self.ident.to_string();
text.push_str(&name);
text.push_str(&"! {}");
Ok(text_sig(text))
}
hir::ItemKind::Mod(ref _mod) => {
let mut text = "mod ".to_owned();
let name = self.ident.to_string();

View File

@ -746,6 +746,7 @@ fn convert_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) {
// These don't define types.
hir::ItemKind::ExternCrate(_)
| hir::ItemKind::Use(..)
| hir::ItemKind::Macro(_)
| hir::ItemKind::Mod(_)
| hir::ItemKind::GlobalAsm(_) => {}
hir::ItemKind::ForeignMod { items, .. } => {

View File

@ -427,6 +427,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> {
}
ItemKind::Trait(..)
| ItemKind::TraitAlias(..)
| ItemKind::Macro(..)
| ItemKind::Mod(..)
| ItemKind::ForeignMod { .. }
| ItemKind::GlobalAsm(..)

View File

@ -91,7 +91,6 @@ impl Clean<Item> for doctree::Module<'_> {
items.extend(self.foreigns.iter().map(|x| x.clean(cx)));
items.extend(self.mods.iter().map(|x| x.clean(cx)));
items.extend(self.items.iter().map(|x| x.clean(cx)).flatten());
items.extend(self.macros.iter().map(|x| x.clean(cx)));
// determine if we should display the inner contents or
// the outer `mod` item for the source code.
@ -1861,6 +1860,10 @@ impl Clean<Vec<Item>> for (&hir::Item<'_>, Option<Symbol>) {
ItemKind::Fn(ref sig, ref generics, body_id) => {
clean_fn_or_proc_macro(item, sig, generics, body_id, &mut name, cx)
}
ItemKind::Macro(ref macro_def) => MacroItem(Macro {
source: display_macro_source(cx, name, &macro_def, def_id, &item.vis),
imported_from: None,
}),
ItemKind::Trait(is_auto, unsafety, ref generics, ref bounds, ref item_ids) => {
let items = item_ids
.iter()
@ -2138,24 +2141,6 @@ impl Clean<Item> for (&hir::ForeignItem<'_>, Option<Symbol>) {
}
}
impl Clean<Item> for (&hir::MacroDef<'_>, Option<Symbol>) {
fn clean(&self, cx: &mut DocContext<'_>) -> Item {
let (item, renamed) = self;
let name = renamed.unwrap_or(item.ident.name);
let def_id = item.def_id.to_def_id();
Item::from_hir_id_and_parts(
item.hir_id(),
Some(name),
MacroItem(Macro {
source: display_macro_source(cx, name, &item.ast, def_id, &item.vis),
imported_from: None,
}),
cx,
)
}
}
impl Clean<TypeBinding> for hir::TypeBinding<'_> {
fn clean(&self, cx: &mut DocContext<'_>) -> TypeBinding {
TypeBinding { name: self.ident.name, kind: self.kind.clean(cx) }

View File

@ -1171,10 +1171,21 @@ impl<'a, 'hir, 'tcx> intravisit::Visitor<'hir> for HirCollector<'a, 'hir, 'tcx>
}
fn visit_item(&mut self, item: &'hir hir::Item<'_>) {
let name = if let hir::ItemKind::Impl(impl_) = &item.kind {
rustc_hir_pretty::id_to_string(&self.map, impl_.self_ty.hir_id)
} else {
item.ident.to_string()
let name = match &item.kind {
hir::ItemKind::Macro(ref macro_def) => {
// FIXME(#88038): Non exported macros have historically not been tested,
// but we really ought to start testing them.
let def_id = item.def_id.to_def_id();
if macro_def.macro_rules && !self.tcx.has_attr(def_id, sym::macro_export) {
intravisit::walk_item(self, item);
return;
}
item.ident.to_string()
}
hir::ItemKind::Impl(impl_) => {
rustc_hir_pretty::id_to_string(&self.map, impl_.self_ty.hir_id)
}
_ => item.ident.to_string(),
};
self.visit_testable(name, item.hir_id(), item.span, |this| {
@ -1216,15 +1227,6 @@ impl<'a, 'hir, 'tcx> intravisit::Visitor<'hir> for HirCollector<'a, 'hir, 'tcx>
intravisit::walk_field_def(this, f);
});
}
fn visit_macro_def(&mut self, macro_def: &'hir hir::MacroDef<'_>) {
self.visit_testable(
macro_def.ident.to_string(),
macro_def.hir_id(),
macro_def.span,
|_| (),
);
}
}
#[cfg(test)]

View File

@ -5,6 +5,7 @@ use rustc_span::{self, Span, Symbol};
use rustc_hir as hir;
#[derive(Debug)]
crate struct Module<'hir> {
crate name: Symbol,
crate where_inner: Span,
@ -13,20 +14,11 @@ crate struct Module<'hir> {
// (item, renamed)
crate items: Vec<(&'hir hir::Item<'hir>, Option<Symbol>)>,
crate foreigns: Vec<(&'hir hir::ForeignItem<'hir>, Option<Symbol>)>,
crate macros: Vec<(&'hir hir::MacroDef<'hir>, Option<Symbol>)>,
}
impl Module<'hir> {
crate fn new(name: Symbol, id: hir::HirId, where_inner: Span) -> Module<'hir> {
Module {
name,
id,
where_inner,
mods: Vec::new(),
items: Vec::new(),
foreigns: Vec::new(),
macros: Vec::new(),
}
Module { name, id, where_inner, mods: Vec::new(), items: Vec::new(), foreigns: Vec::new() }
}
crate fn where_outer(&self, tcx: TyCtxt<'_>) -> Span {

View File

@ -9,7 +9,7 @@ use rustc_hir::Node;
use rustc_middle::middle::privacy::AccessLevel;
use rustc_middle::ty::TyCtxt;
use rustc_span;
use rustc_span::def_id::LOCAL_CRATE;
use rustc_span::def_id::{CRATE_DEF_ID, LOCAL_CRATE};
use rustc_span::source_map::Spanned;
use rustc_span::symbol::{kw, sym, Symbol};
@ -79,49 +79,23 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
&krate.module(),
self.cx.tcx.crate_name(LOCAL_CRATE),
);
// 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(def.def_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 == path_segment_ty_ns) {
Some(child_mod) => cur_mod = &mut *child_mod,
None => continue 'exported_macros,
// `#[macro_export] macro_rules!` items are reexported at the top level of the
// crate, regardless of where they're defined. We want to document the
// top level rexport of the macro, not its original definition, since
// the rexport defines the path that a user will actually see. Accordingly,
// we add the rexport as an item here, and then skip over the original
// definition in `visit_item()` below.
for export in self.cx.tcx.module_exports(CRATE_DEF_ID).unwrap_or(&[]) {
if let Res::Def(DefKind::Macro(_), def_id) = export.res {
if let Some(local_def_id) = def_id.as_local() {
if self.cx.tcx.has_attr(def_id, sym::macro_export) {
let hir_id = self.cx.tcx.hir().local_def_id_to_hir_id(local_def_id);
let item = self.cx.tcx.hir().expect_item(hir_id);
top_level_module.items.push((item, None));
}
}
}
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.cache.exact_paths = self.exact_paths;
top_level_module
@ -238,10 +212,6 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
self.inlining = prev;
true
}
Node::MacroDef(def) if !glob => {
om.macros.push((def, renamed));
true
}
_ => false,
};
self.view_item_stack.remove(&res_hir_id);
@ -257,7 +227,10 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
debug!("visiting item {:?}", item);
let name = renamed.unwrap_or(item.ident.name);
if item.vis.node.is_pub() {
let def_id = item.def_id.to_def_id();
let is_pub = item.vis.node.is_pub() || self.cx.tcx.has_attr(def_id, sym::macro_export);
if is_pub {
self.store_path(item.def_id.to_def_id());
}
@ -269,7 +242,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
}
}
// If we're inlining, skip private items.
_ if self.inlining && !item.vis.node.is_pub() => {}
_ if self.inlining && !is_pub => {}
hir::ItemKind::GlobalAsm(..) => {}
hir::ItemKind::Use(_, hir::UseKind::ListStem) => {}
hir::ItemKind::Use(ref path, kind) => {
@ -285,7 +258,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
// If there was a private module in the current path then don't bother inlining
// anything as it will probably be stripped anyway.
if item.vis.node.is_pub() && self.inside_public_path {
if is_pub && self.inside_public_path {
let please_inline = attrs.iter().any(|item| match item.meta_item_list() {
Some(ref list) if item.has_name(sym::doc) => {
list.iter().any(|i| i.has_name(sym::inline))
@ -307,6 +280,26 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
om.items.push((item, renamed))
}
hir::ItemKind::Macro(ref macro_def) => {
// `#[macro_export] macro_rules!` items are handled seperately in `visit()`,
// above, since they need to be documented at the module top level. Accordingly,
// we only want to handle macros if one of three conditions holds:
//
// 1. This macro was defined by `macro`, and thus isn't covered by the case
// above.
// 2. This macro isn't marked with `#[macro_export]`, and thus isn't covered
// by the case above.
// 3. We're inlining, since a reexport where inlining has been requested
// should be inlined even if it is also documented at the top level.
let def_id = item.def_id.to_def_id();
let is_macro_2_0 = !macro_def.macro_rules;
let nonexported = !self.cx.tcx.has_attr(def_id, sym::macro_export);
if is_macro_2_0 || nonexported || self.inlining {
om.items.push((item, renamed));
}
}
hir::ItemKind::Mod(ref m) => {
om.mods.push(self.visit_mod_contents(&item.vis, item.hir_id(), m, name));
}

View File

@ -1,4 +1,4 @@
error: missing documentation for macro
error: missing documentation for a macro
--> $DIR/deny-missing-docs-macro.rs:6:1
|
LL | macro_rules! foo {

View File

@ -0,0 +1,23 @@
// FIXME: If two macros in the same module have the same name
// (yes, that's a thing), rustdoc lists both of them on the index page,
// but only documents the first one on the page for the macro.
// Fortunately, this can only happen in document private items mode,
// but it still isn't ideal beahvior.
//
// See https://github.com/rust-lang/rust/pull/88019#discussion_r693920453
//
// compile-flags: --document-private-items
// @has macro_document_private_duplicate/index.html 'Doc 1.'
// @has macro_document_private_duplicate/macro.a_macro.html 'Doc 1.'
/// Doc 1.
macro_rules! a_macro {
() => ()
}
// @has macro_document_private_duplicate/index.html 'Doc 2.'
// @!has macro_document_private_duplicate/macro.a_macro.html 'Doc 2.'
/// Doc 2.
macro_rules! a_macro {
() => ()
}

View File

@ -0,0 +1,19 @@
// Checks that private macros are documented when `--document-private-items`
// is present.
//
// This is a regression test for issue #73754.
//
// compile-flags: --document-private-items
#![feature(decl_macro)]
// @has macro_document_private/macro.some_macro.html
macro some_macro {
(a: tt) => {}
}
// @has macro_document_private/macro.another_macro.html
macro_rules! another_macro {
(a: tt) => {}
}

View File

@ -0,0 +1,16 @@
// Checks that it is possible to make a macro public through a `pub use` of its
// parent module.
//
// This is a regression test for issue #87257.
#![feature(decl_macro)]
mod outer {
pub mod inner {
pub macro some_macro() {}
}
}
// @has macro_indirect_use/inner/index.html
// @has macro_indirect_use/inner/macro.some_macro.html
pub use outer::inner;

View File

@ -0,0 +1,17 @@
// This checks that exported macros lint as part of their module of origin, not
// the root module.
//
// check-pass
//! Top level documentation
#![deny(missing_docs)]
#[allow(missing_docs)]
mod module {
#[macro_export]
macro_rules! hello {
() => ()
}
}
fn main() {}

View File

@ -0,0 +1,17 @@
// Checks that you can set a lint level specficially for a macro definition.
//
// This is a regression test for issue #59306.
//
// check-pass
#[deny(missing_docs)]
mod module {
#[allow(missing_docs)]
#[macro_export]
macro_rules! hello {
() => ()
}
}
fn main() {}

View File

@ -29,13 +29,13 @@ mod submodule {
#[macro_export]
macro_rules! exported_to_top_level {
//~^ ERROR missing documentation for macro
//~^ ERROR missing documentation for a macro
() => ()
}
}
pub macro top_level_pub_macro {
//~^ ERROR missing documentation for macro
//~^ ERROR missing documentation for a macro
() => ()
}

View File

@ -1,4 +1,4 @@
error: missing documentation for macro
error: missing documentation for a macro
--> $DIR/missing-doc-private-macro.rs:31:5
|
LL | macro_rules! exported_to_top_level {
@ -10,7 +10,7 @@ note: the lint level is defined here
LL | #![deny(missing_docs)]
| ^^^^^^^^^^^^
error: missing documentation for macro
error: missing documentation for a macro
--> $DIR/missing-doc-private-macro.rs:37:1
|
LL | pub macro top_level_pub_macro {

View File

@ -1,7 +1,8 @@
// run-pass
// aux-build:unstable-macros.rs
#![feature(unstable_macros, local_unstable)]
#![unstable(feature = "one_two_three_testing", issue = "none")]
#![feature(staged_api, unstable_macros, local_unstable)]
#[macro_use] extern crate unstable_macros;

View File

@ -122,6 +122,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
},
hir::ItemKind::Const(..)
| hir::ItemKind::Enum(..)
| hir::ItemKind::Macro(..)
| hir::ItemKind::Mod(..)
| hir::ItemKind::Static(..)
| hir::ItemKind::Struct(..)

View File

@ -118,6 +118,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline {
},
hir::ItemKind::Const(..)
| hir::ItemKind::Enum(..)
| hir::ItemKind::Macro(..)
| hir::ItemKind::Mod(..)
| hir::ItemKind::Static(..)
| hir::ItemKind::Struct(..)

View File

@ -381,6 +381,13 @@ fn print_item(cx: &LateContext<'_>, item: &hir::Item<'_>) {
let item_ty = cx.tcx.type_of(did);
println!("function of type {:#?}", item_ty);
},
hir::ItemKind::Macro(ref macro_def) => {
if macro_def.macro_rules {
println!("macro introduced by `macro_rules!`");
} else {
println!("macro introduced by `macro`");
}
},
hir::ItemKind::Mod(..) => println!("module"),
hir::ItemKind::ForeignMod { abi, .. } => println!("foreign module with abi: {}", abi),
hir::ItemKind::GlobalAsm(asm) => println!("global asm: {:?}", asm),