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: &str) -> 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            // The following is used only when an unusual command-line
191            // argument is used. bindgen_cchar16_t is not a real type;
192            // but this allows downstream postprocessors to distinguish
193            // this case and do something special for C++ bindings
194            // containing the C++ type char16_t.
195            IntKind::Char16 => syn::parse_quote! { bindgen_cchar16_t },
196            IntKind::SChar => raw_type(ctx, "c_schar"),
197            IntKind::UChar => raw_type(ctx, "c_uchar"),
198            IntKind::Short => raw_type(ctx, "c_short"),
199            IntKind::UShort => raw_type(ctx, "c_ushort"),
200            IntKind::Int => raw_type(ctx, "c_int"),
201            IntKind::UInt => raw_type(ctx, "c_uint"),
202            IntKind::Long => raw_type(ctx, "c_long"),
203            IntKind::ULong => raw_type(ctx, "c_ulong"),
204            IntKind::LongLong => raw_type(ctx, "c_longlong"),
205            IntKind::ULongLong => raw_type(ctx, "c_ulonglong"),
206            IntKind::WChar => {
207                let layout =
208                    layout.expect("Couldn't compute wchar_t's layout?");
209                Layout::known_type_for_size(layout.size)
210                    .expect("Non-representable wchar_t?")
211            }
212
213            IntKind::I8 => syn::parse_quote! { i8 },
214            IntKind::U8 => syn::parse_quote! { u8 },
215            IntKind::I16 => syn::parse_quote! { i16 },
216            IntKind::U16 => syn::parse_quote! { u16 },
217            IntKind::I32 => syn::parse_quote! { i32 },
218            IntKind::U32 => syn::parse_quote! { u32 },
219            IntKind::I64 => syn::parse_quote! { i64 },
220            IntKind::U64 => syn::parse_quote! { u64 },
221            IntKind::Custom { name, .. } => {
222                syn::parse_str(name).expect("Invalid integer type.")
223            }
224            IntKind::U128 => {
225                if true {
226                    syn::parse_quote! { u128 }
227                } else {
228                    // Best effort thing, but wrong alignment
229                    // unfortunately.
230                    syn::parse_quote! { [u64; 2] }
231                }
232            }
233            IntKind::I128 => {
234                if true {
235                    syn::parse_quote! { i128 }
236                } else {
237                    syn::parse_quote! { [u64; 2] }
238                }
239            }
240        }
241    }
242
243    pub(crate) fn float_kind_rust_type(
244        ctx: &BindgenContext,
245        fk: FloatKind,
246        layout: Option<Layout>,
247    ) -> syn::Type {
248        // TODO: we probably should take the type layout into account more
249        // often?
250        //
251        // Also, maybe this one shouldn't be the default?
252        match (fk, ctx.options().convert_floats) {
253            (FloatKind::Float16, _) => {
254                // TODO: do f16 when rust lands it
255                ctx.generated_bindgen_float16();
256                if ctx.options().enable_cxx_namespaces {
257                    syn::parse_quote! { root::__BindgenFloat16 }
258                } else {
259                    syn::parse_quote! { __BindgenFloat16 }
260                }
261            }
262            (FloatKind::Float, true) => syn::parse_quote! { f32 },
263            (FloatKind::Double, true) => syn::parse_quote! { f64 },
264            (FloatKind::Float, false) => raw_type(ctx, "c_float"),
265            (FloatKind::Double, false) => raw_type(ctx, "c_double"),
266            (FloatKind::LongDouble, _) => {
267                if let Some(layout) = layout {
268                    match layout.size {
269                        4 => syn::parse_quote! { f32 },
270                        8 => syn::parse_quote! { f64 },
271                        // TODO(emilio): If rust ever gains f128 we should
272                        // use it here and below.
273                        _ => super::integer_type(layout)
274                            .unwrap_or(syn::parse_quote! { f64 }),
275                    }
276                } else {
277                    debug_assert!(
278                        false,
279                        "How didn't we know the layout for a primitive type?"
280                    );
281                    syn::parse_quote! { f64 }
282                }
283            }
284            (FloatKind::Float128, _) => {
285                if true {
286                    syn::parse_quote! { u128 }
287                } else {
288                    syn::parse_quote! { [u64; 2] }
289                }
290            }
291        }
292    }
293
294    pub(crate) fn int_expr(val: i64) -> TokenStream {
295        // Don't use quote! { #val } because that adds the type suffix.
296        let val = proc_macro2::Literal::i64_unsuffixed(val);
297        quote!(#val)
298    }
299
300    pub(crate) fn uint_expr(val: u64) -> TokenStream {
301        // Don't use quote! { #val } because that adds the type suffix.
302        let val = proc_macro2::Literal::u64_unsuffixed(val);
303        quote!(#val)
304    }
305
306    pub(crate) fn cstr_expr(mut string: String) -> TokenStream {
307        string.push('\0');
308        let b = proc_macro2::Literal::byte_string(string.as_bytes());
309        quote! {
310            #b
311        }
312    }
313
314    pub(crate) fn float_expr(
315        ctx: &BindgenContext,
316        f: f64,
317    ) -> Result<TokenStream, ()> {
318        if f.is_finite() {
319            let val = proc_macro2::Literal::f64_unsuffixed(f);
320
321            return Ok(quote!(#val));
322        }
323
324        let prefix = ctx.trait_prefix();
325        let rust_target = ctx.options().rust_target;
326
327        if f.is_nan() {
328            // FIXME: This should be done behind a `RustFeature` instead
329            #[allow(deprecated)]
330            let tokens = if rust_target >= RustTarget::Stable_1_43 {
331                quote! {
332                    f64::NAN
333                }
334            } else {
335                quote! {
336                    ::#prefix::f64::NAN
337                }
338            };
339            return Ok(tokens);
340        }
341
342        if f.is_infinite() {
343            let tokens = if f.is_sign_positive() {
344                // FIXME: This should be done behind a `RustFeature` instead
345                #[allow(deprecated)]
346                if rust_target >= RustTarget::Stable_1_43 {
347                    quote! {
348                        f64::INFINITY
349                    }
350                } else {
351                    quote! {
352                        ::#prefix::f64::INFINITY
353                    }
354                }
355            } else {
356                // FIXME: This should be done behind a `RustFeature` instead
357                #[allow(deprecated)]
358                // Negative infinity
359                if rust_target >= RustTarget::Stable_1_43 {
360                    quote! {
361                        f64::NEG_INFINITY
362                    }
363                } else {
364                    quote! {
365                        ::#prefix::f64::NEG_INFINITY
366                    }
367                }
368            };
369            return Ok(tokens);
370        }
371
372        warn!("Unknown non-finite float number: {f:?}");
373        Err(())
374    }
375
376    pub(crate) fn arguments_from_signature(
377        signature: &FunctionSig,
378        ctx: &BindgenContext,
379    ) -> Vec<TokenStream> {
380        let mut unnamed_arguments = 0;
381        signature
382            .argument_types()
383            .iter()
384            .map(|&(ref name, _ty)| {
385                let name = if let Some(ref name) = *name {
386                    ctx.rust_ident(name)
387                } else {
388                    unnamed_arguments += 1;
389                    ctx.rust_ident(format!("arg{unnamed_arguments}"))
390                };
391                quote! { #name }
392            })
393            .collect()
394    }
395}