use syntax::ast;
use syntax::codemap;
use syntax::print;
use Error;
use Level;
pub fn rust_to_c(ty: &ast::Ty, assoc: &str) -> Result<Option<String>, Error> {
match ty.node {
ast::TyKind::BareFn(ref bare_fn) => fn_ptr_to_c(bare_fn, ty.span, assoc),
ast::TyKind::Path(None, ref path) => {
if path.segments.len() == 1 &&
path.segments[0].identifier.name == "Option" {
if let Some(ref param) = path.segments[0].parameters {
if let ast::PathParameters::AngleBracketed(ref d) = **param {
assert!(d.lifetimes.is_empty() && d.bindings.is_empty());
if d.types.len() == 1 {
if let ast::TyKind::BareFn(ref bare_fn) = d.types[0].node {
return fn_ptr_to_c(bare_fn, ty.span, assoc);
}
}
}
}
}
Ok(Some(format!("{} {}", try_some!(anon_rust_to_c(ty)), assoc)))
}
_ => Ok(Some(format!("{} {}", try_some!(anon_rust_to_c(ty)), assoc))),
}
}
fn anon_rust_to_c(ty: &ast::Ty) -> Result<Option<String>, Error> {
match ty.node {
ast::TyKind::BareFn(..) => Err(Error {
level: Level::Error,
span: Some(ty.span),
message: "C function pointers must have a name or function declaration associated with them".into(),
}),
ast::TyKind::Ptr(ref ptr) => ptr_to_c(ptr),
ast::TyKind::Path(None, ref path) => path_to_c(path),
_ => {
let new_type = print::pprust::ty_to_string(ty);
if new_type == "()" {
Ok(Some("void".into()))
} else {
Err(Error {
level: Level::Error,
span: Some(ty.span),
message: format!("cheddar can not handle the type `{}`", new_type),
})
}
},
}
}
fn ptr_to_c(ty: &ast::MutTy) -> Result<Option<String>, Error> {
let new_type = try_some!(anon_rust_to_c(&ty.ty));
let const_spec = match ty.mutbl {
ast::Mutability::Immutable => " const" ,
ast::Mutability::Mutable => "",
};
Ok(Some(format!("{}{}*", new_type, const_spec)))
}
fn fn_ptr_to_c(fn_ty: &ast::BareFnTy, fn_span: codemap::Span, inner: &str) -> Result<Option<String>, Error> {
use syntax::abi::Abi;
match fn_ty.abi {
Abi::C | Abi::Cdecl | Abi::Stdcall | Abi::Fastcall | Abi::System => {},
_ => return Ok(None),
}
if !fn_ty.lifetimes.is_empty() {
return Err(Error {
level: Level::Error,
span: Some(fn_span),
message: "cheddar can not handle lifetimes".into(),
});
}
let fn_decl: &ast::FnDecl = &*fn_ty.decl;
let mut buf_without_return = format!("(*{})(", inner);
let has_args = !fn_decl.inputs.is_empty();
for arg in &fn_decl.inputs {
let arg_name = print::pprust::pat_to_string(&*arg.pat);
let arg_type = try_some!(rust_to_c(&*arg.ty, &arg_name));
buf_without_return.push_str(&arg_type);
buf_without_return.push_str(", ");
}
if has_args {
buf_without_return.pop();
buf_without_return.pop();
} else {
buf_without_return.push_str("void");
}
buf_without_return.push(')');
let output_type = &fn_decl.output;
let full_declaration = match *output_type {
ast::FunctionRetTy::Ty(ref ty) if ty.node == ast::TyKind::Never => {
return Err(Error {
level: Level::Error,
span: Some(ty.span),
message: "panics across a C boundary are naughty!".into(),
});
},
ast::FunctionRetTy::Default(..) => format!("void {}", buf_without_return),
ast::FunctionRetTy::Ty(ref ty) => try_some!(rust_to_c(&*ty, &buf_without_return)),
};
Ok(Some(full_declaration))
}
fn path_to_c(path: &ast::Path) -> Result<Option<String>, Error> {
if path.segments.is_empty() {
Err(Error {
level: Level::Bug,
span: Some(path.span),
message: "what the fuck have you done to this type?!".into(),
})
} else if path.segments.len() > 1 {
let (ty, module) = path.segments.split_last()
.expect("already checked that there were at least two elements");
let ty: &str = &ty.identifier.name.as_str();
let mut segments = Vec::with_capacity(module.len());
for segment in module {
segments.push(String::from(&*segment.identifier.name.as_str()));
}
let module = segments.join("::");
match &*module {
"libc" => Ok(Some(libc_ty_to_c(ty).into())),
"std::os::raw" => Ok(Some(osraw_ty_to_c(ty).into())),
_ => Err(Error {
level: Level::Error,
span: Some(path.span),
message: "cheddar can not handle types in other modules (except `libc` and `std::os::raw`)".into(),
}),
}
} else {
Ok(Some(rust_ty_to_c(&path.segments[0].identifier.name.as_str()).into()))
}
}
fn libc_ty_to_c(ty: &str) -> &str {
match ty {
"c_void" => "void",
"c_float" => "float",
"c_double" => "double",
"c_char" => "char",
"c_schar" => "signed char",
"c_uchar" => "unsigned char",
"c_short" => "short",
"c_ushort" => "unsigned short",
"c_int" => "int",
"c_uint" => "unsigned int",
"c_long" => "long",
"c_ulong" => "unsigned long",
"c_longlong" => "long long",
"c_ulonglong" => "unsigned long long",
ty => ty,
}
}
fn osraw_ty_to_c(ty: &str) -> &str {
match ty {
"c_void" => "void",
"c_char" => "char",
"c_double" => "double",
"c_float" => "float",
"c_int" => "int",
"c_long" => "long",
"c_longlong" => "long long",
"c_schar" => "signed char",
"c_short" => "short",
"c_uchar" => "unsigned char",
"c_uint" => "unsigned int",
"c_ulong" => "unsigned long",
"c_ulonglong" => "unsigned long long",
"c_ushort" => "unsigned short",
ty => ty,
}
}
fn rust_ty_to_c(ty: &str) -> &str {
match ty {
"()" => "void",
"f32" => "float",
"f64" => "double",
"i8" => "int8_t",
"i16" => "int16_t",
"i32" => "int32_t",
"i64" => "int64_t",
"isize" => "intptr_t",
"u8" => "uint8_t",
"u16" => "uint16_t",
"u32" => "uint32_t",
"u64" => "uint64_t",
"usize" => "uintptr_t",
ty => ty,
}
}
#[cfg(test)]
mod test {
fn ty(source: &str) -> ::syntax::ast::Ty {
let sess = ::syntax::parse::ParseSess::new();
let result = {
let mut parser = ::syntax::parse::new_parser_from_source_str(
&sess,
"".into(),
source.into(),
);
parser.parse_ty()
};
match result {
Ok(p) => (*p).clone(),
_ => panic!("internal testing error: could not parse type from {:?}", source),
}
}
#[test]
#[ignore]
fn generics() {
let name = "azazael";
let source = "Result<f64, i32>";
let typ = super::anon_rust_to_c(&ty(source))
.expect(&format!("error while parsing {:?} with no name", source));
assert!(typ.is_none(), "successfully parsed invalid type {:?} with no name", source);
let source = "Option<i16>";
let typ = super::rust_to_c(&ty(source), name)
.expect(&format!("error while parsing {:?} with name {:?}", source, name));
assert!(typ.is_none(), "successfully parsed invalid type {:?} with name {:?}", source, name);
}
#[test]
fn pure_rust_types() {
let type_map = [
("()", "void"),
("f32", "float"),
("f64", "double"),
("i8", "int8_t"),
("i16", "int16_t"),
("i32", "int32_t"),
("i64", "int64_t"),
("isize", "intptr_t"),
("u8", "uint8_t"),
("u16", "uint16_t"),
("u32", "uint32_t"),
("u64", "uint64_t"),
("usize", "uintptr_t"),
];
let name = "gabriel";
for &(rust_type, correct_c_type) in &type_map {
let parsed_c_type = super::anon_rust_to_c(&ty(rust_type))
.expect(&format!("error while parsing {:?} with no name", rust_type))
.expect(&format!("did not parse {:?} with no name", rust_type));
assert_eq!(parsed_c_type, correct_c_type);
let parsed_c_type = super::rust_to_c(&ty(rust_type), name)
.expect(&format!("error while parsing {:?} with name {:?}", rust_type, name))
.expect(&format!("did not parse {:?} with name {:?}", rust_type, name));
assert_eq!(parsed_c_type, format!("{} {}", correct_c_type, name));
}
}
#[test]
fn libc_types() {
let type_map = [
("libc::c_void", "void"),
("libc::c_float", "float"),
("libc::c_double", "double"),
("libc::c_char", "char"),
("libc::c_schar", "signed char"),
("libc::c_uchar", "unsigned char"),
("libc::c_short", "short"),
("libc::c_ushort", "unsigned short"),
("libc::c_int", "int"),
("libc::c_uint", "unsigned int"),
("libc::c_long", "long"),
("libc::c_ulong", "unsigned long"),
("libc::c_longlong", "long long"),
("libc::c_ulonglong", "unsigned long long"),
("libc::size_t", "size_t"),
("libc::dirent", "dirent"),
("libc::FILE", "FILE"),
];
let name = "lucifer";
for &(rust_type, correct_c_type) in &type_map {
let parsed_c_type = super::anon_rust_to_c(&ty(rust_type))
.expect(&format!("error while parsing {:?} with no name", rust_type))
.expect(&format!("did not parse {:?} with no name", rust_type));
assert_eq!(parsed_c_type, correct_c_type);
let parsed_c_type = super::rust_to_c(&ty(rust_type), name)
.expect(&format!("error while parsing {:?} with name {:?}", rust_type, name))
.expect(&format!("did not parse {:?} with name {:?}", rust_type, name));
assert_eq!(parsed_c_type, format!("{} {}", correct_c_type, name));
}
}
#[test]
fn const_pointers() {
let name = "maalik";
let source = "*const u8";
let parsed_type = super::anon_rust_to_c(&ty(source))
.expect(&format!("error while parsing {:?} with no name", source))
.expect(&format!("did not parse {:?} with no name", source));
assert_eq!(parsed_type, "uint8_t const*");
let source = "*const ()";
let parsed_type = super::rust_to_c(&ty(source), name)
.expect(&format!("error while parsing {:?} with name {:?}", source, name))
.expect(&format!("did not parse {:?} with name {:?}", source, name));
assert_eq!(parsed_type, format!("void const* {}", name));
let source = "*const *const f64";
let parsed_type = super::anon_rust_to_c(&ty(source))
.expect(&format!("error while parsing {:?} with no name", source))
.expect(&format!("did not parse {:?} with no name", source));
assert_eq!(parsed_type, "double const* const*");
let source = "*const *const i64";
let parsed_type = super::rust_to_c(&ty(source), name)
.expect(&format!("error while parsing {:?} with name {:?}", source, name))
.expect(&format!("did not parse {:?} with name {:?}", source, name));
assert_eq!(parsed_type, format!("int64_t const* const* {}", name));
}
#[test]
fn mut_pointers() {
let name = "raphael";
let source = "*mut u16";
let parsed_type = super::anon_rust_to_c(&ty(source))
.expect(&format!("error while parsing {:?} with no name", source))
.expect(&format!("did not parse {:?} with no name", source));
assert_eq!(parsed_type, "uint16_t*");
let source = "*mut f32";
let parsed_type = super::rust_to_c(&ty(source), name)
.expect(&format!("error while parsing {:?} with name {:?}", source, name))
.expect(&format!("did not parse {:?} with name {:?}", source, name));
assert_eq!(parsed_type, format!("float* {}", name));
let source = "*mut *mut *mut i32";
let parsed_type = super::anon_rust_to_c(&ty(source))
.expect(&format!("error while parsing {:?} with no name", source))
.expect(&format!("did not parse {:?} with no name", source));
assert_eq!(parsed_type, "int32_t***");
let source = "*mut *mut i8";
let parsed_type = super::rust_to_c(&ty(source), name)
.expect(&format!("error while parsing {:?} with name {:?}", source, name))
.expect(&format!("did not parse {:?} with name {:?}", source, name));
assert_eq!(parsed_type, format!("int8_t** {}", name));
}
#[test]
fn mixed_pointers() {
let name = "samael";
let source = "*const *mut *const bool";
let parsed_type = super::anon_rust_to_c(&ty(source))
.expect(&format!("error while parsing {:?} with no name", source))
.expect(&format!("did not parse {:?} with no name", source));
assert_eq!(parsed_type, "bool const** const*");
let source = "*mut *mut *const libc::c_ulonglong";
let parsed_type = super::rust_to_c(&ty(source), name)
.expect(&format!("error while parsing {:?} with name {:?}", source, name))
.expect(&format!("did not parse {:?} with name {:?}", source, name));
assert_eq!(parsed_type, format!("unsigned long long const*** {}", name));
let source = "*const *mut *mut i8";
let parsed_type = super::rust_to_c(&ty(source), name)
.expect(&format!("error while parsing {:?} with name {:?}", source, name))
.expect(&format!("did not parse {:?} with name {:?}", source, name));
assert_eq!(parsed_type, format!("int8_t** const* {}", name));
}
#[test]
fn function_pointers() {
let name = "sariel";
let source = "fn(a: bool)";
let parsed_type = super::anon_rust_to_c(&ty(source));
assert!(parsed_type.is_err(), "C function pointers should have an inner or name associated");
let source = "fn(a: i8) -> f64";
let parsed_type = super::rust_to_c(&ty(source), name)
.expect(&format!("error while parsing {:?} with name {:?}", source, name));
assert!(parsed_type.is_none(), "parsed a non-C function pointer");
let source = "extern fn(hi: libc::c_int) -> libc::c_double";
let parsed_type = super::rust_to_c(&ty(source), name)
.expect(&format!("error while parsing {:?} with name {:?}", source, name))
.expect(&format!("did not parse {:?} with name {:?}", source, name));
assert_eq!(parsed_type, format!("double (*{})(int hi)", name));
let source = "Option<extern fn(hi: libc::c_int) -> libc::c_double>";
let parsed_type = super::rust_to_c(&ty(source), name)
.expect(&format!("error while parsing {:?} with name {:?}", source, name))
.expect(&format!("did not parse {:?} with name {:?}", source, name));
assert_eq!(parsed_type, format!("double (*{})(int hi)", name));
}
#[test]
fn paths() {
let name = "zachariel";
let source = "MyType";
let parsed_type = super::anon_rust_to_c(&ty(source))
.expect(&format!("error while parsing {:?} with no name", source))
.expect(&format!("did not parse {:?} with no name", source));
assert_eq!(parsed_type, "MyType");
let source = "SomeType";
let parsed_type = super::rust_to_c(&ty(source), name)
.expect(&format!("error while parsing {:?} with name {:?}", source, name))
.expect(&format!("did not parse {:?} with name {:?}", source, name));
assert_eq!(parsed_type, format!("SomeType {}", name));
let source = "my_mod::MyType";
let parsed_type = super::anon_rust_to_c(&ty(source));
assert!(parsed_type.is_err(), "can't use a multi-segment path which isn't `libc`");
let source = "some_mod::SomeType";
let parsed_type = super::rust_to_c(&ty(source), name);
assert!(parsed_type.is_err(), "can't use a multi-segment path which isn't `libc`");
}
}