//! Helpers for code generation that don't need macro expansion. use crate::ir::context::BindgenContext; use crate::ir::layout::Layout; use proc_macro2::{Ident, Span, TokenStream}; use quote::TokenStreamExt; pub mod attributes { use proc_macro2::{Ident, Span, TokenStream}; use std::str::FromStr; pub fn repr(which: &str) -> TokenStream { let which = Ident::new(which, Span::call_site()); quote! { #[repr( #which )] } } pub fn repr_list(which_ones: &[&str]) -> TokenStream { let which_ones = which_ones .iter() .cloned() .map(|one| TokenStream::from_str(one).expect("repr to be valid")); quote! { #[repr( #( #which_ones ),* )] } } pub fn derives(which_ones: &[&str]) -> TokenStream { let which_ones = which_ones .iter() .cloned() .map(|one| TokenStream::from_str(one).expect("derive to be valid")); quote! { #[derive( #( #which_ones ),* )] } } pub fn inline() -> TokenStream { quote! { #[inline] } } pub fn must_use() -> TokenStream { quote! { #[must_use] } } pub fn non_exhaustive() -> TokenStream { quote! { #[non_exhaustive] } } pub fn doc(comment: String) -> TokenStream { if comment.is_empty() { quote!() } else { quote!(#[doc = #comment]) } } pub fn link_name(name: &str) -> TokenStream { // LLVM mangles the name by default but it's already mangled. // Prefixing the name with \u{1} should tell LLVM to not mangle it. let name = format!("\u{1}{}", name); quote! { #[link_name = #name] } } } /// Generates a proper type for a field or type with a given `Layout`, that is, /// a type with the correct size and alignment restrictions. pub fn blob(ctx: &BindgenContext, layout: Layout) -> TokenStream { let opaque = layout.opaque(); // FIXME(emilio, #412): We fall back to byte alignment, but there are // some things that legitimately are more than 8-byte aligned. // // Eventually we should be able to `unwrap` here, but... let ty_name = match opaque.known_rust_type_for_array(ctx) { Some(ty) => ty, None => { warn!("Found unknown alignment on code generation!"); "u8" } }; let ty_name = Ident::new(ty_name, Span::call_site()); let data_len = opaque.array_size(ctx).unwrap_or(layout.size); if data_len == 1 { quote! { #ty_name } } else { quote! { [ #ty_name ; #data_len ] } } } /// Integer type of the same size as the given `Layout`. pub fn integer_type( ctx: &BindgenContext, layout: Layout, ) -> Option { let name = Layout::known_type_for_size(ctx, layout.size)?; let name = Ident::new(name, Span::call_site()); Some(quote! { #name }) } /// Generates a bitfield allocation unit type for a type with the given `Layout`. pub fn bitfield_unit(ctx: &BindgenContext, layout: Layout) -> TokenStream { let mut tokens = quote! {}; if ctx.options().enable_cxx_namespaces { tokens.append_all(quote! { root:: }); } let size = layout.size; tokens.append_all(quote! { __BindgenBitfieldUnit<[u8; #size]> }); tokens } pub mod ast_ty { use crate::ir::context::BindgenContext; use crate::ir::function::FunctionSig; use crate::ir::layout::Layout; use crate::ir::ty::FloatKind; use proc_macro2::{self, TokenStream}; use std::str::FromStr; pub fn c_void(ctx: &BindgenContext) -> TokenStream { // ctypes_prefix takes precedence match ctx.options().ctypes_prefix { Some(ref prefix) => { let prefix = TokenStream::from_str(prefix.as_str()).unwrap(); quote! { #prefix::c_void } } None => { if ctx.options().use_core && ctx.options().rust_features.core_ffi_c_void { quote! { ::core::ffi::c_void } } else { quote! { ::std::os::raw::c_void } } } } } pub fn raw_type(ctx: &BindgenContext, name: &str) -> TokenStream { let ident = ctx.rust_ident_raw(name); match ctx.options().ctypes_prefix { Some(ref prefix) => { let prefix = TokenStream::from_str(prefix.as_str()).unwrap(); quote! { #prefix::#ident } } None => { if ctx.options().use_core && ctx.options().rust_features().core_ffi_c { quote! { ::core::ffi::#ident } } else { quote! { ::std::os::raw::#ident } } } } } pub fn float_kind_rust_type( ctx: &BindgenContext, fk: FloatKind, layout: Option, ) -> TokenStream { // TODO: we probably should take the type layout into account more // often? // // Also, maybe this one shouldn't be the default? match (fk, ctx.options().convert_floats) { (FloatKind::Float, true) => quote! { f32 }, (FloatKind::Double, true) => quote! { f64 }, (FloatKind::Float, false) => raw_type(ctx, "c_float"), (FloatKind::Double, false) => raw_type(ctx, "c_double"), (FloatKind::LongDouble, _) => { match layout { Some(layout) => { match layout.size { 4 => quote! { f32 }, 8 => quote! { f64 }, // TODO(emilio): If rust ever gains f128 we should // use it here and below. _ => super::integer_type(ctx, layout) .unwrap_or(quote! { f64 }), } } None => { debug_assert!( false, "How didn't we know the layout for a primitive type?" ); quote! { f64 } } } } (FloatKind::Float128, _) => { if ctx.options().rust_features.i128_and_u128 { quote! { u128 } } else { quote! { [u64; 2] } } } } } pub fn int_expr(val: i64) -> TokenStream { // Don't use quote! { #val } because that adds the type suffix. let val = proc_macro2::Literal::i64_unsuffixed(val); quote!(#val) } pub fn uint_expr(val: u64) -> TokenStream { // Don't use quote! { #val } because that adds the type suffix. let val = proc_macro2::Literal::u64_unsuffixed(val); quote!(#val) } pub fn byte_array_expr(bytes: &[u8]) -> TokenStream { let mut bytes: Vec<_> = bytes.to_vec(); bytes.push(0); quote! { [ #(#bytes),* ] } } pub fn cstr_expr(mut string: String) -> TokenStream { string.push('\0'); let b = proc_macro2::Literal::byte_string(string.as_bytes()); quote! { #b } } pub fn float_expr(ctx: &BindgenContext, f: f64) -> Result { if f.is_finite() { let val = proc_macro2::Literal::f64_unsuffixed(f); return Ok(quote!(#val)); } let prefix = ctx.trait_prefix(); if f.is_nan() { return Ok(quote! { ::#prefix::f64::NAN }); } if f.is_infinite() { return Ok(if f.is_sign_positive() { quote! { ::#prefix::f64::INFINITY } } else { quote! { ::#prefix::f64::NEG_INFINITY } }); } warn!("Unknown non-finite float number: {:?}", f); Err(()) } pub fn arguments_from_signature( signature: &FunctionSig, ctx: &BindgenContext, ) -> Vec { let mut unnamed_arguments = 0; signature .argument_types() .iter() .map(|&(ref name, _ty)| match *name { Some(ref name) => { let name = ctx.rust_ident(name); quote! { #name } } None => { unnamed_arguments += 1; let name = ctx.rust_ident(format!("arg{}", unnamed_arguments)); quote! { #name } } }) .collect() } }