1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
#![deny(missing_docs)]
extern crate proc_macro;
use {proc_macro::TokenStream, syn::export::TokenStream2};
#[proc_macro_attribute]
pub fn nested(_attrs: TokenStream, input: TokenStream) -> TokenStream {
let mut input_fn: syn::ItemFn = syn::parse_macro_input!(input);
let tmp = std::mem::replace(&mut input_fn.attrs, Vec::new());
let mut doc_attrs = Vec::new();
for attr in tmp {
match attr.parse_meta() {
Ok(syn::Meta::NameValue(syn::MetaNameValue { ref path, .. })) => {
if let Some(id) = path.get_ident() {
if *id == "doc" {
doc_attrs.push(attr);
}
}
}
_ => input_fn.attrs.push(attr),
}
}
let docs_fn_sig = docs_fn_signature(&input_fn);
let mangled_name = syn::Ident::new(
&format!("__{}_impl", &input_fn.sig.ident),
input_fn.sig.ident.span(),
);
let macro_name = std::mem::replace(&mut input_fn.sig.ident, mangled_name.clone());
quote::quote!(
topo::unstable_make_topo_macro!(
#macro_name #mangled_name
match ($($arg:expr),*)
subst ($($arg),*)
doc (
#(#doc_attrs)*
#docs_fn_sig
)
);
#[doc(hidden)]
#input_fn
)
.into()
}
fn docs_fn_signature(input_fn: &syn::ItemFn) -> TokenStream2 {
let doc_fn_sig = syn::ItemFn {
attrs: vec![],
vis: syn::Visibility::Inherited,
sig: input_fn.sig.clone(),
block: Box::new(syn::Block {
brace_token: syn::token::Brace {
span: proc_macro::Span::call_site().into(),
},
stmts: vec![],
}),
};
let doc_fn_sig = quote::quote!(#doc_fn_sig).to_string();
use std::io::prelude::*;
let doc_fn_sig = if let Ok(mut rustfmt) = std::process::Command::new("rustfmt")
.stdin(std::process::Stdio::piped())
.stdout(std::process::Stdio::piped())
.spawn()
{
let stdin = rustfmt.stdin.as_mut().unwrap();
stdin.write_all(doc_fn_sig.as_bytes()).unwrap();
let output = rustfmt.wait_with_output().unwrap();
if output.status.success() {
String::from_utf8(output.stdout).unwrap()
} else {
doc_fn_sig
}
} else {
doc_fn_sig
};
["", "# Signature", "", "```text", &doc_fn_sig, "```"]
.iter()
.map(|&l| quote::quote!(#[doc = #l]))
.fold(quote::quote!(), |a, b| quote::quote!(#a #b))
}