1
+ //! A proc macro to analyze the libm APIs We want to exhaustively match all
2
+ // fields here to create a compilation error if new fields are added.
3
+ #![ allow( clippy:: unneeded_field_pattern) ]
4
+
1
5
extern crate proc_macro;
2
6
use self :: proc_macro:: TokenStream ;
3
7
use quote:: quote;
@@ -10,7 +14,7 @@ use syn::parse_macro_input;
10
14
#[ proc_macro]
11
15
pub fn for_each_api ( input : TokenStream ) -> TokenStream {
12
16
let files = get_libm_files ( ) ;
13
- let functions = get_functions ( files) ;
17
+ let functions = get_functions ( & files) ;
14
18
let input = parse_macro_input ! ( input as syn:: Ident ) ;
15
19
let mut tokens = proc_macro2:: TokenStream :: new ( ) ;
16
20
for function in functions {
@@ -45,7 +49,7 @@ fn get_libm_files() -> Vec<syn::File> {
45
49
let mut files = Vec :: new ( ) ;
46
50
for entry in walkdir:: WalkDir :: new ( libm_src_dir)
47
51
. into_iter ( )
48
- . filter_map ( |e| e . ok ( ) )
52
+ . filter_map ( Result :: ok )
49
53
{
50
54
use std:: io:: Read ;
51
55
let file_path = entry. path ( ) ;
@@ -97,160 +101,158 @@ macro_rules! syn_to_str {
97
101
98
102
/// Extracts all public functions from the libm files while
99
103
/// doing some sanity checks on the function signatures.
100
- fn get_functions ( files : Vec < syn:: File > ) -> Vec < FnSig > {
104
+ fn get_functions ( files : & [ syn:: File ] ) -> Vec < FnSig > {
101
105
let mut error = false ;
102
106
let mut functions = Vec :: new ( ) ;
103
107
// Traverse all files matching function items
104
108
for item in files. iter ( ) . flat_map ( |f| f. items . iter ( ) ) {
105
109
let mut e = false ;
106
- match item {
107
- syn:: Item :: Fn ( syn:: ItemFn {
108
- vis : syn:: Visibility :: Public ( _) ,
109
- ident,
110
- constness,
111
- asyncness,
112
- unsafety,
113
- attrs,
114
- abi,
115
- decl,
116
- block : _,
117
- } ) => {
118
- // Build a function signature while doing some sanity checks
119
- let mut fn_sig = FnSig {
120
- ident : ident. clone ( ) ,
121
- c_abi : false ,
122
- arg_tys : Vec :: new ( ) ,
123
- ret_ty : None ,
124
- } ;
125
- macro_rules! err {
126
- ( $msg: expr) => { {
127
- #[ cfg( feature = "analyze" ) ]
128
- {
129
- eprintln!( "[error]: Function \" {}\" {}" , fn_sig. name( ) , $msg) ;
130
- }
131
- #[ allow( unused_assignments) ]
132
- {
133
- e = true ;
134
- }
135
- ( )
136
- } } ;
137
- }
138
- if let Some ( syn:: Abi {
139
- name : Some ( l) ,
140
- extern_token : _,
141
- } ) = abi
142
- {
143
- if l. value ( ) == "C" {
144
- fn_sig. c_abi = true ;
110
+ if let syn:: Item :: Fn ( syn:: ItemFn {
111
+ vis : syn:: Visibility :: Public ( _) ,
112
+ ident,
113
+ constness,
114
+ asyncness,
115
+ unsafety,
116
+ attrs,
117
+ abi,
118
+ decl,
119
+ block : _,
120
+ } ) = item
121
+ {
122
+ // Build a function signature while doing some sanity checks
123
+ let mut fn_sig = FnSig {
124
+ ident : ident. clone ( ) ,
125
+ c_abi : false ,
126
+ arg_tys : Vec :: new ( ) ,
127
+ ret_ty : None ,
128
+ } ;
129
+ macro_rules! err {
130
+ ( $msg: expr) => { {
131
+ #[ cfg( feature = "analyze" ) ]
132
+ {
133
+ eprintln!( "[error]: Function \" {}\" {}" , fn_sig. name( ) , $msg) ;
145
134
}
146
- }
147
- // If the function signature isn't extern "C", we aren't ABI compatible
148
- // with libm.
149
- if !fn_sig. c_abi {
150
- // FIXME: we should error here, but right that would break everything,
151
- // so we disable erroring.
152
- let e2 = e;
153
- err ! ( "not `extern \" C\" `" ) ;
154
- e = e2;
155
- }
156
- // Right now there are no const fn functions. We might add them
157
- // in the future, and at that point, we should tune this here.
158
- // In the mean time, error if somebody tries.
159
- if let Some ( _) = constness {
160
- err ! ( "is const" ) ;
161
- }
162
- // No functions should be async fn
163
- if let Some ( _) = asyncness {
164
- err ! ( "is async" ) ;
165
- }
166
- // FIXME: Math functions are not unsafe. Some functions in the
167
- // libm C API take pointers, but in our API take repr(Rust)
168
- // tuples (for some reason). Once we fix those to have the same
169
- // API as C libm, we should use references on their signature
170
- // instead, and make them safe.
171
- if let Some ( _) = unsafety {
172
- let e2 = e;
173
- err ! ( "is unsafe" ) ;
174
- e = e2;
175
- }
176
- let syn:: FnDecl {
177
- fn_token : _,
178
- generics,
179
- paren_token : _,
180
- inputs,
181
- variadic,
182
- output,
183
- } = ( * * decl) . clone ( ) ;
184
-
185
- // Forbid generic parameters, lifetimes, and consts in public APIs:
186
- if variadic. is_some ( ) {
187
- err ! ( format!(
188
- "contains variadic arguments \" {}\" " ,
189
- syn_to_str!( variadic. unwrap( ) )
190
- ) ) ;
191
- }
192
- if generics. type_params ( ) . into_iter ( ) . count ( ) != 0 {
193
- err ! ( format!(
194
- "contains generic parameters \" {}\" " ,
195
- syn_to_str!( generics. clone( ) )
196
- ) ) ;
197
- }
198
- if generics. lifetimes ( ) . into_iter ( ) . count ( ) != 0 {
199
- err ! ( format!(
200
- "contains lifetime parameters \" {}\" " ,
201
- syn_to_str!( generics. clone( ) )
202
- ) ) ;
203
- }
204
- if generics. const_params ( ) . into_iter ( ) . count ( ) != 0 {
205
- err ! ( format!(
206
- "contains const parameters \" {}\" " ,
207
- syn_to_str!( generics. clone( ) )
208
- ) ) ;
209
- }
210
- // FIXME: we can do better here, but right now, we should
211
- // error if inline and no_panic are not used, which is the
212
- // case if the public API has no attributes.
213
- //
214
- // We might also want to check other attributes as well.
215
- if attrs. is_empty ( ) {
216
- let e2 = e;
217
- err ! ( format!(
218
- "missing `#[inline]` and `#[no_panic]` attributes {}" ,
219
- attrs
220
- . iter( )
221
- . map( |a| syn_to_str!( a) )
222
- . collect:: <Vec <_>>( )
223
- . join( "," )
224
- ) ) ;
225
- e = e2;
226
- }
227
- // Validate and parse output parameters and function arguments:
228
- match output {
229
- syn:: ReturnType :: Default => ( ) ,
230
- syn:: ReturnType :: Type ( _, ref b) if valid_ty ( & b) => {
231
- fn_sig. ret_ty = Some ( * b. clone ( ) )
135
+ #[ allow( unused_assignments) ]
136
+ {
137
+ e = true ;
232
138
}
233
- other => err ! ( format!( "returns unsupported type {}" , syn_to_str!( other) ) ) ,
139
+ ( )
140
+ } } ;
141
+ }
142
+ if let Some ( syn:: Abi {
143
+ name : Some ( l) ,
144
+ extern_token : _,
145
+ } ) = abi
146
+ {
147
+ if l. value ( ) == "C" {
148
+ fn_sig. c_abi = true ;
234
149
}
235
- for input in inputs {
236
- match input {
237
- syn:: FnArg :: Captured ( ref c) if valid_ty ( & c. ty ) => {
238
- fn_sig. arg_tys . push ( c. ty . clone ( ) )
239
- }
240
- other => err ! ( format!(
241
- "takes unsupported argument type {}" ,
242
- syn_to_str!( other)
243
- ) ) ,
150
+ }
151
+ // If the function signature isn't extern "C", we aren't ABI compatible
152
+ // with libm.
153
+ if !fn_sig. c_abi {
154
+ // FIXME: we should error here, but right that would break everything,
155
+ // so we disable erroring.
156
+ let e2 = e;
157
+ err ! ( "not `extern \" C\" `" ) ;
158
+ e = e2;
159
+ }
160
+ // Right now there are no const fn functions. We might add them
161
+ // in the future, and at that point, we should tune this here.
162
+ // In the mean time, error if somebody tries.
163
+ if constness. is_some ( ) {
164
+ err ! ( "is const" ) ;
165
+ }
166
+ // No functions should be async fn
167
+ if asyncness. is_some ( ) {
168
+ err ! ( "is async" ) ;
169
+ }
170
+ // FIXME: Math functions are not unsafe. Some functions in the
171
+ // libm C API take pointers, but in our API take repr(Rust)
172
+ // tuples (for some reason). Once we fix those to have the same
173
+ // API as C libm, we should use references on their signature
174
+ // instead, and make them safe.
175
+ if unsafety. is_some ( ) {
176
+ let e2 = e;
177
+ err ! ( "is unsafe" ) ;
178
+ e = e2;
179
+ }
180
+ let syn:: FnDecl {
181
+ fn_token : _,
182
+ generics,
183
+ paren_token : _,
184
+ inputs,
185
+ variadic,
186
+ output,
187
+ } = ( * * decl) . clone ( ) ;
188
+
189
+ // Forbid generic parameters, lifetimes, and consts in public APIs:
190
+ if variadic. is_some ( ) {
191
+ err ! ( format!(
192
+ "contains variadic arguments \" {}\" " ,
193
+ syn_to_str!( variadic. unwrap( ) )
194
+ ) ) ;
195
+ }
196
+ if generics. type_params ( ) . count ( ) != 0 {
197
+ err ! ( format!(
198
+ "contains generic parameters \" {}\" " ,
199
+ syn_to_str!( generics. clone( ) )
200
+ ) ) ;
201
+ }
202
+ if generics. lifetimes ( ) . count ( ) != 0 {
203
+ err ! ( format!(
204
+ "contains lifetime parameters \" {}\" " ,
205
+ syn_to_str!( generics. clone( ) )
206
+ ) ) ;
207
+ }
208
+ if generics. const_params ( ) . count ( ) != 0 {
209
+ err ! ( format!(
210
+ "contains const parameters \" {}\" " ,
211
+ syn_to_str!( generics. clone( ) )
212
+ ) ) ;
213
+ }
214
+ // FIXME: we can do better here, but right now, we should
215
+ // error if inline and no_panic are not used, which is the
216
+ // case if the public API has no attributes.
217
+ //
218
+ // We might also want to check other attributes as well.
219
+ if attrs. is_empty ( ) {
220
+ let e2 = e;
221
+ err ! ( format!(
222
+ "missing `#[inline]` and `#[no_panic]` attributes {}" ,
223
+ attrs
224
+ . iter( )
225
+ . map( |a| syn_to_str!( a) )
226
+ . collect:: <Vec <_>>( )
227
+ . join( "," )
228
+ ) ) ;
229
+ e = e2;
230
+ }
231
+ // Validate and parse output parameters and function arguments:
232
+ match output {
233
+ syn:: ReturnType :: Default => ( ) ,
234
+ syn:: ReturnType :: Type ( _, ref b) if valid_ty ( & b) => fn_sig. ret_ty = Some ( * b. clone ( ) ) ,
235
+ other => err ! ( format!( "returns unsupported type {}" , syn_to_str!( other) ) ) ,
236
+ }
237
+ for input in inputs {
238
+ match input {
239
+ syn:: FnArg :: Captured ( ref c) if valid_ty ( & c. ty ) => {
240
+ fn_sig. arg_tys . push ( c. ty . clone ( ) )
244
241
}
242
+ other => err ! ( format!(
243
+ "takes unsupported argument type {}" ,
244
+ syn_to_str!( other)
245
+ ) ) ,
245
246
}
246
- // If there was an error, we skip the function:
247
- if !e {
248
- functions. push ( fn_sig) ;
249
- } else {
250
- error = true ;
251
- }
252
247
}
253
- _ => ( ) ,
248
+ // If there was an error, we skip the function.
249
+ // Otherwise, the user macro is expanded with
250
+ // the function:
251
+ if e {
252
+ error = true ;
253
+ } else {
254
+ functions. push ( fn_sig) ;
255
+ }
254
256
}
255
257
}
256
258
if error {
0 commit comments