rustc_builtin_macros/
rustdoc_skip_body.rs1use rustc_ast::token::{self, Delimiter, IdentKind};
5use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree};
6use rustc_ast::{
7 AssocItemKind, AttrArgs, AttrItem, Attribute, DelimArgs, ExprKind, Fn, ItemKind, MetaItem,
8 Safety,
9};
10use rustc_expand::base::*;
11use rustc_span::{Ident, Span, sym};
12use thin_vec::ThinVec;
13
14pub(crate) fn expand(
15 cx: &mut ExtCtxt<'_>,
16 attr_path_sp: Span,
17 meta_item: &MetaItem,
18 mut item: Annotatable,
19) -> Vec<Annotatable> {
20 if !meta_item.is_word() {
21 cx.dcx().struct_span_err(attr_path_sp, "`rustdoc_skip_body` doesn't take arguments").emit();
22 }
23 let mut is_expected_item = false;
24 match item {
25 Annotatable::Item(ref mut item) => {
26 if let ItemKind::Fn(ref mut fn_) = item.kind {
27 expand_macro(cx, attr_path_sp, fn_);
28 is_expected_item = true;
29 }
30 }
31 Annotatable::AssocItem(ref mut item, _) => {
32 if let AssocItemKind::Fn(ref mut fn_) = item.kind {
33 expand_macro(cx, attr_path_sp, fn_);
34 is_expected_item = true;
35 }
36 }
37 _ => {}
38 }
39 if !is_expected_item {
40 cx.dcx()
41 .struct_span_err(attr_path_sp, "`rustdoc_skip_body` can only be used on functions")
42 .emit();
43 }
44 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[item]))vec![item]
45}
46
47fn cfg_not_doc(cx: &mut ExtCtxt<'_>, sp: Span) -> Attribute {
48 let not_doc = TokenStream::new(::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[TokenTree::token_alone(token::Ident(sym::not, IdentKind::Normal),
sp),
TokenTree::Delimited(DelimSpan::from_single(sp),
DelimSpacing::new(Spacing::JointHidden, Spacing::Alone),
Delimiter::Parenthesis,
TokenStream::token_alone(token::Ident(sym::doc,
IdentKind::Normal), sp))]))vec![
50 TokenTree::token_alone(token::Ident(sym::not, IdentKind::Normal), sp),
51 TokenTree::Delimited(
52 DelimSpan::from_single(sp),
53 DelimSpacing::new(Spacing::JointHidden, Spacing::Alone),
54 Delimiter::Parenthesis,
55 TokenStream::token_alone(token::Ident(sym::doc, IdentKind::Normal), sp),
56 ),
57 ]);
58
59 cx.attr_nested(
60 AttrItem {
61 unsafety: Safety::Default,
62 path: cx.path_ident(sp, Ident::new(sym::cfg, sp)),
63 args: AttrArgs::Delimited(DelimArgs {
64 dspan: DelimSpan::from_single(sp),
65 delim: Delimiter::Parenthesis,
66 tokens: not_doc,
67 }),
68 span: sp,
69 },
70 sp,
71 )
72}
73
74fn expand_macro(cx: &mut ExtCtxt<'_>, sp: Span, fn_: &mut Fn) {
75 let Some(old_body) = fn_.body.take() else {
76 return;
78 };
79 let mut old_body = cx.expr_block(old_body);
80 old_body.attrs.push(cfg_not_doc(cx, sp));
82
83 let mut loop_expr = cx.expr(sp, ExprKind::Loop(cx.block(sp, ThinVec::new()), None, sp));
85 loop_expr.attrs.push(cx.attr_nested_word(sym::cfg, sym::doc, sp));
86
87 let mut stmts = ThinVec::new();
88 stmts.push(cx.stmt_expr(loop_expr));
89 stmts.push(cx.stmt_expr(old_body));
90
91 fn_.body = Some(cx.block(sp, stmts));
93}