Skip to content

Commit 8f8d206

Browse files
committed
Keep radix for integer literals in generated bindings
1 parent 1f02556 commit 8f8d206

File tree

5 files changed

+188
-38
lines changed

5 files changed

+188
-38
lines changed

bindgen/clang.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#![deny(clippy::missing_docs_in_private_items)]
66

77
use crate::ir::context::BindgenContext;
8+
use crate::ir::var::LiteralRadix;
89
use clang_sys::*;
910
use std::cmp;
1011

@@ -973,6 +974,20 @@ impl Cursor {
973974
pub(crate) fn is_inline_namespace(&self) -> bool {
974975
unsafe { clang_Cursor_isInlineNamespace(self.x) != 0 }
975976
}
977+
978+
/// Obtain the number base (radix) of a literal definition corresponding to the cursor.
979+
///
980+
/// Returns `None` if unable to infer a base.
981+
pub(crate) fn get_literal_radix(&self) -> Option<LiteralRadix> {
982+
self.cexpr_tokens()
983+
.iter()
984+
.filter(|cexpr_token| {
985+
cexpr_token.kind == cexpr::token::Kind::Literal
986+
})
987+
.find_map(|lit_tok| {
988+
LiteralRadix::from_literal_token_raw(&lit_tok.raw)
989+
})
990+
}
976991
}
977992

978993
/// A struct that owns the tokenizer result from a given cursor.

bindgen/codegen/helpers.rs

Lines changed: 61 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ pub(crate) mod ast_ty {
139139
use crate::ir::function::FunctionSig;
140140
use crate::ir::layout::Layout;
141141
use crate::ir::ty::{FloatKind, IntKind};
142+
use crate::ir::var::LiteralRadix;
142143
use crate::RustTarget;
143144
use proc_macro2::TokenStream;
144145
use std::str::FromStr;
@@ -291,16 +292,70 @@ pub(crate) mod ast_ty {
291292
}
292293
}
293294

