Skip to content

Commit cbe154c

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

2 files changed

Lines changed: 60 additions & 15 deletions

File tree

src/librustdoc/clean/utils.rs

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -585,38 +585,68 @@ 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+
let mut tokens = tokens.iter();
593594
let mut out = String::new();
594-
for matcher in matchers {
595+
while let Some(mut token) = tokens.next() {
596+
// If this an attr/derive rule, it looks like `attr() () => {}`, so the token needs to be
597+
// handled at the same time as the actual matcher.
598+
//
599+
// Without that, we would end up with `attr()` on one line and the matcher `()` on another.
600+
let pre = if matches!(token, TokenTree::Token(..)) {
601+
let pre = format!("{}() ", render_macro_matcher(tcx, token));
602+
// Skipping the always empty `()` following the attr/derive ident.
603+
tokens.next();
604+
let Some(next) = tokens.next() else {
605+
return out;
606+
};
607+
token = next;
608+
pre
609+
} else {
610+
String::new()
611+
};
595612
writeln!(
596613
out,
597-
" {matcher} => {{ ... }}{arm_delim}",
598-
matcher = render_macro_matcher(tcx, matcher),
614+
" {pre}{matcher} => {{ ... }}{arm_delim}",
615+
matcher = render_macro_matcher(tcx, token),
599616
)
600617
.unwrap();
618+
// We skip the `=>`, macro "body" and the delimiter closing that "body" since we don't
619+
// render them.
620+
tokens.next();
621+
tokens.next();
622+
tokens.next();
601623
}
602624
out
603625
}
604626

605627
pub(super) fn display_macro_source(tcx: TyCtxt<'_>, name: Symbol, def: &ast::MacroDef) -> String {
606628
// 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-
609629
if def.macro_rules {
610-
format!("macro_rules! {name} {{\n{arms}}}", arms = render_macro_arms(tcx, matchers, ";"))
630+
format!(
631+
"macro_rules! {name} {{\n{arms}}}",
632+
arms = render_macro_arms(tcx, &def.body.tokens, ";")
633+
)
611634
} else {
612-
if matchers.len() <= 1 {
635+
if def.body.tokens.len() <= 4 {
613636
format!(
614637
"macro {name}{matchers} {{\n ...\n}}",
615-
matchers =
616-
matchers.map(|matcher| render_macro_matcher(tcx, matcher)).collect::<String>(),
638+
matchers = def
639+
.body
640+
.tokens
641+
.get(0)
642+
.map(|matcher| render_macro_matcher(tcx, matcher))
643+
.unwrap_or_default(),
617644
)
618645
} else {
619-
format!("macro {name} {{\n{arms}}}", arms = render_macro_arms(tcx, matchers, ","))
646+
format!(
647+
"macro {name} {{\n{arms}}}",
648+
arms = render_macro_arms(tcx, &def.body.tokens, ",")
649+
)
620650
}
621651
}
622652
}

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)