blob: ae8c21bc942313a31ecaa7462521e591c4bb7c75 [file] [log] [blame]
Alex Crichton8e5f0cd2015-09-09 22:46:19 -07001extern crate gcc;
2extern crate syntex_syntax as syntax;
3
Alex Crichton310d6232015-09-10 10:56:31 -07004use std::collections::HashSet;
Alex Crichton8e5f0cd2015-09-09 22:46:19 -07005use std::env;
6use std::fs::File;
7use std::io::BufWriter;
8use std::io::prelude::*;
9use std::path::{Path, PathBuf};
10
Alex Crichton31504842015-09-10 23:43:41 -070011use syntax::abi::Abi;
Alex Crichton8e5f0cd2015-09-09 22:46:19 -070012use syntax::ast;
Alex Crichtona9adfbf2015-09-09 23:21:27 -070013use syntax::attr::{self, ReprAttr};
Alex Crichton31504842015-09-10 23:43:41 -070014use syntax::diagnostic::SpanHandler;
15use syntax::ext::base::SyntaxExtension;
16use syntax::ext::expand;
17use syntax::parse::token::{intern, InternedString};
Alex Crichton8e5f0cd2015-09-09 22:46:19 -070018use syntax::parse::{self, ParseSess};
19use syntax::visit::{self, Visitor};
20
Alex Crichton310d6232015-09-10 10:56:31 -070021macro_rules! t {
22 ($e:expr) => (match $e {
23 Ok(e) => e,
24 Err(e) => panic!("{} failed with {}", stringify!($e), e),
25 })
26}
27
Alex Crichtona9adfbf2015-09-09 23:21:27 -070028struct TestGenerator<'a> {
Alex Crichton310d6232015-09-10 10:56:31 -070029 target: String,
Alex Crichton8e5f0cd2015-09-09 22:46:19 -070030 rust: Box<Write>,
31 c: Box<Write>,
Alex Crichtona9adfbf2015-09-09 23:21:27 -070032 sh: &'a SpanHandler,
Alex Crichton310d6232015-09-10 10:56:31 -070033 structs: HashSet<String>,
Alex Crichton31504842015-09-10 23:43:41 -070034 abi: Abi,
Alex Crichton310d6232015-09-10 10:56:31 -070035}
36
37struct StructFinder {
38 structs: HashSet<String>,
39}
40
41impl<'a> TestGenerator<'a> {
Alex Crichton0df7c102015-09-10 16:35:37 -070042 fn defines(&self) -> Vec<&'static str> {
43 let mut ret = Vec::new();
Alex Crichton31504842015-09-10 23:43:41 -070044
45 // Pull in extra goodies on linux
Alex Crichton0df7c102015-09-10 16:35:37 -070046 if self.target.contains("unknown-linux") {
47 ret.push("_GNU_SOURCE");
48 }
Alex Crichton31504842015-09-10 23:43:41 -070049
50 // MSVC doesn't have stdalign.h so get alignof ourselves
Alex Crichtonac2bd852015-09-10 17:21:20 -070051 if self.target.contains("msvc") {
52 ret.push("alignof __alignof");
53 }
Alex Crichton31504842015-09-10 23:43:41 -070054
55 // Pull in extra goodies on mingw
56 if self.target.contains("windows") {
57 ret.push("_WIN32_WINNT 0x8000");
58 }
Alex Crichton0df7c102015-09-10 16:35:37 -070059 return ret
60 }
61
Alex Crichton310d6232015-09-10 10:56:31 -070062 fn headers(&self) -> Vec<&'static str> {
Alex Crichtonac2bd852015-09-10 17:21:20 -070063 let mut base = Vec::new();
64
Alex Crichtonf81e3d32015-09-11 15:27:09 -070065 base.extend([
Alex Crichton0df7c102015-09-10 16:35:37 -070066 "errno.h",
67 "fcntl.h",
Alex Crichton0df7c102015-09-10 16:35:37 -070068 "limits.h",
Alex Crichton310d6232015-09-10 10:56:31 -070069 "stddef.h",
70 "stdint.h",
Alex Crichton0df7c102015-09-10 16:35:37 -070071 "stdio.h",
72 "stdlib.h",
Alex Crichton310d6232015-09-10 10:56:31 -070073 "sys/stat.h",
Alex Crichton310d6232015-09-10 10:56:31 -070074 "sys/types.h",
Alex Crichton310d6232015-09-10 10:56:31 -070075 "time.h",
Alex Crichton310d6232015-09-10 10:56:31 -070076 "wchar.h",
Alex Crichtonf81e3d32015-09-11 15:27:09 -070077 ].iter().cloned());
Alex Crichton310d6232015-09-10 10:56:31 -070078
79 if self.target.contains("apple-darwin") {
Alex Crichtone8606192015-09-10 20:19:44 -070080 base.push("mach-o/dyld.h");
Alex Crichton310d6232015-09-10 10:56:31 -070081 base.push("mach/mach_time.h");
82 }
Alex Crichtonac2bd852015-09-10 17:21:20 -070083
Alex Crichton310d6232015-09-10 10:56:31 -070084 if self.target.contains("unknown-linux") {
85 base.push("linux/if_packet.h");
86 base.push("net/ethernet.h");
87 }
88
Alex Crichtonac2bd852015-09-10 17:21:20 -070089 if self.target.contains("windows") {
Alex Crichton7da9b102015-09-10 20:57:14 -070090 base.push("winsock2.h"); // must be before windows.h
91
92 base.push("direct.h");
93 base.push("io.h");
Alex Crichton13a6f2d2015-09-10 18:10:58 -070094 base.push("sys/utime.h");
Alex Crichton7da9b102015-09-10 20:57:14 -070095 base.push("windows.h");
96 base.push("process.h");
97 base.push("ws2ipdef.h");
Alex Crichton31504842015-09-10 23:43:41 -070098
99 if self.target.contains("gnu") {
100 base.push("stdalign.h");
101 base.push("ws2tcpip.h");
102 }
Alex Crichtonac2bd852015-09-10 17:21:20 -0700103 } else {
Alex Crichton22378822015-09-10 19:59:23 -0700104 base.push("ctype.h");
105 base.push("dirent.h");
Alex Crichtonac2bd852015-09-10 17:21:20 -0700106 base.push("glob.h");
107 base.push("ifaddrs.h");
108 base.push("net/if.h");
109 base.push("netdb.h");
110 base.push("netinet/in.h");
111 base.push("netinet/ip.h");
112 base.push("netinet/tcp.h");
113 base.push("pthread.h");
114 base.push("signal.h");
115 base.push("stdalign.h");
Alex Crichtone8606192015-09-10 20:19:44 -0700116 base.push("string.h");
Alex Crichton22378822015-09-10 19:59:23 -0700117 base.push("sys/file.h");
118 base.push("sys/ioctl.h");
Alex Crichtonac2bd852015-09-10 17:21:20 -0700119 base.push("sys/mman.h");
120 base.push("sys/resource.h");
121 base.push("sys/socket.h");
Alex Crichtone8606192015-09-10 20:19:44 -0700122 base.push("sys/sysctl.h");
Alex Crichtonac2bd852015-09-10 17:21:20 -0700123 base.push("sys/time.h");
124 base.push("sys/un.h");
Alex Crichton22378822015-09-10 19:59:23 -0700125 base.push("sys/wait.h");
Alex Crichtonac2bd852015-09-10 17:21:20 -0700126 base.push("unistd.h");
Alex Crichton22378822015-09-10 19:59:23 -0700127 base.push("utime.h");
Alex Crichtonac2bd852015-09-10 17:21:20 -0700128 }
129
Alex Crichton310d6232015-09-10 10:56:31 -0700130 return base
131 }
132
133 fn rust2c(&self, ty: &str) -> String {
Alex Crichton13a6f2d2015-09-10 18:10:58 -0700134 let windows = self.target.contains("windows");
Alex Crichton310d6232015-09-10 10:56:31 -0700135 match ty {
136 t if t.starts_with("c_") => {
137 match &ty[2..].replace("long", " long")[..] {
138 s if s.starts_with("u") => format!("unsigned {}", &s[1..]),
139 "short" => format!("short"),
140 s if s.starts_with("s") => format!("signed {}", &s[1..]),
141 s => s.to_string(),
142 }
143 }
Alex Crichton31504842015-09-10 23:43:41 -0700144
145 // Just pass all these through, no need for a "struct" prefix
Alex Crichton22378822015-09-10 19:59:23 -0700146 "glob_t" |
147 "FILE" |
148 "DIR" |
149 "fpos_t" => ty.to_string(),
Alex Crichton310d6232015-09-10 10:56:31 -0700150 t if t.starts_with("pthread") => t.to_string(),
151
Alex Crichton31504842015-09-10 23:43:41 -0700152 // Windows uppercase structs don't have `struct` in front, there's a
153 // few special cases for windows, and then otherwise put `struct` in
154 // front of everything.
Alex Crichton13a6f2d2015-09-10 18:10:58 -0700155 t if self.structs.contains(t) => {
156 if windows && ty.chars().next().unwrap().is_uppercase() {
157 t.to_string()
158 } else if windows && t == "stat" {
159 "struct __stat64".to_string()
Alex Crichton7da9b102015-09-10 20:57:14 -0700160 } else if windows && t == "utimbuf" {
161 "struct __utimbuf64".to_string()
Alex Crichton13a6f2d2015-09-10 18:10:58 -0700162 } else {
163 format!("struct {}", t)
164 }
165 }
Alex Crichton310d6232015-09-10 10:56:31 -0700166
Alex Crichton31504842015-09-10 23:43:41 -0700167 // Fixup a few types on windows that don't actually exist.
Alex Crichton13a6f2d2015-09-10 18:10:58 -0700168 "time64_t" if windows => "__time64_t".to_string(),
169 "ssize_t" if windows => "SSIZE_T".to_string(),
Alex Crichton31504842015-09-10 23:43:41 -0700170
Alex Crichton310d6232015-09-10 10:56:31 -0700171 t => t.to_string(),
172 }
173 }
174
175 fn rust2cfield(&self, struct_: &str, field: &str) -> String {
176 match field {
Alex Crichton31504842015-09-10 23:43:41 -0700177 // Our stat *_nsec fields normally don't actually exist but are part
178 // of a timeval struct
Alex Crichton310d6232015-09-10 10:56:31 -0700179 s if s.ends_with("_nsec") && struct_ == "stat" => {
180 if self.target.contains("apple-darwin") {
181 s.replace("_nsec", "spec.tv_nsec")
182 } else {
183 s.replace("e_nsec", ".tv_nsec")
184 }
185 }
186 s => s.to_string(),
187 }
188 }
189
190 fn cfg_list(&self) -> Vec<(&'static str, Option<&'static str>)> {
191 let mut ret = Vec::new();
192 let (arch, target_pointer_width) = if self.target.starts_with("x86_64") {
193 ("x86_64", "64")
194 } else if self.target.starts_with("i686") {
195 ("x86", "32")
196 } else {
197 panic!("unknown arch/pointer width: {}", self.target)
198 };
199 let (os, family, env) = if self.target.contains("unknown-linux") {
200 ("linux", "unix", "gnu")
201 } else if self.target.contains("apple-darwin") {
202 ("macos", "unix", "")
Alex Crichtonac2bd852015-09-10 17:21:20 -0700203 } else if self.target.contains("windows-msvc") {
204 ("windows", "windows", "msvc")
205 } else if self.target.contains("windows-gnu") {
206 ("windows", "windows", "gnu")
Alex Crichton310d6232015-09-10 10:56:31 -0700207 } else {
208 panic!("unknown os/family width: {}", self.target)
209 };
210
211 ret.push((family, None));
212 ret.push(("target_os", Some(os)));
213 ret.push(("target_family", Some(family)));
214 ret.push(("target_arch", Some(arch)));
215 // skip endianness
216 ret.push(("target_pointer_width", Some(target_pointer_width)));
217 ret.push(("target_env", Some(env)));
218
219 return ret
220 }
Alex Crichton8e5f0cd2015-09-09 22:46:19 -0700221}
222
223fn main() {
Alex Crichton310d6232015-09-10 10:56:31 -0700224 // Prep the test generator
225 let target = t!(env::var("TARGET"));
226 let out = PathBuf::from(env::var_os("OUT_DIR").unwrap());
227 let rust_out = BufWriter::new(t!(File::create(out.join("all.rs"))));
228 let c_out = BufWriter::new(t!(File::create(out.join("all.c"))));
Alex Crichton16083062015-09-09 22:59:24 -0700229 let sess = ParseSess::new();
Alex Crichton310d6232015-09-10 10:56:31 -0700230 let mut tg = TestGenerator {
231 target: target,
232 rust: Box::new(rust_out),
233 c: Box::new(c_out),
234 sh: &sess.span_diagnostic,
235 structs: HashSet::new(),
Alex Crichton31504842015-09-10 23:43:41 -0700236 abi: Abi::C,
Alex Crichton310d6232015-09-10 10:56:31 -0700237 };
238
239 // Parse the libc crate
Alex Crichton8e5f0cd2015-09-09 22:46:19 -0700240 let src = Path::new("../src/lib.rs");
241 let cfg = Vec::new();
Alex Crichton31504842015-09-10 23:43:41 -0700242 let krate = parse::parse_crate_from_file(src, cfg, &sess);
243
244 // expand macros
245 let ecfg = expand::ExpansionConfig::default("libc".to_string());
246 let exts = vec![
247 (intern("macro_rules"), SyntaxExtension::MacroRulesTT),
248 ];
249 let mut krate = expand::expand_crate(&sess, ecfg, Vec::new(),
250 exts, &mut Vec::new(), krate);
Alex Crichton8e5f0cd2015-09-09 22:46:19 -0700251
Alex Crichton310d6232015-09-10 10:56:31 -0700252 // Strip the crate down to just what's configured for our target
253 for (k, v) in tg.cfg_list() {
254 let s = InternedString::new;
255 krate.config.push(match v {
256 Some(v) => attr::mk_name_value_item_str(s(k), s(v)),
257 None => attr::mk_word_item(s(k)),
258 });
259 }
Alex Crichton8e5f0cd2015-09-09 22:46:19 -0700260 let mut gated_cfgs = Vec::new();
261 let krate = syntax::config::strip_unconfigured_items(&sess.span_diagnostic,
262 krate,
263 &mut gated_cfgs);
264
Alex Crichton310d6232015-09-10 10:56:31 -0700265 // Probe the crate to find all structs (used to convert type names to names
266 // in C).
267 let mut structs = StructFinder {
268 structs: HashSet::new(),
269 };
270 visit::walk_crate(&mut structs, &krate);
271 tg.structs = structs.structs;
Alex Crichton8e5f0cd2015-09-09 22:46:19 -0700272
Alex Crichton0df7c102015-09-10 16:35:37 -0700273 // Prep the C file by emitting header stuff
274 for define in tg.defines() {
275 t!(writeln!(tg.c, "#define {}", define));
276 }
Alex Crichton310d6232015-09-10 10:56:31 -0700277 for header in tg.headers() {
278 t!(writeln!(tg.c, "#include <{}>", header));
Alex Crichton16083062015-09-09 22:59:24 -0700279 }
Alex Crichton0df7c102015-09-10 16:35:37 -0700280
281 // Walk the crate, emitting test cases for everything found
Alex Crichton310d6232015-09-10 10:56:31 -0700282 visit::walk_crate(&mut tg, &krate);
Alex Crichton16083062015-09-09 22:59:24 -0700283
Alex Crichton310d6232015-09-10 10:56:31 -0700284 // Compile our C shim to be linked into tests
Alex Crichtonac2bd852015-09-10 17:21:20 -0700285 let mut cfg = gcc::Config::new();
286 cfg.file(out.join("all.c"));
287
288 if tg.target.contains("msvc") {
Alex Crichton13a6f2d2015-09-10 18:10:58 -0700289 cfg.flag("/W3").flag("/Wall").flag("/WX")
290 .flag("/wd4820") // weird warning about adding padding?
Alex Crichton7da9b102015-09-10 20:57:14 -0700291 .flag("/wd4100") // don't warn about unused parameters
292 .flag("/wd4996"); // don't warn about deprecated functions
Alex Crichtonac2bd852015-09-10 17:21:20 -0700293 } else {
Alex Crichtonb01a8cc2015-09-10 17:37:22 -0700294 cfg.flag("-Wall").flag("-Wextra").flag("-Werror")
Alex Crichton5f624a52015-09-10 17:39:21 -0700295 .flag("-Wno-unused-parameter");
Alex Crichtonac2bd852015-09-10 17:21:20 -0700296 }
297
298 drop(tg);
299 cfg.compile("liball.a");
Alex Crichton8e5f0cd2015-09-09 22:46:19 -0700300}
301
Alex Crichtona9adfbf2015-09-09 23:21:27 -0700302impl<'a> TestGenerator<'a> {
303 fn test_type(&mut self, ty: &str) {
Alex Crichton310d6232015-09-10 10:56:31 -0700304 match ty {
305 "sighandler_t" => return,
306 _ => {}
307 }
Alex Crichton22378822015-09-10 19:59:23 -0700308 let c = self.rust_ty_to_c_ty(ty);
Alex Crichton310d6232015-09-10 10:56:31 -0700309 self.test_size_align(ty, &c);
Alex Crichtona9adfbf2015-09-09 23:21:27 -0700310 }
Alex Crichton16083062015-09-09 22:59:24 -0700311
Alex Crichton3e5155b2015-09-09 23:46:19 -0700312 fn test_struct(&mut self, ty: &str, s: &ast::StructDef) {
Alex Crichton22378822015-09-10 19:59:23 -0700313 let cty = self.rust_ty_to_c_ty(ty);
Alex Crichtona9adfbf2015-09-09 23:21:27 -0700314 self.test_size_align(ty, &cty);
Alex Crichton3e5155b2015-09-09 23:46:19 -0700315
Alex Crichton310d6232015-09-10 10:56:31 -0700316 t!(writeln!(self.rust, r#"
Alex Crichton3e5155b2015-09-09 23:46:19 -0700317 #[test]
318 fn field_offset_size_{ty}() {{
Alex Crichton310d6232015-09-10 10:56:31 -0700319 "#, ty = ty));
Alex Crichton3e5155b2015-09-09 23:46:19 -0700320 for field in s.fields.iter() {
321 let name = match field.node.kind {
322 ast::NamedField(name, ast::Public) => name,
323 ast::NamedField(_, ast::Inherited) => continue,
324 ast::UnnamedField(..) => panic!("no tuple structs in FFI"),
325 };
326
Alex Crichton310d6232015-09-10 10:56:31 -0700327 let cfield = self.rust2cfield(ty, &name.to_string());
Alex Crichton3e5155b2015-09-09 23:46:19 -0700328
Alex Crichton310d6232015-09-10 10:56:31 -0700329 t!(writeln!(self.c, r#"
Alex Crichton671376b2015-09-10 17:39:03 -0700330 uint64_t __test_offset_{ty}_{rust_field}(void) {{
Alex Crichton3e5155b2015-09-09 23:46:19 -0700331 return offsetof({cty}, {c_field});
332 }}
Alex Crichton671376b2015-09-10 17:39:03 -0700333 uint64_t __test_size_{ty}_{rust_field}(void) {{
Alex Crichton3e5155b2015-09-09 23:46:19 -0700334 {cty}* foo = NULL;
335 return sizeof(foo->{c_field});
336 }}
Alex Crichton310d6232015-09-10 10:56:31 -0700337 "#, ty = ty, cty = cty, rust_field = name, c_field = cfield));
338 t!(writeln!(self.rust, r#"
Alex Crichton3e5155b2015-09-09 23:46:19 -0700339 extern {{
340 fn __test_offset_{ty}_{field}() -> u64;
341 fn __test_size_{ty}_{field}() -> u64;
342 }}
343 unsafe {{
344 let foo = 0 as *const {ty};
345 same(offset_of!({ty}, {field}),
346 __test_offset_{ty}_{field}(),
347 "field offset {field} of {ty}");
348 same(mem::size_of_val(&(*foo).{field}) as u64,
349 __test_size_{ty}_{field}(),
350 "field size {field} of {ty}");
351 }}
Alex Crichton310d6232015-09-10 10:56:31 -0700352 "#, ty = ty, field = name));
Alex Crichton3e5155b2015-09-09 23:46:19 -0700353 }
Alex Crichton310d6232015-09-10 10:56:31 -0700354 t!(writeln!(self.rust, r#"
Alex Crichton3e5155b2015-09-09 23:46:19 -0700355 }}
Alex Crichton310d6232015-09-10 10:56:31 -0700356 "#));
Alex Crichtona9adfbf2015-09-09 23:21:27 -0700357 }
358
359 fn test_size_align(&mut self, rust: &str, c: &str) {
Alex Crichton310d6232015-09-10 10:56:31 -0700360 t!(writeln!(self.c, r#"
Alex Crichton671376b2015-09-10 17:39:03 -0700361 uint64_t __test_size_{ty}(void) {{ return sizeof({cty}); }}
362 uint64_t __test_align_{ty}(void) {{ return alignof({cty}); }}
Alex Crichton310d6232015-09-10 10:56:31 -0700363 "#, ty = rust, cty = c));
364 t!(writeln!(self.rust, r#"
Alex Crichton8e5f0cd2015-09-09 22:46:19 -0700365 #[test]
Alex Crichtona9adfbf2015-09-09 23:21:27 -0700366 fn size_align_{ty}() {{
Alex Crichton16083062015-09-09 22:59:24 -0700367 extern {{
Alex Crichtona9adfbf2015-09-09 23:21:27 -0700368 fn __test_size_{ty}() -> u64;
369 fn __test_align_{ty}() -> u64;
Alex Crichton16083062015-09-09 22:59:24 -0700370 }}
371 unsafe {{
Alex Crichton3e5155b2015-09-09 23:46:19 -0700372 same(mem::size_of::<{ty}>() as u64,
373 __test_size_{ty}(), "size");
Alex Crichtonc8b895c2015-09-10 13:24:15 -0700374 same(align::<{ty}>() as u64,
Alex Crichton3e5155b2015-09-09 23:46:19 -0700375 __test_align_{ty}(), "align");
Alex Crichton16083062015-09-09 22:59:24 -0700376 }}
Alex Crichton8e5f0cd2015-09-09 22:46:19 -0700377 }}
Alex Crichton310d6232015-09-10 10:56:31 -0700378 "#, ty = rust));
Alex Crichtona9adfbf2015-09-09 23:21:27 -0700379 }
380
Alex Crichton22378822015-09-10 19:59:23 -0700381 fn rust_ty_to_c_ty(&self, mut rust_ty: &str) -> String {
382 let mut cty = self.rust2c(&rust_ty.replace("*mut ", "")
383 .replace("*const ", ""));
384 while rust_ty.starts_with("*") {
385 if rust_ty.starts_with("*const") {
386 cty = format!("const {}*", cty);
387 rust_ty = &rust_ty[7..];
388 } else {
389 cty = format!("{}*", cty);
390 rust_ty = &rust_ty[5..];
391 }
392 }
393 return cty
394 }
395
Alex Crichton0df7c102015-09-10 16:35:37 -0700396 fn test_const(&mut self, name: &str, rust_ty: &str) {
Alex Crichton31504842015-09-10 23:43:41 -0700397 let mingw = self.target.contains("windows-gnu");
398
399 // Apparently these don't exist in mingw headers?
400 match name {
401 "MEM_RESET_UNDO" |
402 "FILE_ATTRIBUTE_NO_SCRUB_DATA" |
403 "FILE_ATTRIBUTE_INTEGRITY_STREAM" |
404 "ERROR_NOTHING_TO_TERMINATE" if mingw => return,
405 _ => {}
406 }
407
Alex Crichton22378822015-09-10 19:59:23 -0700408 let cty = self.rust_ty_to_c_ty(rust_ty);
Alex Crichton31504842015-09-10 23:43:41 -0700409
410 // SIG_IGN has weird types on platforms, just worry about it as a size_t
Alex Crichton0df7c102015-09-10 16:35:37 -0700411 let cast = if name == "SIG_IGN" {"(size_t)"} else {""};
Alex Crichton31504842015-09-10 23:43:41 -0700412
Alex Crichton0df7c102015-09-10 16:35:37 -0700413 t!(writeln!(self.c, r#"
Alex Crichton13a6f2d2015-09-10 18:10:58 -0700414 int __test_const_{name}({cty} *outptr) {{
415 *outptr = {cast}({name});
416 return 1;
Alex Crichtone7afdd82015-09-10 17:15:20 -0700417 }}
Alex Crichton0df7c102015-09-10 16:35:37 -0700418 "#, name = name, cast = cast, cty = cty));
419 t!(writeln!(self.rust, r#"
420 #[test]
421 fn const_{name}() {{
422 extern {{
Alex Crichtone7afdd82015-09-10 17:15:20 -0700423 fn __test_const_{name}(out: *mut {ty}) -> c_int;
Alex Crichton0df7c102015-09-10 16:35:37 -0700424 }}
425 unsafe {{
Alex Crichtone7afdd82015-09-10 17:15:20 -0700426 let mut o = mem::zeroed();
427 if __test_const_{name}(&mut o) == 0 {{
428 panic!("not defined");
429 }} else {{
430 same({name}, o, "value");
431 }}
Alex Crichton0df7c102015-09-10 16:35:37 -0700432 }}
433 }}
434 "#, ty = rust_ty, name = name));
435 }
436
Alex Crichton7da9b102015-09-10 20:57:14 -0700437 fn test_extern_fn(&mut self, name: &str, cname: &str,
438 args: &[String], ret: &str,
Alex Crichton31504842015-09-10 23:43:41 -0700439 variadic: bool, abi: Abi) {
Alex Crichton22378822015-09-10 19:59:23 -0700440 match name {
441 // manually verified
442 "execv" |
443 "execve" |
444 "execvp" |
Alex Crichton7da9b102015-09-10 20:57:14 -0700445 "execvpe" |
Alex Crichton22378822015-09-10 19:59:23 -0700446 "glob" |
447 "getrlimit" |
448 "setrlimit" |
Alex Crichtone8606192015-09-10 20:19:44 -0700449 "signal" |
Alex Crichton22378822015-09-10 19:59:23 -0700450 "getopt" => return,
451 _ => {}
452 }
453 let args = if args.len() == 0 && !variadic {
454 "void".to_string()
455 } else {
456 args.iter().map(|a| self.rust_ty_to_c_ty(a)).collect::<Vec<_>>()
457 .connect(", ") + if variadic {", ..."} else {""}
458 };
459 let cret = self.rust_ty_to_c_ty(ret);
Alex Crichton31504842015-09-10 23:43:41 -0700460 let abi = match abi {
461 Abi::C => "",
462 Abi::Stdcall => "__stdcall ",
463 Abi::System if self.target.contains("i686-pc-windows") => {
464 "__stdcall "
465 }
466 Abi::System => "",
467 a => panic!("unknown ABI: {}", a),
468 };
Alex Crichton22378822015-09-10 19:59:23 -0700469 t!(writeln!(self.c, r#"
Alex Crichton31504842015-09-10 23:43:41 -0700470 {ret} ({abi}*__test_fn_{name}(void))({args}) {{
Alex Crichton7da9b102015-09-10 20:57:14 -0700471 return {cname};
Alex Crichton22378822015-09-10 19:59:23 -0700472 }}
Alex Crichton31504842015-09-10 23:43:41 -0700473 "#, name = name, cname = cname, args = args, ret = cret, abi = abi));
Alex Crichton22378822015-09-10 19:59:23 -0700474 t!(writeln!(self.rust, r#"
475 #[test]
Alex Crichton7da9b102015-09-10 20:57:14 -0700476 #[cfg_attr(windows, ignore)] // FIXME -- dllimport weirdness?
Alex Crichton22378822015-09-10 19:59:23 -0700477 fn fn_{name}() {{
478 extern {{
479 fn __test_fn_{name}() -> size_t;
480 }}
481 unsafe {{
482 same({name} as usize,
483 __test_fn_{name}() as usize, "function pointer");
484 }}
485 }}
486 "#, name = name));
487 }
488
489 fn assert_no_generics(&self, _i: ast::Ident, generics: &ast::Generics) {
490 assert!(generics.lifetimes.len() == 0);
491 assert!(generics.ty_params.len() == 0);
492 assert!(generics.where_clause.predicates.len() == 0);
Alex Crichton8e5f0cd2015-09-09 22:46:19 -0700493 }
Alex Crichton0df7c102015-09-10 16:35:37 -0700494
495 fn ty2name(&self, ty: &ast::Ty) -> String {
496 match ty.node {
497 ast::TyPath(_, ref path) => {
498 path.segments.last().unwrap().identifier.to_string()
499 }
500 ast::TyPtr(ref t) => {
501 format!("*{} {}", match t.mutbl {
502 ast::MutImmutable => "const",
503 ast::MutMutable => "mut",
504 }, self.ty2name(&t.ty))
505 }
Alex Crichton22378822015-09-10 19:59:23 -0700506 ast::TyBareFn(ref t) => {
507 assert!(t.lifetimes.len() == 0);
508 let (ret, mut args, variadic) = self.decl2rust(&t.decl);
509 assert!(!variadic);
510 if args.len() == 0 {
511 args.push("void".to_string());
512 }
513 format!("{}(*)({})", ret, args.connect(", "))
514 }
Alex Crichton0df7c102015-09-10 16:35:37 -0700515 _ => panic!("unknown ty {:?}", ty),
516 }
517 }
Alex Crichton22378822015-09-10 19:59:23 -0700518
519 fn decl2rust(&self, decl: &ast::FnDecl) -> (String, Vec<String>, bool) {
520 let args = decl.inputs.iter().map(|arg| {
521 self.ty2name(&arg.ty)
522 }).collect::<Vec<_>>();
523 let ret = match decl.output {
524 ast::NoReturn(..) |
525 ast::DefaultReturn(..) => "void".to_string(),
526 ast::Return(ref t) => self.ty2name(t),
527 };
528 (ret, args, decl.variadic)
529 }
Alex Crichton8e5f0cd2015-09-09 22:46:19 -0700530}
531
Alex Crichtona9adfbf2015-09-09 23:21:27 -0700532impl<'a, 'v> Visitor<'v> for TestGenerator<'a> {
Alex Crichton22378822015-09-10 19:59:23 -0700533 fn visit_item(&mut self, i: &'v ast::Item) {
Alex Crichton31504842015-09-10 23:43:41 -0700534 let prev_abi = self.abi;
Alex Crichton22378822015-09-10 19:59:23 -0700535 match i.node {
536 ast::ItemTy(_, ref generics) => {
537 self.assert_no_generics(i.ident, generics);
538 self.test_type(&i.ident.to_string());
539 }
Alex Crichtona9adfbf2015-09-09 23:21:27 -0700540
Alex Crichton22378822015-09-10 19:59:23 -0700541 ast::ItemStruct(ref s, ref generics) => {
542 self.assert_no_generics(i.ident, generics);
543 let is_c = i.attrs.iter().any(|a| {
Alex Crichtona9adfbf2015-09-09 23:21:27 -0700544 attr::find_repr_attrs(self.sh, a).iter().any(|a| {
545 *a == ReprAttr::ReprExtern
546 })
Alex Crichton22378822015-09-10 19:59:23 -0700547 });
548 if !is_c {
549 panic!("{} is not marked #[repr(C)]", i.ident);
550 }
551 self.test_struct(&i.ident.to_string(), s);
552 }
Alex Crichton8e5f0cd2015-09-09 22:46:19 -0700553
Alex Crichton22378822015-09-10 19:59:23 -0700554 ast::ItemConst(ref ty, _) => {
555 let ty = self.ty2name(ty);
556 self.test_const(&i.ident.to_string(), &ty);
557 }
Alex Crichton0df7c102015-09-10 16:35:37 -0700558
Alex Crichton31504842015-09-10 23:43:41 -0700559 ast::ItemForeignMod(ref fm) => {
560 self.abi = fm.abi;
561 }
562
Alex Crichton22378822015-09-10 19:59:23 -0700563 _ => {}
564 }
Alex Crichton31504842015-09-10 23:43:41 -0700565 visit::walk_item(self, i);
566 self.abi = prev_abi;
Alex Crichton22378822015-09-10 19:59:23 -0700567 }
568
569 fn visit_foreign_item(&mut self, i: &'v ast::ForeignItem) {
570 match i.node {
571 ast::ForeignItemFn(ref decl, ref generics) => {
572 self.assert_no_generics(i.ident, generics);
573 let (ret, args, variadic) = self.decl2rust(decl);
Alex Crichton7da9b102015-09-10 20:57:14 -0700574 let cname = match attr::first_attr_value_str_by_name(&i.attrs,
575 "link_name") {
576 Some(ref i) if !i.to_string().contains("$") => {
577 i.to_string()
578 }
579 _ => i.ident.to_string(),
580 };
Alex Crichton31504842015-09-10 23:43:41 -0700581 let abi = self.abi;
Alex Crichton7da9b102015-09-10 20:57:14 -0700582 self.test_extern_fn(&i.ident.to_string(), &cname, &args, &ret,
Alex Crichton31504842015-09-10 23:43:41 -0700583 variadic, abi);
Alex Crichton22378822015-09-10 19:59:23 -0700584 }
585 ast::ForeignItemStatic(_, _) => {
586 }
587 }
588 visit::walk_foreign_item(self, i)
589 }
Alex Crichton8e5f0cd2015-09-09 22:46:19 -0700590}
Alex Crichton310d6232015-09-10 10:56:31 -0700591
592impl<'v> Visitor<'v> for StructFinder {
Alex Crichton22378822015-09-10 19:59:23 -0700593 fn visit_item(&mut self, i: &'v ast::Item) {
594 match i.node {
595 ast::ItemStruct(..) => {
596 self.structs.insert(i.ident.to_string());
597 }
598 ast::ItemEnum(..) => {
599 self.structs.insert(i.ident.to_string());
600 }
Alex Crichton310d6232015-09-10 10:56:31 -0700601
Alex Crichton22378822015-09-10 19:59:23 -0700602 _ => {}
603 }
604 visit::walk_item(self, i)
605 }
Alex Crichton310d6232015-09-10 10:56:31 -0700606}