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