Skip to content

Commit eef2a58

Browse files
Correctly generate bang macro declaration in docs for attr/derive kinds
1 parent 702d85b commit eef2a58

2 files changed

Lines changed: 61 additions & 15 deletions

File tree

src/librustdoc/clean/utils.rs

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -585,38 +585,69 @@ pub(crate) static RUSTDOC_VERSION: Lazy<&'static str> =
585585

586586
/// Render a sequence of macro arms in a format suitable for displaying to the user
587587
/// as part of an item declaration.
588-
fn render_macro_arms<'a>(
588+
fn render_macro_arms(
589589
tcx: TyCtxt<'_>,
590-
matchers: impl Iterator<Item = &'a TokenTree>,
590+
tokens: &rustc_ast::tokenstream::TokenStream,
591591
arm_delim: &str,
592592
) -> String {
593+
eprintln!("============>\n{tokens:#?}");
594+
let mut tokens = tokens.iter();
593595
let mut out = String::new();
594-
for matcher in matchers {
596+
while let Some(mut token) = tokens.next() {
597+
// If this an attr/derive rule, it looks like `attr() () => {}`, so the token needs to be
598+
// handled at the same time as the actual matcher.
599+
//
600+
// Without that, we would end up with `attr()` on one line and the matcher `()` on another.
601+
let pre = if matches!(token, TokenTree::Token(..)) {
602+
let pre = format!("{}() ", render_macro_matcher(tcx, token));
603+
// Skipping the always empty `()` following the attr/derive ident.
604+
tokens.next();
605+
let Some(next) = tokens.next() else {
606+
return out;
607+
};
608+
token = next;
609+
pre
610+
} else {
611+
String::new()
612+
};
595613
writeln!(
596614
out,
597-
" {matcher} => {{ ... }}{arm_delim}",
598-
matcher = render_macro_matcher(tcx, matcher),
615+
" {pre}{matcher} => {{ ... }}{arm_delim}",
616+
matcher = render_macro_matcher(tcx, token),
599617
)
600618
.unwrap();
619+
// We skip the `=>`, macro "body" and the delimiter closing that "body" since we don't
620+
// render them.
621+
tokens.next();
622+
tokens.next();
623+
tokens.next();
601624
}
602625
out
603626
}
604627

605628
pub(super) fn display_macro_source(tcx: TyCtxt<'_>, name: Symbol, def: &ast::MacroDef) -> String {
606629
// Extract the spans of all matchers. They represent the "interface" of the macro.
607-
let matchers = def.body.tokens.chunks(4).map(|arm| &arm[0]);
608-
609630
if def.macro_rules {
610-
format!("macro_rules! {name} {{\n{arms}}}", arms = render_macro_arms(tcx, matchers, ";"))
631+
format!(
632+
"macro_rules! {name} {{\n{arms}}}",
633+
arms = render_macro_arms(tcx, &def.body.tokens, ";")
634+
)
611635
} else {
612-
if matchers.len() <= 1 {
636+
if def.body.tokens.len() <= 4 {
613637
format!(
614638
"macro {name}{matchers} {{\n ...\n}}",
615-
matchers =
616-
matchers.map(|matcher| render_macro_matcher(tcx, matcher)).collect::<String>(),
639+
matchers = def
640+
.body
641+
.tokens
642+
.get(0)
643+
.map(|matcher| render_macro_matcher(tcx, matcher))
644+
.unwrap_or_default(),
617645
)
618646
} else {
619-
format!("macro {name} {{\n{arms}}}", arms = render_macro_arms(tcx, matchers, ","))
647+
format!(
648+
"macro {name} {{\n{arms}}}",
649+
arms = render_macro_arms(tcx, &def.body.tokens, ",")
650+
)
620651
}
621652
}
622653
}

tests/rustdoc-gui/attr-macros.goml

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ assert-text: ("#rustdoc-modnav .block.macro .current", "b")
77

88
define-function: (
99
"check_macro",
10-
[name, info],
10+
[name, info, kind],
1111
block {
1212
// It should be present twice in the sidebar.
1313
assert-count: ("#rustdoc-modnav a[href='macro." + |name| + ".html']", 2)
@@ -24,11 +24,16 @@ define-function: (
2424
assert-count: ("#rustdoc-modnav .current", 2)
2525
// We check it has the expected information.
2626
assert-text: ("h3.macro-info", "ⓘ This is " + |info| + "/function macro")
27+
// We check how the item declaration looks like.
28+
assert-text: (".item-decl", "macro_rules! " + |name| + " {
29+
" + |kind| + "() () => { ... };
30+
() => { ... };
31+
}")
2732
}
2833
)
2934

30-
call-function: ("check_macro", {"name": "attr_macro", "info": "an attribute"})
31-
call-function: ("check_macro", {"name": "derive_macro", "info": "a derive"})
35+
call-function: ("check_macro", {"name": "attr_macro", "info": "an attribute", "kind": "attr"})
36+
call-function: ("check_macro", {"name": "derive_macro", "info": "a derive", "kind": "derive"})
3237

3338
define-function: (
3439
"crate_page",
@@ -74,3 +79,13 @@ define-function: (
7479
go-to: "file://" + |DOC_PATH| + "/test_docs/all.html"
7580
call-function: ("all_items_page", {"name": "attr_macro", "section_id": "attribute-macros"})
7681
call-function: ("all_items_page", {"name": "derive_macro", "section_id": "derives"})
82+
83+
// We now check a macro with all 3 different kinds.
84+
go-to: "file://" + |DOC_PATH| + "/test_docs/macro.one_for_all_macro.html"
85+
assert-text: (".item-decl", "macro_rules! one_for_all_macro {
86+
attr() () => { ... };
87+
derive() () => { ... };
88+
() => { ... };
89+
}")
90+
// We check it has the expected information.
91+
assert-text: ("h3.macro-info", "ⓘ This is an attribute/derive/function macro")

0 commit comments

Comments
 (0)