2
2
3
3
extern crate proc_macro;
4
4
use self :: proc_macro:: TokenStream ;
5
+ use quote:: quote;
6
+ use syn:: parse_macro_input;
5
7
6
8
#[ proc_macro]
7
9
pub fn for_each_api ( input : TokenStream ) -> TokenStream {
8
10
let files = get_libm_files ( ) ;
9
11
let functions = get_functions ( files) ;
10
- input
12
+ let input = parse_macro_input ! ( input as syn:: Ident ) ;
13
+ let mut tokens = proc_macro2:: TokenStream :: new ( ) ;
14
+ for function in functions {
15
+ let id = function. ident ;
16
+ let ret_ty = function. ret_ty ;
17
+ let arg_tys = function. arg_tys ;
18
+ let arg_ids = get_arg_ids ( arg_tys. len ( ) ) ;
19
+ let t = quote ! {
20
+ #input! {
21
+ id: #id;
22
+ arg_tys: #( #arg_tys) , * ;
23
+ arg_ids: #( #arg_ids) , * ;
24
+ ret: #ret_ty;
25
+ }
26
+ } ;
27
+ tokens. extend ( t) ;
28
+ }
29
+ tokens. into ( )
11
30
}
12
31
13
32
/// Traverses the libm crate directory, parsing all .rs files
14
33
fn get_libm_files ( ) -> Vec < ( syn:: File , String ) > {
15
34
let root_dir = std:: path:: Path :: new ( env ! ( "CARGO_MANIFEST_DIR" ) ) ;
16
- dbg ! ( & root_dir) ;
17
35
let libm_dir = root_dir
18
36
. parent ( )
19
37
. expect ( "couldn't access crates/ dir" )
20
38
. join ( "libm" ) ;
21
- dbg ! ( & libm_dir) ;
22
39
let libm_src_dir = libm_dir. join ( "src" ) ;
23
- dbg ! ( & libm_src_dir) ;
24
40
25
41
let mut files = Vec :: new ( ) ;
26
42
for entry in walkdir:: WalkDir :: new ( libm_src_dir)
@@ -38,7 +54,6 @@ fn get_libm_files() -> Vec<(syn::File, String)> {
38
54
continue ;
39
55
}
40
56
41
- eprintln ! ( "{}" , file_path. display( ) ) ;
42
57
let mut file_string = String :: new ( ) ;
43
58
std:: fs:: File :: open ( & file_path)
44
59
. unwrap_or_else ( |_| panic ! ( "can't open file at path: {}" , file_path. display( ) ) )
@@ -53,17 +68,33 @@ fn get_libm_files() -> Vec<(syn::File, String)> {
53
68
struct FnSig {
54
69
ident : syn:: Ident ,
55
70
unsafety : bool ,
56
- constness : bool ,
57
- asyncness : bool ,
58
- attrs : Vec < syn:: Attribute > ,
59
- abi : & ' static str ,
71
+ c_abi : bool ,
72
+ ret_ty : Option < syn:: Type > ,
73
+ arg_tys : Vec < syn:: Type > ,
74
+ }
75
+
76
+ impl FnSig {
77
+ fn name ( & self ) -> String {
78
+ self . ident . to_string ( )
79
+ }
80
+ }
60
81
82
+ macro_rules! syn_to_str {
83
+ ( $e: expr) => { {
84
+ let t = $e;
85
+ let tokens = quote! {
86
+ #t
87
+ } ;
88
+ format!( "{}" , tokens)
89
+ } } ;
61
90
}
62
91
63
92
/// Extracts all public functions from the libm files.
64
- fn get_functions ( files : Vec < ( syn:: File , String ) > ) {
65
- //let mut functions = Vec::new();
93
+ fn get_functions ( files : Vec < ( syn:: File , String ) > ) -> Vec < FnSig > {
94
+ let mut error = false ;
95
+ let mut functions = Vec :: new ( ) ;
66
96
for item in files. iter ( ) . flat_map ( |f| f. 0 . items . iter ( ) ) {
97
+ let mut e = false ;
67
98
match item {
68
99
syn:: Item :: Fn ( syn:: ItemFn {
69
100
vis : syn:: Visibility :: Public ( _) ,
@@ -76,21 +107,167 @@ fn get_functions(files: Vec<(syn::File, String)>) {
76
107
decl,
77
108
block : _,
78
109
} ) => {
79
- if let Some ( syn:: Abi { name : Some ( l) , extern_token : _ } ) = abi {
80
- println ! ( "{:#?}" , l) ;
81
- if l. value ( ) != "C" {
82
- l. span ( ) . unwrap ( ) . warning (
83
- "public libm function is not `extern \" C\" "
84
- ) . emit ( ) ;
110
+ let mut fn_sig = FnSig {
111
+ ident : ident. clone ( ) ,
112
+ unsafety : true ,
113
+ c_abi : false ,
114
+ arg_tys : Vec :: new ( ) ,
115
+ ret_ty : None ,
116
+ } ;
117
+ macro_rules! err {
118
+ ( $msg: expr) => { {
119
+ #[ cfg( feature = "analyze" ) ]
120
+ {
121
+ eprintln!( "[error]: Function \" {}\" {}" , fn_sig. name( ) , $msg) ;
122
+ }
123
+ #[ allow( unused_assignments) ]
124
+ {
125
+ e = true ;
126
+ }
127
+ ( )
128
+ } } ;
129
+ }
130
+ if let Some ( syn:: Abi {
131
+ name : Some ( l) ,
132
+ extern_token : _,
133
+ } ) = abi
134
+ {
135
+ if l. value ( ) == "C" {
136
+ fn_sig. c_abi = true ;
137
+ }
138
+ }
139
+ if let Some ( _) = constness {
140
+ err ! ( "is const" ) ;
141
+ }
142
+ if let Some ( _) = asyncness {
143
+ err ! ( "is async" ) ;
144
+ }
145
+ if & None == unsafety {
146
+ fn_sig. unsafety = false ;
147
+ }
148
+ let syn:: FnDecl {
149
+ fn_token : _,
150
+ generics,
151
+ paren_token : _,
152
+ inputs,
153
+ variadic,
154
+ output,
155
+ } = ( * * decl) . clone ( ) ;
156
+
157
+ if variadic. is_some ( ) {
158
+ err ! ( format!(
159
+ "contains variadic arguments \" {}\" " ,
160
+ syn_to_str!( variadic. unwrap( ) )
161
+ ) ) ;
162
+ }
163
+ if generics. type_params ( ) . into_iter ( ) . count ( ) != 0 {
164
+ err ! ( format!(
165
+ "contains generic parameters \" {}\" " ,
166
+ syn_to_str!( generics. clone( ) )
167
+ ) ) ;
168
+ }
169
+ if generics. lifetimes ( ) . into_iter ( ) . count ( ) != 0 {
170
+ err ! ( format!(
171
+ "contains lifetime parameters \" {}\" " ,
172
+ syn_to_str!( generics. clone( ) )
173
+ ) ) ;
174
+ }
175
+ if generics. const_params ( ) . into_iter ( ) . count ( ) != 0 {
176
+ err ! ( format!(
177
+ "contains const parameters \" {}\" " ,
178
+ syn_to_str!( generics. clone( ) )
179
+ ) ) ;
180
+ }
181
+ if attrs. is_empty ( ) {
182
+ err ! ( format!(
183
+ "missing `#[inline]` and `#[no_panic]` attributes {}" ,
184
+ attrs
185
+ . iter( )
186
+ . map( |a| syn_to_str!( a) )
187
+ . collect:: <Vec <_>>( )
188
+ . join( "," )
189
+ ) ) ;
190
+ } // TODO: might want to check other attributes as well
191
+ if !fn_sig. c_abi {
192
+ // FIXME: do not disable test if this fails - otherwise no test passes
193
+ let e2 = e;
194
+ err ! ( "not `extern \" C\" `" ) ;
195
+ e = e2;
196
+ }
197
+ match output {
198
+ syn:: ReturnType :: Default => ( ) ,
199
+ syn:: ReturnType :: Type ( _, ref b) if valid_ty ( & b) => {
200
+ fn_sig. ret_ty = Some ( * b. clone ( ) )
201
+ }
202
+ other => err ! ( format!( "returns unsupported type {}" , syn_to_str!( other) ) ) ,
203
+ }
204
+ for input in inputs {
205
+ match input {
206
+ syn:: FnArg :: Captured ( ref c) if valid_ty ( & c. ty ) => {
207
+ fn_sig. arg_tys . push ( c. ty . clone ( ) )
208
+ }
209
+ other => err ! ( format!(
210
+ "takes unsupported argument type {}" ,
211
+ syn_to_str!( other)
212
+ ) ) ,
85
213
}
214
+ }
215
+ if !e {
216
+ functions. push ( fn_sig) ;
86
217
} else {
87
- ident. span ( ) . unwrap ( ) . warning (
88
- "public libm function is not `extern \" C\" "
89
- ) . emit ( ) ;
218
+ error = true ;
90
219
}
91
- println ! ( "{:#?}" , ident) ;
92
220
}
93
221
_ => ( ) ,
94
222
}
95
223
}
224
+ if error {
225
+ // too many errors:
226
+ // panic!("errors found");
227
+ }
228
+ functions
229
+ }
230
+
231
+ /// Parses a type into a String - arg is true if the type is an argument, and
232
+ /// false if its a return value.
233
+ fn valid_ty ( t : & syn:: Type ) -> bool {
234
+ match t {
235
+ syn:: Type :: Ptr ( p) => {
236
+ let c = p. const_token . is_some ( ) ;
237
+ let m = p. mutability . is_some ( ) ;
238
+ assert ! ( !( c && m) ) ;
239
+ match & * p. elem {
240
+ syn:: Type :: Path ( _) => valid_ty ( & p. elem ) ,
241
+ // Only one layer of pointers allowed:
242
+ _ => false ,
243
+ }
244
+ }
245
+ syn:: Type :: Path ( p) => {
246
+ assert ! ( p. qself. is_none( ) ) ;
247
+ assert_eq ! ( p. path. segments. len( ) , 1 ) ;
248
+ let s = p
249
+ . path
250
+ . segments
251
+ . first ( )
252
+ . unwrap ( )
253
+ . into_value ( )
254
+ . ident
255
+ . to_string ( ) ;
256
+ match s. as_str ( ) {
257
+ "i8" | "i16" | "i32" | "i64" | "isize" | "u8" | "u16" | "u32" | "u64" | "usize"
258
+ | "f32" | "f64" => true ,
259
+ _ => false ,
260
+ }
261
+ }
262
+ _ => false ,
263
+ }
264
+ }
265
+
266
+ fn get_arg_ids ( len : usize ) -> Vec < syn:: Ident > {
267
+ let mut ids = Vec :: new ( ) ;
268
+ for i in 0 ..len {
269
+ let x = format ! ( "x{}" , i) ;
270
+ ids. push ( syn:: Ident :: new ( & x, proc_macro2:: Span :: call_site ( ) ) ) ;
271
+ }
272
+ ids
96
273
}
0 commit comments