bindgen/codegen/
helpers.rs

1//! Helpers for code generation that don't need macro expansion.
2
3use proc_macro2::{Ident, Span};
4
5use crate::ir::context::BindgenContext;
6use crate::ir::layout::Layout;
7
8pub(crate) mod attributes {
9    use proc_macro2::{Ident, Span, TokenStream};
10    use std::{borrow::Cow, str::FromStr};
11
12    pub(crate) fn repr(which: &str) -> TokenStream {
13        let which = Ident::new(which, Span::call_site());
14        quote! {
15            #[repr( #which )]
16        }
17    }
18
19    pub(crate) fn repr_list(which_ones: &[&str]) -> TokenStream {
20        let which_ones = which_ones
21            .iter()
22            .map(|one| TokenStream::from_str(one).expect("repr to be valid"));
23        quote! {
24            #[repr( #( #which_ones ),* )]
25        }
26    }
27
28    pub(crate) fn derives(which_ones: &[&str]) -> TokenStream {
29        let which_ones = which_ones
30            .iter()
31            .map(|one| TokenStream::from_str(one).expect("derive to be valid"));
32        quote! {
33            #[derive( #( #which_ones ),* )]
34        }
35    }
36
37    pub(crate) fn inline() -> TokenStream {
38        quote! {
39            #[inline]
40        }
41    }
42
43    pub(crate) fn must_use() -> TokenStream {
44        quote! {
45            #[must_use]
46        }
47    }
48
49    pub(crate) fn non_exhaustive() -> TokenStream {
50        quote! {
51            #[non_exhaustive]
52        }
53    }
54
55    pub(crate) fn doc(comment: String) -> TokenStream {
56        if comment.is_empty() {
57            quote!()
58        } else {
59            quote!(#[doc = #comment])
60        }
61    }
62
63    pub(crate) fn link_name<const MANGLE: bool>(name: &str) -> TokenStream {
64        // LLVM mangles the name by default but it's already mangled.
65        // Prefixing the name with \u{1} should tell LLVM to not mangle it.
66        let name: Cow<'_, str> = if MANGLE {
67            name.into()
68        } else {
69            format!("\u{1}{name}").into()
70        };
71
72        quote! {
73            #[link_name = #name]
74        }
75    }
76}
77
78/// The `ffi_safe` argument should be true if this is a type that the user might
79/// reasonably use, e.g. not struct padding, where the `__BindgenOpaqueArray` is
80/// just noise.
81/// TODO: Should this be `MaybeUninit`, since padding bytes are effectively
82/// uninitialized?
83pub(crate) fn blob(
84    ctx: &BindgenContext,
85    layout: Layout,
86    ffi_safe: bool,
87) -> syn::Type {
88    let opaque = layout.opaque();
89
90    // FIXME(emilio, #412): We fall back to byte alignment, but there are
91    // some things that legitimately are more than 8-byte aligned.
92    //
93    // Eventually we should be able to `unwrap` here, but...
94    let ty = opaque.known_rust_type_for_array().unwrap_or_else(|| {
95        warn!("Found unknown alignment on code generation!");
96        syn::parse_quote! { u8 }
97    });
98
99    let data_len = opaque.array_size().unwrap_or(layout.size);
100
101    if data_len == 1 {
102        ty
103    } else if ffi_safe && ctx.options().rust_features().min_const_generics {
104        ctx.generated_opaque_array();
105        if ctx.options().enable_cxx_namespaces {
106            syn::parse_quote! { root::__BindgenOpaqueArray<#ty, #data_len> }
107        } else {
108            syn::parse_quote! { __BindgenOpaqueArray<#ty, #data_len> }
109        }
110    } else {
111        // This is not FFI safe as an argument; the struct above is
112        // preferable.
113        syn::parse_quote! { [ #ty ; #data_len ] }
114    }
115}
116
117/// Integer type of the same size as the given `Layout`.
118pub(crate) fn integer_type(layout: Layout) -> Option<syn::Type> {
119    Layout::known_type_for_size(layout.size)
120}
121
122pub(crate) const BITFIELD_UNIT: &str = "__BindgenBitfieldUnit";
123
124/// Generates a bitfield allocation unit type for a type with the given `Layout`.
125pub(crate) fn bitfield_unit(ctx: &BindgenContext, layout: Layout) -> syn::Type {
126    let size = layout.size;
127    let bitfield_unit_name = Ident::new(BITFIELD_UNIT, Span::call_site());
128    let ty = syn::parse_quote! { #bitfield_unit_name<[u8; #size]> };
129
130    if ctx.options().enable_cxx_namespaces {
131        return syn::parse_quote! { root::#ty };
132    }
133
134    ty
135}
136
137pub(crate) mod ast_ty {
138    use crate::ir::context::BindgenContext;
139    use crate::ir::function::FunctionSig;
140    use crate::ir::layout::Layout;
141    use crate::ir::ty::{FloatKind, IntKind};
142    use crate::RustTarget;
143    use proc_macro2::TokenStream;
144    use std::str::FromStr;
145
146    pub(crate) fn c_void(ctx: &BindgenContext) -> syn::Type {
147        // ctypes_prefix takes precedence
148        match ctx.options().ctypes_prefix {
149            Some(ref prefix) => {
150                let prefix = TokenStream::from_str(prefix.as_str()).unwrap();
151                syn::parse_quote! { #prefix::c_void }
152            }
153            None => {
154                if ctx.options().use_core {
155                    syn::parse_quote! { ::core::ffi::c_void }
156                } else {
157                    syn::parse_quote! { ::std::os::raw::c_void }
158                }
159            }
160        }
161    }
162
163    pub(crate) fn raw_type(ctx: &BindgenContext, name: &str) -> syn::Type {
164        let ident = ctx.rust_ident_raw(name);
165        match ctx.options().ctypes_prefix {
166            Some(ref prefix) => {
167                let prefix = TokenStream::from_str(prefix.as_str()).unwrap();
168                syn::parse_quote! { #prefix::#ident }
169            }
170            None => {
171                if ctx.options().use_core &&
172                    ctx.options().rust_features().core_ffi_c
173                {
174                    syn::parse_quote! { ::core::ffi::#ident }
175                } else {
176                    syn::parse_quote! { ::std::os::raw::#ident }
177                }
178            }
179        }
180    }
181
182    pub(crate) fn int_kind_rust_type(
183        ctx: &BindgenContext,
184        ik: IntKind,
185        layout: Option<Layout>,
186    ) -> syn::Type {
187        match ik {
188            IntKind::Bool => syn::parse_quote! { bool },
189            IntKind::Char { .. } => raw_type(ctx, "c_char"),
190            IntKind::SChar => raw_type(ctx, "c_schar"),
191            IntKind::UChar => raw_type(ctx, "c_uchar"),
192            IntKind::Short => raw_type(ctx, "c_short"),
193            IntKind::UShort => raw_type(ctx, "c_ushort"),
194            IntKind::Int => raw_type(ctx, "c_int"),
195            IntKind::UInt => raw_type(ctx, "c_uint"),
196            IntKind::Long => raw_type(ctx, "c_long"),
197            IntKind::ULong => raw_type(ctx, "c_ulong"),
198            IntKind::LongLong => raw_type(ctx, "c_longlong"),
199            IntKind::ULongLong => raw_type(ctx, "c_ulonglong"),
200            IntKind::WChar => {
201                let layout =
202                    layout.expect("Couldn't compute wchar_t's layout?");
203                Layout::known_type_for_size(layout.size)
204                    .expect("Non-representable wchar_t?")
205            }
206
207            IntKind::I8 => syn::parse_quote! { i8 },
208            IntKind::U8 => syn::parse_quote! { u8 },
209            IntKind::I16 => syn::parse_quote! { i16 },
210            IntKind::U16 => syn::parse_quote! { u16 },
211            IntKind::I32 => syn::parse_quote! { i32 },
212            IntKind::U32 => syn::parse_quote! { u32 },
213            IntKind::I64 => syn::parse_quote! { i64 },
214            IntKind::U64 => syn::parse_quote! { u64 },
215            IntKind::Custom { name, .. } => {
216                syn::parse_str(name).expect("Invalid integer type.")
217            }
218            IntKind::U128 => {
219                if true {
220                    syn::parse_quote! { u128 }
221                } else {
222                    // Best effort thing, but wrong alignment
223                    // unfortunately.
224                    syn::parse_quote! { [u64; 2] }
225                }
226            }
227            IntKind::I128 => {
228                if true {
229                    syn::parse_quote! { i128 }
230                } else {
231                    syn::parse_quote! { [u64; 2] }
232                }
233            }
234        }
235    }
236
237    pub(crate) fn float_kind_rust_type(
238        ctx: &BindgenContext,
239        fk: FloatKind,
240        layout: Option<Layout>,
241    ) -> syn::Type {
242        // TODO: we probably should take the type layout into account more
243        // often?
244        //
245        // Also, maybe this one shouldn't be the default?
246        match (fk, ctx.options().convert_floats) {
247            (FloatKind::Float16, _) => {
248                // TODO: do f16 when rust lands it
249                ctx.generated_bindgen_float16();
250                if ctx.options().enable_cxx_namespaces {
251                    syn::parse_quote! { root::__BindgenFloat16 }
252                } else {
253                    syn::parse_quote! { __BindgenFloat16 }
254                }
255            }
256            (FloatKind::Float, true) => syn::parse_quote! { f32 },
257            (FloatKind::Double, true) => syn::parse_quote! { f64 },
258            (FloatKind::Float, false) => raw_type(ctx, "c_float"),
259            (FloatKind::Double, false) => raw_type(ctx, "c_double"),
260            (FloatKind::LongDouble, _) => {
261                if let Some(layout) = layout {
262                    match layout.size {
263                        4 => syn::parse_quote! { f32 },
264                        8 => syn::parse_quote! { f64 },
265                        // TODO(emilio): If rust ever gains f128 we should
266                        // use it here and below.
267                        _ => super::integer_type(layout)
268                            .unwrap_or(syn::parse_quote! { f64 }),
269                    }
270                } else {
271                    debug_assert!(
272                        false,
273                        "How didn't we know the layout for a primitive type?"
274                    );
275                    syn::parse_quote! { f64 }
276                }
277            }
278            (FloatKind::Float128, _) => {
279                if true {
280                    syn::parse_quote! { u128 }
281                } else {
282                    syn::parse_quote! { [u64; 2] }
283                }
284            }
285        }
286    }
287
288    pub(crate) fn int_expr(val: i64) -> TokenStream {
289        // Don't use quote! { #val } because that adds the type suffix.
290        let val = proc_macro2::Literal::i64_unsuffixed(val);
291        quote!(#val)
292    }
293
294    pub(crate) fn uint_expr(val: u64) -> TokenStream {
295        // Don't use quote! { #val } because that adds the type suffix.
296        let val = proc_macro2::Literal::u64_unsuffixed(val);
297        quote!(#val)
298    }
299
300    pub(crate) fn cstr_expr(mut string: String) -> TokenStream {
301        string.push('\0');
302        let b = proc_macro2::Literal::byte_string(string.as_bytes());
303        quote! {
304            #b
305        }
306    }
307
308    pub(crate) fn float_expr(
309        ctx: &BindgenContext,
310        f: f64,
311    ) -> Result<TokenStream, ()> {
312        if f.is_finite() {
313            let val = proc_macro2::Literal::f64_unsuffixed(f);
314
315            return Ok(quote!(#val));
316        }
317
318        let prefix = ctx.trait_prefix();
319        let rust_target = ctx.options().rust_target;
320
321        if f.is_nan() {
322            // FIXME: This should be done behind a `RustFeature` instead
323            #[allow(deprecated)]
324            let tokens = if rust_target >= RustTarget::Stable_1_43 {
325                quote! {
326                    f64::NAN
327                }
328            } else {
329                quote! {
330                    ::#prefix::f64::NAN
331                }
332            };
333            return Ok(tokens);
334        }
335
336        if f.is_infinite() {
337            let tokens = if f.is_sign_positive() {
338                // FIXME: This should be done behind a `RustFeature` instead
339                #[allow(deprecated)]
340                if rust_target >= RustTarget::Stable_1_43 {
341                    quote! {
342                        f64::INFINITY
343                    }
344                } else {
345                    quote! {
346                        ::#prefix::f64::INFINITY
347                    }
348                }
349            } else {
350                // FIXME: This should be done behind a `RustFeature` instead
351                #[allow(deprecated)]
352                // Negative infinity
353                if rust_target >= RustTarget::Stable_1_43 {
354                    quote! {
355                        f64::NEG_INFINITY
356                    }
357                } else {
358                    quote! {
359                        ::#prefix::f64::NEG_INFINITY
360                    }
361                }
362            };
363            return Ok(tokens);
364        }
365
366        warn!("Unknown non-finite float number: {f:?}");
367        Err(())
368    }
369
370    pub(crate) fn arguments_from_signature(
371        signature: &FunctionSig,
372        ctx: &BindgenContext,
373    ) -> Vec<TokenStream> {
374        let mut unnamed_arguments = 0;
375        signature
376            .argument_types()
377            .iter()
378            .map(|&(ref name, _ty)| {
379                let name = if let Some(ref name) = *name {
380                    ctx.rust_ident(name)
381                } else {
382                    unnamed_arguments += 1;
383                    ctx.rust_ident(format!("arg{unnamed_arguments}"))
384                };
385                quote! { #name }
386            })
387            .collect()
388    }
389}