@@ -5,6 +5,10 @@ use self::proc_macro::TokenStream;
5
5
use quote:: quote;
6
6
use syn:: parse_macro_input;
7
7
8
+ /// `input` contains a single identifier, corresponding to a user-defined macro.
9
+ /// This identifier is expanded for each libm public API.
10
+ ///
11
+ /// See tests/analyze or below for the API.
8
12
#[ proc_macro]
9
13
pub fn for_each_api ( input : TokenStream ) -> TokenStream {
10
14
let files = get_libm_files ( ) ;
@@ -30,14 +34,16 @@ pub fn for_each_api(input: TokenStream) -> TokenStream {
30
34
}
31
35
32
36
/// Traverses the libm crate directory, parsing all .rs files
33
- fn get_libm_files ( ) -> Vec < ( syn:: File , String ) > {
37
+ fn get_libm_files ( ) -> Vec < syn:: File > {
38
+ // Find the directory of the libm crate:
34
39
let root_dir = std:: path:: Path :: new ( env ! ( "CARGO_MANIFEST_DIR" ) ) ;
35
40
let libm_dir = root_dir
36
41
. parent ( )
37
42
. expect ( "couldn't access crates/ dir" )
38
43
. join ( "libm" ) ;
39
44
let libm_src_dir = libm_dir. join ( "src" ) ;
40
45
46
+ // Traverse all Rust files, parsing them as `syn::File`
41
47
let mut files = Vec :: new ( ) ;
42
48
for entry in walkdir:: WalkDir :: new ( libm_src_dir)
43
49
. into_iter ( )
@@ -51,23 +57,25 @@ fn get_libm_files() -> Vec<(syn::File, String)> {
51
57
. expect ( "can't format file path" )
52
58
. ends_with ( ".rs" )
53
59
{
60
+ // If the path is a directory or not a ".rs" file => skip it.
54
61
continue ;
55
62
}
56
63
64
+ // Read the file into a string, and parse it into an AST using syn.
57
65
let mut file_string = String :: new ( ) ;
58
66
std:: fs:: File :: open ( & file_path)
59
67
. unwrap_or_else ( |_| panic ! ( "can't open file at path: {}" , file_path. display( ) ) )
60
68
. read_to_string ( & mut file_string)
61
69
. expect ( "failed to read file to string" ) ;
62
70
let file = syn:: parse_file ( & file_string) . expect ( "failed to parse" ) ;
63
- files. push ( ( file, file_path . to_str ( ) . unwrap ( ) . to_string ( ) ) ) ;
71
+ files. push ( file) ;
64
72
}
65
73
files
66
74
}
67
75
76
+ /// Function signature that will be expanded for the user macro.
68
77
struct FnSig {
69
78
ident : syn:: Ident ,
70
- unsafety : bool ,
71
79
c_abi : bool ,
72
80
ret_ty : Option < syn:: Type > ,
73
81
arg_tys : Vec < syn:: Type > ,
@@ -89,11 +97,13 @@ macro_rules! syn_to_str {
89
97
} } ;
90
98
}
91
99
92
- /// Extracts all public functions from the libm files.
93
- fn get_functions ( files : Vec < ( syn:: File , String ) > ) -> Vec < FnSig > {
100
+ /// Extracts all public functions from the libm files while
101
+ /// doing some sanity checks on the function signatures.
102
+ fn get_functions ( files : Vec < syn:: File > ) -> Vec < FnSig > {
94
103
let mut error = false ;
95
104
let mut functions = Vec :: new ( ) ;
96
- for item in files. iter ( ) . flat_map ( |f| f. 0 . items . iter ( ) ) {
105
+ // Traverse all files matching function items
106
+ for item in files. iter ( ) . flat_map ( |f| f. items . iter ( ) ) {
97
107
let mut e = false ;
98
108
match item {
99
109
syn:: Item :: Fn ( syn:: ItemFn {
@@ -107,9 +117,9 @@ fn get_functions(files: Vec<(syn::File, String)>) -> Vec<FnSig> {
107
117
decl,
108
118
block : _,
109
119
} ) => {
120
+ // Build a function signature while doing some sanity checks
110
121
let mut fn_sig = FnSig {
111
122
ident : ident. clone ( ) ,
112
- unsafety : true ,
113
123
c_abi : false ,
114
124
arg_tys : Vec :: new ( ) ,
115
125
ret_ty : None ,
@@ -136,14 +146,31 @@ fn get_functions(files: Vec<(syn::File, String)>) -> Vec<FnSig> {
136
146
fn_sig. c_abi = true ;
137
147
}
138
148
}
149
+ // If the function signature isn't extern "C", we aren't ABI compatible
150
+ // with libm.
151
+ if !fn_sig. c_abi {
152
+ // FIXME: we should error here, but right that would break everything,
153
+ // so we disable erroring.
154
+ let e2 = e;
155
+ err ! ( "not `extern \" C\" `" ) ;
156
+ e = e2;
157
+ }
158
+ // Right now no functions are const fn - they could be, but that
159
+ // change should be explicit - so error if somebody tries.
139
160
if let Some ( _) = constness {
140
161
err ! ( "is const" ) ;
141
162
}
163
+ // No functions should be async fn
142
164
if let Some ( _) = asyncness {
143
165
err ! ( "is async" ) ;
144
166
}
145
- if & None == unsafety {
146
- fn_sig. unsafety = false ;
167
+ // FIXME: Math functions shouldn't be unsafe. Some functions
168
+ // that should take pointers use repr(Rust) tuples. When we fix
169
+ // those, they should use references are not pointers.
170
+ if let Some ( _) = unsafety {
171
+ let e2 = e;
172
+ err ! ( "is unsafe" ) ;
173
+ e = e2;
147
174
}
148
175
let syn:: FnDecl {
149
176
fn_token : _,
@@ -154,6 +181,7 @@ fn get_functions(files: Vec<(syn::File, String)>) -> Vec<FnSig> {
154
181
output,
155
182
} = ( * * decl) . clone ( ) ;
156
183
184
+ // Forbid generic parameters, lifetimes, and consts in public APIs:
157
185
if variadic. is_some ( ) {
158
186
err ! ( format!(
159
187
"contains variadic arguments \" {}\" " ,
@@ -178,7 +206,13 @@ fn get_functions(files: Vec<(syn::File, String)>) -> Vec<FnSig> {
178
206
syn_to_str!( generics. clone( ) )
179
207
) ) ;
180
208
}
209
+ // FIXME: we can do better here, but right now, we should
210
+ // error if inline and no_panic are not used, which is the
211
+ // case if the public API has no attributes.
212
+ //
213
+ // We might also want to check other attributes as well.
181
214
if attrs. is_empty ( ) {
215
+ let e2 = e;
182
216
err ! ( format!(
183
217
"missing `#[inline]` and `#[no_panic]` attributes {}" ,
184
218
attrs
@@ -187,13 +221,9 @@ fn get_functions(files: Vec<(syn::File, String)>) -> Vec<FnSig> {
187
221
. collect:: <Vec <_>>( )
188
222
. join( "," )
189
223
) ) ;
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
224
e = e2;
196
225
}
226
+ // Validate and parse output parameters and function arguments:
197
227
match output {
198
228
syn:: ReturnType :: Default => ( ) ,
199
229
syn:: ReturnType :: Type ( _, ref b) if valid_ty ( & b) => {
@@ -212,6 +242,7 @@ fn get_functions(files: Vec<(syn::File, String)>) -> Vec<FnSig> {
212
242
) ) ,
213
243
}
214
244
}
245
+ // If there was an error, we skip the function:
215
246
if !e {
216
247
functions. push ( fn_sig) ;
217
248
} else {
@@ -254,7 +285,8 @@ fn valid_ty(t: &syn::Type) -> bool {
254
285
. ident
255
286
. to_string ( ) ;
256
287
match s. as_str ( ) {
257
- "i8" | "i16" | "i32" | "i64" | "isize" | "u8" | "u16" | "u32" | "u64" | "usize"
288
+ | "i8" | "i16" | "i32" | "i64" | "isize"
289
+ | "u8" | "u16" | "u32" | "u64" | "usize"
258
290
| "f32" | "f64" => true ,
259
291
_ => false ,
260
292
}
@@ -263,6 +295,7 @@ fn valid_ty(t: &syn::Type) -> bool {
263
295
}
264
296
}
265
297
298
+ /// Returns a vector containing `len` identifiers.
266
299
fn get_arg_ids ( len : usize ) -> Vec < syn:: Ident > {
267
300
let mut ids = Vec :: new ( ) ;
268
301
for i in 0 ..len {
0 commit comments