294-
pub(crate) fn int_expr(val: i64) -> TokenStream {
295+
pub(crate) fn int_expr(
296+
val: i64,
297+
radix: Option<&LiteralRadix>,
298+
) -> TokenStream {
295299
// Don't use quote! { #val } because that adds the type suffix.
296-
let val = proc_macro2::Literal::i64_unsuffixed(val);
297-
quote!(#val)
300+
let sign = if val.is_negative() { "-" } else { "" };
301+
if let Some(radix) = radix {
302+
match radix {
303+
LiteralRadix::Decimal => {
304+
let val = proc_macro2::Literal::i64_unsuffixed(val);
305+
quote!(#val)
306+
}
307+
LiteralRadix::Binary => {
308+
let val = val.unsigned_abs();
309+
let val = format!("{sign}0b{val:b}");
310+
TokenStream::from_str(val.as_str()).unwrap()
311+
}
312+
LiteralRadix::Octal => {
313+
let val = val.unsigned_abs();
314+
let val = format!("{sign}0o{val:o}");
315+
TokenStream::from_str(val.as_str()).unwrap()
316+
}
317+
LiteralRadix::Hexadecimal => {
318+
let val = val.unsigned_abs();
319+
let val = format!("{sign}0x{val:x}");
320+
TokenStream::from_str(val.as_str()).unwrap()
321+
}
322+
}
323+
} else {
324+
// same as for Decimal
325+
let val = proc_macro2::Literal::i64_unsuffixed(val);
326+
quote!(#val)
327+
}
298328
}
299329

300-
pub(crate) fn uint_expr(val: u64) -> TokenStream {
330+
pub(crate) fn uint_expr(
331+
val: u64,
332+
radix: Option<&LiteralRadix>,
333+
) -> TokenStream {
301334
// Don't use quote! { #val } because that adds the type suffix.
302-
let val = proc_macro2::Literal::u64_unsuffixed(val);
303-
quote!(#val)
335+
if let Some(radix) = radix {
336+
match radix {
337+
LiteralRadix::Decimal => {
338+
let val = proc_macro2::Literal::u64_unsuffixed(val);
339+
quote!(#val)
340+
}
341+
LiteralRadix::Binary => {
342+
let val = format!("0b{val:b}");
343+
TokenStream::from_str(val.as_str()).unwrap()
344+
}
345+
LiteralRadix::Octal => {
346+
let val = format!("0o{val:o}");
347+
TokenStream::from_str(val.as_str()).unwrap()
348+
}
349+
LiteralRadix::Hexadecimal => {
350+
let val = format!("0x{val:x}");
351+
TokenStream::from_str(val.as_str()).unwrap()
352+
}
353+
}
354+
} else {
355+
// same as for Decimal
356+
let val = proc_macro2::Literal::u64_unsuffixed(val);
357+
quote!(#val)
358+
}
304359
}
305360

306361
pub(crate) fn cstr_expr(mut string: String) -> TokenStream {

bindgen/codegen/mod.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -692,6 +692,7 @@ impl CodeGenerator for Var {
692692
});
693693
}
694694
VarType::Int(val) => {
695+
let radix = self.radix();
695696
let int_kind = var_ty
696697
.into_resolver()
697698
.through_type_aliases()
@@ -701,9 +702,9 @@ impl CodeGenerator for Var {
701702
.as_integer()
702703
.unwrap();
703704
let val = if int_kind.is_signed() {
704-
helpers::ast_ty::int_expr(val)
705+
helpers::ast_ty::int_expr(val, radix)
705706
} else {
706-
helpers::ast_ty::uint_expr(val as _)
707+
helpers::ast_ty::uint_expr(val as _, radix)
707708
};
708709
result.push(quote! {
709710
#(#attrs)*
@@ -2438,7 +2439,7 @@ impl CodeGenerator for CompInfo {
24382439
if let Some(explicit) = explicit_align {
24392440
// Ensure that the struct has the correct alignment even in
24402441
// presence of alignas.
2441-
let explicit = helpers::ast_ty::int_expr(explicit as i64);
2442+
let explicit = helpers::ast_ty::int_expr(explicit as i64, None);
24422443
attributes.push(quote! {
24432444
#[repr(align(#explicit))]
24442445
});
@@ -3384,11 +3385,15 @@ impl EnumBuilder {
33843385
let is_rust_enum = self.is_rust_enum();
33853386
let expr = match variant.val() {
33863387
EnumVariantValue::Boolean(v) if is_rust_enum => {
3387-
helpers::ast_ty::uint_expr(u64::from(v))
3388+
helpers::ast_ty::uint_expr(u64::from(v), None)
33883389
}
33893390
EnumVariantValue::Boolean(v) => quote!(#v),
3390-
EnumVariantValue::Signed(v) => helpers::ast_ty::int_expr(v),
3391-
EnumVariantValue::Unsigned(v) => helpers::ast_ty::uint_expr(v),
3391+
EnumVariantValue::Signed(v) => {
3392+
helpers::ast_ty::int_expr(v, variant.radix())
3393+
}
3394+
EnumVariantValue::Unsigned(v) => {
3395+
helpers::ast_ty::uint_expr(v, variant.radix())
3396+
}
33923397
};
33933398

33943399
match self.kind {

bindgen/ir/enum_ty.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use super::item::Item;
66
use super::ty::{Type, TypeKind};
77
use crate::clang;
88
use crate::ir::annotations::Annotations;
9+
use crate::ir::var::LiteralRadix;
910
use crate::parse::ParseError;
1011
use crate::regex_set::RegexSet;
1112

@@ -101,8 +102,10 @@ impl Enum {
101102
} else {
102103
cursor.enum_val_unsigned().map(EnumVariantValue::Unsigned)
103104
};
105+
104106
if let Some(val) = value {
105107
let name = cursor.spelling();
108+
let radix = cursor.get_literal_radix();
106109
let annotations = Annotations::new(&cursor);
107110
let custom_behavior = ctx
108111
.options()
@@ -142,6 +145,7 @@ impl Enum {
142145
comment,
143146
val,
144147
custom_behavior,
148+
radix,
145149
));
146150
}
147151
}
@@ -254,6 +258,9 @@ pub(crate) struct EnumVariant {
254258

255259
/// The custom behavior this variant may have, if any.
256260
custom_behavior: Option<EnumVariantCustomBehavior>,
261+
262+
/// The radix of the literal value of the variant.
263+
radix: Option<LiteralRadix>,
257264
}
258265

259266
/// A constant value assigned to an enumeration variant.
@@ -277,13 +284,15 @@ impl EnumVariant {
277284
comment: Option<String>,
278285
val: EnumVariantValue,
279286
custom_behavior: Option<EnumVariantCustomBehavior>,
287+
radix: Option<LiteralRadix>,
280288
) -> Self {
281289
EnumVariant {
282290
name,
283291
name_for_allowlisting,
284292
comment,
285293
val,
286294
custom_behavior,
295+
radix,
287296
}
288297
}
289298

@@ -302,6 +311,11 @@ impl EnumVariant {
302311
self.val
303312
}
304313

314+
/// Get this variant's radix.
315+
pub(crate) fn radix(&self) -> Option<&LiteralRadix> {
316+
self.radix.as_ref()
317+
}
318+
305319
/// Get this variant's documentation.
306320
pub(crate) fn comment(&self) -> Option<&str> {
307321
self.comment.as_deref()

0 commit comments

Comments
 (0)