summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/clang.rs96
-rw-r--r--src/codegen/mod.rs52
-rw-r--r--src/ir/analysis/derive.rs10
-rw-r--r--src/ir/annotations.rs4
-rw-r--r--src/ir/comp.rs40
-rw-r--r--src/ir/context.rs34
-rw-r--r--src/ir/function.rs2
-rw-r--r--src/ir/layout.rs4
-rw-r--r--src/ir/traversal.rs58
-rw-r--r--src/ir/ty.rs13
-rw-r--r--src/lib.rs92
-rw-r--r--src/options.rs43
12 files changed, 311 insertions, 137 deletions
diff --git a/src/clang.rs b/src/clang.rs
index 587cc0ba..2aab9618 100644
--- a/src/clang.rs
+++ b/src/clang.rs
@@ -276,6 +276,56 @@ impl Cursor {
true
}
+ /// Is the referent any kind of template parameter?
+ pub fn is_template_parameter(&self) -> bool {
+ matches!(
+ self.kind(),
+ CXCursor_TemplateTemplateParameter |
+ CXCursor_TemplateTypeParameter |
+ CXCursor_NonTypeTemplateParameter
+ )
+ }
+
+ /// Does the referent's type or value depend on a template parameter?
+ pub fn is_dependent_on_template_parameter(&self) -> bool {
+ fn visitor(
+ found_template_parameter: &mut bool,
+ cur: Cursor,
+ ) -> CXChildVisitResult {
+ // If we found a template parameter, it is dependent.
+ if cur.is_template_parameter() {
+ *found_template_parameter = true;
+ return CXChildVisit_Break;
+ }
+
+ // Get the referent and traverse it as well.
+ if let Some(referenced) = cur.referenced() {
+ if referenced.is_template_parameter() {
+ *found_template_parameter = true;
+ return CXChildVisit_Break;
+ }
+
+ referenced
+ .visit(|next| visitor(found_template_parameter, next));
+ if *found_template_parameter {
+ return CXChildVisit_Break;
+ }
+ }
+
+ // Continue traversing the AST at the original cursor.
+ CXChildVisit_Recurse
+ }
+
+ if self.is_template_parameter() {
+ return true;
+ }
+
+ let mut found_template_parameter = false;
+ self.visit(|next| visitor(&mut found_template_parameter, next));
+
+ found_template_parameter
+ }
+
/// Is this cursor pointing a valid referent?
pub fn is_valid(&self) -> bool {
unsafe { clang_isInvalid(self.kind()) == 0 }
@@ -485,9 +535,45 @@ impl Cursor {
!self.is_defaulted_function()
}
+ /// Is the referent a bit field declaration?
+ pub fn is_bit_field(&self) -> bool {
+ unsafe { clang_Cursor_isBitField(self.x) != 0 }
+ }
+
+ /// Get a cursor to the bit field's width expression, or `None` if it's not
+ /// a bit field.
+ pub fn bit_width_expr(&self) -> Option<Cursor> {
+ if !self.is_bit_field() {
+ return None;
+ }
+
+ let mut result = None;
+ self.visit(|cur| {
+ // The first child may or may not be a TypeRef, depending on whether
+ // the field's type is builtin. Skip it.
+ if cur.kind() == CXCursor_TypeRef {
+ return CXChildVisit_Continue;
+ }
+
+ // The next expression or literal is the bit width.
+ result = Some(cur);
+
+ CXChildVisit_Break
+ });
+
+ result
+ }
+
/// Get the width of this cursor's referent bit field, or `None` if the
- /// referent is not a bit field.
+ /// referent is not a bit field or if the width could not be evaluated.
pub fn bit_width(&self) -> Option<u32> {
+ // It is not safe to check the bit width without ensuring it doesn't
+ // depend on a template parameter. See
+ // https://github.com/rust-lang/rust-bindgen/issues/2239
+ if self.bit_width_expr()?.is_dependent_on_template_parameter() {
+ return None;
+ }
+
unsafe {
let w = clang_getFieldDeclBitWidth(self.x);
if w == -1 {
@@ -1789,9 +1875,15 @@ pub fn ast_dump(c: &Cursor, depth: isize) -> CXChildVisitResult {
format!(" {}number-of-template-args = {}", prefix, num),
);
}
- if let Some(width) = c.bit_width() {
+
+ if c.is_bit_field() {
+ let width = match c.bit_width() {
+ Some(w) => w.to_string(),
+ None => "<unevaluable>".to_string(),
+ };
print_indent(depth, format!(" {}bit-width = {}", prefix, width));
}
+
if let Some(ty) = c.enum_type() {
print_indent(
depth,
diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs
index ee4a6bfd..ca4cbf23 100644
--- a/src/codegen/mod.rs
+++ b/src/codegen/mod.rs
@@ -1212,6 +1212,7 @@ impl CodeGenerator for TemplateInstantiation {
trait FieldCodegen<'a> {
type Extra;
+ #[allow(clippy::too_many_arguments)]
fn codegen<F, M>(
&self,
ctx: &BindgenContext,
@@ -2187,36 +2188,41 @@ impl CodeGenerator for CompInfo {
field.offset().map(|offset| {
let field_offset = offset / 8;
let field_name = ctx.rust_ident(name);
- // Put each check in its own function, so
- // that rustc with opt-level=0 doesn't take
- // too much stack space, see #2218.
- let test_fn = Ident::new(&format!("test_field_{}", name), Span::call_site());
quote! {
- fn #test_fn() {
- assert_eq!(
- unsafe {
- let uninit = ::#prefix::mem::MaybeUninit::<#canonical_ident>::uninit();
- let ptr = uninit.as_ptr();
- ::#prefix::ptr::addr_of!((*ptr).#field_name) as usize - ptr as usize
- },
- #field_offset,
- concat!("Offset of field: ", stringify!(#canonical_ident), "::", stringify!(#field_name))
- );
- }
- #test_fn();
+ assert_eq!(
+ unsafe {
+ ::#prefix::ptr::addr_of!((*ptr).#field_name) as usize - ptr as usize
+ },
+ #field_offset,
+ concat!("Offset of field: ", stringify!(#canonical_ident), "::", stringify!(#field_name))
+ );
}
})
})
- .collect::<Vec<proc_macro2::TokenStream>>()
+ .collect()
+ };
+
+ let uninit_decl = if !check_field_offset.is_empty() {
+ // FIXME: When MSRV >= 1.59.0, we can use
+ // > const PTR: *const #canonical_ident = ::#prefix::mem::MaybeUninit::uninit().as_ptr();
+ Some(quote! {
+ // Use a shared MaybeUninit so that rustc with
+ // opt-level=0 doesn't take too much stack space,
+ // see #2218.
+ const UNINIT: ::#prefix::mem::MaybeUninit<#canonical_ident> = ::#prefix::mem::MaybeUninit::uninit();
+ let ptr = UNINIT.as_ptr();
+ })
+ } else {
+ None
};
let item = quote! {
#[test]
fn #fn_name() {
+ #uninit_decl
assert_eq!(#size_of_expr,
#size,
concat!("Size of: ", stringify!(#canonical_ident)));
-
#check_struct_align
#( #check_field_offset )*
}
@@ -2558,7 +2564,7 @@ impl MethodCodegen for Method {
}
/// A helper type that represents different enum variations.
-#[derive(Copy, Clone, PartialEq, Debug)]
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum EnumVariation {
/// The code for this enum will use a Rust enum. Note that creating this in unsafe code
/// (including FFI) with an invalid value will invoke undefined behaviour, whether or not
@@ -3208,7 +3214,7 @@ impl CodeGenerator for Enum {
ctx,
enum_ty,
&ident,
- &Ident::new(&*mangled_name, Span::call_site()),
+ &Ident::new(&mangled_name, Span::call_site()),
existing_variant_name,
enum_rust_ty.clone(),
result,
@@ -3277,7 +3283,7 @@ impl CodeGenerator for Enum {
}
/// Enum for the default type of macro constants.
-#[derive(Copy, Clone, PartialEq, Debug)]
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum MacroTypeVariation {
/// Use i32 or i64
Signed,
@@ -3321,7 +3327,7 @@ impl std::str::FromStr for MacroTypeVariation {
}
/// Enum for how aliases should be translated.
-#[derive(Copy, Clone, PartialEq, Debug)]
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum AliasVariation {
/// Convert to regular Rust alias
TypeAlias,
@@ -4324,7 +4330,7 @@ impl CodeGenerator for ObjCInterface {
pub(crate) fn codegen(
context: BindgenContext,
-) -> (Vec<proc_macro2::TokenStream>, BindgenOptions) {
+) -> (Vec<proc_macro2::TokenStream>, BindgenOptions, Vec<String>) {
context.gen(|context| {
let _t = context.timer("codegen");
let counter = Cell::new(0);
diff --git a/src/ir/analysis/derive.rs b/src/ir/analysis/derive.rs
index f63458e5..d888cd55 100644
--- a/src/ir/analysis/derive.rs
+++ b/src/ir/analysis/derive.rs
@@ -485,11 +485,11 @@ impl DeriveTrait {
fn consider_edge_tmpl_inst(&self) -> EdgePredicate {
match self {
DeriveTrait::PartialEqOrPartialOrd => consider_edge_default,
- _ => |kind| match kind {
- EdgeKind::TemplateArgument | EdgeKind::TemplateDeclaration => {
- true
- }
- _ => false,
+ _ => |kind| {
+ matches!(
+ kind,
+ EdgeKind::TemplateArgument | EdgeKind::TemplateDeclaration
+ )
},
}
}
diff --git a/src/ir/annotations.rs b/src/ir/annotations.rs
index 9bcda508..288c11eb 100644
--- a/src/ir/annotations.rs
+++ b/src/ir/annotations.rs
@@ -7,7 +7,7 @@
use crate::clang;
/// What kind of accessor should we provide for a field?
-#[derive(Copy, PartialEq, Clone, Debug)]
+#[derive(Copy, PartialEq, Eq, Clone, Debug)]
pub enum FieldAccessorKind {
/// No accessor.
None,
@@ -25,7 +25,7 @@ pub enum FieldAccessorKind {
/// documentation:
///
/// http://www.stack.nl/~dimitri/doxygen/manual/docblocks.html
-#[derive(Default, Clone, PartialEq, Debug)]
+#[derive(Default, Clone, PartialEq, Eq, Debug)]
pub struct Annotations {
/// Whether this item is marked as opaque. Only applies to types.
opaque: bool,
diff --git a/src/ir/comp.rs b/src/ir/comp.rs
index a221e520..fdf6a963 100644
--- a/src/ir/comp.rs
+++ b/src/ir/comp.rs
@@ -20,7 +20,7 @@ use std::io;
use std::mem;
/// The kind of compound type.
-#[derive(Debug, Copy, Clone, PartialEq)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum CompKind {
/// A struct.
Struct,
@@ -29,7 +29,7 @@ pub enum CompKind {
}
/// The kind of C++ method.
-#[derive(Debug, Copy, Clone, PartialEq)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum MethodKind {
/// A constructor. We represent it as method for convenience, to avoid code
/// duplication.
@@ -55,12 +55,10 @@ pub enum MethodKind {
impl MethodKind {
/// Is this a destructor method?
pub fn is_destructor(&self) -> bool {
- match *self {
- MethodKind::Destructor | MethodKind::VirtualDestructor { .. } => {
- true
- }
- _ => false,
- }
+ matches!(
+ *self,
+ MethodKind::Destructor | MethodKind::VirtualDestructor { .. }
+ )
}
/// Is this a pure virtual method?
@@ -1045,6 +1043,11 @@ pub struct CompInfo {
/// size_t)
has_non_type_template_params: bool,
+ /// Whether this type has a bit field member whose width couldn't be
+ /// evaluated (e.g. if it depends on a template parameter). We generate an
+ /// opaque type in this case.
+ has_unevaluable_bit_field_width: bool,
+
/// Whether we saw `__attribute__((packed))` on or within this type.
packed_attr: bool,
@@ -1078,6 +1081,7 @@ impl CompInfo {
has_destructor: false,
has_nonempty_base: false,
has_non_type_template_params: false,
+ has_unevaluable_bit_field_width: false,
packed_attr: false,
found_unknown_attr: false,
is_forward_declaration: false,
@@ -1317,7 +1321,21 @@ impl CompInfo {
}
}
- let bit_width = cur.bit_width();
+ let bit_width = if cur.is_bit_field() {
+ let width = cur.bit_width();
+
+ // Make opaque type if the bit width couldn't be
+ // evaluated.
+ if width.is_none() {
+ ci.has_unevaluable_bit_field_width = true;
+ return CXChildVisit_Break;
+ }
+
+ width
+ } else {
+ None
+ };
+
let field_type = Item::from_ty_or_ref(
cur.cur_type(),
cur,
@@ -1753,7 +1771,9 @@ impl IsOpaque for CompInfo {
type Extra = Option<Layout>;
fn is_opaque(&self, ctx: &BindgenContext, layout: &Option<Layout>) -> bool {
- if self.has_non_type_template_params {
+ if self.has_non_type_template_params ||
+ self.has_unevaluable_bit_field_width
+ {
return true;
}
diff --git a/src/ir/context.rs b/src/ir/context.rs
index 3bfe549b..7837e594 100644
--- a/src/ir/context.rs
+++ b/src/ir/context.rs
@@ -459,17 +459,15 @@ pub struct BindgenContext {
/// Populated when we enter codegen by `compute_has_float`; always `None`
/// before that and `Some` after.
has_float: Option<HashSet<ItemId>>,
+
+ /// The set of warnings raised during binding generation.
+ warnings: Vec<String>,
}
/// A traversal of allowlisted items.
struct AllowlistedItemsTraversal<'ctx> {
ctx: &'ctx BindgenContext,
- traversal: ItemTraversal<
- 'ctx,
- ItemSet,
- Vec<ItemId>,
- for<'a> fn(&'a BindgenContext, Edge) -> bool,
- >,
+ traversal: ItemTraversal<'ctx, ItemSet, Vec<ItemId>>,
}
impl<'ctx> Iterator for AllowlistedItemsTraversal<'ctx> {
@@ -579,6 +577,7 @@ If you encounter an error missing from this list, please file an issue or a PR!"
have_destructor: None,
has_type_param_in_array: None,
has_float: None,
+ warnings: Vec::new(),
}
}
@@ -821,7 +820,7 @@ If you encounter an error missing from this list, please file an issue or a PR!"
name.contains('$') ||
matches!(
name,
- "abstract" | "alignof" | "as" | "async" | "become" |
+ "abstract" | "alignof" | "as" | "async" | "await" | "become" |
"box" | "break" | "const" | "continue" | "crate" | "do" |
"dyn" | "else" | "enum" | "extern" | "false" | "final" |
"fn" | "for" | "if" | "impl" | "in" | "let" | "loop" |
@@ -1134,7 +1133,10 @@ If you encounter an error missing from this list, please file an issue or a PR!"
/// Enter the code generation phase, invoke the given callback `cb`, and
/// leave the code generation phase.
- pub(crate) fn gen<F, Out>(mut self, cb: F) -> (Out, BindgenOptions)
+ pub(crate) fn gen<F, Out>(
+ mut self,
+ cb: F,
+ ) -> (Out, BindgenOptions, Vec<String>)
where
F: FnOnce(&Self) -> Out,
{
@@ -1171,7 +1173,7 @@ If you encounter an error missing from this list, please file an issue or a PR!"
self.compute_cannot_derive_partialord_partialeq_or_eq();
let ret = cb(&self);
- (ret, self.options)
+ (ret, self.options, self.warnings)
}
/// When the `testing_only_extra_assertions` feature is enabled, this
@@ -2430,16 +2432,24 @@ If you encounter an error missing from this list, please file an issue or a PR!"
self.allowlisted = Some(allowlisted);
self.codegen_items = Some(codegen_items);
+ let mut warnings = Vec::new();
+
for item in self.options().allowlisted_functions.unmatched_items() {
- warn!("unused option: --allowlist-function {}", item);
+ warnings
+ .push(format!("unused option: --allowlist-function {}", item));
}
for item in self.options().allowlisted_vars.unmatched_items() {
- warn!("unused option: --allowlist-var {}", item);
+ warnings.push(format!("unused option: --allowlist-var {}", item));
}
for item in self.options().allowlisted_types.unmatched_items() {
- warn!("unused option: --allowlist-type {}", item);
+ warnings.push(format!("unused option: --allowlist-type {}", item));
+ }
+
+ for msg in warnings {
+ warn!("{}", msg);
+ self.warnings.push(msg);
}
}
diff --git a/src/ir/function.rs b/src/ir/function.rs
index 288c049b..89905351 100644
--- a/src/ir/function.rs
+++ b/src/ir/function.rs
@@ -19,7 +19,7 @@ use std::io;
const RUST_DERIVE_FUNPTR_LIMIT: usize = 12;
/// What kind of a function are we looking at?
-#[derive(Debug, Copy, Clone, PartialEq)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum FunctionKind {
/// A plain, free function.
Function,
diff --git a/src/ir/layout.rs b/src/ir/layout.rs
index 6cf91131..6f450307 100644
--- a/src/ir/layout.rs
+++ b/src/ir/layout.rs
@@ -7,7 +7,7 @@ use crate::ir::context::BindgenContext;
use std::cmp;
/// A type that represents the struct layout of a type.
-#[derive(Debug, Clone, Copy, PartialEq)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Layout {
/// The size (in bytes) of this layout.
pub size: usize,
@@ -93,7 +93,7 @@ impl Layout {
}
/// When we are treating a type as opaque, it is just a blob with a `Layout`.
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Opaque(pub Layout);
impl Opaque {
diff --git a/src/ir/traversal.rs b/src/ir/traversal.rs
index 088e744a..f14483f2 100644
--- a/src/ir/traversal.rs
+++ b/src/ir/traversal.rs
@@ -179,17 +179,10 @@ pub enum EdgeKind {
/// A predicate to allow visiting only sub-sets of the whole IR graph by
/// excluding certain edges from being followed by the traversal.
-pub trait TraversalPredicate {
- /// Should the traversal follow this edge, and visit everything that is
- /// reachable through it?
- fn should_follow(&self, ctx: &BindgenContext, edge: Edge) -> bool;
-}
-
-impl TraversalPredicate for for<'a> fn(&'a BindgenContext, Edge) -> bool {
- fn should_follow(&self, ctx: &BindgenContext, edge: Edge) -> bool {
- (*self)(ctx, edge)
- }
-}
+///
+/// The predicate must return true if the traversal should follow this edge
+/// and visit everything that is reachable through it.
+pub type TraversalPredicate = for<'a> fn(&'a BindgenContext, Edge) -> bool;
/// A `TraversalPredicate` implementation that follows all edges, and therefore
/// traversals using this predicate will see the whole IR graph reachable from
@@ -378,11 +371,10 @@ pub trait Trace {
/// An graph traversal of the transitive closure of references between items.
///
/// See `BindgenContext::allowlisted_items` for more information.
-pub struct ItemTraversal<'ctx, Storage, Queue, Predicate>
+pub struct ItemTraversal<'ctx, Storage, Queue>
where
Storage: TraversalStorage<'ctx>,
Queue: TraversalQueue,
- Predicate: TraversalPredicate,
{
ctx: &'ctx BindgenContext,
@@ -393,25 +385,23 @@ where
queue: Queue,
/// The predicate that determines which edges this traversal will follow.
- predicate: Predicate,
+ predicate: TraversalPredicate,
/// The item we are currently traversing.
currently_traversing: Option<ItemId>,
}
-impl<'ctx, Storage, Queue, Predicate>
- ItemTraversal<'ctx, Storage, Queue, Predicate>
+impl<'ctx, Storage, Queue> ItemTraversal<'ctx, Storage, Queue>
where
Storage: TraversalStorage<'ctx>,
Queue: TraversalQueue,
- Predicate: TraversalPredicate,
{
/// Begin a new traversal, starting from the given roots.
pub fn new<R>(
ctx: &'ctx BindgenContext,
roots: R,
- predicate: Predicate,
- ) -> ItemTraversal<'ctx, Storage, Queue, Predicate>
+ predicate: TraversalPredicate,
+ ) -> ItemTraversal<'ctx, Storage, Queue>
where
R: IntoIterator<Item = ItemId>,
{
@@ -433,16 +423,14 @@ where
}
}
-impl<'ctx, Storage, Queue, Predicate> Tracer
- for ItemTraversal<'ctx, Storage, Queue, Predicate>
+impl<'ctx, Storage, Queue> Tracer for ItemTraversal<'ctx, Storage, Queue>
where
Storage: TraversalStorage<'ctx>,
Queue: TraversalQueue,
- Predicate: TraversalPredicate,
{
fn visit_kind(&mut self, item: ItemId, kind: EdgeKind) {
let edge = Edge::new(item, kind);
- if !self.predicate.should_follow(self.ctx, edge) {
+ if !(self.predicate)(self.ctx, edge) {
return;
}
@@ -454,12 +442,10 @@ where
}
}
-impl<'ctx, Storage, Queue, Predicate> Iterator
- for ItemTraversal<'ctx, Storage, Queue, Predicate>
+impl<'ctx, Storage, Queue> Iterator for ItemTraversal<'ctx, Storage, Queue>
where
Storage: TraversalStorage<'ctx>,
Queue: TraversalQueue,
- Predicate: TraversalPredicate,
{
type Item = ItemId;
@@ -488,21 +474,5 @@ where
///
/// See `BindgenContext::assert_no_dangling_item_traversal` for more
/// information.
-pub type AssertNoDanglingItemsTraversal<'ctx> = ItemTraversal<
- 'ctx,
- Paths<'ctx>,
- VecDeque<ItemId>,
- for<'a> fn(&'a BindgenContext, Edge) -> bool,
->;
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- #[allow(dead_code)]
- fn traversal_predicate_is_object_safe() {
- // This should compile only if TraversalPredicate is object safe.
- fn takes_by_trait_object(_: &dyn TraversalPredicate) {}
- }
-}
+pub type AssertNoDanglingItemsTraversal<'ctx> =
+ ItemTraversal<'ctx, Paths<'ctx>, VecDeque<ItemId>>;
diff --git a/src/ir/ty.rs b/src/ir/ty.rs
index d573408c..c85bc687 100644
--- a/src/ir/ty.rs
+++ b/src/ir/ty.rs
@@ -564,7 +564,7 @@ impl TemplateParameters for TypeKind {
}
/// The kind of float this type represents.
-#[derive(Debug, Copy, Clone, PartialEq)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum FloatKind {
/// A `float`.
Float,
@@ -1031,7 +1031,16 @@ impl Type {
CXType_ObjCObjectPointer |
CXType_MemberPointer |
CXType_Pointer => {
- let pointee = ty.pointee_type().unwrap();
+ let mut pointee = ty.pointee_type().unwrap();
+ if *ty != canonical_ty {
+ let canonical_pointee =
+ canonical_ty.pointee_type().unwrap();
+ // clang sometimes loses pointee constness here, see
+ // #2244.
+ if canonical_pointee.is_const() != pointee.is_const() {
+ pointee = canonical_pointee;
+ }
+ }
let inner =
Item::from_ty_or_ref(pointee, location, None, ctx);
TypeKind::Pointer(inner)
diff --git a/src/lib.rs b/src/lib.rs
index 3d09ab71..b90faba6 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -86,6 +86,7 @@ use std::{env, iter};
// Some convenient typedefs for a fast hash map and hash set.
type HashMap<K, V> = ::rustc_hash::FxHashMap<K, V>;
type HashSet<K> = ::rustc_hash::FxHashSet<K>;
+use quote::ToTokens;
pub(crate) use std::collections::hash_map::Entry;
/// Default prefix for the anon fields.
@@ -587,6 +588,10 @@ impl Builder {
output_vector.push("--vtable-generation".into());
}
+ if self.options.sort_semantically {
+ output_vector.push("--sort-semantically".into());
+ }
+
// Add clang arguments
output_vector.push("--".into());
@@ -1476,6 +1481,14 @@ impl Builder {
self
}
+ /// If true, enables the sorting of the output in a predefined manner
+ ///
+ /// TODO: Perhaps move the sorting order out into a config
+ pub fn sort_semantically(mut self, doit: bool) -> Self {
+ self.options.sort_semantically = doit;
+ self
+ }
+
/// Generate the Rust bindings using the options built up thus far.
pub fn generate(mut self) -> Result<Bindings, BindgenError> {
// Add any extra arguments from the environment to the clang command line.
@@ -2005,6 +2018,9 @@ struct BindgenOptions {
/// Emit vtable functions.
vtable_generation: bool,
+
+ /// Sort the code generation
+ sort_semantically: bool,
}
/// TODO(emilio): This is sort of a lie (see the error message that results from
@@ -2153,6 +2169,7 @@ impl Default for BindgenOptions {
c_naming: false,
force_explicit_padding: false,
vtable_generation: false,
+ sort_semantically: false,
}
}
}
@@ -2222,6 +2239,7 @@ impl std::error::Error for BindgenError {}
#[derive(Debug)]
pub struct Bindings {
options: BindgenOptions,
+ warnings: Vec<String>,
module: proc_macro2::TokenStream,
}
@@ -2435,10 +2453,65 @@ impl Bindings {
parse(&mut context)?;
}
- let (items, options) = codegen::codegen(context);
+ let (items, options, warnings) = codegen::codegen(context);
+
+ if options.sort_semantically {
+ let module_wrapped_tokens =
+ quote!(mod wrapper_for_sorting_hack { #( #items )* });
+
+ // This semantically sorting business is a hack, for now. This means that we are
+ // re-parsing already generated code using `syn` (as opposed to `quote`) because
+ // `syn` provides us more control over the elements.
+ // One caveat is that some of the items coming from `quote`d output might have
+ // multiple items within them. Hence, we have to wrap the incoming in a `mod`.
+ // The two `unwrap`s here are deliberate because
+ // The first one won't panic because we build the `mod` and know it is there
+ // The second one won't panic because we know original output has something in
+ // it already.
+ let mut syn_parsed_items =
+ syn::parse2::<syn::ItemMod>(module_wrapped_tokens)
+ .unwrap()
+ .content
+ .unwrap()
+ .1;
+
+ syn_parsed_items.sort_by_key(|item| match item {
+ syn::Item::Type(_) => 0,
+ syn::Item::Struct(_) => 1,
+ syn::Item::Const(_) => 2,
+ syn::Item::Fn(_) => 3,
+ syn::Item::Enum(_) => 4,
+ syn::Item::Union(_) => 5,
+ syn::Item::Static(_) => 6,
+ syn::Item::Trait(_) => 7,
+ syn::Item::TraitAlias(_) => 8,
+ syn::Item::Impl(_) => 9,
+ syn::Item::Mod(_) => 10,
+ syn::Item::Use(_) => 11,
+ syn::Item::Verbatim(_) => 12,
+ syn::Item::ExternCrate(_) => 13,
+ syn::Item::ForeignMod(_) => 14,
+ syn::Item::Macro(_) => 15,
+ syn::Item::Macro2(_) => 16,
+ _ => 18,
+ });
+
+ let synful_items = syn_parsed_items
+ .into_iter()
+ .map(|item| item.into_token_stream());
+
+ return Ok(Bindings {
+ options,
+ warnings,
+ module: quote! {
+ #( #synful_items )*
+ },
+ });
+ }
Ok(Bindings {
options,
+ warnings,
module: quote! {
#( #items )*
},
@@ -2583,6 +2656,23 @@ impl Bindings {
_ => Ok(Cow::Owned(source)),
}
}
+
+ /// Emit all the warning messages raised while generating the bindings in a build script.
+ ///
+ /// If you are using `bindgen` outside of a build script you should use [`Bindings::warnings`]
+ /// and handle the messages accordingly instead.
+ #[inline]
+ pub fn emit_warnings(&self) {
+ for message in &self.warnings {
+ println!("cargo:warning={}", message);
+ }
+ }
+
+ /// Return all the warning messages raised while generating the bindings.
+ #[inline]
+ pub fn warnings(&self) -> &[String] {
+ &self.warnings
+ }
}
impl std::fmt::Display for Bindings {
diff --git a/src/options.rs b/src/options.rs
index 4e1fb93f..83da21f4 100644
--- a/src/options.rs
+++ b/src/options.rs
@@ -55,21 +55,18 @@ where
bitfield flags.",
)
.value_name("regex")
- .takes_value(true)
.multiple_occurrences(true)
.number_of_values(1),
Arg::new("newtype-enum")
.long("newtype-enum")
.help("Mark any enum whose name matches <regex> as a newtype.")
.value_name("regex")
- .takes_value(true)
.multiple_occurrences(true)
.number_of_values(1),
Arg::new("rustified-enum")
.long("rustified-enum")
.help("Mark any enum whose name matches <regex> as a Rust enum.")
.value_name("regex")
- .takes_value(true)
.multiple_occurrences(true)
.number_of_values(1),
Arg::new("constified-enum")
@@ -79,7 +76,6 @@ where
constants.",
)
.value_name("regex")
- .takes_value(true)
.multiple_occurrences(true)
.number_of_values(1),
Arg::new("constified-enum-module")
@@ -89,7 +85,6 @@ where
constants.",
)
.value_name("regex")
- .takes_value(true)
.multiple_occurrences(true)
.number_of_values(1),
Arg::new("default-macro-constant-type")
@@ -117,7 +112,6 @@ where
normal type aliasing.",
)
.value_name("regex")
- .takes_value(true)
.multiple_occurrences(true)
.number_of_values(1),
Arg::new("new-type-alias")
@@ -127,7 +121,6 @@ where
a new type generated for it.",
)
.value_name("regex")
- .takes_value(true)
.multiple_occurrences(true)
.number_of_values(1),
Arg::new("new-type-alias-deref")
@@ -137,7 +130,6 @@ where
a new type with Deref and DerefMut to the inner type.",
)
.value_name("regex")
- .takes_value(true)
.multiple_occurrences(true)
.number_of_values(1),
Arg::new("blocklist-type")
@@ -145,7 +137,6 @@ where
.long("blocklist-type")
.help("Mark <type> as hidden.")
.value_name("type")
- .takes_value(true)
.multiple_occurrences(true)
.number_of_values(1),
Arg::new("blocklist-function")
@@ -153,7 +144,6 @@ where
.long("blocklist-function")
.help("Mark <function> as hidden.")
.value_name("function")
- .takes_value(true)
.multiple_occurrences(true)
.number_of_values(1),
Arg::new("blocklist-item")
@@ -161,7 +151,6 @@ where
.long("blocklist-item")
.help("Mark <item> as hidden.")
.value_name("item")
- .takes_value(true)
.multiple_occurrences(true)
.number_of_values(1),
Arg::new("blocklist-file")
@@ -169,7 +158,6 @@ where
.long("blocklist-file")
.help("Mark all contents of <path> as hidden.")
.value_name("path")
- .takes_value(true)
.multiple_occurrences(true)
.number_of_values(1),
Arg::new("no-layout-tests")
@@ -256,14 +244,12 @@ where
"Use the given prefix before raw types instead of \
::std::os::raw.",
)
- .value_name("prefix")
- .takes_value(true),
+ .value_name("prefix"),
Arg::new("anon-fields-prefix")
.long("anon-fields-prefix")
.help("Use the given prefix for the anon fields.")
.value_name("prefix")
- .default_value(DEFAULT_ANON_FIELDS_PREFIX)
- .takes_value(true),
+ .default_value(DEFAULT_ANON_FIELDS_PREFIX),
Arg::new("time-phases")
.long("time-phases")
.help("Time the different bindgen phases and print to stderr"),
@@ -278,8 +264,7 @@ where
Arg::new("emit-ir-graphviz")
.long("emit-ir-graphviz")
.help("Dump graphviz dot file.")
- .value_name("path")
- .takes_value(true),
+ .value_name("path"),
Arg::new("enable-cxx-namespaces")
.long("enable-cxx-namespaces")
.help("Enable support for C++ namespaces."),
@@ -343,7 +328,6 @@ where
.long("opaque-type")
.help("Mark <type> as opaque.")
.value_name("type")
- .takes_value(true)
.multiple_occurrences(true)
.number_of_values(1),
Arg::new("output")
@@ -354,13 +338,11 @@ where
Arg::new("raw-line")
.long("raw-line")
.help("Add a raw line of Rust code at the beginning of output.")
- .takes_value(true)
.multiple_occurrences(true)
.number_of_values(1),
Arg::new("module-raw-line")
.long("module-raw-line")
.help("Add a raw line of Rust code to a given module.")
- .takes_value(true)
.multiple_occurrences(true)
.number_of_values(2)
.value_names(&["module-name", "raw-line"]),
@@ -389,7 +371,6 @@ where
generated.",
)
.value_name("regex")
- .takes_value(true)
.multiple_occurrences(true)
.number_of_values(1),
Arg::new("generate-inline-functions")
@@ -403,7 +384,6 @@ where
not be generated.",
)
.value_name("regex")
- .takes_value(true)
.multiple_occurrences(true)
.number_of_values(1),
Arg::new("allowlist-var")
@@ -415,7 +395,6 @@ where
generated.",
)
.value_name("regex")
- .takes_value(true)
.multiple_occurrences(true)
.number_of_values(1),
Arg::new("allowlist-file")
@@ -423,7 +402,6 @@ where
.long("allowlist-file")
.help("Allowlist all contents of <path>.")
.value_name("path")
- .takes_value(true)
.multiple_occurrences(true)
.number_of_values(1),
Arg::new("verbose")
@@ -464,49 +442,42 @@ where
This parameter is incompatible with --no-rustfmt-bindings.",
)
.value_name("path")
- .takes_value(true)
.multiple_occurrences(false)
.number_of_values(1),
Arg::new("no-partialeq")
.long("no-partialeq")
.help("Avoid deriving PartialEq for types matching <regex>.")
.value_name("regex")
- .takes_value(true)
.multiple_occurrences(true)
.number_of_values(1),
Arg::new("no-copy")
.long("no-copy")
.help("Avoid deriving Copy for types matching <regex>.")
.value_name("regex")
- .takes_value(true)
.multiple_occurrences(true)
.number_of_values(1),
Arg::new("no-debug")
.long("no-debug")
.help("Avoid deriving Debug for types matching <regex>.")
.value_name("regex")
- .takes_value(true)
.multiple_occurrences(true)
.number_of_values(1),
Arg::new("no-default")
.long("no-default")
.help("Avoid deriving/implement Default for types matching <regex>.")
.value_name("regex")
- .takes_value(true)
.multiple_occurrences(true)
.number_of_values(1),
Arg::new("no-hash")
.long("no-hash")
.help("Avoid deriving Hash for types matching <regex>.")
.value_name("regex")
- .takes_value(true)
.multiple_occurrences(true)
.number_of_values(1),
Arg::new("must-use-type")
.long("must-use-type")
.help("Add #[must_use] annotation to types matching <regex>.")
.value_name("regex")
- .takes_value(true)
.multiple_occurrences(true)
.number_of_values(1),
Arg::new("enable-function-attribute-detection")
@@ -521,7 +492,6 @@ where
Arg::new("wasm-import-module-name")
.long("wasm-import-module-name")
.value_name("name")
- .takes_value(true)
.help("The name to be used in a #[link(wasm_import_module = ...)] statement"),
Arg::new("dynamic-loading")
.long("dynamic-loading")
@@ -545,6 +515,9 @@ where
Arg::new("vtable-generation")
.long("vtable-generation")
.help("Enables generation of vtable functions."),
+ Arg::new("sort-semantically")
+ .long("sort-semantically")
+ .help("Enables sorting of code generation in a predefined manner."),
Arg::new("V")
.long("version")
.help("Prints the version, and exits"),
@@ -1030,5 +1003,9 @@ where
builder = builder.vtable_generation(true);
}
+ if matches.is_present("sort-semantically") {
+ builder = builder.sort_semantically(true);
+ }
+
Ok((builder, output, verbose))
}