Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 80 additions & 34 deletions src/librustdoc/clean/inline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,27 @@ pub(crate) fn try_inline(
attrs: Option<(&[hir::Attribute], Option<LocalDefId>)>,
visited: &mut DefIdSet,
) -> Option<Vec<clean::Item>> {
fn try_inline_inner(
cx: &mut DocContext<'_>,
kind: clean::ItemKind,
did: DefId,
name: Symbol,
import_def_id: Option<LocalDefId>,
) -> clean::Item {
cx.inlined.insert(did.into());
let mut item = crate::clean::generate_item_with_correct_attrs(
cx,
kind,
did,
name,
import_def_id.as_slice(),
None,
);
// The visibility needs to reflect the one from the reexport and not from the "source" DefId.
item.inner.inline_stmt_id = import_def_id;
item
}

let did = res.opt_def_id()?;
if did.is_local() {
return None;
Expand Down Expand Up @@ -138,34 +159,31 @@ pub(crate) fn try_inline(
})
}
Res::Def(DefKind::Macro(kinds), did) => {
let mac = build_macro(cx, did, name, kinds);
let (mac, others) = build_macro(cx, did, name, kinds);

// FIXME: handle attributes and derives that aren't proc macros, and macros with
// multiple kinds
let type_kind = match kinds {
MacroKinds::BANG => ItemType::Macro,
MacroKinds::ATTR => ItemType::ProcAttribute,
MacroKinds::DERIVE => ItemType::ProcDerive,
_ => todo!("Handle macros with multiple kinds"),
_ if kinds.contains(MacroKinds::BANG) => ItemType::Macro,
_ => panic!("unsupported macro kind {kinds:?}"),
};
record_extern_fqn(cx, did, type_kind);
mac
let first = try_inline_inner(cx, mac, did, name, import_def_id);
if let Some(others) = others {
for mac_kind in others {
let mut mac = first.clone();
mac.inner.kind = mac_kind;
ret.push(mac);
}
}
ret.push(first);
return Some(ret);
}
_ => return None,
};

cx.inlined.insert(did.into());
let mut item = crate::clean::generate_item_with_correct_attrs(
cx,
kind,
did,
name,
import_def_id.as_slice(),
None,
);
// The visibility needs to reflect the one from the reexport and not from the "source" DefId.
item.inner.inline_stmt_id = import_def_id;
ret.push(item);
ret.push(try_inline_inner(cx, kind, did, name, import_def_id));
Some(ret)
}

