Skip to content

Commit 6b4f8f6

Browse files
committed
Keep radix for integer literals in generated bindings
1 parent 1f02556 commit 6b4f8f6

File tree

5 files changed

+172
-31
lines changed

5 files changed

+172
-31
lines changed

bindgen/clang.rs

Lines changed: 28 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,33 @@ 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+
if lit_tok.raw.len() > 1 {
989+
match lit_tok.raw[0] {
990+
b'0' => match lit_tok.raw[1] {
991+
b'x' | b'X' => Some(LiteralRadix::Hexadecimal),
992+
b'b' | b'B' => Some(LiteralRadix::Binary),
993+
b'0'..=b'7' => Some(LiteralRadix::Octal),
994+
_ => None,
995+
},
996+
b'1'..=b'9' => Some(LiteralRadix::Decimal),
997+
_ => None,
998+
}
999+
} else {
1000+
Some(LiteralRadix::Decimal)
1001+
}
1002+
})
1003+
}
9761004
}
9771005

9781006
/// 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.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.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.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: 15 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,11 @@ 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+
109+
let radix = cursor.get_literal_radix();
106110
let annotations = Annotations::new(&cursor);
107111
let custom_behavior = ctx
108112
.options()
@@ -142,6 +146,7 @@ impl Enum {
142146
comment,
143147
val,
144148
custom_behavior,
149+
radix,
145150
));
146151
}
147152
}
@@ -254,6 +259,9 @@ pub(crate) struct EnumVariant {
254259

255260
/// The custom behavior this variant may have, if any.
256261
custom_behavior: Option<EnumVariantCustomBehavior>,
262+
263+
/// The radix of the literal value of the variant.
264+
radix: Option<LiteralRadix>,
257265
}
258266

259267
/// A constant value assigned to an enumeration variant.
@@ -277,13 +285,15 @@ impl EnumVariant {
277285
comment: Option<String>,
278286
val: EnumVariantValue,
279287
custom_behavior: Option<EnumVariantCustomBehavior>,
288+
radix: Option<LiteralRadix>,
280289
) -> Self {
281290
EnumVariant {
282291
name,
283292
name_for_allowlisting,
284293
comment,
285294
val,
286295
custom_behavior,
296+
radix,
287297
}
288298
}
289299

@@ -302,6 +312,11 @@ impl EnumVariant {
302312
self.val
303313
}
304314

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

bindgen/ir/var.rs

Lines changed: 57 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,19 @@ pub(crate) enum VarType {
3030
String(Vec<u8>),
3131
}
3232

33+
/// Integer literal's radix.
34+
#[derive(Debug)]
35+
pub(crate) enum LiteralRadix {
36+
/// Binary (base 2).
37+
Binary,
38+
/// Octal (base 8).
39+
Octal,
40+
/// Decimal (base 10).
41+
Decimal,
42+
/// Hexadecimal (base 16).
43+
Hexadecimal,
44+
}
45+
3346
/// A `Var` is our intermediate representation of a variable.
3447
#[derive(Debug)]
3548
pub(crate) struct Var {
@@ -45,6 +58,8 @@ pub(crate) struct Var {
4558
val: Option<VarType>,
4659
/// Whether this variable is const.
4760
is_const: bool,
61+
/// The radix of the variable, if integer.
62+
radix: Option<LiteralRadix>,
4863
}
4964

5065
impl Var {
@@ -56,18 +71,26 @@ impl Var {
5671
ty: TypeId,
5772
val: Option<VarType>,
5873
is_const: bool,
74+
radix: Option<LiteralRadix>,
5975
) -> Var {
6076
assert!(!name.is_empty());
77+
6178
Var {
6279
name,
6380
mangled_name,
6481
link_name,
6582
ty,
6683
val,
6784
is_const,
85+
radix,
6886
}
6987
}
7088

89+
/// The radix of this integer variable, if any.
90+
pub(crate) fn radix(&self) -> Option<&LiteralRadix> {
91+
self.radix.as_ref()
92+
}
93+
7194
/// Is this variable `const` qualified?
7295
pub(crate) fn is_const(&self) -> bool {
7396
self.is_const
@@ -180,6 +203,7 @@ impl ClangSubItemParser for Var {
180203
use cexpr::expr::EvalResult;
181204
use cexpr::literal::CChar;
182205
use clang_sys::*;
206+
183207
match cursor.kind() {
184208
CXCursor_MacroDefinition => {
185209
for callbacks in &ctx.options().parse_callbacks {
@@ -198,6 +222,7 @@ impl ClangSubItemParser for Var {
198222
}
199223

200224
let value = parse_macro(ctx, &cursor);
225+
let radix = cursor.get_literal_radix();
201226

202227
let Some((id, value)) = value else {
203228
return Err(ParseError::Continue);
@@ -265,7 +290,7 @@ impl ClangSubItemParser for Var {
265290
let ty = Item::builtin_type(type_kind, true, ctx);
266291

267292
Ok(ParseResult::New(
268-
Var::new(name, None, None, ty, Some(val), true),
293+
Var::new(name, None, None, ty, Some(val), true, radix),
269294
Some(cursor),
270295
))
271296
}
@@ -334,39 +359,52 @@ impl ClangSubItemParser for Var {
334359
// TODO: Strings, though the lookup is a bit more hard (we need
335360
// to look at the canonical type of the pointee too, and check
336361
// is char, u8, or i8 I guess).
337-
let value = if is_integer {
362+
let (value, radix) = if is_integer {
338363
let TypeKind::Int(kind) = *canonical_ty.unwrap().kind()
339364
else {
340365
unreachable!()
341366
};
342367

343368
let mut val = cursor.evaluate().and_then(|v| v.as_int());
369+
370+
let radix = cursor.get_literal_radix();
371+
344372
if val.is_none() || !kind.signedness_matches(val.unwrap()) {
345373
val = get_integer_literal_from_cursor(&cursor);
346374
}
347375

348-
val.map(|val| {
349-
if kind == IntKind::Bool {
350-
VarType::Bool(val != 0)
351-
} else {
352-
VarType::Int(val)
353-
}
354-
})
376+
(
377+
val.map(|val| {
378+
if kind == IntKind::Bool {
379+
VarType::Bool(val != 0)
380+
} else {
381+
VarType::Int(val)
382+
}
383+
}),
384+
radix,
385+
)
355386
} else if is_float {
356-
cursor
357-
.evaluate()
358-
.and_then(|v| v.as_double())
359-
.map(VarType::Float)
387+
(
388+
cursor
389+
.evaluate()
390+
.and_then(|v| v.as_double())
391+
.map(VarType::Float),
392+
None,
393+
)
360394
} else {
361-
cursor
362-
.evaluate()
363-
.and_then(|v| v.as_literal_string())
364-
.map(VarType::String)
395+
(
396+
cursor
397+
.evaluate()
398+
.and_then(|v| v.as_literal_string())
399+
.map(VarType::String),
400+
None,
401+
)
365402
};
366403

367404
let mangling = cursor_mangling(ctx, &cursor);
368-
let var =
369-
Var::new(name, mangling, link_name, ty, value, is_const);
405+
let var = Var::new(
406+
name, mangling, link_name, ty, value, is_const, radix,
407+
);
370408

371409
Ok(ParseResult::New(var, Some(cursor)))
372410
}

0 commit comments

Comments
 (0)