diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/clang.rs | 96 | ||||
-rw-r--r-- | src/codegen/mod.rs | 52 | ||||
-rw-r--r-- | src/ir/analysis/derive.rs | 10 | ||||
-rw-r--r-- | src/ir/annotations.rs | 4 | ||||
-rw-r--r-- | src/ir/comp.rs | 40 | ||||
-rw-r--r-- | src/ir/context.rs | 34 | ||||
-rw-r--r-- | src/ir/function.rs | 2 | ||||
-rw-r--r-- | src/ir/layout.rs | 4 | ||||
-rw-r--r-- | src/ir/traversal.rs | 58 | ||||
-rw-r--r-- | src/ir/ty.rs | 13 | ||||
-rw-r--r-- | src/lib.rs | 92 | ||||
-rw-r--r-- | src/options.rs | 43 |
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) @@ -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)) } |