Expand Down Expand Up @@ -762,24 +780,52 @@ fn build_macro(
def_id: DefId,
name: Symbol,
macro_kinds: MacroKinds,
) -> clean::ItemKind {
) -> (clean::ItemKind, Option<Vec<clean::ItemKind>>) {
match CStore::from_tcx(cx.tcx).load_macro_untracked(def_id, cx.tcx) {
// FIXME: handle attributes and derives that aren't proc macros, and macros with multiple
// kinds
LoadedMacro::MacroDef { def, .. } => match macro_kinds {
MacroKinds::BANG => clean::MacroItem(clean::Macro {
source: utils::display_macro_source(cx, name, &def),
macro_rules: def.macro_rules,
}),
MacroKinds::DERIVE => clean::ProcMacroItem(clean::ProcMacro {
kind: MacroKind::Derive,
helpers: Vec::new(),
}),
MacroKinds::ATTR => clean::ProcMacroItem(clean::ProcMacro {
kind: MacroKind::Attr,
helpers: Vec::new(),
}),
_ => todo!("Handle macros with multiple kinds"),
MacroKinds::BANG => (
clean::MacroItem(
clean::Macro {
source: utils::display_macro_source(cx, name, &def),
macro_rules: def.macro_rules,
},
MacroKinds::BANG,
),
None,
),
MacroKinds::DERIVE => (
clean::ProcMacroItem(clean::ProcMacro {
kind: MacroKind::Derive,
helpers: Vec::new(),
}),
None,
),
MacroKinds::ATTR => (
clean::ProcMacroItem(clean::ProcMacro {
kind: MacroKind::Attr,
helpers: Vec::new(),
}),
None,
),
_ if macro_kinds.contains(MacroKinds::BANG) => {
let kind = clean::MacroItem(
clean::Macro {
source: utils::display_macro_source(cx, name, &def),
macro_rules: def.macro_rules,
},
macro_kinds,
);
let mut ret = vec![];
for kind in macro_kinds.iter().filter(|kind| *kind != MacroKinds::BANG) {
match kind {
MacroKinds::ATTR => ret.push(clean::AttrMacroItem),
MacroKinds::DERIVE => ret.push(clean::DeriveMacroItem),
_ => panic!("unsupported macro kind {kind:?}"),
}
}
(kind, Some(ret))
}
_ => panic!("unsupported macro kind {macro_kinds:?}"),
Comment on lines +810 to +828
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that binarycat points it out, I notice that it's not just the tests not covering every combination of MacroKinds. The code itself can't do it, even though the compiler can.

If it can't do it, then I think the design is flawed.

Instead of generating placeholders and trying to cram multiple macro kinds into a single clean::Item, maybe it should instead take the easy road and just generate duplicate pages? That way, you don't have to do any of this plumbing, and, anyway, this feature is quite niche and most crates will only want to support one mode per macro.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is unreachable unless new macro kinds are added. a downside of the bitflags crate is it kinda ruins exhaustiveness checking for cases like this, due to having no distinction between a set of flags and a single flag. something like enumset on the other hand, doesn't have this limitation.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no case here that covers this:

macro_rules! macro {
    attr() () => {};
    derive() () => {};
}

Such a macro has MacroKinds::DERIVE | MacroKinds::ATTR, but since it doesn't have MacroKinds::BANG, it falls through to the todo!() line.

Copy link
Contributor

@notriddle notriddle Oct 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I implemented #148005, which should fix this problem. It also adds a bit less code.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are you misunderstanding what bitflags iter does?

...am i misunderstanding what bitflags iter does?

Copy link
Contributor

@notriddle notriddle Oct 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure? It says it does this:

https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/def/struct.MacroKinds.html#method.iter

Yield a set of contained flags values.

Each yielded flags value will correspond to a defined named flag. Any unknown bits will be yielded together as a final flags value.

So, assuming there aren't any unknown bits (if there are, it should fall into the default match arm and panic), and assuming that "contained" means "if I call contains() it'll return true" (I don't know what else it would mean), this should do what I want, which is to iterate through each MacroKind that is turned on for that particular macro.

In any case, I copied over most of the same test cases, and they pass.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of generating placeholders and trying to cram multiple macro kinds into a single clean::Item, maybe it should instead take the easy road and just generate duplicate pages? That way, you don't have to do any of this plumbing, and, anyway, this feature is quite niche and most crates will only want to support one mode per macro.

It's exactly what I wanted to avoid ^^' (hence all the extra code)

However if we add support for /// on the attr/derive branches, then this approach makes more sense.

},
LoadedMacro::ProcMacro(ext) => {
// Proc macros can only have a single kind
Expand All @@ -789,7 +835,7 @@ fn build_macro(
MacroKinds::DERIVE => MacroKind::Derive,
_ => unreachable!(),
};
clean::ProcMacroItem(clean::ProcMacro { kind, helpers: ext.helper_attrs })
(clean::ProcMacroItem(clean::ProcMacro { kind, helpers: ext.helper_attrs }), None)
}
}
}
Expand Down
61 changes: 48 additions & 13 deletions src/librustdoc/clean/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2841,19 +2841,54 @@ fn clean_maybe_renamed_item<'tcx>(
generics: clean_generics(generics, cx),
fields: variant_data.fields().iter().map(|x| clean_field(x, cx)).collect(),
}),
// FIXME: handle attributes and derives that aren't proc macros, and macros with
// multiple kinds
ItemKind::Macro(_, macro_def, MacroKinds::BANG) => MacroItem(Macro {
source: display_macro_source(cx, name, macro_def),
macro_rules: macro_def.macro_rules,
}),
ItemKind::Macro(_, _, MacroKinds::ATTR) => {
clean_proc_macro(item, &mut name, MacroKind::Attr, cx)
}
ItemKind::Macro(_, _, MacroKinds::DERIVE) => {
clean_proc_macro(item, &mut name, MacroKind::Derive, cx)
}
ItemKind::Macro(_, _, _) => todo!("Handle macros with multiple kinds"),
ItemKind::Macro(_, macro_def, kinds) => match kinds {
MacroKinds::BANG => MacroItem(
Macro {
source: display_macro_source(cx, name, macro_def),
macro_rules: macro_def.macro_rules,
},
MacroKinds::BANG,
),
MacroKinds::ATTR => clean_proc_macro(item, &mut name, MacroKind::Attr, cx),
MacroKinds::DERIVE => clean_proc_macro(item, &mut name, MacroKind::Derive, cx),
_ if kinds.contains(MacroKinds::BANG) => {
let kind = MacroItem(
Macro {
source: display_macro_source(cx, name, macro_def),
macro_rules: macro_def.macro_rules,
},
kinds,
);
let mac = generate_item_with_correct_attrs(
cx,
kind,
item.owner_id.def_id.to_def_id(),
name,
import_ids,
renamed,
);

let mut ret = Vec::with_capacity(3);
for kind in kinds.iter().filter(|kind| *kind != MacroKinds::BANG) {
match kind {
MacroKinds::ATTR => {
let mut attr = mac.clone();
attr.inner.kind = AttrMacroItem;
ret.push(attr);
}
MacroKinds::DERIVE => {
let mut derive = mac.clone();
derive.inner.kind = DeriveMacroItem;
ret.push(derive);
}
_ => panic!("unsupported macro kind {kind:?}"),
}
}
ret.push(mac);
return ret;
}
_ => panic!("unsupported macro kind {kinds:?}"),
},
// proc macros can have a name set by attributes
ItemKind::Fn { ref sig, generics, body: body_id, .. } => {
clean_fn_or_proc_macro(item, sig, generics, body_id, &mut name, cx)
Expand Down
26 changes: 23 additions & 3 deletions src/librustdoc/clean/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use rustc_abi::{ExternAbi, VariantIdx};
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
use rustc_data_structures::thin_vec::ThinVec;
use rustc_hir::attrs::{AttributeKind, DeprecatedSince, Deprecation};
use rustc_hir::def::{CtorKind, DefKind, Res};
use rustc_hir::def::{CtorKind, DefKind, MacroKinds, Res};
use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId};
use rustc_hir::lang_items::LangItem;
use rustc_hir::{BodyId, ConstStability, Mutability, Stability, StableSince, find_attr};
Expand Down Expand Up @@ -669,6 +669,18 @@ impl Item {
ItemType::from(self)
}

/// Generates the HTML file name based on the item kind.
pub(crate) fn html_filename(&self) -> String {
let type_ = if self.is_macro_placeholder() { ItemType::Macro } else { self.type_() };
format!("{type_}.{}.html", self.name.unwrap())
}

/// If the current item is a "fake" macro (ie, `AttrMacroItem | ItemKind::DeriveMacroItem` which
/// don't hold any data), it returns `true`.
pub(crate) fn is_macro_placeholder(&self) -> bool {
matches!(self.kind, ItemKind::AttrMacroItem | ItemKind::DeriveMacroItem)
}

pub(crate) fn is_default(&self) -> bool {
match self.kind {
ItemKind::MethodItem(_, Some(defaultness)) => {
Expand Down Expand Up @@ -834,7 +846,13 @@ pub(crate) enum ItemKind {
ForeignStaticItem(Static, hir::Safety),
/// `type`s from an extern block
ForeignTypeItem,
MacroItem(Macro),
MacroItem(Macro, MacroKinds),
/// This is NOT an attribute proc-macro but a bang macro with support for being used as an
/// attribute macro.
AttrMacroItem,
/// This is NOT an attribute proc-macro but a bang macro with support for being used as a
/// derive macro.
DeriveMacroItem,
ProcMacroItem(ProcMacro),
PrimitiveItem(PrimitiveType),
/// A required associated constant in a trait declaration.
Expand Down Expand Up @@ -889,7 +907,9 @@ impl ItemKind {
| ForeignFunctionItem(_, _)
| ForeignStaticItem(_, _)
| ForeignTypeItem
| MacroItem(_)
| MacroItem(..)
| AttrMacroItem
| DeriveMacroItem
| ProcMacroItem(_)
| PrimitiveItem(_)
| RequiredAssocConstItem(..)
Expand Down
4 changes: 3 additions & 1 deletion src/librustdoc/fold.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,9 @@ pub(crate) trait DocFolder: Sized {
| ForeignFunctionItem(..)
| ForeignStaticItem(..)
| ForeignTypeItem
| MacroItem(_)
| MacroItem(..)
| AttrMacroItem
| DeriveMacroItem
| ProcMacroItem(_)
| PrimitiveItem(_)
| RequiredAssocConstItem(..)
Expand Down
6 changes: 4 additions & 2 deletions src/librustdoc/formats/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -378,8 +378,10 @@ impl DocFolder for CacheBuilder<'_, '_> {
| clean::RequiredAssocTypeItem(..)
| clean::AssocTypeItem(..)
| clean::StrippedItem(..)
| clean::KeywordItem
| clean::AttributeItem => {
| clean::AttributeItem
| clean::AttrMacroItem
| clean::DeriveMacroItem
| clean::KeywordItem => {
// FIXME: Do these need handling?
// The person writing this comment doesn't know.
// So would rather leave them to an expert,
Expand Down
8 changes: 6 additions & 2 deletions src/librustdoc/formats/item_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ item_type! {
// This number is reserved for use in JavaScript
// Generic = 26,
Attribute = 27,
BangMacroAttribute = 28,
BangMacroDerive = 29,
}

impl<'a> From<&'a clean::Item> for ItemType {
Expand Down Expand Up @@ -128,6 +130,8 @@ impl<'a> From<&'a clean::Item> for ItemType {
clean::ForeignFunctionItem(..) => ItemType::Function, // no ForeignFunction
clean::ForeignStaticItem(..) => ItemType::Static, // no ForeignStatic
clean::MacroItem(..) => ItemType::Macro,
clean::AttrMacroItem => ItemType::BangMacroAttribute,
clean::DeriveMacroItem => ItemType::BangMacroDerive,
clean::PrimitiveItem(..) => ItemType::Primitive,
clean::RequiredAssocConstItem(..)
| clean::ProvidedAssocConstItem(..)
Expand Down Expand Up @@ -221,8 +225,8 @@ impl ItemType {
ItemType::AssocConst => "associatedconstant",
ItemType::ForeignType => "foreigntype",
ItemType::Keyword => "keyword",
ItemType::ProcAttribute => "attr",
ItemType::ProcDerive => "derive",
ItemType::ProcAttribute | ItemType::BangMacroAttribute => "attr",
ItemType::ProcDerive | ItemType::BangMacroDerive => "derive",
ItemType::TraitAlias => "traitalias",
ItemType::Attribute => "attribute",
}
Expand Down
4 changes: 2 additions & 2 deletions src/librustdoc/html/markdown.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2038,7 +2038,7 @@ fn is_default_id(id: &str) -> bool {
| "crate-search"
| "crate-search-div"
// This is the list of IDs used in HTML generated in Rust (including the ones
// used in tera template files).
// used in askama template files).
| "themeStyle"
| "settings-menu"
| "help-button"
Expand Down Expand Up @@ -2077,7 +2077,7 @@ fn is_default_id(id: &str) -> bool {
| "blanket-implementations-list"
| "deref-methods"
| "layout"
| "aliased-type"
| "aliased-type",
)
}

Expand Down
Loading
Loading